FDS support (missing sound emulation)

This commit is contained in:
Souryo 2016-01-28 20:47:16 -05:00
parent a68625c8ac
commit df8e66a5e7
32 changed files with 1630 additions and 675 deletions

View file

@ -239,12 +239,12 @@ uint32_t BaseMapper::GetCHRPageCount()
string BaseMapper::GetBatteryFilename()
{
return FolderUtilities::GetSaveFolder() + _romFilename + ".sav";
return FolderUtilities::GetSaveFolder() + FolderUtilities::GetFilename(_romFilename, false) + ".sav";
}
void BaseMapper::RestoreOriginalPrgRam()
{
memcpy(_prgRom, _originalPrgRom, GetPrgSize());
memcpy(_prgRom, _originalPrgRom.data(), _originalPrgRom.size());
}
void BaseMapper::InitializeChrRam()
@ -303,9 +303,9 @@ void BaseMapper::StreamState(bool saving)
}
}
void BaseMapper::Initialize(ROMLoader &romLoader)
void BaseMapper::Initialize(RomData &romData)
{
_romFilename = romLoader.GetFilename();
_romFilename = romData.Filename;
_batteryFilename = GetBatteryFilename();
_saveRamSize = GetSaveRamSize(); //Needed because we need to call SaveBattery() in the destructor (and calling virtual functions in the destructor doesn't work correctly)
@ -314,14 +314,22 @@ void BaseMapper::Initialize(ROMLoader &romLoader)
memset(_isRegisterAddr, 0, sizeof(_isRegisterAddr));
AddRegisterRange(RegisterStartAddress(), RegisterEndAddress());
_mirroringType = romLoader.GetMirroringType();
romLoader.GetPrgRom(&_prgRom);
romLoader.GetPrgRom(&_originalPrgRom);
romLoader.GetChrRom(&_chrRom);
_prgSize = romLoader.GetPrgSize();
_chrRomSize = romLoader.GetChrSize();
_hasBattery = romLoader.HasBattery() || ForceBattery();
_isPalRom = romLoader.IsPalRom();
_mirroringType = romData.MirroringType;
_prgSize = (uint32_t)romData.PrgRom.size();
_chrRomSize = (uint32_t)romData.ChrRom.size();
_originalPrgRom = romData.PrgRom;
_prgRom = new uint8_t[_prgSize];
_chrRom = new uint8_t[_chrRomSize];
memcpy(_prgRom, romData.PrgRom.data(), _prgSize);
if(_chrRomSize > 0) {
memcpy(_chrRom, romData.ChrRom.data(), _chrRomSize);
}
_hasBattery = romData.HasBattery || ForceBattery();
_isPalRom = romData.IsPalRom;
_crc32 = romData.Crc32;
_hasBusConflicts = HasBusConflicts();
_saveRam = new uint8_t[_saveRamSize];
@ -360,6 +368,7 @@ void BaseMapper::Initialize(ROMLoader &romLoader)
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam);
InitMapper();
InitMapper(romData);
MessageManager::RegisterNotificationListener(this);
@ -374,7 +383,6 @@ BaseMapper::~BaseMapper()
delete[] _chrRam;
delete[] _chrRom;
delete[] _prgRom;
delete[] _originalPrgRom;
delete[] _saveRam;
delete[] _workRam;
@ -475,6 +483,11 @@ bool BaseMapper::IsPalRom()
return _isPalRom;
}
uint32_t BaseMapper::GetCrc32()
{
return _crc32;
}
MirroringType BaseMapper::GetMirroringType()
{
return _mirroringType;

View file

@ -4,7 +4,7 @@
#include "Snapshotable.h"
#include "IMemoryHandler.h"
#include "MessageManager.h"
#include "ROMLoader.h"
#include "RomLoader.h"
enum class PrgMemoryType
{
@ -60,8 +60,10 @@ private:
uint32_t _prgPageNumbers[64];
uint32_t _chrPageNumbers[64];
uint8_t* _originalPrgRom = nullptr;
uint32_t _crc32 = 0;
vector<uint8_t> _originalPrgRom;
protected:
uint8_t* _prgRom = nullptr;
@ -77,6 +79,7 @@ protected:
bool _hasBattery = false;
virtual void InitMapper() = 0;
virtual void InitMapper(RomData &romData) { }
virtual uint16_t GetPRGPageSize() = 0;
virtual uint16_t GetCHRPageSize() = 0;
@ -141,7 +144,7 @@ protected:
MirroringType GetMirroringType();
public:
void Initialize(ROMLoader &romLoader);
void Initialize(RomData &romData);
virtual ~BaseMapper();
virtual void Reset(bool softReset) { }
@ -155,6 +158,7 @@ public:
void SetDefaultNametables(uint8_t* nametableA, uint8_t* nametableB);
bool IsPalRom();
uint32_t GetCrc32();
uint8_t ReadRAM(uint16_t addr);
virtual void WriteRAM(uint16_t addr, uint8_t value);

View file

@ -33,6 +33,7 @@ enum class IRQSource
External = 1,
FrameCounter = 2,
DMC = 4,
FdsDisk = 8,
};
struct State

View file

@ -5,6 +5,7 @@
#include "MapperFactory.h"
#include "Debugger.h"
#include "MessageManager.h"
#include "RomLoader.h"
#include "EmulationSettings.h"
#include "../Utilities/Timer.h"
#include "../Utilities/FolderUtilities.h"
@ -93,13 +94,13 @@ bool Console::LoadROM(string filename, uint32_t crc32Hash)
string currentRomFilepath = Console::GetROMPath();
string currentFolder = FolderUtilities::GetFolderName(currentRomFilepath);
if(!currentRomFilepath.empty()) {
if(ROMLoader::GetCRC32(Console::GetROMPath()) == crc32Hash) {
if(RomLoader::GetCRC32(Console::GetROMPath()) == crc32Hash) {
//Current game matches, no need to do anything
return true;
}
//Try to find the game in the same folder as the current game's folder
string match = ROMLoader::FindMatchingRomInFolder(currentFolder, filename, crc32Hash);
string match = RomLoader::FindMatchingRomInFolder(currentFolder, filename, crc32Hash);
if(!match.empty()) {
Console::LoadROM(match);
return true;
@ -108,7 +109,7 @@ bool Console::LoadROM(string filename, uint32_t crc32Hash)
for(string folder : FolderUtilities::GetKnowGameFolders()) {
if(folder != currentFolder) {
string match = ROMLoader::FindMatchingRomInFolder(folder, filename, crc32Hash);
string match = RomLoader::FindMatchingRomInFolder(folder, filename, crc32Hash);
if(!match.empty()) {
Console::LoadROM(match);
return true;
@ -123,6 +124,11 @@ string Console::GetROMPath()
return Instance->_romFilepath;
}
uint32_t Console::GetCrc32()
{
return Instance->_mapper->GetCrc32();
}
void Console::Reset(bool softReset)
{
Movie::Stop();

View file

@ -61,8 +61,8 @@ class Console
static void LoadROM(string filepath, stringstream *filestream = nullptr);
static bool LoadROM(string romName, uint32_t crc32Hash);
static void ApplyIpsPatch(string ipsFilename);
static string FindMatchingRomInFolder(string folder, string romFilename, uint32_t crc32Hash);
static string GetROMPath();
static uint32_t GetCrc32();
static shared_ptr<Console> GetInstance();
static void Release();

View file

@ -366,7 +366,10 @@
<ClInclude Include="DebugState.h" />
<ClInclude Include="DefaultVideoFilter.h" />
<ClInclude Include="ExpressionEvaluator.h" />
<ClInclude Include="FDS.h" />
<ClInclude Include="FdsLoader.h" />
<ClInclude Include="HdVideoFilter.h" />
<ClInclude Include="iNesLoader.h" />
<ClInclude Include="IremH3001.h" />
<ClInclude Include="IremLrog017.h" />
<ClInclude Include="IremTamS1.h" />
@ -401,6 +404,7 @@
<ClInclude Include="MMC3_49.h" />
<ClInclude Include="MMC3_52.h" />
<ClInclude Include="MMC3_ChrRam.h" />
<ClInclude Include="RomData.h" />
<ClInclude Include="NtdecTc112.h" />
<ClInclude Include="Rambo1.h" />
<ClInclude Include="SoundMixer.h" />
@ -480,7 +484,7 @@
<ClInclude Include="PPU.h" />
<ClInclude Include="CPU.h" />
<ClInclude Include="MemoryManager.h" />
<ClInclude Include="ROMLoader.h" />
<ClInclude Include="RomLoader.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="Sunsoft184.h" />
<ClInclude Include="SunSoft3.h" />
@ -523,6 +527,7 @@
<ClCompile Include="DisassemblyInfo.cpp" />
<ClCompile Include="EmulationSettings.cpp" />
<ClCompile Include="ExpressionEvaluator.cpp" />
<ClCompile Include="FDS.cpp" />
<ClCompile Include="GameClient.cpp" />
<ClCompile Include="GameClientConnection.cpp" />
<ClCompile Include="GameConnection.cpp" />
@ -530,6 +535,7 @@
<ClCompile Include="GameServerConnection.cpp" />
<ClCompile Include="HdVideoFilter.cpp" />
<ClCompile Include="NtscFilter.cpp" />
<ClCompile Include="RomLoader.cpp" />
<ClCompile Include="SoundMixer.cpp" />
<ClCompile Include="StandardController.cpp" />
<ClCompile Include="MapperFactory.cpp" />

View file

@ -36,6 +36,33 @@
<Filter Include="VideoDecoder\HD">
<UniqueIdentifier>{a6994cb5-f9d2-416c-84ab-c1abe4975eb1}</UniqueIdentifier>
</Filter>
<Filter Include="Nes\RomLoader">
<UniqueIdentifier>{cfdbaafc-8e74-4e09-80fd-30f8bc833c88}</UniqueIdentifier>
</Filter>
<Filter Include="Nes\Mappers\MMC">
<UniqueIdentifier>{264ce0a6-616f-4098-8d7d-645d1fa3672b}</UniqueIdentifier>
</Filter>
<Filter Include="Nes\Mappers\Unnamed">
<UniqueIdentifier>{d2059959-ca5c-48ae-8b55-b84cd0d65633}</UniqueIdentifier>
</Filter>
<Filter Include="Nes\Mappers\Sunsoft">
<UniqueIdentifier>{82a608bc-1b40-4551-9c82-b4c9fdfe60f0}</UniqueIdentifier>
</Filter>
<Filter Include="Nes\Mappers\Irem">
<UniqueIdentifier>{c99c645d-703b-471a-a01b-97375bfb3aaf}</UniqueIdentifier>
</Filter>
<Filter Include="Nes\Mappers\Sachen">
<UniqueIdentifier>{4d835f6a-a005-4f62-8c6b-d5cdb9d96dd2}</UniqueIdentifier>
</Filter>
<Filter Include="Nes\Mappers\Namco">
<UniqueIdentifier>{609b9f1a-b212-4322-b835-f68cd59e6c8a}</UniqueIdentifier>
</Filter>
<Filter Include="Nes\Mappers\Jaleco">
<UniqueIdentifier>{b3fac043-0477-496b-80d1-77c4969262e3}</UniqueIdentifier>
</Filter>
<Filter Include="Nes\Mappers\FDS">
<UniqueIdentifier>{4a7af167-f6cb-4173-b7ca-04ed7c5858b1}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
@ -53,9 +80,6 @@
<ClInclude Include="MapperFactory.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC1.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="NROM.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
@ -65,18 +89,12 @@
<ClInclude Include="UNROM.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC3.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Snapshotable.h">
<Filter>Nes\Interfaces</Filter>
</ClInclude>
<ClInclude Include="AXROM.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC2.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="ColorDreams.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
@ -116,9 +134,6 @@
<ClInclude Include="Nanjing.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC3_189.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Debugger.h">
<Filter>Debugger</Filter>
</ClInclude>
@ -173,9 +188,6 @@
<ClInclude Include="BF909x.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC5.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="HdNesPack.h">
<Filter>VideoDecoder\HD</Filter>
</ClInclude>
@ -191,18 +203,9 @@
<ClInclude Include="AutoRomTest.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="MMC4.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="CpRom.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="JalecoSs88006.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="IremG101.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="TaitoTc0190.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
@ -215,57 +218,18 @@
<ClInclude Include="Nina03_06.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="JalecoJfxx.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Bandai74161_7432.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="TaitoX1005.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Sunsoft89.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Sunsoft93.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Sunsoft184.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="BnRom.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Nina01.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Sachen_147.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Sachen_145.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Sachen_149.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Sachen_148.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Namco108.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Namco108_76.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Namco108_88.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Namco108_154.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Namco108_95.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="DefaultVideoFilter.h">
<Filter>VideoDecoder</Filter>
</ClInclude>
@ -317,9 +281,6 @@
<ClInclude Include="PPU.h">
<Filter>Nes</Filter>
</ClInclude>
<ClInclude Include="ROMLoader.h">
<Filter>Nes</Filter>
</ClInclude>
<ClInclude Include="StandardController.h">
<Filter>Nes</Filter>
</ClInclude>
@ -344,129 +305,21 @@
<ClInclude Include="UnRom_180.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="IremTamS1.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="UnRom_94.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC3_115.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="BF9096.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper242.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper240.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper246.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper231.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper200.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="NtdecTc112.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC3_ChrRam.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper58.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper107.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="JalecoJf17_19.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="JalecoJf16.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="IremLrog017.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper203.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper202.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper201.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC3_205.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC3_37.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC3_12.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC3_47.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC3_44.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC3_49.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC3_52.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper57.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper61.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper62.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="SunSoft3.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="VRC3.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="SunSoft4.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper112.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper225.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper230.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="JalecoJf11_14.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper91.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="IremH3001.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Rambo1.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="Mapper226.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="MMC3_45.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="BaseMapper.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
@ -485,6 +338,192 @@
<ClInclude Include="VRC7.h">
<Filter>Nes\Mappers</Filter>
</ClInclude>
<ClInclude Include="FdsLoader.h">
<Filter>Nes\RomLoader</Filter>
</ClInclude>
<ClInclude Include="iNesLoader.h">
<Filter>Nes\RomLoader</Filter>
</ClInclude>
<ClInclude Include="RomLoader.h">
<Filter>Nes\RomLoader</Filter>
</ClInclude>
<ClInclude Include="RomData.h">
<Filter>Nes\RomLoader</Filter>
</ClInclude>
<ClInclude Include="MMC1.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC2.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC3.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC3_12.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC3_37.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC3_44.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC3_45.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC3_47.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC3_49.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC3_52.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC3_115.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC3_189.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC3_205.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC3_ChrRam.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC4.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="MMC5.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
<ClInclude Include="Mapper57.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper58.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper61.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper62.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper91.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper107.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper112.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper200.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper201.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper202.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper203.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper225.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper226.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper230.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper231.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper240.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper242.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="Mapper246.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
<ClInclude Include="SunSoft3.h">
<Filter>Nes\Mappers\Sunsoft</Filter>
</ClInclude>
<ClInclude Include="SunSoft4.h">
<Filter>Nes\Mappers\Sunsoft</Filter>
</ClInclude>
<ClInclude Include="Sunsoft89.h">
<Filter>Nes\Mappers\Sunsoft</Filter>
</ClInclude>
<ClInclude Include="Sunsoft93.h">
<Filter>Nes\Mappers\Sunsoft</Filter>
</ClInclude>
<ClInclude Include="Sunsoft184.h">
<Filter>Nes\Mappers\Sunsoft</Filter>
</ClInclude>
<ClInclude Include="Namco108.h">
<Filter>Nes\Mappers\Namco</Filter>
</ClInclude>
<ClInclude Include="Namco108_76.h">
<Filter>Nes\Mappers\Namco</Filter>
</ClInclude>
<ClInclude Include="Namco108_88.h">
<Filter>Nes\Mappers\Namco</Filter>
</ClInclude>
<ClInclude Include="Namco108_95.h">
<Filter>Nes\Mappers\Namco</Filter>
</ClInclude>
<ClInclude Include="Namco108_154.h">
<Filter>Nes\Mappers\Namco</Filter>
</ClInclude>
<ClInclude Include="IremG101.h">
<Filter>Nes\Mappers\Irem</Filter>
</ClInclude>
<ClInclude Include="IremH3001.h">
<Filter>Nes\Mappers\Irem</Filter>
</ClInclude>
<ClInclude Include="IremLrog017.h">
<Filter>Nes\Mappers\Irem</Filter>
</ClInclude>
<ClInclude Include="IremTamS1.h">
<Filter>Nes\Mappers\Irem</Filter>
</ClInclude>
<ClInclude Include="Sachen_145.h">
<Filter>Nes\Mappers\Sachen</Filter>
</ClInclude>
<ClInclude Include="Sachen_147.h">
<Filter>Nes\Mappers\Sachen</Filter>
</ClInclude>
<ClInclude Include="Sachen_148.h">
<Filter>Nes\Mappers\Sachen</Filter>
</ClInclude>
<ClInclude Include="Sachen_149.h">
<Filter>Nes\Mappers\Sachen</Filter>
</ClInclude>
<ClInclude Include="JalecoJf11_14.h">
<Filter>Nes\Mappers\Jaleco</Filter>
</ClInclude>
<ClInclude Include="JalecoJf16.h">
<Filter>Nes\Mappers\Jaleco</Filter>
</ClInclude>
<ClInclude Include="JalecoJf17_19.h">
<Filter>Nes\Mappers\Jaleco</Filter>
</ClInclude>
<ClInclude Include="JalecoJfxx.h">
<Filter>Nes\Mappers\Jaleco</Filter>
</ClInclude>
<ClInclude Include="JalecoSs88006.h">
<Filter>Nes\Mappers\Jaleco</Filter>
</ClInclude>
<ClInclude Include="FDS.h">
<Filter>Nes\Mappers\FDS</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@ -598,5 +637,11 @@
<ClCompile Include="BaseMapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RomLoader.cpp">
<Filter>Nes\RomLoader</Filter>
</ClCompile>
<ClCompile Include="FDS.cpp">
<Filter>Nes\Mappers\FDS</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -12,6 +12,8 @@ enum EmulationFlags
RemoveSpriteLimit = 0x10,
UseHdPacks = 0x20,
FdsFastForwardOnLoad = 0x2000,
FdsAutoLoadDisk = 0x4000,
Mmc3IrqAltBehavior = 0x8000,
};

4
Core/FDS.cpp Normal file
View file

@ -0,0 +1,4 @@
#include "stdafx.h"
#include "FDS.h"
FDS* FDS::Instance = nullptr;

460
Core/FDS.h Normal file
View file

@ -0,0 +1,460 @@
#pragma once
#include "stdafx.h"
#include <assert.h>
#include "BaseMapper.h"
#include "CPU.h"
#include "EmulationSettings.h"
#include "FdsLoader.h"
#include "Console.h"
class FDS : public BaseMapper
{
private:
static const uint32_t NoDiskInserted = 0xFF;
static const uint32_t DiskInsertDelay = 3600000; //approx 2 sec delay
static FDS* Instance;
//Write registers
uint16_t _irqReloadValue = 0;
uint16_t _irqCounter = 0;
bool _irqEnabled = false;
bool _irqReloadEnabled = false;
bool _diskRegEnabled = true;
bool _soundRegEnabled = true;
uint8_t _writeDataReg = 0;
bool _motorOn = false;
bool _resetTransfer = false;
bool _readMode = false;
bool _crcControl = false;
bool _diskReady = false;
bool _diskIrqEnabled = false;
uint8_t _extConWriteReg = 0;
//Read registers
bool _badCrc = false;
bool _endOfHead = false;
bool _readWriteEnabled = false;
uint8_t _readDataReg = 0;
bool _diskWriteProtected = false;
//Internal values
uint32_t _diskNumber = 0;
uint32_t _newDiskNumber = 0;
uint32_t _newDiskInsertDelay = 0;
uint32_t _diskPosition = 0;
uint32_t _delay = 0;
uint16_t _crcAccumulator;
bool _previousCrcControlFlag = false;
bool _gapEnded = true;
bool _scanningDisk = false;
bool _needIrq = false;
bool _transferComplete = false;
bool _isDirty = false;
vector<uint8_t> _fdsRawData;
vector<vector<uint8_t>> _fdsDiskSides;
string _romFilepath;
protected:
virtual uint16_t GetPRGPageSize() { return 0x2000; }
virtual uint16_t GetCHRPageSize() { return 0x2000; }
virtual uint32_t GetWorkRamPageSize() { return 0x8000; }
virtual uint32_t GetWorkRamSize() { return 0x8000; }
uint16_t RegisterStartAddress() { return 0x4020; }
uint16_t RegisterEndAddress() { return 0x4092; }
bool AllowRegisterRead() { return true; }
void InitMapper()
{
_diskNumber = EmulationSettings::CheckFlag(EmulationFlags::FdsAutoLoadDisk) ? 0 : FDS::NoDiskInserted;
//FDS BIOS
SetCpuMemoryMapping(0xE000, 0xFFFF, 0, PrgMemoryType::PrgRom, MemoryAccessType::Read);
//Work RAM
SetCpuMemoryMapping(0x6000, 0xDFFF, 0, PrgMemoryType::WorkRam, MemoryAccessType::ReadWrite);
//8k of CHR RAM
SelectCHRPage(0, 0);
}
void InitMapper(RomData &romData)
{
_romFilepath = romData.Filename;
_fdsDiskSides = romData.FdsDiskData;
_fdsRawData = romData.RawData;
}
uint32_t GetFdsDiskSideSize(uint8_t side)
{
assert(side < _fdsDiskSides.size());
return (uint32_t)_fdsDiskSides[side].size();
}
uint8_t ReadFdsDisk()
{
assert(_diskNumber < _fdsDiskSides.size());
assert(_diskPosition < _fdsDiskSides[_diskNumber].size());
return _fdsDiskSides[_diskNumber][_diskPosition];
}
void WriteFdsDisk(uint8_t value)
{
assert(_diskNumber < _fdsDiskSides.size());
assert(_diskPosition < _fdsDiskSides[_diskNumber].size());
if(_fdsDiskSides[_diskNumber][_diskPosition - 2] != value) {
_isDirty = true;
}
_fdsDiskSides[_diskNumber][_diskPosition - 2] = value;
}
void ClockIrq()
{
if(_needIrq) {
CPU::SetIRQSource(IRQSource::External);
_needIrq = false;
}
if(_irqEnabled && _irqCounter > 0) {
_irqCounter--;
if(_irqCounter == 0) {
_needIrq = true;
if(_irqReloadEnabled) {
_irqCounter = _irqReloadValue;
} else {
_irqEnabled = false;
_irqReloadValue = 0;
}
}
}
}
void ProcessCpuClock()
{
ClockIrq();
if(EmulationSettings::CheckFlag(EmulationFlags::FdsFastForwardOnLoad)) {
EmulationSettings::SetEmulationSpeed(_scanningDisk ? 0 : 100);
}
if(_newDiskInsertDelay > 0) {
//Insert new disk after delay expires, to allow games to notice the disk was ejected
_newDiskInsertDelay--;
_diskNumber = FDS::NoDiskInserted;
} else {
_diskNumber = _newDiskNumber;
}
if(_diskNumber == FDS::NoDiskInserted || !_motorOn) {
//Disk has been ejected
_endOfHead = true;
_scanningDisk = false;
return;
}
if(_resetTransfer && !_scanningDisk) {
return;
}
if(_endOfHead) {
_delay = 50000;
_endOfHead = false;
_diskPosition = 0;
_gapEnded = false;
return;
}
if(_delay > 0) {
_delay--;
} else {
_scanningDisk = true;
uint8_t diskData = 0;
bool needIrq = _diskIrqEnabled;
if(_readMode) {
diskData = ReadFdsDisk();
if(!_previousCrcControlFlag) {
UpdateCrc(diskData);
}
if(!_diskReady) {
_gapEnded = false;
_crcAccumulator = 0;
} else if(diskData && !_gapEnded) {
_gapEnded = true;
needIrq = false;
}
if(_gapEnded) {
_transferComplete = true;
_readDataReg = diskData;
if(needIrq) {
CPU::SetIRQSource(IRQSource::FdsDisk);
}
}
} else {
if(!_crcControl) {
_transferComplete = true;
diskData = _writeDataReg;
if(needIrq) {
CPU::SetIRQSource(IRQSource::FdsDisk);
}
}
if(!_diskReady) {
diskData = 0x00;
}
if(!_crcControl) {
UpdateCrc(diskData);
} else {
if(!_previousCrcControlFlag) {
//Finish CRC calculation
UpdateCrc(0x00);
UpdateCrc(0x00);
}
diskData = _crcAccumulator & 0xFF;
_crcAccumulator >>= 8;
}
WriteFdsDisk(diskData);
_gapEnded = false;
}
_previousCrcControlFlag = _crcControl;
_diskPosition++;
if(_diskPosition >= GetFdsDiskSideSize(_diskNumber)) {
_motorOn = false;
} else {
_delay = 150;
}
}
}
void UpdateCrc(uint8_t value)
{
for(uint16_t n = 0x01; n <= 0x80; n <<= 1) {
uint8_t carry = (_crcAccumulator & 1);
_crcAccumulator >>= 1;
if(carry) {
_crcAccumulator ^= 0x8408;
}
if(value & n) {
_crcAccumulator ^= 0x8000;
}
}
}
void WriteRegister(uint16_t addr, uint8_t value)
{
if(!_diskRegEnabled && addr >= 0x4024 && addr <= 0x4026) {
return;
}
switch(addr) {
case 0x4020:
_irqReloadValue = (_irqReloadValue & 0xFF00) | value;
CPU::ClearIRQSource(IRQSource::External);
break;
case 0x4021:
_irqReloadValue = (_irqReloadValue & 0x00FF) | (value << 8);
CPU::ClearIRQSource(IRQSource::External);
break;
case 0x4022:
_irqReloadEnabled = (value & 0x01) == 0x01;
_irqEnabled = (value & 0x02) == 0x02;
_irqCounter = _irqReloadValue;
CPU::ClearIRQSource(IRQSource::External);
break;
case 0x4023:
_diskRegEnabled = (value & 0x01) == 0x01;
_soundRegEnabled = (value & 0x02) == 0x02;
break;
case 0x4024:
_writeDataReg = value;
_transferComplete = false;
CPU::ClearIRQSource(IRQSource::FdsDisk);
break;
case 0x4025:
_motorOn = (value & 0x01) == 0x01;
_resetTransfer = (value & 0x02) == 0x02;
_readMode = (value & 0x04) == 0x04;
SetMirroringType(value & 0x08 ? MirroringType::Horizontal : MirroringType::Vertical);
_crcControl = (value & 0x10) == 0x10;
//Bit 6 is not used, always 1
_diskReady = (value & 0x40) == 0x40;
_diskIrqEnabled = (value & 0x80) == 0x80;
break;
case 0x4026:
_extConWriteReg = value;
break;
}
}
bool IsDiskInserted()
{
return _diskNumber != FDS::NoDiskInserted;
}
uint8_t ReadRegister(uint16_t addr)
{
uint8_t value = 0;
if(_diskRegEnabled) {
switch(addr) {
case 0x4030:
value |= CPU::HasIRQSource(IRQSource::External) ? 0x01 : 0x00;
value |= _transferComplete ? 0x02 : 0x00;
value |= _badCrc ? 0x10 : 0x00;
//value |= _endOfHead ? 0x40 : 0x00;
//value |= _diskRegEnabled ? 0x80 : 0x00;
_transferComplete = false;
CPU::ClearIRQSource(IRQSource::External);
CPU::ClearIRQSource(IRQSource::FdsDisk);
return value;
case 0x4031:
_transferComplete = false;
CPU::ClearIRQSource(IRQSource::FdsDisk);
return _readDataReg;
case 0x4032:
value |= !IsDiskInserted() ? 0x01 : 0x00; //Disk not in drive
value |= !IsDiskInserted() || !_scanningDisk ? 0x02 : 0x00; //Disk not ready
value |= !IsDiskInserted() ? 0x04 : 0x00; //Disk not writable
return value;
case 0x4033:
//Always return good battery
return 0x80 & _extConWriteReg;
}
}
//Return open bus
return (addr & 0xFF00) >> 8;
}
void StreamState(bool saving)
{
BaseMapper::StreamState(saving);
Stream<uint16_t>(_irqReloadValue);
Stream<uint16_t>(_irqCounter);
Stream<bool>(_irqEnabled);
Stream<bool>(_irqReloadEnabled);
Stream<bool>(_diskRegEnabled);
Stream<bool>(_soundRegEnabled);
Stream<uint8_t>(_writeDataReg);
Stream<bool>(_motorOn);
Stream<bool>(_resetTransfer);
Stream<bool>(_readMode);
Stream<bool>(_crcControl);
Stream<bool>(_diskReady);
Stream<bool>(_diskIrqEnabled);
Stream<uint8_t>(_extConWriteReg);
Stream<bool>(_badCrc);
Stream<bool>(_endOfHead);
Stream<bool>(_readWriteEnabled);
Stream<uint8_t>(_readDataReg);
Stream<bool>(_diskWriteProtected);
Stream<uint32_t>(_diskNumber);
Stream<uint32_t>(_newDiskNumber);
Stream<uint32_t>(_newDiskInsertDelay);
Stream<uint32_t>(_diskPosition);
Stream<uint32_t>(_delay);
Stream<bool>(_previousCrcControlFlag);
Stream<bool>(_gapEnded);
Stream<bool>(_scanningDisk);
Stream<bool>(_needIrq);
Stream<bool>(_transferComplete);
Stream<bool>(_isDirty);
}
public:
FDS()
{
FDS::Instance = this;
}
~FDS()
{
if(_isDirty) {
FdsLoader loader;
loader.SaveIpsFile(_romFilepath, _fdsRawData, _fdsDiskSides);
}
if(FDS::Instance == this) {
FDS::Instance = nullptr;
}
}
static uint32_t GetSideCount()
{
if(FDS::Instance) {
return (uint32_t)FDS::Instance->_fdsDiskSides.size();
} else {
return 0;
}
}
static void InsertDisk(uint32_t diskNumber)
{
if(FDS::Instance) {
Console::Pause();
FDS::Instance->_newDiskNumber = diskNumber;
FDS::Instance->_newDiskInsertDelay = FDS::DiskInsertDelay;
Console::Resume();
MessageManager::SendNotification(ConsoleNotificationType::FdsDiskChanged);
}
}
static void SwitchDiskSide()
{
if(FDS::Instance) {
Console::Pause();
FDS::Instance->_newDiskNumber = (FDS::Instance->_diskNumber & 0x01) ? (FDS::Instance->_diskNumber & 0xFE) : (FDS::Instance->_diskNumber | 0x01);
FDS::Instance->_newDiskInsertDelay = FDS::DiskInsertDelay;
Console::Resume();
MessageManager::SendNotification(ConsoleNotificationType::FdsDiskChanged);
}
}
static void EjectDisk()
{
if(FDS::Instance) {
Console::Pause();
FDS::Instance->_diskNumber = NoDiskInserted;
FDS::Instance->_newDiskInsertDelay = 0;
Console::Resume();
MessageManager::SendNotification(ConsoleNotificationType::FdsDiskChanged);
}
}
};

171
Core/FdsLoader.h Normal file
View file

@ -0,0 +1,171 @@
#pragma once
#include "stdafx.h"
#include <algorithm>
#include "../Utilities/FolderUtilities.h"
#include "../Utilities/IpsPatcher.h"
#include "RomData.h"
#include "MessageManager.h"
#include "MapperFactory.h"
class FdsLoader
{
private:
const size_t FdsDiskSideCapacity = 65500;
private:
void AddGaps(vector<uint8_t>& diskSide, uint8_t* readBuffer)
{
//Start image with 28300 bits of gap
diskSide.insert(diskSide.end(), 28300 / 8, 0);
for(size_t j = 0; j < FdsDiskSideCapacity;) {
uint8_t blockType = readBuffer[j];
uint32_t blockLength = 1;
switch(blockType) {
case 1: blockLength = 56; break; //Disk header
case 2: blockLength = 2; break; //File count
case 3: blockLength = 16; break; //File header
case 4: blockLength = 1 + readBuffer[j - 3] + readBuffer[j - 2] * 0x100; break;
default: return; //End parsing when we encounter an invalid block type (This is what Nestopia apppears to do)
}
if(blockType == 0) {
diskSide.push_back(blockType);
} else {
diskSide.push_back(0x80);
diskSide.insert(diskSide.end(), &readBuffer[j], &readBuffer[j] + blockLength);
//Fake CRC value
diskSide.push_back(0x4D);
diskSide.push_back(0x62);
//Insert 976 bits of gap after a block
diskSide.insert(diskSide.end(), 976 / 8, 0);
}
j += blockLength;
}
}
vector<uint8_t> RebuildFdsFile(vector<vector<uint8_t>> diskData, bool needHeader)
{
vector<uint8_t> output;
output.reserve(diskData.size() * FdsDiskSideCapacity + 16);
if(needHeader) {
uint8_t header[16] = { 'F', 'D', 'S', '\x1a', (uint8_t)diskData.size(), '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' };
output.insert(output.end(), header, header + sizeof(header));
}
for(vector<uint8_t> &diskSide : diskData) {
bool inGap = true;
size_t i = 0, len = diskSide.size();
size_t gapNeeded = FdsDiskSideCapacity;
uint32_t fileSize = 0;
while(i < len) {
if(inGap) {
if(diskSide[i] == 0x80) {
inGap = false;
}
i++;
} else {
uint32_t blockLength = 1;
switch(diskSide[i]) {
case 1: blockLength = 56; break; //Disk header
case 2: blockLength = 2; break; //File count
case 3: blockLength = 16; fileSize = diskSide[i + 13] + diskSide[i + 14] * 0x100; break; //File header
case 4: blockLength = 1 + fileSize; break;
}
output.insert(output.end(), &diskSide[i], &diskSide[i] + blockLength);
gapNeeded -= blockLength;
i += blockLength;
i += 2; //Skip CRC after block
inGap = true;
}
}
output.insert(output.end(), gapNeeded, 0);
}
return output;
}
void LoadDiskData(vector<uint8_t>& romFile, RomData &romData)
{
uint8_t numberOfSides = 0;
size_t fileOffset = 0;
bool hasHeader = memcmp(romFile.data(), "FDS\x1a", 4) == 0;
if(hasHeader) {
numberOfSides = romFile[4];
fileOffset = 16;
} else {
numberOfSides = (uint8_t)(romFile.size() / 65500);
}
for(uint32_t i = 0; i < numberOfSides; i++) {
romData.FdsDiskData.push_back(vector<uint8_t>());
vector<uint8_t> &fdsDiskImage = romData.FdsDiskData.back();
AddGaps(fdsDiskImage, &romFile[fileOffset]);
fileOffset += FdsDiskSideCapacity;
//Ensure the image is 65500 bytes
if(fdsDiskImage.size() < FdsDiskSideCapacity) {
fdsDiskImage.resize(FdsDiskSideCapacity);
}
}
}
vector<uint8_t> LoadBios()
{
//For FDS, the PRG ROM is the FDS BIOS (8k)
vector<uint8_t> biosData;
ifstream biosFile("FdsBios.bin", ios::in | ios::binary);
if(biosFile) {
return vector<uint8_t>(std::istreambuf_iterator<char>(biosFile), {});
} else {
MessageManager::SendNotification(ConsoleNotificationType::FdsBiosNotFound);
}
return {};
}
public:
void SaveIpsFile(string filename, vector<uint8_t> &originalDiskData, vector<vector<uint8_t>> &currentDiskData)
{
bool needHeader = (memcmp(originalDiskData.data(), "FDS\x1a", 4) == 0);
vector<uint8_t> newData = RebuildFdsFile(currentDiskData, needHeader);
vector<uint8_t> ipsData = IpsPatcher::CreatePatch(originalDiskData, newData);
string fdsSaveFilepath = FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), FolderUtilities::GetFilename(filename, false) + ".ips");
ofstream outputIps(fdsSaveFilepath, ios::binary);
if(outputIps) {
outputIps.write((char*)ipsData.data(), ipsData.size());
outputIps.close();
}
}
RomData LoadRom(vector<uint8_t>& romFile, string filename)
{
//Apply save data (saved as an IPS file), if found
string fdsSaveFilepath = FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), FolderUtilities::GetFilename(filename, false) + ".ips");
romFile = IpsPatcher::PatchBuffer(fdsSaveFilepath, romFile);
RomData romData;
romData.MapperID = MapperFactory::FdsMapperID;
romData.SubMapperID = 0;
romData.MirroringType = MirroringType::Vertical;
romData.HasBattery = false;
romData.IsPalRom = false;
romData.PrgRom = LoadBios();
if(romData.PrgRom.size() != 0x2000) {
romData.Error = true;
} else {
LoadDiskData(romFile, romData);
}
return romData;
}
};

View file

@ -3,7 +3,7 @@
#include "MessageManager.h"
#include "NetMessage.h"
#include "Console.h"
#include "ROMLoader.h"
#include "RomLoader.h"
#include "../Utilities/FolderUtilities.h"
class GameInformationMessage : public NetMessage
@ -27,10 +27,10 @@ protected:
public:
GameInformationMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
GameInformationMessage(string filepath, uint8_t port, bool paused) : NetMessage(MessageType::GameInformation)
GameInformationMessage(string filepath, uint32_t crc32Hash, uint8_t port, bool paused) : NetMessage(MessageType::GameInformation)
{
CopyString(&_romFilename, _romFilenameLength, FolderUtilities::GetFilename(filepath, true));
_crc32Hash = ROMLoader::GetCRC32(filepath);
_crc32Hash = crc32Hash;
_controllerPort = port;
_paused = paused;
}

View file

@ -38,7 +38,7 @@ GameServerConnection::~GameServerConnection()
void GameServerConnection::SendGameInformation()
{
Console::Pause();
GameInformationMessage gameInfo(Console::GetROMPath(), _controllerPort, EmulationSettings::CheckFlag(EmulationFlags::Paused));
GameInformationMessage gameInfo(Console::GetROMPath(), Console::GetCrc32(), _controllerPort, EmulationSettings::CheckFlag(EmulationFlags::Paused));
SendNetMessage(gameInfo);
SaveStateMessage saveState;
SendNetMessage(saveState);
@ -109,6 +109,7 @@ void GameServerConnection::ProcessNotification(ConsoleNotificationType type, voi
case ConsoleNotificationType::GameReset:
case ConsoleNotificationType::StateLoaded:
case ConsoleNotificationType::CheatAdded:
case ConsoleNotificationType::FdsDiskChanged:
SendGameInformation();
break;
default:

View file

@ -15,6 +15,8 @@ enum class ConsoleNotificationType
PpuFrameDone = 9,
MovieEnded = 10,
ResolutionChanged = 11,
FdsDiskChanged = 12,
FdsBiosNotFound = 13,
};
class INotificationListener

View file

@ -1,7 +1,7 @@
#include "stdafx.h"
#include "MessageManager.h"
#include "MapperFactory.h"
#include "ROMLoader.h"
#include "RomLoader.h"
#include "AXROM.h"
#include "Bandai74161_7432.h"
#include "BnRom.h"
@ -10,6 +10,7 @@
#include "CNROM.h"
#include "CpRom.h"
#include "ColorDreams.h"
#include "FDS.h"
#include "GxRom.h"
#include "IremG101.h"
#include "IremH3001.h"
@ -87,13 +88,13 @@
#include "VRC6.h"
#include "VRC7.h"
BaseMapper* MapperFactory::GetMapperFromID(ROMLoader &romLoader)
BaseMapper* MapperFactory::GetMapperFromID(RomData &romData)
{
#ifdef _DEBUG
MessageManager::DisplayMessage("Game Info", "Mapper: " + std::to_string(romLoader.GetMapperID()));
MessageManager::DisplayMessage("Game Info", "Mapper: " + std::to_string(romData.MapperID));
#endif
switch(romLoader.GetMapperID()) {
switch(romData.MapperID) {
case 0: return new NROM();
case 1: return new MMC1();
case 2: return new UNROM();
@ -118,7 +119,7 @@ BaseMapper* MapperFactory::GetMapperFromID(ROMLoader &romLoader)
case 27: return new VRC2_4(VRCVariant::VRC4_27); //Untested
case 32: return new IremG101();
case 33: return new TaitoTc0190();
case 34: return (romLoader.GetChrSize() > 0) ? (BaseMapper*)new Nina01() : (BaseMapper*)new BnRom(); //BnROM uses CHR RAM (so no CHR rom in the .NES file)
case 34: return (romData.ChrRom.size() > 0) ? (BaseMapper*)new Nina01() : (BaseMapper*)new BnRom(); //BnROM uses CHR RAM (so no CHR rom in the .NES file)
case 37: return new MMC3_37();
case 38: return new UnlPci556();
case 44: return new MMC3_44();
@ -143,7 +144,7 @@ BaseMapper* MapperFactory::GetMapperFromID(ROMLoader &romLoader)
case 75: return new VRC1();
case 76: return new Namco108_76();
case 77: return new IremLrog017();
case 78: return new JalecoJf16(romLoader.GetSubMapper() == 3);
case 78: return new JalecoJf16(romData.SubMapperID == 3);
case 79: return new Nina03_06(false);
case 80: return new TaitoX1005(false);
case 85: return new VRC7();
@ -196,6 +197,8 @@ BaseMapper* MapperFactory::GetMapperFromID(ROMLoader &romLoader)
case 240: return new Mapper240();
case 242: return new Mapper242();
case 246: return new Mapper246();
case MapperFactory::FdsMapperID: return new FDS();
}
MessageManager::DisplayMessage("Error", "Unsupported mapper, cannot load game.");
@ -204,13 +207,14 @@ BaseMapper* MapperFactory::GetMapperFromID(ROMLoader &romLoader)
shared_ptr<BaseMapper> MapperFactory::InitializeFromFile(string romFilename, stringstream *filestream, string ipsFilename)
{
ROMLoader loader;
RomLoader loader;
if(loader.LoadFile(romFilename, filestream, ipsFilename)) {
shared_ptr<BaseMapper> mapper(GetMapperFromID(loader));
RomData romData = loader.GetRomData();
shared_ptr<BaseMapper> mapper(GetMapperFromID(romData));
if(mapper) {
mapper->Initialize(loader);
mapper->Initialize(romData);
return mapper;
}
}

View file

@ -1,11 +1,14 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "RomData.h"
class MapperFactory
{
private:
static BaseMapper* GetMapperFromID(ROMLoader &romLoader);
static BaseMapper* GetMapperFromID(RomData &romData);
public:
static const uint16_t FdsMapperID = 65535;
static shared_ptr<BaseMapper> InitializeFromFile(string romFilename, stringstream *filestream, string ipsFilename);
};

View file

@ -3,7 +3,7 @@
#include "Movie.h"
#include "Console.h"
#include "../Utilities/FolderUtilities.h"
#include "ROMLoader.h"
#include "RomLoader.h"
Movie* Movie::Instance = new Movie();
@ -178,7 +178,7 @@ bool Movie::Save()
string romFilepath = Console::GetROMPath();
string romFilename = FolderUtilities::GetFilename(romFilepath, true);
uint32_t romCrc32 = ROMLoader::GetCRC32(romFilepath);
uint32_t romCrc32 = RomLoader::GetCRC32(romFilepath);
_file.write((char*)&romCrc32, sizeof(romCrc32));
uint32_t romNameSize = (uint32_t)romFilename.size();
@ -233,7 +233,7 @@ bool Movie::Load(std::stringstream &file, bool autoLoadRom)
bool loadedGame = true;
if(autoLoadRom) {
string currentRom = Console::GetROMPath();
if(currentRom.empty() || romCrc32 != ROMLoader::GetCRC32(currentRom)) {
if(currentRom.empty() || romCrc32 != RomLoader::GetCRC32(currentRom)) {
//Loaded game isn't the same as the game used for the movie, attempt to load the correct game
loadedGame = Console::LoadROM(romFilename, romCrc32);
}

View file

@ -6,406 +6,25 @@
#include "../Utilities/ZipReader.h"
#include "../Utilities/CRC32.h"
#include "../Utilities/IpsPatcher.h"
#include "RomData.h"
enum class MirroringType
{
Horizontal,
Vertical,
ScreenAOnly,
ScreenBOnly,
FourScreens
};
enum class RomHeaderVersion
{
iNes = 0,
Nes2_0 = 1,
OldiNes = 2
};
struct NESHeader
{
/*
Thing Archaic iNES NES 2.0
Byte 6 Mapper low nibble, Mirroring, Battery/Trainer flags
Byte 7 Unused Mapper high nibble, Vs. Mapper high nibble, NES 2.0 signature, PlayChoice, Vs.
Byte 8 Unused Total PRG RAM size (linear) Mapper highest nibble, mapper variant
Byte 9 Unused TV system Upper bits of ROM size
Byte 10 Unused Unused PRG RAM size (logarithmic; battery and non-battery)
Byte 11 Unused Unused VRAM size (logarithmic; battery and non-battery)
Byte 12 Unused Unused TV system
Byte 13 Unused Unused Vs. PPU variant
*/
char NES[4];
uint8_t PrgCount;
uint8_t ChrCount;
uint8_t Byte6;
uint8_t Byte7;
uint8_t Byte8;
uint8_t Byte9;
uint8_t Byte10;
uint8_t Byte11;
uint8_t Byte12;
uint8_t Byte13;
uint8_t Reserved[2];
uint16_t GetMapperID()
{
switch(GetRomHeaderVersion()) {
case RomHeaderVersion::Nes2_0:
return (Byte8 & 0x0F << 4) | (Byte7 & 0xF0) | (Byte6 >> 4);
default:
case RomHeaderVersion::iNes:
return (Byte7 & 0xF0) | (Byte6 >> 4);
case RomHeaderVersion::OldiNes:
return (Byte6 >> 4);
}
}
bool HasBattery()
{
return (Byte6 & 0x02) == 0x02;
}
bool HasTrainer()
{
return (Byte6 & 0x04) == 0x04;
}
bool IsPalRom()
{
switch(GetRomHeaderVersion()) {
case RomHeaderVersion::Nes2_0: return (Byte12 & 0x01) == 0x01;
case RomHeaderVersion::iNes: return (Byte9 & 0x01) == 0x01;
default: return false;
}
}
RomHeaderVersion GetRomHeaderVersion()
{
if((Byte7 & 0x0C) == 0x08) {
return RomHeaderVersion::Nes2_0;
} else if((Byte7 & 0x0C) == 0x00) {
return RomHeaderVersion::iNes;
} else {
return RomHeaderVersion::OldiNes;
}
}
uint32_t GetPrgSize()
{
if(GetRomHeaderVersion() == RomHeaderVersion::Nes2_0) {
return (((Byte9 & 0x0F) << 4) | PrgCount) * 0x4000;
} else {
return PrgCount * 0x4000;
}
}
uint32_t GetChrSize()
{
if(GetRomHeaderVersion() == RomHeaderVersion::Nes2_0) {
return (((Byte9 & 0xF0) << 4) | ChrCount) * 0x2000;
} else {
return ChrCount * 0x2000;
}
}
uint32_t GetWorkRamSize()
{
uint8_t value = Byte10 & 0x0F;
return value == 0 ? 0 : 128 * (uint32_t)std::pow(2, value);
}
uint32_t GetSaveRamSize()
{
uint8_t value = (Byte10 & 0xF0) >> 4;
return value == 0 ? 0 : 128 * (uint32_t)std::pow(2, value);
}
uint32_t GetChrRamSize()
{
uint8_t value = Byte11 & 0x0F;
return value == 0 ? 0 : 128 * (uint32_t)std::pow(2, value);
}
uint32_t GetSavedChrRamSize()
{
uint8_t value = (Byte10 & 0xF0) >> 4;
return value == 0 ? 0 : 128 * (uint32_t)std::pow(2, value);
}
uint8_t GetSubMapper()
{
if(GetRomHeaderVersion() == RomHeaderVersion::Nes2_0) {
return (Byte8 & 0xF0) >> 4;
} else {
return 0;
}
}
MirroringType GetMirroringType()
{
if(Byte6 & 0x08) {
return MirroringType::FourScreens;
} else {
return Byte6 & 0x01 ? MirroringType::Vertical : MirroringType::Horizontal;
}
}
void SanitizeHeader(size_t romLength)
{
size_t calculatedLength = sizeof(NESHeader) + 0x4000 * PrgCount;
while(calculatedLength > romLength) {
PrgCount--;
calculatedLength = sizeof(NESHeader) + 0x4000 * PrgCount;
}
calculatedLength = sizeof(NESHeader) + 0x4000 * PrgCount + 0x2000 * ChrCount;
while(calculatedLength > romLength) {
ChrCount--;
calculatedLength = sizeof(NESHeader) + 0x4000 * PrgCount + 0x2000 * ChrCount;
}
}
};
class ROMLoader
class RomLoader
{
private:
NESHeader _header;
RomData _romData;
string _filename;
uint8_t* _prgRAM = nullptr;
uint8_t* _chrRAM = nullptr;
uint32_t _crc32;
string _ipsFilename;
bool LoadFromZip(stringstream &zipFile)
{
bool result = false;
bool LoadFromZip(stringstream &zipFile);
bool LoadFromStream(stringstream &romFile);
uint32_t GetFileSize(stringstream &file);
uint32_t fileSize;
uint8_t* buffer = ReadFile(zipFile, fileSize);
ZipReader reader;
reader.LoadZipArchive(buffer, fileSize);
vector<string> fileList = reader.GetFileList();
for(string filename : fileList) {
std::transform(filename.begin(), filename.end(), filename.begin(), ::tolower);
if(filename.length() > 4) {
if(filename.substr(filename.length() - 4, 4).compare(".nes") == 0) {
uint8_t* fileBuffer = nullptr;
size_t fileSize = 0;
reader.ExtractFile(filename, &fileBuffer, fileSize);
if(fileBuffer) {
result = LoadFromMemory(fileBuffer, fileSize);
delete[] fileBuffer;
break;
}
}
}
}
delete[] buffer;
return result;
}
bool LoadFromFile(stringstream &romFile)
{
uint32_t fileSize;
uint8_t* buffer = ReadFile(romFile, fileSize);
bool result = LoadFromMemory(buffer, fileSize);
delete[] buffer;
return result;
}
uint32_t GetFileSize(stringstream &file)
{
file.seekg(0, ios::end);
uint32_t fileSize = (uint32_t)file.tellg();
file.seekg(0, ios::beg);
return fileSize;
}
uint8_t* ReadFile(stringstream &file, uint32_t &fileSize)
{
fileSize = GetFileSize(file);
uint8_t* buffer = new uint8_t[fileSize];
file.read((char*)buffer, fileSize);
return buffer;
}
bool LoadFromMemory(uint8_t* buffer, size_t length)
{
if(!_ipsFilename.empty()) {
//Apply IPS patch
uint8_t* patchedFile = nullptr;
size_t patchedSize = 0;
if(IpsPatcher::PatchBuffer(_ipsFilename, buffer, length, &patchedFile, patchedSize)) {
buffer = patchedFile;
length = patchedSize;
}
}
_crc32 = CRC32::GetCRC(buffer, length);
if(memcmp(buffer, "NES", 3) == 0 && length >= sizeof(NESHeader)) {
memcpy((char*)&_header, buffer, sizeof(NESHeader));
buffer += sizeof(NESHeader);
_header.SanitizeHeader(length);
_prgRAM = new uint8_t[_header.GetPrgSize()];
_chrRAM = new uint8_t[_header.GetChrSize()];
memcpy(_prgRAM, buffer, _header.GetPrgSize());
buffer += _header.GetPrgSize();
memcpy(_chrRAM, buffer, _header.GetChrSize());
return true;
}
return false;
}
uint8_t* ReadFile(stringstream &file, uint32_t &fileSize);
bool LoadFromMemory(uint8_t* buffer, size_t length);
public:
ROMLoader()
{
}
~ROMLoader()
{
if(_prgRAM) {
delete[] _prgRAM;
_prgRAM = nullptr;
}
if(_chrRAM) {
delete[] _chrRAM;
_chrRAM = nullptr;
}
}
bool LoadFile(string filename, stringstream *filestream = nullptr, string ipsFilename = "")
{
_ipsFilename = ipsFilename;
stringstream ss;
if(!filestream) {
ifstream file(filename, ios::in | ios::binary);
if(file) {
ss << file.rdbuf();
file.close();
filestream = &ss;
}
}
filestream->seekg(0, ios::beg);
bool result = false;
char header[3];
filestream->read(header, 3);
if(memcmp(header, "NES", 3) == 0) {
_filename = FolderUtilities::GetFilename(filename, false);
filestream->seekg(0, ios::beg);
result = LoadFromFile(*filestream);
} else if(memcmp(header, "PK", 2) == 0) {
_filename = FolderUtilities::GetFilename(filename, false);
filestream->seekg(0, ios::beg);
result = LoadFromZip(*filestream);
}
return result;
}
void GetPrgRom(uint8_t** buffer)
{
*buffer = new uint8_t[GetPrgSize()];
memcpy(*buffer, _prgRAM, GetPrgSize());
}
void GetChrRom(uint8_t** buffer)
{
*buffer = new uint8_t[GetChrSize()];
memcpy(*buffer, _chrRAM, GetChrSize());
}
uint32_t GetPrgSize()
{
return _header.GetPrgSize();
}
uint32_t GetChrSize()
{
return _header.GetChrSize();
}
MirroringType GetMirroringType()
{
return _header.GetMirroringType();
}
uint16_t GetMapperID()
{
return _header.GetMapperID();
}
uint8_t GetSubMapper()
{
return _header.GetSubMapper();
}
bool HasBattery()
{
return _header.HasBattery();
}
bool IsPalRom()
{
return _header.IsPalRom() || _filename.find("(e)") != string::npos || _filename.find("(E)") != string::npos;
}
string GetFilename()
{
return _filename;
}
static uint32_t GetCRC32(string filename)
{
ROMLoader loader;
uint32_t crc = 0;
if(loader.LoadFile(filename)) {
crc = loader._crc32;
}
return crc;
}
static string FindMatchingRomInFolder(string folder, string romFilename, uint32_t crc32Hash)
{
std::transform(romFilename.begin(), romFilename.end(), romFilename.begin(), ::tolower);
vector<string> romFiles = FolderUtilities::GetFilesInFolder(folder, "*.nes", true);
for(string zipFile : FolderUtilities::GetFilesInFolder(folder, "*.zip", true)) {
romFiles.push_back(zipFile);
}
for(string romFile : romFiles) {
//Quick search by filename
std::transform(romFile.begin(), romFile.end(), romFile.begin(), ::tolower);
if(FolderUtilities::GetFilename(romFile, true).compare(romFilename) == 0) {
if(ROMLoader::GetCRC32(romFile) == crc32Hash) {
return romFile;
}
}
}
for(string romFile : romFiles) {
//Slower search by CRC value
if(ROMLoader::GetCRC32(romFile) == crc32Hash) {
//Matching ROM found
return romFile;
}
}
return "";
}
bool LoadFile(string filename, stringstream *filestream = nullptr, string ipsFilename = "");
RomData GetRomData();
static uint32_t GetCRC32(string filename);
static string FindMatchingRomInFolder(string folder, string romFilename, uint32_t crc32Hash);
};

183
Core/RomData.h Normal file
View file

@ -0,0 +1,183 @@
#pragma once
#include "stdafx.h"
enum class MirroringType
{
Horizontal,
Vertical,
ScreenAOnly,
ScreenBOnly,
FourScreens
};
enum class RomHeaderVersion
{
iNes = 0,
Nes2_0 = 1,
OldiNes = 2
};
struct RomData
{
string Filename;
uint16_t MapperID;
uint8_t SubMapperID;
bool HasBattery;
bool IsPalRom;
MirroringType MirroringType;
vector<uint8_t> PrgRom;
vector<uint8_t> ChrRom;
vector<vector<uint8_t>> FdsDiskData;
vector<uint8_t> RawData;
uint32_t Crc32;
bool Error = false;
};
struct NESHeader
{
/*
Thing Archaic iNES NES 2.0
Byte 6 Mapper low nibble, Mirroring, Battery/Trainer flags
Byte 7 Unused Mapper high nibble, Vs. Mapper high nibble, NES 2.0 signature, PlayChoice, Vs.
Byte 8 Unused Total PRG RAM size (linear) Mapper highest nibble, mapper variant
Byte 9 Unused TV system Upper bits of ROM size
Byte 10 Unused Unused PRG RAM size (logarithmic; battery and non-battery)
Byte 11 Unused Unused VRAM size (logarithmic; battery and non-battery)
Byte 12 Unused Unused TV system
Byte 13 Unused Unused Vs. PPU variant
*/
char NES[4];
uint8_t PrgCount;
uint8_t ChrCount;
uint8_t Byte6;
uint8_t Byte7;
uint8_t Byte8;
uint8_t Byte9;
uint8_t Byte10;
uint8_t Byte11;
uint8_t Byte12;
uint8_t Byte13;
uint8_t Reserved[2];
uint16_t GetMapperID()
{
switch(GetRomHeaderVersion()) {
case RomHeaderVersion::Nes2_0:
return (Byte8 & 0x0F << 4) | (Byte7 & 0xF0) | (Byte6 >> 4);
default:
case RomHeaderVersion::iNes:
return (Byte7 & 0xF0) | (Byte6 >> 4);
case RomHeaderVersion::OldiNes:
return (Byte6 >> 4);
}
}
bool HasBattery()
{
return (Byte6 & 0x02) == 0x02;
}
bool HasTrainer()
{
return (Byte6 & 0x04) == 0x04;
}
bool IsPalRom()
{
switch(GetRomHeaderVersion()) {
case RomHeaderVersion::Nes2_0: return (Byte12 & 0x01) == 0x01;
case RomHeaderVersion::iNes: return (Byte9 & 0x01) == 0x01;
default: return false;
}
}
RomHeaderVersion GetRomHeaderVersion()
{
if((Byte7 & 0x0C) == 0x08) {
return RomHeaderVersion::Nes2_0;
} else if((Byte7 & 0x0C) == 0x00) {
return RomHeaderVersion::iNes;
} else {
return RomHeaderVersion::OldiNes;
}
}
uint32_t GetPrgSize()
{
if(GetRomHeaderVersion() == RomHeaderVersion::Nes2_0) {
return (((Byte9 & 0x0F) << 4) | PrgCount) * 0x4000;
} else {
return PrgCount * 0x4000;
}
}
uint32_t GetChrSize()
{
if(GetRomHeaderVersion() == RomHeaderVersion::Nes2_0) {
return (((Byte9 & 0xF0) << 4) | ChrCount) * 0x2000;
} else {
return ChrCount * 0x2000;
}
}
uint32_t GetWorkRamSize()
{
uint8_t value = Byte10 & 0x0F;
return value == 0 ? 0 : 128 * (uint32_t)std::pow(2, value);
}
uint32_t GetSaveRamSize()
{
uint8_t value = (Byte10 & 0xF0) >> 4;
return value == 0 ? 0 : 128 * (uint32_t)std::pow(2, value);
}
uint32_t GetChrRamSize()
{
uint8_t value = Byte11 & 0x0F;
return value == 0 ? 0 : 128 * (uint32_t)std::pow(2, value);
}
uint32_t GetSavedChrRamSize()
{
uint8_t value = (Byte10 & 0xF0) >> 4;
return value == 0 ? 0 : 128 * (uint32_t)std::pow(2, value);
}
uint8_t GetSubMapper()
{
if(GetRomHeaderVersion() == RomHeaderVersion::Nes2_0) {
return (Byte8 & 0xF0) >> 4;
} else {
return 0;
}
}
MirroringType GetMirroringType()
{
if(Byte6 & 0x08) {
return MirroringType::FourScreens;
} else {
return Byte6 & 0x01 ? MirroringType::Vertical : MirroringType::Horizontal;
}
}
void SanitizeHeader(size_t romLength)
{
size_t calculatedLength = sizeof(NESHeader) + 0x4000 * PrgCount;
while(calculatedLength > romLength) {
PrgCount--;
calculatedLength = sizeof(NESHeader) + 0x4000 * PrgCount;
}
calculatedLength = sizeof(NESHeader) + 0x4000 * PrgCount + 0x2000 * ChrCount;
while(calculatedLength > romLength) {
ChrCount--;
calculatedLength = sizeof(NESHeader) + 0x4000 * PrgCount + 0x2000 * ChrCount;
}
}
};

164
Core/RomLoader.cpp Normal file
View file

@ -0,0 +1,164 @@
#pragma once
#include "stdafx.h"
#include "RomLoader.h"
#include "iNesLoader.h"
#include "FdsLoader.h"
bool RomLoader::LoadFromZip(stringstream &zipFile)
{
bool result = false;
uint32_t fileSize;
uint8_t* buffer = ReadFile(zipFile, fileSize);
ZipReader reader;
reader.LoadZipArchive(buffer, fileSize);
vector<string> fileList = reader.GetFileList();
for(string filename : fileList) {
std::transform(filename.begin(), filename.end(), filename.begin(), ::tolower);
if(filename.length() > 4) {
if(filename.substr(filename.length() - 4, 4).compare(".nes") == 0 || filename.substr(filename.length() - 4, 4).compare(".fds") == 0) {
uint8_t* fileBuffer = nullptr;
size_t fileSize = 0;
reader.ExtractFile(filename, &fileBuffer, fileSize);
if(fileBuffer) {
result = LoadFromMemory(fileBuffer, fileSize);
delete[] fileBuffer;
break;
}
}
}
}
delete[] buffer;
return result;
}
bool RomLoader::LoadFromStream(stringstream &romFile)
{
uint32_t fileSize;
uint8_t* buffer = ReadFile(romFile, fileSize);
bool result = LoadFromMemory(buffer, fileSize);
delete[] buffer;
return result;
}
uint32_t RomLoader::GetFileSize(stringstream &file)
{
file.seekg(0, ios::end);
uint32_t fileSize = (uint32_t)file.tellg();
file.seekg(0, ios::beg);
return fileSize;
}
uint8_t* RomLoader::ReadFile(stringstream &file, uint32_t &fileSize)
{
fileSize = GetFileSize(file);
uint8_t* buffer = new uint8_t[fileSize];
file.read((char*)buffer, fileSize);
return buffer;
}
bool RomLoader::LoadFromMemory(uint8_t* buffer, size_t length)
{
vector<uint8_t> fileData(buffer, buffer + length);
if(!_ipsFilename.empty()) {
//Apply IPS patch
fileData = IpsPatcher::PatchBuffer(_ipsFilename, fileData);
}
if(memcmp(buffer, "NES\x1a", 4) == 0) {
iNesLoader loader;
_romData = loader.LoadRom(fileData);
} else if(memcmp(buffer, "FDS\x1a", 4) == 0 || memcmp(buffer, "\x1*NINTENDO-HVC*", 15) == 0) {
FdsLoader loader;
_romData = loader.LoadRom(fileData, _filename);
}
_romData.RawData = fileData;
_romData.Crc32 = CRC32::GetCRC(buffer, length);
return !_romData.Error;
}
bool RomLoader::LoadFile(string filename, stringstream *filestream, string ipsFilename)
{
_filename = filename;
_ipsFilename = ipsFilename;
stringstream ss;
if(!filestream) {
ifstream file(filename, ios::in | ios::binary);
if(file) {
ss << file.rdbuf();
file.close();
filestream = &ss;
}
}
char header[2];
filestream->seekg(0, ios::beg);
filestream->read(header, 2);
filestream->seekg(0, ios::beg);
if(memcmp(header, "PK", 2) == 0) {
return LoadFromZip(*filestream);
} else {
return LoadFromStream(*filestream);
}
}
RomData RomLoader::GetRomData()
{
_romData.Filename = _filename;
return _romData;
}
uint32_t RomLoader::GetCRC32(string filename)
{
RomLoader loader;
uint32_t crc = 0;
if(loader.LoadFile(filename)) {
crc = loader._romData.Crc32;
}
return crc;
}
string RomLoader::FindMatchingRomInFolder(string folder, string romFilename, uint32_t crc32Hash)
{
std::transform(romFilename.begin(), romFilename.end(), romFilename.begin(), ::tolower);
vector<string> validExtensions = { { "*.nes", "*.zip", "*.fds" } };
vector<string> romFiles;
for(string extension : validExtensions) {
for(string file : FolderUtilities::GetFilesInFolder(folder, extension, true)) {
romFiles.push_back(file);
}
}
for(string romFile : romFiles) {
//Quick search by filename
std::transform(romFile.begin(), romFile.end(), romFile.begin(), ::tolower);
if(FolderUtilities::GetFilename(romFile, true).compare(romFilename) == 0) {
if(RomLoader::GetCRC32(romFile) == crc32Hash) {
return romFile;
}
}
}
for(string romFile : romFiles) {
//Slower search by CRC value
if(RomLoader::GetCRC32(romFile) == crc32Hash) {
//Matching ROM found
return romFile;
}
}
return "";
}

30
Core/iNesLoader.h Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#include "stdafx.h"
class iNesLoader
{
public:
RomData LoadRom(vector<uint8_t>& romFile)
{
RomData romData;
uint8_t* buffer = romFile.data();
NESHeader header;
memcpy((char*)&header, buffer, sizeof(NESHeader));
buffer += sizeof(NESHeader);
header.SanitizeHeader(romFile.size());
romData.MapperID = header.GetMapperID();
romData.SubMapperID = header.GetSubMapper();
romData.MirroringType = header.GetMirroringType();
romData.HasBattery = header.HasBattery();
romData.IsPalRom = header.IsPalRom();
romData.PrgRom.insert(romData.PrgRom.end(), buffer, buffer + header.GetPrgSize());
buffer += header.GetPrgSize();
romData.ChrRom.insert(romData.ChrRom.end(), buffer, buffer + header.GetChrSize());
return romData;
}
};

View file

@ -18,6 +18,8 @@ namespace Mesen.GUI.Config
public bool AssociateNesFiles = false;
public bool AllowInvalidInput = false;
public bool RemoveSpriteLimit = false;
public bool FdsAutoLoadDisk = true;
public bool FdsFastForwardOnLoad = false;
public bool UseAlternativeMmc3Irq = false;
@ -43,6 +45,8 @@ namespace Mesen.GUI.Config
InteropEmu.SetFlag(EmulationFlags.Mmc3IrqAltBehavior, preferenceInfo.UseAlternativeMmc3Irq);
InteropEmu.SetFlag(EmulationFlags.AllowInvalidInput, preferenceInfo.AllowInvalidInput);
InteropEmu.SetFlag(EmulationFlags.RemoveSpriteLimit, preferenceInfo.RemoveSpriteLimit);
InteropEmu.SetFlag(EmulationFlags.FdsAutoLoadDisk, preferenceInfo.FdsAutoLoadDisk);
InteropEmu.SetFlag(EmulationFlags.FdsFastForwardOnLoad, preferenceInfo.FdsFastForwardOnLoad);
}
}
}

View file

@ -48,6 +48,8 @@
this.chkUseAlternativeMmc3Irq = new System.Windows.Forms.CheckBox();
this.chkAllowInvalidInput = new System.Windows.Forms.CheckBox();
this.chkRemoveSpriteLimit = new System.Windows.Forms.CheckBox();
this.chkFdsAutoLoadDisk = new System.Windows.Forms.CheckBox();
this.chkFdsFastForwardOnLoad = new System.Windows.Forms.CheckBox();
this.tlpMain.SuspendLayout();
this.flowLayoutPanel6.SuspendLayout();
this.tabMain.SuspendLayout();
@ -275,10 +277,14 @@
this.tableLayoutPanel1.Controls.Add(this.chkUseAlternativeMmc3Irq, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.chkAllowInvalidInput, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.chkRemoveSpriteLimit, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.chkFdsAutoLoadDisk, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.chkFdsFastForwardOnLoad, 0, 4);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 4;
this.tableLayoutPanel1.RowCount = 6;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
@ -316,6 +322,26 @@
this.chkRemoveSpriteLimit.Text = "Remove sprite limit (Reduces flashing)";
this.chkRemoveSpriteLimit.UseVisualStyleBackColor = true;
//
// chkFdsAutoLoadDisk
//
this.chkFdsAutoLoadDisk.AutoSize = true;
this.chkFdsAutoLoadDisk.Location = new System.Drawing.Point(3, 72);
this.chkFdsAutoLoadDisk.Name = "chkFdsAutoLoadDisk";
this.chkFdsAutoLoadDisk.Size = new System.Drawing.Size(303, 17);
this.chkFdsAutoLoadDisk.TabIndex = 3;
this.chkFdsAutoLoadDisk.Text = "Automatically insert disk 1 side A when starting FDS games";
this.chkFdsAutoLoadDisk.UseVisualStyleBackColor = true;
//
// chkFdsFastForwardOnLoad
//
this.chkFdsFastForwardOnLoad.AutoSize = true;
this.chkFdsFastForwardOnLoad.Location = new System.Drawing.Point(3, 95);
this.chkFdsFastForwardOnLoad.Name = "chkFdsFastForwardOnLoad";
this.chkFdsFastForwardOnLoad.Size = new System.Drawing.Size(302, 17);
this.chkFdsFastForwardOnLoad.TabIndex = 4;
this.chkFdsFastForwardOnLoad.Text = "Automatically fast forward FDS games when disk is loading";
this.chkFdsFastForwardOnLoad.UseVisualStyleBackColor = true;
//
// frmPreferences
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -367,5 +393,7 @@
private System.Windows.Forms.CheckBox chkUseAlternativeMmc3Irq;
private System.Windows.Forms.CheckBox chkAllowInvalidInput;
private System.Windows.Forms.CheckBox chkRemoveSpriteLimit;
private System.Windows.Forms.CheckBox chkFdsAutoLoadDisk;
private System.Windows.Forms.CheckBox chkFdsFastForwardOnLoad;
}
}

View file

@ -26,6 +26,9 @@ namespace Mesen.GUI.Forms.Config
AddBinding("UseAlternativeMmc3Irq", chkUseAlternativeMmc3Irq);
AddBinding("AllowInvalidInput", chkAllowInvalidInput);
AddBinding("RemoveSpriteLimit", chkRemoveSpriteLimit);
AddBinding("FdsAutoLoadDisk", chkFdsAutoLoadDisk);
AddBinding("FdsFastForwardOnLoad", chkFdsFastForwardOnLoad);
}
protected override void OnFormClosed(FormClosedEventArgs e)

View file

@ -117,4 +117,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -51,6 +51,9 @@
this.mnuPause = new System.Windows.Forms.ToolStripMenuItem();
this.mnuReset = new System.Windows.Forms.ToolStripMenuItem();
this.mnuStop = new System.Windows.Forms.ToolStripMenuItem();
this.sepFdsDisk = new System.Windows.Forms.ToolStripSeparator();
this.mnuSelectDisk = new System.Windows.Forms.ToolStripMenuItem();
this.mnuEjectDisk = new System.Windows.Forms.ToolStripMenuItem();
this.mnuOptions = new System.Windows.Forms.ToolStripMenuItem();
this.mnuEmulationSpeed = new System.Windows.Forms.ToolStripMenuItem();
this.mnuEmuSpeedNormal = new System.Windows.Forms.ToolStripMenuItem();
@ -116,6 +119,7 @@
this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem();
this.menuTimer = new System.Windows.Forms.Timer(this.components);
this.dxViewer = new Mesen.GUI.Controls.DXViewer();
this.mnuSwitchDiskSide = new System.Windows.Forms.ToolStripMenuItem();
this.menuStrip.SuspendLayout();
this.SuspendLayout();
//
@ -203,7 +207,11 @@
this.mnuGame.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuPause,
this.mnuReset,
this.mnuStop});
this.mnuStop,
this.sepFdsDisk,
this.mnuSwitchDiskSide,
this.mnuSelectDisk,
this.mnuEjectDisk});
this.mnuGame.Name = "mnuGame";
this.mnuGame.Size = new System.Drawing.Size(50, 20);
this.mnuGame.Text = "Game";
@ -213,7 +221,7 @@
this.mnuPause.Enabled = false;
this.mnuPause.Name = "mnuPause";
this.mnuPause.ShortcutKeyDisplayString = "Esc";
this.mnuPause.Size = new System.Drawing.Size(129, 22);
this.mnuPause.Size = new System.Drawing.Size(200, 22);
this.mnuPause.Text = "Pause";
this.mnuPause.Click += new System.EventHandler(this.mnuPause_Click);
//
@ -221,7 +229,7 @@
//
this.mnuReset.Enabled = false;
this.mnuReset.Name = "mnuReset";
this.mnuReset.Size = new System.Drawing.Size(129, 22);
this.mnuReset.Size = new System.Drawing.Size(200, 22);
this.mnuReset.Text = "Reset";
this.mnuReset.Click += new System.EventHandler(this.mnuReset_Click);
//
@ -229,10 +237,28 @@
//
this.mnuStop.Enabled = false;
this.mnuStop.Name = "mnuStop";
this.mnuStop.Size = new System.Drawing.Size(129, 22);
this.mnuStop.Size = new System.Drawing.Size(200, 22);
this.mnuStop.Text = "Stop";
this.mnuStop.Click += new System.EventHandler(this.mnuStop_Click);
//
// sepFdsDisk
//
this.sepFdsDisk.Name = "sepFdsDisk";
this.sepFdsDisk.Size = new System.Drawing.Size(197, 6);
//
// mnuSelectDisk
//
this.mnuSelectDisk.Name = "mnuSelectDisk";
this.mnuSelectDisk.Size = new System.Drawing.Size(200, 22);
this.mnuSelectDisk.Text = "Select Disk";
//
// mnuEjectDisk
//
this.mnuEjectDisk.Name = "mnuEjectDisk";
this.mnuEjectDisk.Size = new System.Drawing.Size(200, 22);
this.mnuEjectDisk.Text = "Eject Disk";
this.mnuEjectDisk.Click += new System.EventHandler(this.mnuEjectDisk_Click);
//
// mnuOptions
//
this.mnuOptions.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
@ -352,7 +378,7 @@
// mnuScale1x
//
this.mnuScale1x.Name = "mnuScale1x";
this.mnuScale1x.Size = new System.Drawing.Size(152, 22);
this.mnuScale1x.Size = new System.Drawing.Size(85, 22);
this.mnuScale1x.Tag = "1";
this.mnuScale1x.Text = "1x";
this.mnuScale1x.Click += new System.EventHandler(this.mnuScale_Click);
@ -360,7 +386,7 @@
// mnuScale2x
//
this.mnuScale2x.Name = "mnuScale2x";
this.mnuScale2x.Size = new System.Drawing.Size(152, 22);
this.mnuScale2x.Size = new System.Drawing.Size(85, 22);
this.mnuScale2x.Tag = "2";
this.mnuScale2x.Text = "2x";
this.mnuScale2x.Click += new System.EventHandler(this.mnuScale_Click);
@ -368,7 +394,7 @@
// mnuScale3x
//
this.mnuScale3x.Name = "mnuScale3x";
this.mnuScale3x.Size = new System.Drawing.Size(152, 22);
this.mnuScale3x.Size = new System.Drawing.Size(85, 22);
this.mnuScale3x.Tag = "3";
this.mnuScale3x.Text = "3x";
this.mnuScale3x.Click += new System.EventHandler(this.mnuScale_Click);
@ -376,7 +402,7 @@
// mnuScale4x
//
this.mnuScale4x.Name = "mnuScale4x";
this.mnuScale4x.Size = new System.Drawing.Size(152, 22);
this.mnuScale4x.Size = new System.Drawing.Size(85, 22);
this.mnuScale4x.Tag = "4";
this.mnuScale4x.Text = "4x";
this.mnuScale4x.Click += new System.EventHandler(this.mnuScale_Click);
@ -746,6 +772,14 @@
this.dxViewer.Size = new System.Drawing.Size(263, 176);
this.dxViewer.TabIndex = 1;
//
// mnuSwitchDiskSide
//
this.mnuSwitchDiskSide.Name = "mnuSwitchDiskSide";
this.mnuSwitchDiskSide.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.B)));
this.mnuSwitchDiskSide.Size = new System.Drawing.Size(200, 22);
this.mnuSwitchDiskSide.Text = "Switch Disk Side";
this.mnuSwitchDiskSide.Click += new System.EventHandler(this.mnuSwitchDiskSide_Click);
//
// frmMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -847,6 +881,10 @@
private System.Windows.Forms.ToolStripMenuItem mnuVideoFilter;
private System.Windows.Forms.ToolStripMenuItem mnuNoneFilter;
private System.Windows.Forms.ToolStripMenuItem mnuNtscFilter;
private System.Windows.Forms.ToolStripSeparator sepFdsDisk;
private System.Windows.Forms.ToolStripMenuItem mnuSelectDisk;
private System.Windows.Forms.ToolStripMenuItem mnuEjectDisk;
private System.Windows.Forms.ToolStripMenuItem mnuSwitchDiskSide;
}
}

