Vs System: Automatically select proper PPU & input mapping based on rom name/CRC + implemented support for vs system input remappings (some games change the buttons around)

This commit is contained in:
Souryo 2016-07-10 09:05:41 -04:00
parent af0a954547
commit 313340198e
20 changed files with 426 additions and 63 deletions

View File

@ -23,6 +23,18 @@ struct ButtonState
return (uint8_t)A | ((uint8_t)B << 1) | ((uint8_t)Select << 2) | ((uint8_t)Start << 3) |
((uint8_t)Up << 4) | ((uint8_t)Down << 5) | ((uint8_t)Left << 6) | ((uint8_t)Right << 7);
}
void FromByte(uint8_t stateData)
{
A = (stateData & 0x01) == 0x01;
B = (stateData & 0x02) == 0x02;
Select = (stateData & 0x04) == 0x04;
Start = (stateData & 0x08) == 0x08;
Up = (stateData & 0x10) == 0x10;
Down = (stateData & 0x20) == 0x20;
Left = (stateData & 0x40) == 0x40;
Right = (stateData & 0x80) == 0x80;
}
};
class BaseControlDevice : public Snapshotable

View File

@ -371,6 +371,7 @@ void BaseMapper::Initialize(RomData &romData)
_hasBattery = romData.HasBattery || ForceBattery();
_gameSystem = romData.System;
_crc32 = romData.Crc32;
_prgCrc32 = romData.PrgCrc32;
_hasBusConflicts = HasBusConflicts();
_saveRam = new uint8_t[_saveRamSize];
@ -547,6 +548,11 @@ uint32_t BaseMapper::GetCrc32()
return _crc32;
}
uint32_t BaseMapper::GetPrgCrc32()
{
return _prgCrc32;
}
MirroringType BaseMapper::GetMirroringType()
{
return _mirroringType;

View File

@ -53,7 +53,6 @@ private:
uint8_t _nametableIndexes[4];
bool _onlyChrRam = false;
GameSystem _gameSystem;
bool _hasBusConflicts = false;
string _romFilename;
@ -72,6 +71,7 @@ private:
uint32_t _chrPageNumbers[64];
uint32_t _crc32 = 0;
uint32_t _prgCrc32 = 0;
vector<uint8_t> _originalPrgRom;
@ -79,6 +79,7 @@ protected:
NESHeader _nesHeader;
uint16_t _mapperID;
uint8_t _subMapperID;
GameSystem _gameSystem;
uint8_t* _prgRom = nullptr;
uint8_t* _chrRom = nullptr;
@ -176,6 +177,7 @@ public:
GameSystem GetGameSystem();
uint32_t GetCrc32();
uint32_t GetPrgCrc32();
string GetRomName();
uint8_t ReadRAM(uint16_t addr);

View File

@ -163,6 +163,15 @@ uint32_t Console::GetCrc32()
}
}
uint32_t Console::GetPrgCrc32()
{
if(Instance->_mapper) {
return Instance->_mapper->GetPrgCrc32();
} else {
return 0;
}
}
NesModel Console::GetModel()
{
return Instance->_model;

View File

@ -70,6 +70,7 @@ class Console
static string GetROMPath();
static string GetRomName();
static uint32_t GetCrc32();
static uint32_t GetPrgCrc32();
static NesModel GetModel();
static bool IsRunning();

View File

@ -28,8 +28,6 @@ class ControlManager : public Snapshotable, public IMemoryHandler
bool _refreshState = false;
void RefreshAllPorts();
virtual shared_ptr<BaseControlDevice> GetZapper(uint8_t port);
static void RegisterControlDevice(shared_ptr<BaseControlDevice> controlDevice, uint8_t port);
@ -37,6 +35,8 @@ class ControlManager : public Snapshotable, public IMemoryHandler
protected:
uint8_t GetPortValue(uint8_t port);
virtual void RefreshAllPorts();
virtual void StreamState(bool saving);
public:

View File

@ -22,7 +22,7 @@ double EmulationSettings::_reverbStrength = 0;
double EmulationSettings::_reverbDelay = 0;
NesModel EmulationSettings::_model = NesModel::Auto;
PpuModel EmulationSettings::_ppuModel = PpuModel::Ppu2C03;
PpuModel EmulationSettings::_ppuModel = PpuModel::Ppu2C02;
uint32_t EmulationSettings::_emulationSpeed = 100;
uint32_t EmulationSettings::_overclockRate = 100;

View File

@ -140,8 +140,7 @@ struct NtscFilterSettings
enum class ConsoleType
{
Nes = 0,
Famicom = 1,
VsSystem = 2,
Famicom = 1
};
enum class ControllerType

View File

@ -244,6 +244,7 @@ struct RomData
vector<uint8_t> RawData;
uint32_t Crc32 = 0;
uint32_t PrgCrc32 = 0;
bool Error = false;

View File

@ -127,3 +127,13 @@ void StandardController::AddAdditionalController(shared_ptr<BaseControlDevice> c
}
_additionalController = controller;
}
uint32_t StandardController::GetInternalState()
{
return _stateBuffer;
}
void StandardController::SetInternalState(uint32_t state)
{
_stateBuffer = state;
}

