mirror of
https://github.com/SourMesen/Mesen.git
synced 2025-04-02 10:52:48 -04:00
FDS support (missing sound emulation)
This commit is contained in:
parent
a68625c8ac
commit
df8e66a5e7
32 changed files with 1630 additions and 675 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -33,6 +33,7 @@ enum class IRQSource
|
|||
External = 1,
|
||||
FrameCounter = 2,
|
||||
DMC = 4,
|
||||
FdsDisk = 8,
|
||||
};
|
||||
|
||||
struct State
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
|
@ -12,6 +12,8 @@ enum EmulationFlags
|
|||
RemoveSpriteLimit = 0x10,
|
||||
UseHdPacks = 0x20,
|
||||
|
||||
FdsFastForwardOnLoad = 0x2000,
|
||||
FdsAutoLoadDisk = 0x4000,
|
||||
Mmc3IrqAltBehavior = 0x8000,
|
||||
};
|
||||
|
||||
|
|
4
Core/FDS.cpp
Normal file
4
Core/FDS.cpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
#include "stdafx.h"
|
||||
#include "FDS.h"
|
||||
|
||||
FDS* FDS::Instance = nullptr;
|
460
Core/FDS.h
Normal file
460
Core/FDS.h
Normal 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
171
Core/FdsLoader.h
Normal 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>> ¤tDiskData)
|
||||
{
|
||||
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;
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -15,6 +15,8 @@ enum class ConsoleNotificationType
|
|||
PpuFrameDone = 9,
|
||||
MovieEnded = 10,
|
||||
ResolutionChanged = 11,
|
||||
FdsDiskChanged = 12,
|
||||
FdsBiosNotFound = 13,
|
||||
};
|
||||
|
||||
class INotificationListener
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
405
Core/ROMLoader.h
405
Core/ROMLoader.h
|
@ -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
183
Core/RomData.h
Normal 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
164
Core/RomLoader.cpp
Normal 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
30
Core/iNesLoader.h
Normal 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;
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
30
GUI.NET/Forms/Config/frmPreferences.Designer.cs
generated
30
GUI.NET/Forms/Config/frmPreferences.Designer.cs
generated
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
54
GUI.NET/Forms/frmMain.Designer.cs
generated
54
GUI.NET/Forms/frmMain.Designer.cs
generated
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(); }
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
};
|
Loading…
Add table
Reference in a new issue