View file

@ -51,6 +51,7 @@ namespace Mesen.GUI.Forms
menuTimer.Start();
InitializeFdsDiskMenu();
InitializeEmulationSpeedMenu();
UpdateVideoSettings();
@ -186,17 +187,30 @@ namespace Mesen.GUI.Forms
private void _notifListener_OnNotification(InteropEmu.NotificationEventArgs e)
{
if(e.NotificationType == InteropEmu.ConsoleNotificationType.GameLoaded) {
CheatInfo.ApplyCheats();
InitializeStateMenu(mnuSaveState, true);
InitializeStateMenu(mnuLoadState, false);
this.StartEmuThread();
} else if(e.NotificationType == InteropEmu.ConsoleNotificationType.GameStopped) {
CheatInfo.ClearCheats();
} else if(e.NotificationType == InteropEmu.ConsoleNotificationType.ResolutionChanged) {
this.BeginInvoke((MethodInvoker)(() => {
UpdateVideoSettings();
}));
switch(e.NotificationType) {
case InteropEmu.ConsoleNotificationType.GameLoaded:
InitializeFdsDiskMenu();
CheatInfo.ApplyCheats();
InitializeStateMenu(mnuSaveState, true);
InitializeStateMenu(mnuLoadState, false);
this.StartEmuThread();
break;
case InteropEmu.ConsoleNotificationType.GameStopped:
CheatInfo.ClearCheats();
break;
case InteropEmu.ConsoleNotificationType.ResolutionChanged:
this.BeginInvoke((MethodInvoker)(() => {
UpdateVideoSettings();
}));
break;
case InteropEmu.ConsoleNotificationType.FdsBiosNotFound:
this.BeginInvoke((MethodInvoker)(() => {
SelectFdsBiosPrompt();
}));
break;
}
UpdateMenus();
}
@ -204,7 +218,7 @@ namespace Mesen.GUI.Forms
private void mnuOpen_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "All supported formats (*.nes, *.zip, *.ips)|*.NES;*.ZIP;*.IPS|NES Roms (*.nes)|*.NES|ZIP Archives (*.zip)|*.ZIP|IPS Patches (*.ips)|*.IPS|All (*.*)|*.*";
ofd.Filter = "All supported formats (*.nes, *.zip, *.fds, *.ips)|*.NES;*.ZIP;*.IPS;*.FDS|NES Roms (*.nes)|*.NES|Famicom Disk System Roms (*.fds)|*.FDS|ZIP Archives (*.zip)|*.ZIP|IPS Patches (*.ips)|*.IPS|All (*.*)|*.*";
if(ConfigManager.Config.RecentFiles.Count > 0) {
ofd.InitialDirectory = Path.GetDirectoryName(ConfigManager.Config.RecentFiles[0]);
}
@ -213,13 +227,17 @@ namespace Mesen.GUI.Forms
string ipsFile = ofd.FileName;
string romFile = Path.Combine(Path.GetDirectoryName(ofd.FileName), Path.GetFileNameWithoutExtension(ofd.FileName));
if(File.Exists(romFile+".nes") || File.Exists(romFile+".zip")) {
LoadROM(romFile + (File.Exists(romFile+".nes") ? ".nes" : ".zip"));
if(File.Exists(romFile+".nes") || File.Exists(romFile+".zip") || File.Exists(romFile+".fds")) {
string ext = string.Empty;
if(File.Exists(romFile+".nes")) ext = ".nes";
if(File.Exists(romFile+".zip")) ext = ".zip";
if(File.Exists(romFile+".fds")) ext = ".fds";
LoadROM(romFile + ext);
InteropEmu.ApplyIpsPatch(ipsFile);
} else {
if(_emuThread == null) {
if(MessageBox.Show("Please select a ROM matching the IPS patch file.", string.Empty, MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.OK) {
ofd.Filter = "All supported formats (*.nes, *.zip)|*.NES;*.ZIP|NES Roms (*.nes)|*.NES|ZIP Archives (*.zip)|*.ZIP|All (*.*)|*.*";
ofd.Filter = "All supported formats (*.nes, *.zip, *.fds)|*.NES;*.ZIP;*.FDS|NES Roms (*.nes)|*.NES|Famicom Disk System Roms (*.fds)|*.FDS|ZIP Archives (*.zip)|*.ZIP|All (*.*)|*.*";
if(ConfigManager.Config.RecentFiles.Count > 0) {
ofd.InitialDirectory = Path.GetDirectoryName(ConfigManager.Config.RecentFiles[0]);
}
@ -233,23 +251,23 @@ namespace Mesen.GUI.Forms
}
}
} else {
LoadROM(ofd.FileName);
if(ConfigManager.Config.PreferenceInfo.AutoLoadIpsPatches) {
string ipsFile = Path.Combine(Path.GetDirectoryName(ofd.FileName), Path.GetFileNameWithoutExtension(ofd.FileName)) + ".ips";
if(File.Exists(ipsFile)) {
InteropEmu.ApplyIpsPatch(ipsFile);
}
}
LoadROM(ofd.FileName, ConfigManager.Config.PreferenceInfo.AutoLoadIpsPatches);
}
}
}
private void LoadROM(string filename)
private void LoadROM(string filename, bool autoLoadIps = false)
{
_romToLoad = filename;
if(File.Exists(filename)) {
ConfigManager.Config.AddRecentFile(filename);
InteropEmu.LoadROM(filename);
UpdateRecentFiles();
string ipsFile = Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename)) + ".ips";
if(File.Exists(ipsFile)) {
InteropEmu.ApplyIpsPatch(ipsFile);
}
} else {
MessageBox.Show("File not found.", "Mesen", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
@ -738,5 +756,61 @@ namespace Mesen.GUI.Forms
InteropEmu.SetVideoFilter(VideoFilterType.NTSC);
UpdateFilterMenu(VideoFilterType.NTSC);
}
private void InitializeFdsDiskMenu()
{
if(this.InvokeRequired) {
this.BeginInvoke((MethodInvoker)(() => this.InitializeFdsDiskMenu()));
} else {
UInt32 sideCount = InteropEmu.FdsGetSideCount();
mnuSelectDisk.DropDownItems.Clear();
if(sideCount > 0) {
for(UInt32 i = 0; i < sideCount; i++) {
UInt32 diskNumber = i;
ToolStripItem item = mnuSelectDisk.DropDownItems.Add("Disk " + (diskNumber/2+1) + " Side " + (diskNumber % 2 == 0 ? "A" : "B"));
item.Click += (object sender, EventArgs args) => {
InteropEmu.FdsInsertDisk(diskNumber);
};
}
sepFdsDisk.Visible = true;
mnuSelectDisk.Visible = true;
mnuEjectDisk.Visible = true;
mnuSwitchDiskSide.Visible = sideCount > 1;
} else {
sepFdsDisk.Visible = false;
mnuSelectDisk.Visible = false;
mnuEjectDisk.Visible = false;
mnuSwitchDiskSide.Visible = false;
}
}
}
private void mnuEjectDisk_Click(object sender, EventArgs e)
{
InteropEmu.FdsEjectDisk();
}
private void mnuSwitchDiskSide_Click(object sender, EventArgs e)
{
InteropEmu.FdsSwitchDiskSide();
}
private void SelectFdsBiosPrompt()
{
if(MessageBox.Show("FDS bios not found. The bios is required to run FDS games." + Environment.NewLine + Environment.NewLine + "Select bios file now?", "Mesen", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK) {
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "All Files (*.*)|*.*";
if(ofd.ShowDialog() == DialogResult.OK) {
if(MD5Helper.GetMD5Hash(ofd.FileName).ToLowerInvariant() == "ca30b50f880eb660a320674ed365ef7a") {
File.Copy(ofd.FileName, Path.Combine(ConfigManager.HomeFolder, "FdsBios.bin"));
LoadROM(_romToLoad);
} else {
MessageBox.Show("The selected bios file is invalid.", "Mesen", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
}
}

View file

@ -117,10 +117,13 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="menuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="menuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>107, 17</value>
</metadata>
<metadata name="menuTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>125, 17</value>
<value>215, 17</value>
</metadata>
</root>

View file

@ -66,6 +66,11 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void LoadState(UInt32 stateIndex);
[DllImport(DLLPath)] public static extern Int64 GetStateInfo(UInt32 stateIndex);
[DllImport(DLLPath)] public static extern UInt32 FdsGetSideCount();
[DllImport(DLLPath)] public static extern void FdsEjectDisk();
[DllImport(DLLPath)] public static extern void FdsInsertDisk(UInt32 diskNumber);
[DllImport(DLLPath)] public static extern void FdsSwitchDiskSide();
[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);
[DllImport(DLLPath)] public static extern void CheatAddProActionRocky(UInt32 code);
@ -279,6 +284,8 @@ namespace Mesen.GUI
PpuFrameDone = 9,
MovieEnded = 10,
ResolutionChanged = 11,
FdsDiskChanged = 12,
FdsBiosNotFound = 13,
}
public struct KeyMapping
@ -460,7 +467,9 @@ namespace Mesen.GUI
AllowInvalidInput = 0x08,
RemoveSpriteLimit = 0x10,
UseHdPacks = 0x20,
FdsFastForwardOnLoad = 0x2000,
FdsAutoLoadDisk = 0x4000,
Mmc3IrqAltBehavior = 0x8000,
}
@ -541,6 +550,8 @@ namespace Mesen.GUI
return BitConverter.ToString(md5.ComputeHash(entry.Open())).Replace("-", "");
}
}
} else {
return BitConverter.ToString(md5.ComputeHash(File.ReadAllBytes(filename))).Replace("-", "");
}
return null;
}

