2016-06-15 21:59:34 -04:00
|
|
|
#include "stdafx.h"
|
|
|
|
#include "RomData.h"
|
|
|
|
#include "MessageManager.h"
|
|
|
|
#include "../Utilities/CRC32.h"
|
2016-08-27 13:29:02 -04:00
|
|
|
#include "../Utilities/FolderUtilities.h"
|
2017-04-01 15:47:43 -04:00
|
|
|
#include "../Utilities/StringUtilities.h"
|
2017-04-03 23:45:24 -04:00
|
|
|
#include "../Utilities/HexUtilities.h"
|
2016-06-15 21:59:34 -04:00
|
|
|
#include "GameDatabase.h"
|
2016-07-09 15:58:49 -04:00
|
|
|
#include "EmulationSettings.h"
|
2017-04-01 18:02:12 -04:00
|
|
|
#include "UnifLoader.h"
|
2016-06-15 21:59:34 -04:00
|
|
|
|
|
|
|
std::unordered_map<uint32_t, GameInfo> GameDatabase::_gameDatabase;
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
T GameDatabase::ToInt(string value)
|
|
|
|
{
|
|
|
|
if(value.empty()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return std::stoi(value);
|
|
|
|
}
|
|
|
|
|
2018-01-04 19:03:47 -05:00
|
|
|
void GameDatabase::LoadGameDb(vector<string> data)
|
|
|
|
{
|
|
|
|
for(string &row : data) {
|
|
|
|
vector<string> values = StringUtilities::Split(row, ',');
|
|
|
|
if(values.size() >= 16) {
|
2018-07-07 14:52:51 -04:00
|
|
|
GameInfo gameInfo;
|
|
|
|
gameInfo.Crc = (uint32_t)std::stoll(values[0], nullptr, 16);
|
|
|
|
gameInfo.System = values[1];
|
|
|
|
gameInfo.Board = values[2];
|
|
|
|
gameInfo.Pcb = values[3];
|
|
|
|
gameInfo.Chip = values[4];
|
|
|
|
gameInfo.MapperID = (uint16_t)ToInt<uint32_t>(values[5]);
|
|
|
|
gameInfo.PrgRomSize = ToInt<uint32_t>(values[6]);
|
|
|
|
gameInfo.ChrRomSize = ToInt<uint32_t>(values[7]);
|
|
|
|
gameInfo.ChrRamSize = ToInt<uint32_t>(values[8]);
|
|
|
|
gameInfo.WorkRamSize = ToInt<uint32_t>(values[9]);
|
|
|
|
gameInfo.SaveRamSize = ToInt<uint32_t>(values[10]);
|
|
|
|
gameInfo.HasBattery = ToInt<uint32_t>(values[11]) == 0 ? false : true;
|
|
|
|
gameInfo.Mirroring = values[12];
|
|
|
|
gameInfo.InputType = values[13];
|
|
|
|
gameInfo.BusConflicts = values[14];
|
|
|
|
gameInfo.SubmapperID = values[15];
|
|
|
|
gameInfo.VsSystemType = values[16];
|
|
|
|
gameInfo.PpuModel = values[17];
|
2018-01-04 19:03:47 -05:00
|
|
|
|
|
|
|
if(gameInfo.MapperID == 65000) {
|
|
|
|
gameInfo.MapperID = UnifLoader::GetMapperID(gameInfo.Board);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!gameInfo.InputType.empty() && gameInfo.InputType[gameInfo.InputType.size() - 1] == '\r') {
|
|
|
|
gameInfo.InputType = gameInfo.InputType.substr(0, gameInfo.InputType.size() - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
_gameDatabase[gameInfo.Crc] = gameInfo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MessageManager::Log();
|
|
|
|
MessageManager::Log("[DB] Initialized - " + std::to_string(_gameDatabase.size()) + " games in DB");
|
|
|
|
}
|
|
|
|
|
2016-06-15 21:59:34 -04:00
|
|
|
void GameDatabase::InitDatabase()
|
|
|
|
{
|
|
|
|
if(_gameDatabase.size() == 0) {
|
2016-08-27 13:29:02 -04:00
|
|
|
string dbPath = FolderUtilities::CombinePath(FolderUtilities::GetHomeFolder(), "MesenDB.txt");
|
|
|
|
ifstream db(dbPath, ios::in | ios::binary);
|
2018-01-04 19:03:47 -05:00
|
|
|
vector<string> dbData;
|
2016-06-15 21:59:34 -04:00
|
|
|
while(db.good()) {
|
|
|
|
string lineContent;
|
|
|
|
std::getline(db, lineContent);
|
2017-11-19 23:08:23 -05:00
|
|
|
if(lineContent[lineContent.size() - 1] == '\r') {
|
|
|
|
lineContent = lineContent.substr(0, lineContent.size() - 1);
|
|
|
|
}
|
2016-06-15 21:59:34 -04:00
|
|
|
if(lineContent.empty() || lineContent[0] == '#') {
|
|
|
|
continue;
|
|
|
|
}
|
2018-01-04 19:03:47 -05:00
|
|
|
dbData.push_back(lineContent);
|
2016-06-15 21:59:34 -04:00
|
|
|
}
|
2018-01-04 19:03:47 -05:00
|
|
|
LoadGameDb(dbData);
|
2016-06-15 21:59:34 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-01 15:47:43 -04:00
|
|
|
BusConflictType GameDatabase::GetBusConflictType(string busConflictSetting)
|
|
|
|
{
|
|
|
|
if(busConflictSetting.compare("Y") == 0) {
|
|
|
|
return BusConflictType::Yes;
|
|
|
|
} else if(busConflictSetting.compare("N") == 0) {
|
|
|
|
return BusConflictType::No;
|
|
|
|
}
|
|
|
|
return BusConflictType::Default;
|
|
|
|
}
|
|
|
|
|
2016-06-15 21:59:34 -04:00
|
|
|
GameSystem GameDatabase::GetGameSystem(string system)
|
|
|
|
{
|
|
|
|
if(system.compare("NesNtsc") == 0) {
|
|
|
|
return GameSystem::NesNtsc;
|
|
|
|
} else if(system.compare("NesPal") == 0) {
|
|
|
|
return GameSystem::NesPal;
|
|
|
|
} else if(system.compare("Famicom") == 0) {
|
|
|
|
return GameSystem::Famicom;
|
2018-07-07 14:52:51 -04:00
|
|
|
} else if(system.compare("VsSystem") == 0) {
|
|
|
|
return GameSystem::VsSystem;
|
2016-06-15 21:59:34 -04:00
|
|
|
} else if(system.compare("Dendy") == 0) {
|
|
|
|
return GameSystem::Dendy;
|
|
|
|
} else if(system.compare("Playchoice") == 0) {
|
|
|
|
return GameSystem::Playchoice;
|
|
|
|
}
|
|
|
|
|
|
|
|
return GameSystem::NesNtsc;
|
|
|
|
}
|
|
|
|
|
2018-07-07 14:52:51 -04:00
|
|
|
VsSystemType GameDatabase::GetVsSystemType(string system)
|
|
|
|
{
|
|
|
|
if(system.compare("IceClimber") == 0) {
|
|
|
|
return VsSystemType::IceClimberProtection;
|
|
|
|
} else if(system.compare("RaidOnBungelingBay") == 0) {
|
|
|
|
return VsSystemType::RaidOnBungelingBayProtection;
|
|
|
|
} else if(system.compare("RbiBaseball") == 0) {
|
|
|
|
return VsSystemType::RbiBaseballProtection;
|
|
|
|
} else if(system.compare("SuperXevious") == 0) {
|
|
|
|
return VsSystemType::SuperXeviousProtection;
|
|
|
|
} else if(system.compare("TkoBoxing") == 0) {
|
|
|
|
return VsSystemType::TkoBoxingProtection;
|
|
|
|
} else if(system.compare("VsDualSystem") == 0) {
|
|
|
|
return VsSystemType::VsDualSystem;
|
|
|
|
}
|
|
|
|
|
|
|
|
return VsSystemType::Default;
|
|
|
|
}
|
|
|
|
|
|
|
|
PpuModel GameDatabase::GetPpuModel(string model)
|
|
|
|
{
|
|
|
|
if(model.compare("RP2C04-0001") == 0) {
|
|
|
|
return PpuModel::Ppu2C04A;
|
|
|
|
} else if(model.compare("RP2C04-0002") == 0) {
|
|
|
|
return PpuModel::Ppu2C04B;
|
|
|
|
} else if(model.compare("RP2C04-0003") == 0) {
|
|
|
|
return PpuModel::Ppu2C04C;
|
|
|
|
} else if(model.compare("RP2C04-0004") == 0) {
|
|
|
|
return PpuModel::Ppu2C04D;
|
|
|
|
} else if(model.compare("RC2C05-01") == 0) {
|
|
|
|
return PpuModel::Ppu2C05A;
|
|
|
|
} else if(model.compare("RC2C05-02") == 0) {
|
|
|
|
return PpuModel::Ppu2C05B;
|
|
|
|
} else if(model.compare("RC2C05-03") == 0) {
|
|
|
|
return PpuModel::Ppu2C05C;
|
|
|
|
} else if(model.compare("RC2C05-04") == 0) {
|
|
|
|
return PpuModel::Ppu2C05D;
|
|
|
|
} else if(model.compare("RC2C05-05") == 0) {
|
|
|
|
return PpuModel::Ppu2C05E;
|
|
|
|
} else if(model.compare("RP2C03B") == 0 || model.compare("RP2C03G") == 0) {
|
|
|
|
return PpuModel::Ppu2C03;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PpuModel::Ppu2C02;
|
|
|
|
}
|
|
|
|
|
2018-07-07 17:30:13 -04:00
|
|
|
GameInputType GameDatabase::GetInputType(GameSystem system, string inputType)
|
|
|
|
{
|
|
|
|
bool isVsSystem = system == GameSystem::VsSystem;
|
|
|
|
bool isFamicom = (system == GameSystem::Famicom || system == GameSystem::FDS || system == GameSystem::Dendy);
|
|
|
|
|
|
|
|
if(inputType.compare("Zapper") == 0) {
|
|
|
|
if(isVsSystem) {
|
|
|
|
return GameInputType::VsZapper;
|
|
|
|
} else {
|
|
|
|
return GameInputType::Zapper;
|
|
|
|
}
|
|
|
|
} else if(inputType.compare("FourPlayer") == 0) {
|
|
|
|
if(isFamicom) {
|
|
|
|
return GameInputType::FourPlayerAdapter;
|
|
|
|
} else {
|
|
|
|
return GameInputType::FourScore;
|
|
|
|
}
|
|
|
|
} else if(inputType.compare("Arkanoid") == 0) {
|
|
|
|
if(isFamicom) {
|
|
|
|
return GameInputType::ArkanoidControllerFamicom;
|
|
|
|
} else {
|
|
|
|
return GameInputType::ArkanoidControllerNes;
|
|
|
|
}
|
|
|
|
} else if(inputType.compare("OekaKidsTablet") == 0) {
|
|
|
|
return GameInputType::OekaKidsTablet;
|
|
|
|
} else if(inputType.compare("KonamiHypershot") == 0) {
|
|
|
|
return GameInputType::KonamiHyperShot;
|
|
|
|
} else if(inputType.compare("FamilyKeyboard") == 0) {
|
|
|
|
return GameInputType::FamilyBasicKeyboard;
|
|
|
|
} else if(inputType.compare("PartyTap") == 0) {
|
|
|
|
return GameInputType::PartyTap;
|
|
|
|
} else if(inputType.compare("Pachinko") == 0) {
|
|
|
|
return GameInputType::PachinkoController;
|
|
|
|
} else if(inputType.compare("ExcitingBoxing") == 0) {
|
|
|
|
return GameInputType::ExcitingBoxing;
|
|
|
|
} else if(inputType.compare("SuborKeyboard") == 0) {
|
|
|
|
return GameInputType::SuborKeyboardMouse1;
|
|
|
|
} else if(inputType.compare("Mahjong") == 0) {
|
|
|
|
return GameInputType::JissenMahjong;
|
|
|
|
} else if(inputType.compare("BarCodeWorld") == 0) {
|
|
|
|
return GameInputType::BarcodeBattler;
|
|
|
|
} else if(inputType.compare("BandaiHypershot") == 0) {
|
|
|
|
return GameInputType::BandaiHypershot;
|
|
|
|
} else if(inputType.compare("BattleBox") == 0) {
|
|
|
|
return GameInputType::BattleBox;
|
|
|
|
} else if(inputType.compare("TurboFile") == 0) {
|
|
|
|
return GameInputType::TurboFile;
|
|
|
|
} else if(inputType.compare("FamilyTrainer") == 0) {
|
|
|
|
return GameInputType::FamilyTrainerSideA;
|
|
|
|
} else if(inputType.compare("PowerPad") == 0 || inputType.compare("FamilyFunFitness") == 0) {
|
|
|
|
return GameInputType::PowerPadSideA;
|
|
|
|
} else if(inputType.compare("VsSwapped") == 0) {
|
|
|
|
return GameInputType::VsSystemSwapped;
|
|
|
|
} else if(inputType.compare("VsSwapAB") == 0) {
|
|
|
|
return GameInputType::VsSystemSwapAB;
|
|
|
|
} else {
|
|
|
|
return GameInputType::Default;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-09 23:15:08 -05:00
|
|
|
void GameDatabase::InitializeInputDevices(uint32_t romCrc)
|
|
|
|
{
|
|
|
|
InitDatabase();
|
|
|
|
|
|
|
|
auto result = _gameDatabase.find(romCrc);
|
|
|
|
if(result != _gameDatabase.end()) {
|
2018-07-07 17:30:13 -04:00
|
|
|
GameSystem system = GetGameSystem(result->second.System);
|
|
|
|
InitializeInputDevices(GetInputType(system, result->second.InputType), system, true);
|
2018-01-09 23:15:08 -05:00
|
|
|
} else {
|
2018-07-07 17:30:13 -04:00
|
|
|
InitializeInputDevices(GameInputType::Default, GameSystem::NesNtsc, true);
|
2018-01-09 23:15:08 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-07 17:30:13 -04:00
|
|
|
void GameDatabase::InitializeInputDevices(GameInputType inputType, GameSystem system, bool silent)
|
2016-07-09 15:58:49 -04:00
|
|
|
{
|
2018-01-09 23:15:08 -05:00
|
|
|
ControllerType controllers[4] = { ControllerType::StandardController, ControllerType::StandardController, ControllerType::None, ControllerType::None };
|
|
|
|
ExpansionPortDevice expDevice = ExpansionPortDevice::None;
|
|
|
|
EmulationSettings::ClearFlags(EmulationFlags::HasFourScore);
|
2018-01-11 17:01:27 -05:00
|
|
|
|
|
|
|
auto log = [silent](string text) {
|
|
|
|
if(!silent) {
|
|
|
|
MessageManager::Log(text);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-01-09 23:15:08 -05:00
|
|
|
bool isFamicom = (system == GameSystem::Famicom || system == GameSystem::FDS || system == GameSystem::Dendy);
|
2016-09-10 09:43:01 -04:00
|
|
|
|
2018-07-07 17:30:13 -04:00
|
|
|
if(inputType == GameInputType::VsZapper) {
|
|
|
|
//VS Duck Hunt, etc. need the zapper in the first port
|
|
|
|
log("[DB] Input: VS Zapper connected");
|
|
|
|
controllers[0] = ControllerType::Zapper;
|
|
|
|
} else if(inputType == GameInputType::Zapper) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Zapper connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
if(isFamicom) {
|
|
|
|
expDevice = ExpansionPortDevice::Zapper;
|
|
|
|
} else {
|
2018-07-07 17:30:13 -04:00
|
|
|
controllers[1] = ControllerType::Zapper;
|
2018-01-09 23:15:08 -05:00
|
|
|
}
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::FourScore) {
|
|
|
|
log("[DB] Input: Four score connected");
|
|
|
|
EmulationSettings::SetFlags(EmulationFlags::HasFourScore);
|
|
|
|
controllers[2] = controllers[3] = ControllerType::StandardController;
|
|
|
|
} else if(inputType == GameInputType::FourPlayerAdapter) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Four player adapter connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
EmulationSettings::SetFlags(EmulationFlags::HasFourScore);
|
2018-07-07 17:30:13 -04:00
|
|
|
expDevice = ExpansionPortDevice::FourPlayerAdapter;
|
|
|
|
controllers[2] = controllers[3] = ControllerType::StandardController;
|
|
|
|
} else if(inputType == GameInputType::ArkanoidControllerFamicom) {
|
|
|
|
log("[DB] Input: Arkanoid controller (Famicom) connected");
|
|
|
|
expDevice = ExpansionPortDevice::ArkanoidController;
|
|
|
|
} else if(inputType == GameInputType::ArkanoidControllerNes) {
|
|
|
|
log("[DB] Input: Arkanoid controller (NES) connected");
|
|
|
|
controllers[1] = ControllerType::ArkanoidController;
|
|
|
|
} else if(inputType == GameInputType::OekaKidsTablet) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Oeka Kids Tablet connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::OekaKidsTablet;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::KonamiHyperShot) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Konami Hyper Shot connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::KonamiHyperShot;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::FamilyBasicKeyboard) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Family Basic Keyboard connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::FamilyBasicKeyboard;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::PartyTap) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Party Tap connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::PartyTap;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::PachinkoController) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Pachinko controller connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::Pachinko;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::ExcitingBoxing) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Exciting Boxing controller connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::ExcitingBoxing;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::SuborKeyboardMouse1) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Subor mouse connected");
|
|
|
|
log("[DB] Input: Subor keyboard connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::SuborKeyboard;
|
|
|
|
controllers[1] = ControllerType::SuborMouse;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::JissenMahjong) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Jissen Mahjong controller connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::JissenMahjong;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::BarcodeBattler) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Barcode Battler barcode reader connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::BarcodeBattler;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::BandaiHypershot) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Bandai Hyper Shot gun connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::BandaiHyperShot;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::BattleBox) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Battle Box connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::BattleBox;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::TurboFile) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Ascii Turbo File connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::AsciiTurboFile;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::FamilyTrainerSideA || inputType == GameInputType::FamilyTrainerSideB) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Family Trainer mat connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::Famicom;
|
|
|
|
expDevice = ExpansionPortDevice::FamilyTrainerMat;
|
2018-07-07 17:30:13 -04:00
|
|
|
} else if(inputType == GameInputType::PowerPadSideA || inputType == GameInputType::PowerPadSideB) {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: Power Pad connected");
|
2018-01-09 23:15:08 -05:00
|
|
|
system = GameSystem::NesNtsc;
|
|
|
|
controllers[1] = ControllerType::PowerPad;
|
|
|
|
} else {
|
2018-01-11 17:01:27 -05:00
|
|
|
log("[DB] Input: 2 standard controllers connected");
|
2016-07-09 15:58:49 -04:00
|
|
|
}
|
2018-01-09 23:15:08 -05:00
|
|
|
|
|
|
|
isFamicom = (system == GameSystem::Famicom || system == GameSystem::FDS || system == GameSystem::Dendy);
|
|
|
|
EmulationSettings::SetConsoleType(isFamicom ? ConsoleType::Famicom : ConsoleType::Nes);
|
|
|
|
for(int i = 0; i < 4; i++) {
|
|
|
|
EmulationSettings::SetControllerType(i, controllers[i]);
|
|
|
|
}
|
|
|
|
EmulationSettings::SetExpansionDevice(expDevice);
|
2016-07-09 15:58:49 -04:00
|
|
|
}
|
|
|
|
|
2016-06-15 21:59:34 -04:00
|
|
|
uint8_t GameDatabase::GetSubMapper(GameInfo &info)
|
|
|
|
{
|
2017-05-18 22:43:21 -04:00
|
|
|
if(!info.SubmapperID.empty()) {
|
|
|
|
return ToInt<uint8_t>(info.SubmapperID);
|
|
|
|
} else {
|
|
|
|
switch(info.MapperID) {
|
|
|
|
case 1:
|
|
|
|
if(info.Board.find("SEROM") != string::npos ||
|
|
|
|
info.Board.find("SHROM") != string::npos ||
|
|
|
|
info.Board.find("SH1ROM") != string::npos) {
|
|
|
|
//SEROM, SHROM, SH1ROM have fixed PRG banking
|
|
|
|
return 5;
|
|
|
|
}
|
|
|
|
break;
|
2016-07-26 19:19:28 -04:00
|
|
|
|
2017-05-18 22:43:21 -04:00
|
|
|
case 3:
|
|
|
|
if(info.Board.compare("NES-CNROM") == 0) {
|
|
|
|
//Enable bus conflicts for CNROM games
|
|
|
|
//Fixes "Cybernoid - The Fighting Machine" which requires open bus behavior to work properly
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
if(info.Board.compare("ACCLAIM-MC-ACC") == 0) {
|
|
|
|
return 3; //Acclaim MC-ACC (MMC3 clone)
|
|
|
|
} else if(info.Chip.compare("MMC6B") == 0) {
|
|
|
|
return 1; //MMC6 (Star Tropics)
|
|
|
|
}
|
|
|
|
break;
|
2016-06-15 21:59:34 -04:00
|
|
|
|
2017-05-18 22:43:21 -04:00
|
|
|
case 21:
|
|
|
|
if(info.Pcb.compare("352398") == 0) {
|
|
|
|
return 1; //VRC4a
|
|
|
|
} else if(info.Pcb.compare("352889") == 0) {
|
|
|
|
return 2; //VRC4c
|
|
|
|
}
|
|
|
|
break;
|
2016-06-15 21:59:34 -04:00
|
|
|
|
2017-05-18 22:43:21 -04:00
|
|
|
case 23:
|
|
|
|
if(info.Pcb.compare("352396") == 0) {
|
|
|
|
return 2; //VRC4e
|
|
|
|
} else if(info.Pcb.compare("350603") == 0 || info.Pcb.compare("350636") == 0 || info.Pcb.compare("350926") == 0 || info.Pcb.compare("351179") == 0 || info.Pcb.compare("LROG009-00") == 0) {
|
|
|
|
return 3; //VRC2b
|
|
|
|
}
|
|
|
|
break;
|
2016-06-15 21:59:34 -04:00
|
|
|
|
2017-05-18 22:43:21 -04:00
|
|
|
case 25:
|
|
|
|
if(info.Pcb.compare("351406") == 0) {
|
|
|
|
return 1; //VRC4b
|
|
|
|
} else if(info.Pcb.compare("352400") == 0) {
|
|
|
|
return 2; //VRC4d
|
|
|
|
} else if(info.Pcb.compare("351948") == 0) {
|
|
|
|
return 3; //VRC2c
|
|
|
|
}
|
|
|
|
break;
|
2016-06-15 21:59:34 -04:00
|
|
|
|
2017-05-18 22:43:21 -04:00
|
|
|
case 32:
|
|
|
|
if(info.Board.compare("IREM-G101-B") == 0) {
|
|
|
|
return 1; //Major League
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 71:
|
|
|
|
if(info.Board.compare("CAMERICA-BF9097") == 0) {
|
|
|
|
return 1; //Fire Hawk
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 78:
|
|
|
|
if(info.Board.compare("IREM-HOLYDIVER") == 0) {
|
|
|
|
return 3; //Holy Diver
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 185:
|
|
|
|
if(info.Crc == 0x0F05FF0A) {
|
|
|
|
//Seicross (v2)
|
|
|
|
//Not a real submapper, used to alter behavior specifically for this game
|
|
|
|
//This is equivalent to FCEUX's mapper 181
|
|
|
|
return 16;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 210:
|
|
|
|
if(info.Board.compare("NAMCOT-175") == 0) {
|
|
|
|
return 1; //Namco 175
|
|
|
|
} else if(info.Board.compare("NAMCOT-340") == 0) {
|
|
|
|
return 2; //Namco 340
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-06-15 21:59:34 -04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-25 20:04:49 -04:00
|
|
|
bool GameDatabase::GetDbRomSize(uint32_t romCrc, uint32_t &prgSize, uint32_t &chrSize)
|
|
|
|
{
|
|
|
|
InitDatabase();
|
|
|
|
auto result = _gameDatabase.find(romCrc);
|
|
|
|
if(result != _gameDatabase.end()) {
|
|
|
|
prgSize = result->second.PrgRomSize * 1024;
|
|
|
|
chrSize = result->second.ChrRomSize * 1024;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-18 19:11:26 -04:00
|
|
|
bool GameDatabase::GetiNesHeader(uint32_t romCrc, NESHeader &nesHeader)
|
|
|
|
{
|
|
|
|
GameInfo info = {};
|
|
|
|
InitDatabase();
|
|
|
|
auto result = _gameDatabase.find(romCrc);
|
|
|
|
if(result != _gameDatabase.end()) {
|
|
|
|
info = result->second;
|
|
|
|
|
|
|
|
nesHeader.Byte9 = 0;
|
|
|
|
if(info.PrgRomSize > 4096) {
|
|
|
|
uint16_t prgSize = info.PrgRomSize / 16;
|
|
|
|
nesHeader.PrgCount = prgSize & 0xFF;
|
|
|
|
nesHeader.Byte9 |= (prgSize & 0xF00) >> 8;
|
|
|
|
} else {
|
|
|
|
nesHeader.PrgCount = info.PrgRomSize / 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.ChrRomSize > 2048) {
|
|
|
|
uint16_t chrSize = info.ChrRomSize / 8;
|
|
|
|
nesHeader.ChrCount = chrSize & 0xFF;
|
|
|
|
nesHeader.Byte9 |= (chrSize & 0xF00) >> 4;
|
|
|
|
} else {
|
|
|
|
nesHeader.ChrCount = info.ChrRomSize / 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
nesHeader.Byte6 = (info.MapperID & 0x0F) << 4;
|
|
|
|
if(info.HasBattery) {
|
|
|
|
nesHeader.Byte6 |= 0x02;
|
|
|
|
}
|
|
|
|
if(info.Mirroring.compare("v") == 0) {
|
|
|
|
nesHeader.Byte6 |= 0x01;
|
|
|
|
}
|
|
|
|
|
|
|
|
nesHeader.Byte7 = (info.MapperID & 0xF0);
|
|
|
|
GameSystem system = GetGameSystem(info.System);
|
|
|
|
if(system == GameSystem::Playchoice) {
|
|
|
|
nesHeader.Byte7 |= 0x02;
|
2018-07-07 14:52:51 -04:00
|
|
|
} else if(system == GameSystem::VsSystem) {
|
2017-03-18 19:11:26 -04:00
|
|
|
nesHeader.Byte7 |= 0x01;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Don't set this, otherwise the header will be used over the game DB data
|
|
|
|
//nesHeader.Byte7 |= 0x08; //NES 2.0 marker
|
|
|
|
|
|
|
|
nesHeader.Byte8 = ((GetSubMapper(info) & 0x0F) << 4) | ((info.MapperID & 0xF00) >> 8);
|
|
|
|
|
|
|
|
nesHeader.Byte10 = 0;
|
|
|
|
if(info.SaveRamSize > 0) {
|
|
|
|
nesHeader.Byte10 |= ((int)log2(info.SaveRamSize * 1024) - 6) << 4;
|
|
|
|
}
|
|
|
|
if(info.WorkRamSize > 0) {
|
|
|
|
nesHeader.Byte10 |= ((int)log2(info.WorkRamSize * 1024) - 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
nesHeader.Byte11 = 0;
|
|
|
|
if(info.ChrRamSize > 0) {
|
|
|
|
nesHeader.Byte11 |= ((int)log2(info.ChrRamSize * 1024) - 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
nesHeader.Byte12 = system == GameSystem::NesPal ? 0x01 : 0;
|
|
|
|
nesHeader.Byte13 = 0; //VS PPU variant
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-04-14 22:31:19 -04:00
|
|
|
void GameDatabase::SetGameInfo(uint32_t romCrc, RomData &romData, bool updateRomData, bool forHeaderlessRom)
|
2016-07-26 19:19:28 -04:00
|
|
|
{
|
|
|
|
GameInfo info = {};
|
|
|
|
|
2016-06-15 21:59:34 -04:00
|
|
|
InitDatabase();
|
|
|
|
|
|
|
|
auto result = _gameDatabase.find(romCrc);
|
2016-06-19 16:54:34 -04:00
|
|
|
|
2016-06-15 21:59:34 -04:00
|
|
|
if(result != _gameDatabase.end()) {
|
2016-07-26 19:19:28 -04:00
|
|
|
info = result->second;
|
2018-04-14 22:31:19 -04:00
|
|
|
if(!forHeaderlessRom && info.Board == "UNK") {
|
|
|
|
//Boards marked as UNK should only be used for headerless roms (since their data is unverified)
|
2018-07-07 14:52:51 -04:00
|
|
|
romData.Info.DatabaseInfo = {};
|
2018-04-14 22:31:19 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MessageManager::Log("[DB] Game found in database");
|
2016-06-15 21:59:34 -04:00
|
|
|
|
2017-04-01 18:02:12 -04:00
|
|
|
if(info.MapperID < UnifBoards::UnknownBoard) {
|
|
|
|
MessageManager::Log("[DB] Mapper: " + std::to_string(info.MapperID) + " Sub: " + std::to_string(GetSubMapper(info)));
|
2017-05-22 14:25:33 -04:00
|
|
|
} else if(info.MapperID == UnifBoards::UnknownBoard) {
|
|
|
|
MessageManager::DisplayMessage("Error", "UnsupportedMapper", "UNIF: " + info.Board);
|
2017-04-01 18:02:12 -04:00
|
|
|
}
|
2016-06-15 21:59:34 -04:00
|
|
|
|
2016-06-19 16:54:34 -04:00
|
|
|
MessageManager::Log("[DB] System : " + info.System);
|
2018-07-07 14:52:51 -04:00
|
|
|
|
|
|
|
if(GetGameSystem(info.System) == GameSystem::VsSystem) {
|
|
|
|
string type = "VS-UniSystem";
|
|
|
|
switch(GetVsSystemType(info.VsSystemType)) {
|
2018-07-07 22:04:53 -04:00
|
|
|
case VsSystemType::Default: break;
|
2018-07-07 14:52:51 -04:00
|
|
|
case VsSystemType::IceClimberProtection: type = "VS-UniSystem (Ice Climbers)"; break;
|
|
|
|
case VsSystemType::RaidOnBungelingBayProtection: type = "VS-DualSystem (Raid on Bungeling Bay)"; break;
|
|
|
|
case VsSystemType::RbiBaseballProtection: type = "VS-UniSystem (RBI Baseball)"; break;
|
|
|
|
case VsSystemType::SuperXeviousProtection: type = "VS-UniSystem (Super Xevious)"; break;
|
|
|
|
case VsSystemType::TkoBoxingProtection: type = "VS-UniSystem (TKO Boxing)"; break;
|
|
|
|
case VsSystemType::VsDualSystem: type = "VS-DualSystem"; break;
|
|
|
|
}
|
|
|
|
MessageManager::Log("[DB] VS System Type: " + type);
|
|
|
|
}
|
2016-07-30 19:35:28 -04:00
|
|
|
if(!info.Board.empty()) {
|
|
|
|
MessageManager::Log("[DB] Board: " + info.Board);
|
|
|
|
}
|
|
|
|
if(!info.Chip.empty()) {
|
|
|
|
MessageManager::Log("[DB] Chip: " + info.Chip);
|
|
|
|
}
|
2017-05-01 20:45:07 -04:00
|
|
|
|
|
|
|
switch(GetBusConflictType(info.BusConflicts)) {
|
2018-06-25 15:56:05 -04:00
|
|
|
case BusConflictType::Default: break;
|
2017-05-01 20:45:07 -04:00
|
|
|
case BusConflictType::Yes: MessageManager::Log("[DB] Bus conflicts: Yes"); break;
|
|
|
|
case BusConflictType::No: MessageManager::Log("[DB] Bus conflicts: No"); break;
|
2017-04-01 15:47:43 -04:00
|
|
|
}
|
2016-07-26 19:19:28 -04:00
|
|
|
|
2016-06-19 16:54:34 -04:00
|
|
|
if(!info.Mirroring.empty()) {
|
2017-05-01 20:45:07 -04:00
|
|
|
string msg = "[DB] Mirroring: ";
|
|
|
|
switch(info.Mirroring[0]) {
|
|
|
|
case 'h': msg += "Horizontal"; break;
|
|
|
|
case 'v': msg += "Vertical"; break;
|
|
|
|
case '4': msg += "4 Screens"; break;
|
|
|
|
case 'a': msg += "Single screen"; break;
|
|
|
|
}
|
|
|
|
MessageManager::Log(msg);
|
2016-06-19 16:54:34 -04:00
|
|
|
}
|
|
|
|
MessageManager::Log("[DB] PRG ROM: " + std::to_string(info.PrgRomSize) + " KB");
|
|
|
|
MessageManager::Log("[DB] CHR ROM: " + std::to_string(info.ChrRomSize) + " KB");
|
|
|
|
if(info.ChrRamSize > 0) {
|
|
|
|
MessageManager::Log("[DB] CHR RAM: " + std::to_string(info.ChrRamSize) + " KB");
|
|
|
|
}
|
2016-07-26 19:19:28 -04:00
|
|
|
if(info.WorkRamSize > 0) {
|
|
|
|
MessageManager::Log("[DB] Work RAM: " + std::to_string(info.WorkRamSize) + " KB");
|
|
|
|
}
|
|
|
|
if(info.SaveRamSize > 0) {
|
|
|
|
MessageManager::Log("[DB] Save RAM: " + std::to_string(info.SaveRamSize) + " KB");
|
|
|
|
}
|
2016-06-19 16:54:34 -04:00
|
|
|
MessageManager::Log("[DB] Battery: " + string(info.HasBattery ? "Yes" : "No"));
|
|
|
|
|
2016-07-26 19:19:28 -04:00
|
|
|
if(updateRomData) {
|
|
|
|
MessageManager::Log("[DB] Database info will be used instead of file header.");
|
|
|
|
UpdateRomData(info, romData);
|
2016-07-09 15:58:49 -04:00
|
|
|
}
|
|
|
|
|
2018-01-09 23:15:08 -05:00
|
|
|
if(EmulationSettings::CheckFlag(EmulationFlags::AutoConfigureInput)) {
|
2018-07-07 17:30:13 -04:00
|
|
|
InitializeInputDevices(GetInputType(romData.Info.System, info.InputType), romData.Info.System);
|
2018-01-09 23:15:08 -05:00
|
|
|
}
|
2016-07-26 19:19:28 -04:00
|
|
|
#ifdef _DEBUG
|
2018-07-07 14:52:51 -04:00
|
|
|
MessageManager::DisplayMessage("DB", "Mapper: " + std::to_string(romData.Info.MapperID) + " Sub: " + std::to_string(romData.Info.SubMapperID) + " System: " + info.System);
|
2016-07-26 19:19:28 -04:00
|
|
|
#endif
|
2016-07-09 15:58:49 -04:00
|
|
|
} else {
|
|
|
|
MessageManager::Log("[DB] Game not found in database");
|
2016-07-26 19:19:28 -04:00
|
|
|
}
|
|
|
|
|
2018-01-04 19:03:47 -05:00
|
|
|
#ifdef LIBRETRO
|
2018-07-07 14:52:51 -04:00
|
|
|
SetVsSystemDefaults(romData.Info.Hash.PrgCrc32);
|
2018-01-04 19:03:47 -05:00
|
|
|
#endif
|
|
|
|
|
2018-07-07 14:52:51 -04:00
|
|
|
romData.Info.DatabaseInfo = info;
|
2016-07-26 19:19:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void GameDatabase::UpdateRomData(GameInfo &info, RomData &romData)
|
|
|
|
{
|
2018-07-07 14:52:51 -04:00
|
|
|
romData.Info.MapperID = info.MapperID;
|
|
|
|
romData.Info.System = GetGameSystem(info.System);
|
|
|
|
if(romData.Info.System == GameSystem::VsSystem) {
|
2018-07-07 17:30:13 -04:00
|
|
|
romData.Info.VsType = GetVsSystemType(info.VsSystemType);
|
|
|
|
romData.Info.VsPpuModel = GetPpuModel(info.PpuModel);
|
2018-07-07 14:52:51 -04:00
|
|
|
}
|
2018-07-07 17:30:13 -04:00
|
|
|
romData.Info.InputType = GetInputType(romData.Info.System, info.InputType);
|
2018-07-07 14:52:51 -04:00
|
|
|
romData.Info.SubMapperID = GetSubMapper(info);
|
|
|
|
romData.Info.BusConflicts = GetBusConflictType(info.BusConflicts);
|
2017-04-03 23:45:24 -04:00
|
|
|
if(info.ChrRamSize > 0) {
|
|
|
|
romData.ChrRamSize = info.ChrRamSize * 1024;
|
|
|
|
}
|
2016-08-27 13:29:02 -04:00
|
|
|
if(info.WorkRamSize > 0) {
|
|
|
|
romData.WorkRamSize = info.WorkRamSize * 1024;
|
|
|
|
}
|
|
|
|
if(info.SaveRamSize > 0) {
|
|
|
|
romData.SaveRamSize = info.SaveRamSize * 1024;
|
|
|
|
}
|
2018-07-07 14:52:51 -04:00
|
|
|
romData.Info.HasBattery |= info.HasBattery;
|
2016-07-26 19:19:28 -04:00
|
|
|
|
|
|
|
if(!info.Mirroring.empty()) {
|
2017-04-03 23:45:24 -04:00
|
|
|
switch(info.Mirroring[0]) {
|
2018-07-07 14:52:51 -04:00
|
|
|
case 'h': romData.Info.Mirroring = MirroringType::Horizontal; break;
|
|
|
|
case 'v': romData.Info.Mirroring = MirroringType::Vertical; break;
|
|
|
|
case '4': romData.Info.Mirroring = MirroringType::FourScreens; break;
|
|
|
|
case 'a': romData.Info.Mirroring = MirroringType::ScreenAOnly; break;
|
2017-04-03 23:45:24 -04:00
|
|
|
}
|
2016-06-15 21:59:34 -04:00
|
|
|
}
|
2018-01-04 19:03:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void GameDatabase::SetVsSystemDefaults(uint32_t prgCrc32)
|
|
|
|
{
|
2018-07-07 22:04:53 -04:00
|
|
|
//TODO: CLEANUP
|
2018-01-04 19:03:47 -05:00
|
|
|
switch(prgCrc32) {
|
|
|
|
case 0x8850924B:
|
|
|
|
//Tetris
|
2018-07-07 22:04:53 -04:00
|
|
|
//defaultDip = 32; //????
|
2018-01-04 19:03:47 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 0xCF36261E:
|
|
|
|
//SuperSkyKid
|
2018-07-07 22:04:53 -04:00
|
|
|
//inputType = VsInputType::SwapControllers;
|
|
|
|
//model = PpuModel::Ppu2C04A;
|
2018-01-04 19:03:47 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE1AA8214:
|
|
|
|
//StarLuster
|
2018-07-07 22:04:53 -04:00
|
|
|
//defaultDip = 32; //????
|
2018-01-04 19:03:47 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x43A357EF:
|
|
|
|
//IceClimber
|
2018-07-07 22:04:53 -04:00
|
|
|
//inputType = VsInputType::SwapControllers;
|
|
|
|
//model = PpuModel::Ppu2C04D;
|
2018-01-04 19:03:47 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xD4EB5923:
|
|
|
|
//IceClimberB
|
2018-07-07 22:04:53 -04:00
|
|
|
//inputType = VsInputType::SwapControllers;
|
|
|
|
//model = PpuModel::Ppu2C04D;
|
2018-01-04 19:03:47 -05:00
|
|
|
break;
|
|
|
|
|
2018-07-07 14:52:51 -04:00
|
|
|
case 0x737DD1BF: case 0x4BF3972D:
|
2018-01-04 19:03:47 -05:00
|
|
|
//SuperMarioBros
|
2018-07-07 22:04:53 -04:00
|
|
|
//model = PpuModel::Ppu2C04D;
|
2018-01-04 19:03:47 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xAE8063EF:
|
|
|
|
//MachRiderFightingCourse, defaults
|
2018-07-07 22:04:53 -04:00
|
|
|
//model = PpuModel::Ppu2C03;
|
2018-01-04 19:03:47 -05:00
|
|
|
break;
|
|
|
|
|
2018-07-07 14:52:51 -04:00
|
|
|
case 0x8A6A9848: //1 crc left
|
2018-01-04 19:03:47 -05:00
|
|
|
//MachRider
|
2018-07-07 22:04:53 -04:00
|
|
|
//model = PpuModel::Ppu2C04B;
|
2018-01-04 19:03:47 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xC99EC059:
|
|
|
|
//RaidBungelingBay
|
2018-07-07 22:04:53 -04:00
|
|
|
//inputType = VsInputType::SwapControllers;
|
|
|
|
//model = PpuModel::Ppu2C04B;
|
2018-01-04 19:03:47 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF9D3B0A3: case 0x66BB838F: case 0x9924980A:
|
|
|
|
//SuperXevious
|
2018-07-07 22:04:53 -04:00
|
|
|
//model = PpuModel::Ppu2C04A;
|
2018-01-04 19:03:47 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|