2016-08-15 00:12:50 +00:00
|
|
|
#pragma once
|
|
|
|
#include "stdafx.h"
|
2018-02-25 16:27:32 +00:00
|
|
|
#include <unordered_map>
|
2017-04-01 22:02:12 +00:00
|
|
|
#include "../Utilities/CRC32.h"
|
2017-04-24 22:28:50 +00:00
|
|
|
#include "../Utilities/md5.h"
|
2017-04-04 03:45:24 +00:00
|
|
|
#include "../Utilities/HexUtilities.h"
|
2016-08-15 00:12:50 +00:00
|
|
|
#include "RomData.h"
|
|
|
|
#include "GameDatabase.h"
|
2016-11-10 21:57:19 +00:00
|
|
|
#include "UnifBoards.h"
|
2017-04-01 22:02:12 +00:00
|
|
|
#include "EmulationSettings.h"
|
2018-02-25 16:27:32 +00:00
|
|
|
#include "BaseLoader.h"
|
2016-08-15 00:12:50 +00:00
|
|
|
|
2018-02-25 16:27:32 +00:00
|
|
|
class UnifLoader : public BaseLoader
|
2016-08-15 00:12:50 +00:00
|
|
|
{
|
|
|
|
private:
|
2017-04-01 22:02:12 +00:00
|
|
|
static std::unordered_map<string, int> _boardMappings;
|
2016-08-15 00:12:50 +00:00
|
|
|
|
|
|
|
vector<uint8_t> _prgChunks[16];
|
|
|
|
vector<uint8_t> _chrChunks[16];
|
|
|
|
string _mapperName;
|
|
|
|
|
|
|
|
void Read(uint8_t* &data, uint8_t& dest)
|
|
|
|
{
|
|
|
|
dest = data[0];
|
|
|
|
data++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Read(uint8_t* &data, uint32_t& dest)
|
|
|
|
{
|
|
|
|
dest = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
|
|
|
|
data += 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Read(uint8_t* &data, uint8_t* dest, size_t len)
|
|
|
|
{
|
|
|
|
memcpy(dest, data, len);
|
|
|
|
data += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
string ReadString(uint8_t* &data, uint8_t* chunkEnd)
|
|
|
|
{
|
|
|
|
stringstream ss;
|
|
|
|
while(data < chunkEnd) {
|
|
|
|
if(data[0] == 0) {
|
|
|
|
//end of string
|
|
|
|
data = chunkEnd;
|
|
|
|
break;
|
|
|
|
} else {
|
2018-07-08 19:10:52 +00:00
|
|
|
if(data[0] != ' ') {
|
|
|
|
//Ignore spaces
|
|
|
|
ss << (char)data[0];
|
|
|
|
}
|
2016-08-15 00:12:50 +00:00
|
|
|
}
|
|
|
|
data++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
string ReadFourCC(uint8_t* &data)
|
|
|
|
{
|
|
|
|
stringstream ss;
|
|
|
|
for(int i = 0; i < 4; i++) {
|
|
|
|
ss << (char)data[i];
|
|
|
|
}
|
|
|
|
data += 4;
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ReadChunk(uint8_t* &data, uint8_t* dataEnd, RomData& romData)
|
|
|
|
{
|
|
|
|
if(data + 8 > dataEnd) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
string fourCC = ReadFourCC(data);
|
|
|
|
|
|
|
|
uint32_t length;
|
|
|
|
Read(data, length);
|
|
|
|
|
|
|
|
uint8_t* chunkEnd = data + length;
|
|
|
|
if(chunkEnd > dataEnd) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(fourCC.compare("MAPR") == 0) {
|
|
|
|
_mapperName = ReadString(data, chunkEnd);
|
|
|
|
if(_mapperName.size() > 0) {
|
2023-05-13 01:37:50 +00:00
|
|
|
romData.Info.BoardName = _mapperName;
|
2018-07-07 18:52:51 +00:00
|
|
|
romData.Info.MapperID = GetMapperID(_mapperName);
|
|
|
|
if(romData.Info.MapperID == UnifBoards::UnknownBoard) {
|
2018-02-25 16:27:32 +00:00
|
|
|
Log("[UNIF] Error: Unknown board");
|
2017-05-22 18:25:33 +00:00
|
|
|
}
|
2016-08-15 00:12:50 +00:00
|
|
|
} else {
|
|
|
|
romData.Error = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if(fourCC.substr(0, 3).compare("PRG") == 0) {
|
|
|
|
uint32_t chunkNumber;
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << std::hex << fourCC[3];
|
|
|
|
ss >> chunkNumber;
|
|
|
|
|
|
|
|
_prgChunks[chunkNumber].resize(length);
|
|
|
|
Read(data, _prgChunks[chunkNumber].data(), length);
|
|
|
|
} else if(fourCC.substr(0, 3).compare("CHR") == 0) {
|
|
|
|
uint32_t chunkNumber;
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << std::hex << fourCC[3];
|
|
|
|
ss >> chunkNumber;
|
|
|
|
|
|
|
|
_chrChunks[chunkNumber].resize(length);
|
|
|
|
Read(data, _chrChunks[chunkNumber].data(), length);
|
|
|
|
} else if(fourCC.compare("TVCI") == 0) {
|
|
|
|
uint8_t value;
|
|
|
|
Read(data, value);
|
2018-07-07 18:52:51 +00:00
|
|
|
romData.Info.System = value == 1 ? GameSystem::NesPal : GameSystem::NesNtsc;
|
2016-08-15 00:12:50 +00:00
|
|
|
} else if(fourCC.compare("CTRL") == 0) {
|
|
|
|
//not supported
|
|
|
|
} else if(fourCC.compare("BATR") == 0) {
|
|
|
|
uint8_t value;
|
|
|
|
Read(data, value);
|
2018-07-07 18:52:51 +00:00
|
|
|
romData.Info.HasBattery = value > 0;
|
2016-08-15 00:12:50 +00:00
|
|
|
} else if(fourCC.compare("MIRR") == 0) {
|
|
|
|
uint8_t value;
|
|
|
|
Read(data, value);
|
|
|
|
|
|
|
|
switch(value) {
|
|
|
|
default:
|
2018-07-07 18:52:51 +00:00
|
|
|
case 0: romData.Info.Mirroring = MirroringType::Horizontal; break;
|
|
|
|
case 1: romData.Info.Mirroring = MirroringType::Vertical; break;
|
|
|
|
case 2: romData.Info.Mirroring = MirroringType::ScreenAOnly; break;
|
|
|
|
case 3: romData.Info.Mirroring = MirroringType::ScreenBOnly; break;
|
|
|
|
case 4: romData.Info.Mirroring = MirroringType::FourScreens; break;
|
2016-08-15 00:12:50 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//Unsupported/unused FourCCs: PCKn, CCKn, NAME, WRTR, READ, DINF, VROR
|
|
|
|
}
|
|
|
|
|
|
|
|
data = chunkEnd;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-01 22:02:12 +00:00
|
|
|
public:
|
2018-02-25 16:27:32 +00:00
|
|
|
using BaseLoader::BaseLoader;
|
|
|
|
|
2017-04-01 22:02:12 +00:00
|
|
|
static int32_t GetMapperID(string mapperName)
|
2016-08-15 00:12:50 +00:00
|
|
|
{
|
|
|
|
string prefix = mapperName.substr(0, 4);
|
|
|
|
if(prefix.compare("NES-") == 0 || prefix.compare("UNL-") == 0 || prefix.compare("HVC-") == 0 || prefix.compare("BTL-") == 0 || prefix.compare("BMC-") == 0) {
|
|
|
|
mapperName = mapperName.substr(4);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto result = _boardMappings.find(mapperName);
|
|
|
|
if(result != _boardMappings.end()) {
|
|
|
|
return result->second;
|
|
|
|
}
|
|
|
|
|
2016-11-10 21:57:19 +00:00
|
|
|
return UnifBoards::UnknownBoard;
|
2016-08-15 00:12:50 +00:00
|
|
|
}
|
|
|
|
|
2020-01-01 01:23:26 +00:00
|
|
|
void LoadRom(RomData &romData, vector<uint8_t>& romFile)
|
2016-08-15 00:12:50 +00:00
|
|
|
{
|
|
|
|
//Skip header, version & null bytes, start reading at first chunk
|
|
|
|
uint8_t* data = romFile.data() + 32;
|
|
|
|
uint8_t* endOfFile = romFile.data() + romFile.size();
|
|
|
|
|
|
|
|
while(ReadChunk(data, endOfFile, romData)) {
|
|
|
|
//Read all chunks
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int i = 0; i < 16; i++) {
|
|
|
|
romData.PrgRom.insert(romData.PrgRom.end(), _prgChunks[i].begin(), _prgChunks[i].end());
|
|
|
|
romData.ChrRom.insert(romData.ChrRom.end(), _chrChunks[i].begin(), _chrChunks[i].end());
|
|
|
|
}
|
|
|
|
|
|
|
|
if(romData.PrgRom.size() == 0 || _mapperName.empty()) {
|
|
|
|
romData.Error = true;
|
|
|
|
} else {
|
|
|
|
vector<uint8_t> fullRom;
|
|
|
|
fullRom.insert(fullRom.end(), romData.PrgRom.begin(), romData.PrgRom.end());
|
|
|
|
fullRom.insert(fullRom.end(), romData.ChrRom.begin(), romData.ChrRom.end());
|
|
|
|
|
2018-07-07 18:52:51 +00:00
|
|
|
romData.Info.Format = RomFormat::Unif;
|
|
|
|
romData.Info.Hash.PrgCrc32 = CRC32::GetCRC(romData.PrgRom.data(), romData.PrgRom.size());
|
|
|
|
romData.Info.Hash.PrgChrCrc32 = CRC32::GetCRC(fullRom.data(), fullRom.size());
|
|
|
|
romData.Info.Hash.PrgChrMd5 = GetMd5Sum(fullRom.data(), fullRom.size());
|
2016-08-15 00:12:50 +00:00
|
|
|
|
2023-05-13 01:37:50 +00:00
|
|
|
Log("PRG CRC32: 0x" + HexUtilities::ToHex(romData.Info.Hash.PrgCrc32, true));
|
2018-07-07 18:52:51 +00:00
|
|
|
Log("PRG+CHR CRC32: 0x" + HexUtilities::ToHex(romData.Info.Hash.PrgChrCrc32));
|
2018-02-25 16:27:32 +00:00
|
|
|
Log("[UNIF] Board Name: " + _mapperName);
|
|
|
|
Log("[UNIF] PRG ROM: " + std::to_string(romData.PrgRom.size() / 1024) + " KB");
|
|
|
|
Log("[UNIF] CHR ROM: " + std::to_string(romData.ChrRom.size() / 1024) + " KB");
|
2016-08-15 00:12:50 +00:00
|
|
|
if(romData.ChrRom.size() == 0) {
|
2018-02-25 16:27:32 +00:00
|
|
|
Log("[UNIF] CHR RAM: 8 KB");
|
2016-08-15 00:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
string mirroringType;
|
2018-07-07 18:52:51 +00:00
|
|
|
switch(romData.Info.Mirroring) {
|
2016-08-15 00:12:50 +00:00
|
|
|
case MirroringType::Horizontal: mirroringType = "Horizontal"; break;
|
|
|
|
case MirroringType::Vertical: mirroringType = "Vertical"; break;
|
|
|
|
case MirroringType::ScreenAOnly: mirroringType = "1-Screen (A)"; break;
|
|
|
|
case MirroringType::ScreenBOnly: mirroringType = "1-Screen (B)"; break;
|
|
|
|
case MirroringType::FourScreens: mirroringType = "Four Screens"; break;
|
|
|
|
}
|
|
|
|
|
2018-02-25 16:27:32 +00:00
|
|
|
Log("[UNIF] Mirroring: " + mirroringType);
|
2018-07-07 18:52:51 +00:00
|
|
|
Log("[UNIF] Battery: " + string(romData.Info.HasBattery ? "Yes" : "No"));
|
2016-08-15 00:12:50 +00:00
|
|
|
|
2018-02-25 16:27:32 +00:00
|
|
|
if(!_checkOnly) {
|
2018-07-14 02:19:26 +00:00
|
|
|
GameDatabase::SetGameInfo(romData.Info.Hash.PrgChrCrc32, romData, GameDatabase::IsEnabled(), false);
|
2018-02-25 16:27:32 +00:00
|
|
|
}
|
2016-08-15 00:12:50 +00:00
|
|
|
|
2023-05-13 01:37:50 +00:00
|
|
|
if(romData.Info.MapperID == UnifBoards::UnknownBoard) {
|
2016-08-15 00:12:50 +00:00
|
|
|
romData.Error = true;
|
2023-05-13 01:37:50 +00:00
|
|
|
}
|
2016-08-15 00:12:50 +00:00
|
|
|
}
|
|
|
|
}
|
2023-05-13 01:37:50 +00:00
|
|
|
};
|