View file

@ -14,6 +14,7 @@
#include "../Core/EmulationSettings.h"
#include "../Core/VideoDecoder.h"
#include "../Core/AutoRomTest.h"
#include "../Core/FDS.h"
NES::Renderer *_renderer = nullptr;
SoundManager *_soundManager = nullptr;
@ -229,5 +230,11 @@ namespace InteropEmu {
DllExport void __stdcall SetAudioDevice(char* audioDevice) { _soundManager->SetAudioDevice(audioDevice); }
DllExport void __stdcall GetScreenSize(ScreenSize &size) { VideoDecoder::GetInstance()->GetScreenSize(size); }
//FDS functions
DllExport uint32_t __stdcall FdsGetSideCount() { return FDS::GetSideCount(); }
DllExport void __stdcall FdsEjectDisk() { FDS::EjectDisk(); }
DllExport void __stdcall FdsInsertDisk(uint32_t diskNumber) { FDS::InsertDisk(diskNumber); }
DllExport void __stdcall FdsSwitchDiskSide() { FDS::SwitchDiskSide(); }
}
}

View file

@ -1,4 +1,5 @@
#include "stdafx.h"
#include <assert.h>
#include "IpsPatcher.h"
class IpsRecord
@ -6,7 +7,7 @@ class IpsRecord
public:
uint32_t Address = 0;
uint16_t Length = 0;
uint8_t* Replacement = nullptr;
vector<uint8_t> Replacement;
//For RLE records (when length == 0)
uint16_t RepeatCount = 0;
@ -32,22 +33,33 @@ public:
RepeatCount = buffer[1] | (buffer[0] << 8);
Value = buffer[2];
} else {
Replacement = new uint8_t[Length];
ipsFile.read((char*)Replacement, Length);
Replacement.resize(Length);
ipsFile.read((char*)Replacement.data(), Length);
}
return true;
}
}
~IpsRecord()
void WriteRecord(vector<uint8_t> &output)
{
if(Replacement != nullptr) {
delete[] Replacement;
output.push_back((Address >> 16) & 0xFF);
output.push_back((Address >> 8) & 0xFF);
output.push_back(Address & 0xFF);
output.push_back((Length >> 8) & 0xFF);
output.push_back(Length & 0xFF);
if(Length == 0) {
output.push_back((RepeatCount >> 8) & 0xFF);
output.push_back(RepeatCount & 0xFF);
output.push_back(Value);
} else {
output.insert(output.end(), Replacement.data(), Replacement.data() + Replacement.size());
}
}
};
bool IpsPatcher::PatchBuffer(string ipsFilepath, uint8_t* inputBuffer, size_t inputBufferSize, uint8_t** outputBuffer, size_t &outputBufferSize)
vector<uint8_t> IpsPatcher::PatchBuffer(string ipsFilepath, vector<uint8_t> input)
{
ifstream ipsFile(ipsFilepath, std::ios::in | std::ios::binary);
@ -56,17 +68,17 @@ bool IpsPatcher::PatchBuffer(string ipsFilepath, uint8_t* inputBuffer, size_t in
ipsFile.read((char*)&header, 5);
if(memcmp((char*)&header, "PATCH", 5) != 0) {
//Invalid ips file
return false;
return input;
}
vector<IpsRecord*> records;
vector<IpsRecord> records;
int32_t truncateOffset = -1;
size_t maxOutputSize = inputBufferSize;
size_t maxOutputSize = input.size();
while(!ipsFile.eof()) {
IpsRecord *record = new IpsRecord();
if(record->ReadRecord(ipsFile)) {
if(record->Address + record->Length + record->RepeatCount > maxOutputSize) {
maxOutputSize = record->Address + record->Length + record->RepeatCount;
IpsRecord record;
if(record.ReadRecord(ipsFile)) {
if(record.Address + record.Length + record.RepeatCount > maxOutputSize) {
maxOutputSize = record.Address + record.Length + record.RepeatCount;
}
records.push_back(record);
} else {
@ -80,34 +92,87 @@ bool IpsPatcher::PatchBuffer(string ipsFilepath, uint8_t* inputBuffer, size_t in
}
}
outputBufferSize = maxOutputSize;
uint8_t *output = new uint8_t[outputBufferSize];
memset(output, 0, outputBufferSize);
memcpy(output, inputBuffer, inputBufferSize);
vector<uint8_t> output;
output.resize(maxOutputSize);
std::copy(input.begin(), input.end(), output.begin());
for(IpsRecord *record : records) {
if(record->Length == 0) {
memset(output+record->Address, record->Value, record->RepeatCount);
for(IpsRecord record : records) {
if(record.Length == 0) {
std::fill(&output[record.Address], &output[record.Address]+record.RepeatCount, record.Value);
} else {
memcpy(output+record->Address, record->Replacement, record->Length);
std::copy(record.Replacement.begin(), record.Replacement.end(), output.begin()+record.Address);
}
delete record;
}
if(truncateOffset != -1 && (int32_t)outputBufferSize > truncateOffset) {
uint8_t* truncatedOutput = new uint8_t[truncateOffset];
memcpy(truncatedOutput, output, truncateOffset);
delete[] output;
*outputBuffer = truncatedOutput;
} else {
*outputBuffer = output;
if(truncateOffset != -1 && (int32_t)output.size() > truncateOffset) {
output.resize(truncateOffset);
}
ipsFile.close();
return true;
return output;
}
return input;
}
vector<uint8_t> IpsPatcher::CreatePatch(vector<uint8_t> originalData, vector<uint8_t> newData)
{
assert(originalData.size() == newData.size());
vector<uint8_t> patchFile;
uint8_t header[5] = { 'P', 'A', 'T', 'C', 'H' };
patchFile.insert(patchFile.end(), header, header + sizeof(header));
size_t i = 0, len = originalData.size();
while(i < len) {
while(i < len && originalData[i] == newData[i]) {
i++;
}
if(i < len) {
IpsRecord patchRecord;
uint8_t rleByte = newData[i];
uint8_t rleCount = 0;
bool createRleRecord = false;
patchRecord.Address = (uint32_t)i;
patchRecord.Length = 0;
while(i < len && patchRecord.Length < 65535 && originalData[i] != newData[i]) {
if(newData[i] == rleByte) {
rleCount++;
} else if(createRleRecord) {
break;
} else {
rleByte = newData[i];
rleCount = 1;
}
patchRecord.Length++;
i++;
if((patchRecord.Length == rleCount && rleCount > 3) || rleCount > 13) {
//Making a RLE entry would probably save space, so write the current entry and create a RLE entry after it
if(patchRecord.Length == rleCount) {
//Same character since the start of this entry, make the RLE entry now
createRleRecord = true;
} else {
patchRecord.Length -= rleCount;
i -= rleCount;
break;
}
}
}
if(createRleRecord) {
patchRecord.Length = 0;
patchRecord.RepeatCount = rleCount;
patchRecord.Value = rleByte;
} else {
patchRecord.Replacement = vector<uint8_t>(&newData[patchRecord.Address], &newData[patchRecord.Address + patchRecord.Length]);
}
patchRecord.WriteRecord(patchFile);
}
}
return false;
uint8_t endOfFile[3] = { 'E', 'O', 'F' };
patchFile.insert(patchFile.end(), endOfFile, endOfFile + sizeof(endOfFile));
return patchFile;
}

View file

@ -5,5 +5,6 @@
class IpsPatcher
{
public:
static bool PatchBuffer(string ipsFilepath, uint8_t* inputBuffer, size_t inputBufferSize, uint8_t** outputBuffer, size_t &outputBufferSize);
static vector<uint8_t> PatchBuffer(string ipsFilepath, vector<uint8_t> input);
static vector<uint8_t> CreatePatch(vector<uint8_t> originalData, vector<uint8_t> newData);
};