View File

@ -26,5 +26,9 @@ public:
uint8_t GetPortOutput();
void RefreshStateBuffer();
//Used for VS System button unscrambling
uint32_t GetInternalState();
void SetInternalState(uint32_t state);
void AddAdditionalController(shared_ptr<BaseControlDevice> controller);
};

View File

@ -5,6 +5,17 @@
#include "Console.h"
#include "VsZapper.h"
#include <assert.h>
#include "StandardController.h"
enum class VsInputType
{
Default = 0,
TypeA = 1,
TypeB = 2,
TypeC = 3,
TypeD = 4,
TypeE = 5
};
class VsControlManager : public ControlManager
{
@ -15,6 +26,7 @@ private:
bool _serviceButton = false;
bool _coinInserted[2] = { };
int32_t _coinInsertCycle[2] = { };
VsInputType _inputType = VsInputType::Default;
uint32_t _protectionCounter = 0;
uint32_t _protectionData[3][32] = {
@ -96,6 +108,11 @@ public:
_dipSwitches = dipSwitches;
}
void SetInputType(VsInputType inputType)
{
_inputType = inputType;
}
void SetServiceButtonState(bool pushed)
{
_serviceButton = pushed;
@ -113,6 +130,60 @@ public:
return _prgChrSelectBit;
}
void RemapControllerButtons()
{
ButtonState ports[2];
shared_ptr<StandardController> controllers[2];
controllers[0] = std::dynamic_pointer_cast<StandardController>(GetControlDevice(0));
controllers[1] = std::dynamic_pointer_cast<StandardController>(GetControlDevice(1));
if(controllers[0]) {
ports[0].FromByte(controllers[0]->GetInternalState());
}
if(controllers[1]) {
ports[1].FromByte(controllers[1]->GetInternalState());
}
if(_inputType == VsInputType::TypeA) {
std::swap(ports[0], ports[1]);
std::swap(ports[0].Select, ports[0].Start);
std::swap(ports[1].Select, ports[1].Start);
} else if(_inputType == VsInputType::TypeB) {
std::swap(ports[1].Select, ports[0].Start);
std::swap(ports[1].Start, ports[0].Select);
} else if(_inputType == VsInputType::TypeC) {
ports[1].Select = ports[0].Start;
ports[0].Select = false;
ports[0].Start = false;
} else if(_inputType == VsInputType::TypeD) {
std::swap(ports[1].Select, ports[0].Start);
std::swap(ports[1].Start, ports[0].Select);
ports[0].Select = !ports[0].Select;
ports[1].Select = !ports[1].Select;
} else if(_inputType == VsInputType::TypeE) {
std::swap(ports[0], ports[1]);
std::swap(ports[0].B, ports[1].A);
std::swap(ports[1].Select, ports[1].Start);
std::swap(ports[0].Select, ports[0].Start);
}
if(controllers[0]) {
controllers[0]->SetInternalState((controllers[0]->GetInternalState() & ~0xFF) | ports[0].ToByte());
}
if(controllers[1]) {
controllers[1]->SetInternalState((controllers[1]->GetInternalState() & ~0xFF) | ports[1].ToByte());
}
}
void RefreshAllPorts()
{
ControlManager::RefreshAllPorts();
if(_inputType != VsInputType::Default) {
RemapControllerButtons();
}
}
uint8_t ReadRAM(uint16_t addr)
{
UpdateCoinInsertedFlags();

View File

@ -14,14 +14,17 @@ protected:
virtual void InitMapper()
{
if(_prgSize > 0x6000) {
SelectPRGPage(0, 0);
}
//"Note: unlike all other mappers, an undersize mapper 99 image implies open bus instead of mirroring."
//However, it doesn't look like any game actually rely on this behavior? So not implemented for now.
SelectPRGPage(0, 0);
SelectPRGPage(1, 1);
SelectPRGPage(2, 2);
SelectPRGPage(3, 3);
SelectCHRPage(0, 0);
//Force VS system if mapper 99 (since we assume VsControlManager exists below)
_gameSystem = GameSystem::VsUniSystem;
}
void StreamState(bool saving)
@ -31,13 +34,18 @@ protected:
Stream(_prgChrSelectBit);
}
uint8_t ReadVRAM(uint16_t addr, MemoryOperationType memoryOperationType)
void ProcessCpuClock()
{
if(_prgChrSelectBit != VsControlManager::GetInstance()->GetPrgChrSelectBit()) {
_prgChrSelectBit = VsControlManager::GetInstance()->GetPrgChrSelectBit();
if(_prgSize > 0x8000) {
//"Note: In case of games with 40KiB PRG - ROM(as found in VS Gumshoe), the above bit additionally changes 8KiB PRG - ROM at $8000 - $9FFF."
//"Only Vs. Gumshoe uses the 40KiB PRG variant; in the iNES encapsulation, the 8KiB banks are arranged as 0, 1, 2, 3, 0alternate, empty"
SelectPRGPage(0, _prgChrSelectBit << 2);
}
SelectCHRPage(0, _prgChrSelectBit);
}
return BaseMapper::ReadVRAM(addr, memoryOperationType);
}
};

View File

@ -42,6 +42,8 @@ RomData iNesLoader::LoadRom(vector<uint8_t>& romFile)
buffer += header.GetPrgSize();
romData.ChrRom.insert(romData.ChrRom.end(), buffer, buffer + header.GetChrSize());
romData.PrgCrc32 = CRC32::GetCRC(romData.PrgRom.data(), romData.PrgRom.size());
stringstream crcHex;
crcHex << std::hex << std::uppercase << std::setfill('0') << std::setw(8) << romCrc;
MessageManager::Log("PRG+CHR CRC32: 0x" + crcHex.str());

View File

@ -13,6 +13,7 @@ namespace Mesen.GUI.Config
public string GameCrc;
public InteropEmu.PpuModel PpuModel;
public byte DipSwitches;
public InteropEmu.VsInputType InputType;
public static VsConfigInfo GetCurrentGameConfig(bool createNew)
{
@ -23,40 +24,100 @@ namespace Mesen.GUI.Config
}
}
VsConfigInfo newConfig = new VsConfigInfo();
newConfig.GameCrc = crc;
newConfig.GameID = VsGameConfig.GetGameID();
VsGameConfig gameConfig = VsGameConfig.GetGameConfig(newConfig.GameID);
if(gameConfig != null) {
newConfig.PpuModel = gameConfig.PpuModel;
newConfig.DipSwitches = gameConfig.DefaultDipSwitches;
newConfig.InputType = gameConfig.InputType;
}
if(createNew) {
VsConfigInfo newConfig = new VsConfigInfo();
newConfig.GameCrc = crc;
ConfigManager.Config.VsConfig.Add(newConfig);
return newConfig;
} else {
return null;
}
}
return newConfig;
}
public static void ApplyConfig()
{
VsConfigInfo configInfo = GetCurrentGameConfig(false);
if(configInfo != null) {
InteropEmu.VsSetGameConfig(configInfo.PpuModel, configInfo.DipSwitches);
}
InteropEmu.VsSetGameConfig(configInfo.PpuModel, configInfo.InputType, configInfo.DipSwitches);
}
}
public class VsGameConfig
{
public string GameName;
public string GameID;
public InteropEmu.VsInputType InputType;
public InteropEmu.PpuModel PpuModel;
public byte DefaultDipSwitches;
public List<List<string>> DipSwitches;
private static Dictionary<string, VsGameConfig> _gameConfigs = new Dictionary<string, VsGameConfig>();
public static string GetGameID(string romName)
public static string GetGameIdByCrc(UInt32 prgCrc32)
{
romName = romName.ToLowerInvariant().Replace(" ", "");
foreach(KeyValuePair<string, VsGameConfig> kvp in _gameConfigs) {
if(romName.Contains(kvp.Key.ToLowerInvariant().Replace(" ", "")) || romName.Contains(kvp.Value.GameName.ToLowerInvariant().Replace(" ", ""))) {
return kvp.Key;
switch(prgCrc32) {
case 0xEB2DBA63: return "TKOBoxing";
case 0x135ADF7C: return "RBIBaseball";
case 0xED588F00: return "DuckHunt";
case 0x16D3F469: return "NinjaJajamaruKun";
case 0x8850924B: return "Tetris";
case 0x8C0C2DF5: return "TopGun";
case 0x70901B25: return "Slalom";
case 0xCF36261E: return "SuperSkyKid";
case 0xE1AA8214: return "StarLuster";
case 0xD5D7EAC4: return "DrMario";
case 0xFFBEF374: return "Castlevania";
case 0xE2C0A2BE: return "Platoon";
case 0x29155E0C: return "ExciteBike";
case 0xCBE85490: return "ExciteBikeB";
case 0x07138C06: return "CluCluLand";
case 0x43A357EF: return "IceClimber";
case 0xD4EB5923: return "IceClimberB";
case 0x737DD1BF: case 0x4BF3972D: case 0x8B60CC58: case 0x8192C804: return "SuperMarioBros";
case 0xE528F651: return "Pinball";
case 0xEC461DB9: return "PinballB";
case 0xAE8063EF: return "MachRiderFightingCourse";
case 0x0B65A917: case 0x8A6A9848: return "MachRider";
case 0x46914E3E: return "Soccer";
case 0x70433F2C: return "BattleCity";
case 0xD99A2087: return "Gradius";
case 0x1E438D52: return "Goonies";
case 0xFF5135A3: return "HoganAlley";
case 0x17AE56BE: return "FreedomForce";
case 0xC99EC059: return "RaidBungelingBay";
case 0xF9D3B0A3: case 0x66BB838F: case 0x9924980A: return "SuperXevious";
case 0xA93A5AEE: return "Golf";
case 0xCC2C4B5D: case 0x86167220: return "GolfB";
case 0xCA85E56D: return "MightyBombJack";
case 0xFE446787: return "Gumshoe";
}
return null;
}
public static string GetGameID()
{
string gameID = GetGameIdByCrc(InteropEmu.GetRomInfo().PrgCrc32);
if(gameID != null) {
return gameID;
} else {
//Try to guess the game based on filename
string romName = InteropEmu.GetRomInfo().GetRomName().ToLowerInvariant().Replace(" ", "");
foreach(KeyValuePair<string, VsGameConfig> kvp in _gameConfigs) {
if(romName.Contains(kvp.Key.ToLowerInvariant().Replace(" ", "")) || romName.Contains(kvp.Value.GameName.ToLowerInvariant().Replace(" ", ""))) {
return kvp.Key;
}
}
}
return "Unknown";
}
@ -81,8 +142,23 @@ namespace Mesen.GUI.Config
foreach(XmlNode gameNode in config.SelectNodes("/VsSystemGames/Game")) {
var gameConfig = new VsGameConfig();
gameConfig.GameID = gameNode.Attributes["ID"].Value;
gameConfig.GameName = gameNode.Attributes["Localization"].Value;
if(gameNode.Attributes["DefaultDip"] != null) {
gameConfig.DefaultDipSwitches = (byte)Int32.Parse(gameNode.Attributes["DefaultDip"].Value);
}
if(gameNode.Attributes["PpuModel"] != null) {
gameConfig.PpuModel = (InteropEmu.PpuModel)Enum.Parse(typeof(InteropEmu.PpuModel), gameNode.Attributes["PpuModel"].Value);
} else {
gameConfig.PpuModel = InteropEmu.PpuModel.Ppu2C03;
}
if(gameNode.Attributes["InputType"] != null) {
gameConfig.InputType = (InteropEmu.VsInputType)Enum.Parse(typeof(InteropEmu.VsInputType), "Type" + gameNode.Attributes["InputType"].Value);
} else {
gameConfig.InputType = InteropEmu.VsInputType.Default;
}
gameConfig.DipSwitches = new List<List<string>>();
foreach(XmlNode dipSwitch in gameNode.SelectNodes("DipSwitch")) {
if(dipSwitch.Attributes["Localization"] != null) {
var list = new List<string>();

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<VsSystemGames>
<Game ID="BattleCity" Localization="Battle City">
<Game ID="BattleCity" Localization="Battle City" InputType="B" PpuModel="Ppu2C04A">
<DipSwitch Localization="Credits for 2 Players">
<Option>1</Option>
<Option>2</Option>
@ -23,7 +23,7 @@
<Option>RP2C04-0004</Option>
</DipSwitch>
</Game>
<Game ID="Castlevania" Localization="Castlevania">
<Game ID="Castlevania" Localization="Castlevania" InputType="A" PpuModel="Ppu2C04B">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>3 coins = 1 credit</Option>
@ -49,7 +49,7 @@
<Option>Hard</Option>
</DipSwitch>
</Game>
<Game ID="CluCluLand" Localization="Clu Clu Land">
<Game ID="CluCluLand" Localization="Clu Clu Land" InputType="B" PpuModel="Ppu2C04D">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>2 coins = 1 credit</Option>
@ -70,7 +70,7 @@
</DipSwitch>
<DipSwitch />
</Game>
<Game ID="DrMario" Localization="Dr. Mario">
<Game ID="DrMario" Localization="Dr. Mario" InputType="B" PpuModel="Ppu2C04C">
<DipSwitch Localization="Drop Rate Increases After">
<Option>7 Pills</Option>
<Option>8 Pills</Option>
@ -126,7 +126,7 @@
<Option>100000</Option>
</DipSwitch>
</Game>
<Game ID="ExciteBike" Localization="Excitebike">
<Game ID="ExciteBike" Localization="Excitebike" InputType="A" PpuModel="Ppu2C04D">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>2 coins = 1 credit</Option>
@ -152,7 +152,33 @@
<Option>Hard</Option>
</DipSwitch>
</Game>
<Game ID="FreedomForce" Localization="Freedom Force">
<Game ID="ExciteBikeB" Localization="Excitebike" InputType="A" PpuModel="Ppu2C04C">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>2 coins = 1 credit</Option>
<Option>1 coin = 3 credits</Option>
<Option>4 coins = 1 credit</Option>
<Option>1 coin = 2 credits</Option>
<Option>3 coins = 1 credit</Option>
<Option>1 coin = 4 credits</Option>
<Option>Free Play</Option>
</DipSwitch>
<DipSwitch Localization="Bonus">
<Option>100k and Every 50k</Option>
<Option>100k Only</Option>
<Option>Every 100k</Option>
<Option>None</Option>
</DipSwitch>
<DipSwitch Localization="Part 1 Qualifying Time">
<Option>Normal</Option>
<Option>Hard</Option>
</DipSwitch>
<DipSwitch Localization="Part 2 Qualifying Time">
<Option>Normal</Option>
<Option>Hard</Option>
</DipSwitch>
</Game>
<Game ID="FreedomForce" Localization="Freedom Force" PpuModel="Ppu2C04A">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>3 coins = 1 credit</Option>
@ -169,7 +195,7 @@
<DipSwitch />
<DipSwitch />
</Game>
<Game ID="Golf" Localization="Golf">
<Game ID="Golf" Localization="Golf" InputType="B">
<DipSwitch Localization="Coins">
<Option>Free Play</Option>
<Option>1 coin = 1 credit</Option>
@ -199,7 +225,37 @@
<Option>Hard</Option>
</DipSwitch>
</Game>
<Game ID="Goonies" Localization="Goonies">
<Game ID="GolfB" Localization="Golf" InputType="B" PpuModel="Ppu2C04B">
<DipSwitch Localization="Coins">
<Option>Free Play</Option>
<Option>1 coin = 1 credit</Option>
<Option>1 coin = 3 credits</Option>
<Option>3 coins = 1 credit</Option>
<Option>1 coin = 4 credits</Option>
<Option>2 coins = 1 credit</Option>
<Option>1 coin = 2 credits</Option>
<Option>4 coins = 1 credit</Option>
</DipSwitch>
<DipSwitch Localization="Hole Size">
<Option>Large</Option>
<Option>Small</Option>
</DipSwitch>
<DipSwitch Localization="Points per Stroke">
<Option>Easy</Option>
<Option>Hard</Option>
</DipSwitch>
<DipSwitch Localization="Starting Points">
<Option>10</Option>
<Option>16</Option>
<Option>13</Option>
<Option>20</Option>
</DipSwitch>
<DipSwitch Localization="Difficulty Vs. Computer">
<Option>Easy</Option>
<Option>Hard</Option>
</DipSwitch>
</Game>
<Game ID="Goonies" Localization="Goonies" InputType="A" PpuModel="Ppu2C04C">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>3 coins = 1 credit</Option>
@ -225,7 +281,7 @@
<Option>On</Option>
</DipSwitch>
</Game>
<Game ID="Gradius" Localization="Gradius">
<Game ID="Gradius" Localization="Gradius" InputType="B" PpuModel="Ppu2C04A">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>3 coins = 1 credit</Option>
@ -255,7 +311,7 @@
<Option>On</Option>
</DipSwitch>
</Game>
<Game ID="Gumshoe" Localization="Gumshoe">
<Game ID="Gumshoe" Localization="Gumshoe" PpuModel="Ppu2C05C">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>3 coins = 1 credit</Option>
@ -285,7 +341,7 @@
<Option>100000</Option>
</DipSwitch>
</Game>
<Game ID="HoganAlley" Localization="Hogan's Alley">
<Game ID="HoganAlley" Localization="Hogan's Alley" PpuModel="Ppu2C04A">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>3 coins = 1 credit</Option>
@ -313,7 +369,7 @@
<Option>100000</Option>
</DipSwitch>
</Game>
<Game ID="IceClimber" Localization="Ice Climber">
<Game ID="IceClimber" Localization="Ice Climber" InputType="B" PpuModel="Ppu2C04D">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>2 coins = 1 credit</Option>
@ -339,7 +395,33 @@
<Option>Short</Option>
</DipSwitch>
</Game>
<Game ID="MachRider" Localization="Mach Rider">
<Game ID="IceClimberB" Localization="Ice Climber" InputType="D" PpuModel="Ppu2C04D">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>2 coins = 1 credit</Option>
<Option>1 coin = 3 credits</Option>
<Option>4 coins = 1 credit</Option>
<Option>1 coin = 2 credits</Option>
<Option>3 coins = 1 credit</Option>
<Option>1 coin = 4 credits</Option>
<Option>Free Play</Option>
</DipSwitch>
<DipSwitch Localization="Lives">
<Option>3</Option>
<Option>5</Option>
<Option>4</Option>
<Option>7</Option>
</DipSwitch>
<DipSwitch Localization="Difficulty">
<Option>Normal</Option>
<Option>Hard</Option>
</DipSwitch>
<DipSwitch Localization="Time before the bear">
<Option>Long</Option>
<Option>Short</Option>
</DipSwitch>
</Game>
<Game ID="MachRider" Localization="Mach Rider" InputType="A" PpuModel="Ppu2C04B">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>2 coins = 1 credit</Option>
@ -379,7 +461,7 @@
<DipSwitch />
<DipSwitch />
</Game>
<Game ID="MightyBombJack" Localization="Mighty Bomb Jack">
<Game ID="MightyBombJack" Localization="Mighty Bomb Jack" InputType="A" PpuModel="Ppu2C05B">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>2 coins = 1 credit</Option>
@ -400,7 +482,7 @@
<DipSwitch />
<DipSwitch />
</Game>
<Game ID="NinjaJajamaruKun" Localization="Ninja Jajamaru Kun">
<Game ID="NinjaJajamaruKun" Localization="Ninja Jajamaru Kun" PpuModel="Ppu2C05A" InputType="C">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>2 coins = 1 credit</Option>
@ -424,7 +506,7 @@
<Option>On</Option>
</DipSwitch>
</Game>
<Game ID="Pinball" Localization="Pinball">
<Game ID="Pinball" Localization="Pinball" InputType="E">
<DipSwitch Localization="Coins">
<Option>Free Play</Option>
<Option>1 coin = 1 credit</Option>
@ -448,7 +530,31 @@
<Option>Fast</Option>
</DipSwitch>
</Game>
<Game ID="Platoon" Localization="Platoon">
<Game ID="PinballB" Localization="Pinball" InputType="A" PpuModel="Ppu2C04A">
<DipSwitch Localization="Coins">
<Option>Free Play</Option>
<Option>1 coin = 1 credit</Option>
<Option>1 coin = 3 credits</Option>
<Option>3 coins = 1 credit</Option>
<Option>1 coin = 4 credits</Option>
<Option>2 coins = 1 credit</Option>
<Option>1 coin = 2 credits</Option>
<Option>4 coins = 1 credit</Option>
</DipSwitch>
<DipSwitch />
<DipSwitch />
<DipSwitch Localization="Balls">
<Option>3</Option>
<Option>5</Option>
<Option>4</Option>
<Option>2</Option>
</DipSwitch>
<DipSwitch Localization="Ball Speed">
<Option>Normal</Option>
<Option>Fast</Option>
</DipSwitch>
</Game>
<Game ID="Platoon" Localization="Platoon" InputType="A" PpuModel="Ppu2C04A">
<DipSwitch />
<DipSwitch />
<DipSwitch Localization="Demo Sounds">
@ -468,7 +574,7 @@
<Option>Free Play</Option>
</DipSwitch>
</Game>
<Game ID="RBI Baseball" Localization="RBI Baseball">
<Game ID="RBIBaseball" Localization="RBI Baseball" InputType="B" PpuModel="Ppu2C04A">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>1 coin = 2 credits</Option>
@ -495,7 +601,7 @@
<Option>Unused</Option>
</DipSwitch>
</Game>
<Game ID="RaidBungelingBay" Localization="Raid on Bungeling Bay">
<Game ID="RaidBungelingBay" Localization="Raid on Bungeling Bay" InputType="D" PpuModel="Ppu2C04B">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>2 coins = 1 credit</Option>
@ -515,7 +621,7 @@
<DipSwitch />
<DipSwitch />
</Game>
<Game ID="Slalom" Localization="Slalom">
<Game ID="Slalom" Localization="Slalom" InputType="A" PpuModel="Ppu2C04B">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>3 coins = 1 credit</Option>
@ -545,7 +651,7 @@
<Option>On</Option>
</DipSwitch>
</Game>
<Game ID="Soccer" Localization="Soccer">
<Game ID="Soccer" Localization="Soccer" InputType="B" PpuModel="Ppu2C04C">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>2 coins = 1 credit</Option>
@ -569,7 +675,7 @@
<Option>Very Hard</Option>
</DipSwitch>
</Game>
<Game ID="StarLuster" Localization="Star Luster">
<Game ID="StarLuster" Localization="Star Luster" InputType="A" DefaultDip="32" PpuModel="Ppu2C04A">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>2 coins = 1 credit</Option>
@ -587,7 +693,7 @@
</DipSwitch>
<DipSwitch />
</Game>
<Game ID="SuperMarioBros" Localization="Super Mario Bros">
<Game ID="SuperMarioBros" Localization="Super Mario Bros" InputType="A" PpuModel="Ppu2C04D">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>1 coin = 3 credits</Option>
@ -617,7 +723,7 @@
<Option>3</Option>
</DipSwitch>
</Game>
<Game ID="SuperSkyKid" Localization="Super Sky Kid">
<Game ID="SuperSkyKid" Localization="Super Sky Kid" InputType="C" PpuModel="Ppu2C04A">
<DipSwitch />
<DipSwitch />
<DipSwitch Localization="Lives">
@ -641,7 +747,7 @@
<Option>Unused</Option>
</DipSwitch>
</Game>
<Game ID="SuperXevious" Localization="Super Xevious">
<Game ID="SuperXevious" Localization="Super Xevious" InputType="A" PpuModel="Ppu2C04A">
<DipSwitch />
<DipSwitch />
<DipSwitch />
@ -659,7 +765,7 @@
<Option>RP2C04-0004</Option>
</DipSwitch>
</Game>
<Game ID="Tetris" Localization="Tetris">
<Game ID="Tetris" Localization="Tetris" InputType="B" DefaultDip="32">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>2 coins = 1 credit</Option>
@ -677,7 +783,7 @@
</DipSwitch>
<DipSwitch />
</Game>
<Game ID="TKOBoxing" Localization="TKO Boxing">
<Game ID="TKOBoxing" Localization="TKO Boxing" InputType="A" PpuModel="Ppu2C04C">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>1 coin = 2 credits</Option>
@ -694,7 +800,7 @@
<DipSwitch />
<DipSwitch />
</Game>
<Game ID="TopGun" Localization="Top Gun">
<Game ID="TopGun" Localization="Top Gun" InputType="A" PpuModel="Ppu2C05D">
<DipSwitch Localization="Coins">
<Option>1 coin = 1 credit</Option>
<Option>3 coins = 1 credit</Option>

View File

@ -33,13 +33,17 @@
this.lblPpuModel = new System.Windows.Forms.Label();
this.cboPpuModel = new System.Windows.Forms.ComboBox();
this.grpDipSwitches = new System.Windows.Forms.GroupBox();
this.btnReset = new System.Windows.Forms.Button();
this.baseConfigPanel.SuspendLayout();
this.tlpMain.SuspendLayout();
this.SuspendLayout();
//
// baseConfigPanel
//
this.baseConfigPanel.Controls.Add(this.btnReset);
this.baseConfigPanel.Location = new System.Drawing.Point(0, 295);
this.baseConfigPanel.Size = new System.Drawing.Size(305, 29);
this.baseConfigPanel.Controls.SetChildIndex(this.btnReset, 0);
//
// tlpMain
//
@ -112,6 +116,17 @@
this.grpDipSwitches.TabStop = false;
this.grpDipSwitches.Text = "DIP Switches";
//
// btnReset
//
this.btnReset.AutoSize = true;
this.btnReset.Location = new System.Drawing.Point(6, 3);
this.btnReset.Name = "btnReset";
this.btnReset.Size = new System.Drawing.Size(94, 23);
this.btnReset.TabIndex = 3;
this.btnReset.Text = "Reset to Default";
this.btnReset.UseVisualStyleBackColor = true;
this.btnReset.Click += new System.EventHandler(this.btnReset_Click);
//
// frmVsGameConfig
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -126,6 +141,8 @@
this.Text = "Game Configuration";
this.Controls.SetChildIndex(this.tlpMain, 0);
this.Controls.SetChildIndex(this.baseConfigPanel, 0);
this.baseConfigPanel.ResumeLayout(false);
this.baseConfigPanel.PerformLayout();
this.tlpMain.ResumeLayout(false);
this.tlpMain.PerformLayout();
this.ResumeLayout(false);
@ -140,5 +157,6 @@
private System.Windows.Forms.GroupBox grpDipSwitches;
private System.Windows.Forms.ComboBox cboGame;
private System.Windows.Forms.Label lblGame;
private System.Windows.Forms.Button btnReset;
}
}

View File

@ -13,20 +13,31 @@ namespace Mesen.GUI.Forms.Config
{
public partial class frmVsGameConfig : BaseConfigForm
{
private class DropdownElement
{
public string Name;
public string ID;
public override string ToString()
{
return Name;
}
}
public frmVsGameConfig(VsConfigInfo configInfo)
{
InitializeComponent();
Entity = configInfo;
if(string.IsNullOrWhiteSpace(configInfo.GameID)) {
configInfo.GameID = VsGameConfig.GetGameID(InteropEmu.GetRomInfo().GetRomName());
if(VsGameConfig.GetGameIdByCrc(InteropEmu.GetRomInfo().PrgCrc32) != null) {
cboGame.Enabled = false;
}
AddBinding("PpuModel", cboPpuModel);
foreach(KeyValuePair<string, VsGameConfig> kvp in VsGameConfig.GetGameConfigs()) {
cboGame.Items.Add(kvp.Value.GameName);
cboGame.Items.Add(new DropdownElement { Name = kvp.Value.GameName, ID = kvp.Value.GameID });
if(kvp.Key == configInfo.GameID) {
cboGame.SelectedIndex = cboGame.Items.Count - 1;
}
@ -35,7 +46,7 @@ namespace Mesen.GUI.Forms.Config
private void cboGame_SelectedIndexChanged(object sender, EventArgs e)
{
VsGameConfig config = VsGameConfig.GetGameConfig(VsGameConfig.GetGameID(cboGame.SelectedItem.ToString()));
VsGameConfig config = VsGameConfig.GetGameConfig(((DropdownElement)cboGame.SelectedItem).ID);
UpdateDipSwitches(config, false);
}
@ -125,7 +136,17 @@ namespace Mesen.GUI.Forms.Config
base.UpdateConfig();
((VsConfigInfo)Entity).DipSwitches = (byte)GetDipSwitchValue();
((VsConfigInfo)Entity).GameID = VsGameConfig.GetGameID(cboGame.SelectedItem.ToString());
((VsConfigInfo)Entity).GameID = ((DropdownElement)cboGame.SelectedItem).ID;
}
private void btnReset_Click(object sender, EventArgs e)
{
VsGameConfig defaultConfig = VsGameConfig.GetGameConfig(((DropdownElement)cboGame.SelectedItem).ID);
((VsConfigInfo)Entity).DipSwitches = defaultConfig.DefaultDipSwitches;
((VsConfigInfo)Entity).PpuModel = defaultConfig.PpuModel;
((VsConfigInfo)Entity).InputType = defaultConfig.InputType;
UpdateUI();
UpdateDipSwitches(defaultConfig, false);
}
}

View File

@ -114,7 +114,7 @@ namespace Mesen.GUI
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsVsSystem();
[DllImport(DLLPath)] public static extern void VsInsertCoin(UInt32 port);
[DllImport(DLLPath)] public static extern void VsSetGameConfig(PpuModel model, byte dipSwitches);
[DllImport(DLLPath)] public static extern void VsSetGameConfig(PpuModel model, VsInputType inputType, byte dipSwitches);
[DllImport(DLLPath)] public static extern void CheatAddCustom(UInt32 address, Byte value, Int32 compareValue, [MarshalAs(UnmanagedType.I1)]bool isRelativeAddress);
[DllImport(DLLPath)] public static extern void CheatAddGameGenie([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string code);
@ -387,6 +387,16 @@ namespace Mesen.GUI
ArkanoidController = 3,
}
public enum VsInputType
{
Default = 0,
TypeA = 1,
TypeB = 2,
TypeC = 3,
TypeD = 4,
TypeE = 5
}
public enum PpuModel
{
Ppu2C02 = 0,
@ -627,17 +637,20 @@ namespace Mesen.GUI
{
public IntPtr RomNamePointer;
public UInt32 Crc32;
public UInt32 PrgCrc32;
}
public class RomInfo
{
public string RomName;
public UInt32 Crc32;
public UInt32 PrgCrc32;
public RomInfo(InteropRomInfo romInfo)
{
this.RomName = UTF8Marshaler.GetStringFromIntPtr(romInfo.RomNamePointer);
this.Crc32 = romInfo.Crc32;
this.PrgCrc32 = romInfo.PrgCrc32;
}
public string GetRomName()
@ -779,8 +792,7 @@ namespace Mesen.GUI
public enum ConsoleType
{
Nes = 0,
Famicom = 1,
//VsSystem = 2,
Famicom = 1
}
public enum AudioChannel

View File

@ -49,6 +49,7 @@ namespace InteropEmu {
{
const char* RomName;
uint32_t Crc32;
uint32_t PrgCrc32;
};
extern "C" {
@ -141,6 +142,7 @@ namespace InteropEmu {
_returnString = Console::GetRomName();
romInfo.RomName = _returnString.c_str();
romInfo.Crc32 = Console::GetCrc32();
romInfo.PrgCrc32 = Console::GetPrgCrc32();
} else {
RomLoader romLoader;
if(romLoader.LoadFile(filename, nullptr, "", archiveFileIndex)) {
@ -149,10 +151,12 @@ namespace InteropEmu {
_returnString = romData.RomName;
romInfo.RomName = _returnString.c_str();
romInfo.Crc32 = romData.Crc32;
romInfo.PrgCrc32 = romData.PrgCrc32;
} else {
_returnString = "";
romInfo.RomName = _returnString.c_str();
romInfo.Crc32 = 0;
romInfo.PrgCrc32 = 0;
}
}
}
@ -373,12 +377,13 @@ namespace InteropEmu {
}
}
DllExport void __stdcall VsSetGameConfig(PpuModel model, uint8_t dipSwitches)
DllExport void __stdcall VsSetGameConfig(PpuModel model, VsInputType inputType, uint8_t dipSwitches)
{
VsControlManager* vs = VsControlManager::GetInstance();
if(vs) {
EmulationSettings::SetPpuModel(model);
vs->SetDipSwitches(dipSwitches);
vs->SetInputType(inputType);
}
}
}