From df8e66a5e716f31934246e912fb9c6919610e7bf Mon Sep 17 00:00:00 2001 From: Souryo Date: Thu, 28 Jan 2016 20:47:16 -0500 Subject: [PATCH] FDS support (missing sound emulation) --- Core/BaseMapper.cpp | 39 +- Core/BaseMapper.h | 12 +- Core/CPU.h | 1 + Core/Console.cpp | 12 +- Core/Console.h | 2 +- Core/Core.vcxproj | 8 +- Core/Core.vcxproj.filters | 393 ++++++++------- Core/EmulationSettings.h | 2 + Core/FDS.cpp | 4 + Core/FDS.h | 460 ++++++++++++++++++ Core/FdsLoader.h | 171 +++++++ Core/GameInformationMessage.h | 6 +- Core/GameServerConnection.cpp | 3 +- Core/INotificationListener.h | 2 + Core/MapperFactory.cpp | 22 +- Core/MapperFactory.h | 5 +- Core/Movie.cpp | 6 +- Core/ROMLoader.h | 405 +-------------- Core/RomData.h | 183 +++++++ Core/RomLoader.cpp | 164 +++++++ Core/iNesLoader.h | 30 ++ GUI.NET/Config/PreferenceInfo.cs | 4 + .../Forms/Config/frmPreferences.Designer.cs | 30 +- GUI.NET/Forms/Config/frmPreferences.cs | 3 + GUI.NET/Forms/Config/frmPreferences.resx | 3 + GUI.NET/Forms/frmMain.Designer.cs | 54 +- GUI.NET/Forms/frmMain.cs | 120 ++++- GUI.NET/Forms/frmMain.resx | 7 +- GUI.NET/InteropEmu.cs | 13 +- InteropDLL/ConsoleWrapper.cpp | 7 + Utilities/IpsPatcher.cpp | 131 +++-- Utilities/IpsPatcher.h | 3 +- 32 files changed, 1630 insertions(+), 675 deletions(-) create mode 100644 Core/FDS.cpp create mode 100644 Core/FDS.h create mode 100644 Core/FdsLoader.h create mode 100644 Core/RomData.h create mode 100644 Core/RomLoader.cpp create mode 100644 Core/iNesLoader.h diff --git a/Core/BaseMapper.cpp b/Core/BaseMapper.cpp index 80d747f0..cea876b6 100644 --- a/Core/BaseMapper.cpp +++ b/Core/BaseMapper.cpp @@ -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; diff --git a/Core/BaseMapper.h b/Core/BaseMapper.h index c7f0f4d6..940c3d5c 100644 --- a/Core/BaseMapper.h +++ b/Core/BaseMapper.h @@ -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 _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); diff --git a/Core/CPU.h b/Core/CPU.h index 35c0b9e8..3a3d4262 100644 --- a/Core/CPU.h +++ b/Core/CPU.h @@ -33,6 +33,7 @@ enum class IRQSource External = 1, FrameCounter = 2, DMC = 4, + FdsDisk = 8, }; struct State diff --git a/Core/Console.cpp b/Core/Console.cpp index e8db00d4..ca691380 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -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(); diff --git a/Core/Console.h b/Core/Console.h index 33cd4df0..3dd61887 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -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 GetInstance(); static void Release(); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index ac289499..4ca2dfee 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -366,7 +366,10 @@ + + + @@ -401,6 +404,7 @@ + @@ -480,7 +484,7 @@ - + @@ -523,6 +527,7 @@ + @@ -530,6 +535,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index ee7c9098..13332a7a 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -36,6 +36,33 @@ {a6994cb5-f9d2-416c-84ab-c1abe4975eb1} + + {cfdbaafc-8e74-4e09-80fd-30f8bc833c88} + + + {264ce0a6-616f-4098-8d7d-645d1fa3672b} + + + {d2059959-ca5c-48ae-8b55-b84cd0d65633} + + + {82a608bc-1b40-4551-9c82-b4c9fdfe60f0} + + + {c99c645d-703b-471a-a01b-97375bfb3aaf} + + + {4d835f6a-a005-4f62-8c6b-d5cdb9d96dd2} + + + {609b9f1a-b212-4322-b835-f68cd59e6c8a} + + + {b3fac043-0477-496b-80d1-77c4969262e3} + + + {4a7af167-f6cb-4173-b7ca-04ed7c5858b1} + @@ -53,9 +80,6 @@ Nes\Mappers - - Nes\Mappers - Nes\Mappers @@ -65,18 +89,12 @@ Nes\Mappers - - Nes\Mappers - Nes\Interfaces Nes\Mappers - - Nes\Mappers - Nes\Mappers @@ -116,9 +134,6 @@ Nes\Mappers - - Nes\Mappers - Debugger @@ -173,9 +188,6 @@ Nes\Mappers - - Nes\Mappers - VideoDecoder\HD @@ -191,18 +203,9 @@ Header Files - - Nes\Mappers - Nes\Mappers - - Nes\Mappers - - - Nes\Mappers - Nes\Mappers @@ -215,57 +218,18 @@ Nes\Mappers - - Nes\Mappers - Nes\Mappers Nes\Mappers - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - Nes\Mappers Nes\Mappers - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - VideoDecoder @@ -317,9 +281,6 @@ Nes - - Nes - Nes @@ -344,129 +305,21 @@ Nes\Mappers - - Nes\Mappers - Nes\Mappers - - Nes\Mappers - Nes\Mappers - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - Nes\Mappers - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - Nes\Mappers - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - - - Nes\Mappers - Nes\Mappers - - Nes\Mappers - - - Nes\Mappers - Nes\Mappers @@ -485,6 +338,192 @@ Nes\Mappers + + Nes\RomLoader + + + Nes\RomLoader + + + Nes\RomLoader + + + Nes\RomLoader + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\MMC + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Unnamed + + + Nes\Mappers\Sunsoft + + + Nes\Mappers\Sunsoft + + + Nes\Mappers\Sunsoft + + + Nes\Mappers\Sunsoft + + + Nes\Mappers\Sunsoft + + + Nes\Mappers\Namco + + + Nes\Mappers\Namco + + + Nes\Mappers\Namco + + + Nes\Mappers\Namco + + + Nes\Mappers\Namco + + + Nes\Mappers\Irem + + + Nes\Mappers\Irem + + + Nes\Mappers\Irem + + + Nes\Mappers\Irem + + + Nes\Mappers\Sachen + + + Nes\Mappers\Sachen + + + Nes\Mappers\Sachen + + + Nes\Mappers\Sachen + + + Nes\Mappers\Jaleco + + + Nes\Mappers\Jaleco + + + Nes\Mappers\Jaleco + + + Nes\Mappers\Jaleco + + + Nes\Mappers\Jaleco + + + Nes\Mappers\FDS + @@ -598,5 +637,11 @@ Source Files + + Nes\RomLoader + + + Nes\Mappers\FDS + \ No newline at end of file diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index e7b21236..7720f329 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -12,6 +12,8 @@ enum EmulationFlags RemoveSpriteLimit = 0x10, UseHdPacks = 0x20, + FdsFastForwardOnLoad = 0x2000, + FdsAutoLoadDisk = 0x4000, Mmc3IrqAltBehavior = 0x8000, }; diff --git a/Core/FDS.cpp b/Core/FDS.cpp new file mode 100644 index 00000000..6b35ecc5 --- /dev/null +++ b/Core/FDS.cpp @@ -0,0 +1,4 @@ +#include "stdafx.h" +#include "FDS.h" + +FDS* FDS::Instance = nullptr; \ No newline at end of file diff --git a/Core/FDS.h b/Core/FDS.h new file mode 100644 index 00000000..dcf80d90 --- /dev/null +++ b/Core/FDS.h @@ -0,0 +1,460 @@ +#pragma once +#include "stdafx.h" +#include +#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 _fdsRawData; + vector> _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(_irqReloadValue); + Stream(_irqCounter); + Stream(_irqEnabled); + Stream(_irqReloadEnabled); + + Stream(_diskRegEnabled); + Stream(_soundRegEnabled); + + Stream(_writeDataReg); + + Stream(_motorOn); + Stream(_resetTransfer); + Stream(_readMode); + Stream(_crcControl); + Stream(_diskReady); + Stream(_diskIrqEnabled); + + Stream(_extConWriteReg); + + Stream(_badCrc); + Stream(_endOfHead); + Stream(_readWriteEnabled); + + Stream(_readDataReg); + + Stream(_diskWriteProtected); + + Stream(_diskNumber); + Stream(_newDiskNumber); + Stream(_newDiskInsertDelay); + Stream(_diskPosition); + Stream(_delay); + Stream(_previousCrcControlFlag); + Stream(_gapEnded); + Stream(_scanningDisk); + Stream(_needIrq); + Stream(_transferComplete); + Stream(_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); + } + } +}; \ No newline at end of file diff --git a/Core/FdsLoader.h b/Core/FdsLoader.h new file mode 100644 index 00000000..5e967d14 --- /dev/null +++ b/Core/FdsLoader.h @@ -0,0 +1,171 @@ +#pragma once +#include "stdafx.h" +#include +#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& 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 RebuildFdsFile(vector> diskData, bool needHeader) + { + vector 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 &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& 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()); + vector &fdsDiskImage = romData.FdsDiskData.back(); + + AddGaps(fdsDiskImage, &romFile[fileOffset]); + fileOffset += FdsDiskSideCapacity; + + //Ensure the image is 65500 bytes + if(fdsDiskImage.size() < FdsDiskSideCapacity) { + fdsDiskImage.resize(FdsDiskSideCapacity); + } + } + } + + vector LoadBios() + { + //For FDS, the PRG ROM is the FDS BIOS (8k) + vector biosData; + + ifstream biosFile("FdsBios.bin", ios::in | ios::binary); + if(biosFile) { + return vector(std::istreambuf_iterator(biosFile), {}); + } else { + MessageManager::SendNotification(ConsoleNotificationType::FdsBiosNotFound); + } + return {}; + } + +public: + void SaveIpsFile(string filename, vector &originalDiskData, vector> ¤tDiskData) + { + bool needHeader = (memcmp(originalDiskData.data(), "FDS\x1a", 4) == 0); + vector newData = RebuildFdsFile(currentDiskData, needHeader); + vector 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& 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; + } +}; \ No newline at end of file diff --git a/Core/GameInformationMessage.h b/Core/GameInformationMessage.h index 7bdea6a2..a996d74e 100644 --- a/Core/GameInformationMessage.h +++ b/Core/GameInformationMessage.h @@ -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; } diff --git a/Core/GameServerConnection.cpp b/Core/GameServerConnection.cpp index 3ab9cf4a..1ca0eee0 100644 --- a/Core/GameServerConnection.cpp +++ b/Core/GameServerConnection.cpp @@ -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: diff --git a/Core/INotificationListener.h b/Core/INotificationListener.h index 4e4c4f9f..28691f81 100644 --- a/Core/INotificationListener.h +++ b/Core/INotificationListener.h @@ -15,6 +15,8 @@ enum class ConsoleNotificationType PpuFrameDone = 9, MovieEnded = 10, ResolutionChanged = 11, + FdsDiskChanged = 12, + FdsBiosNotFound = 13, }; class INotificationListener diff --git a/Core/MapperFactory.cpp b/Core/MapperFactory.cpp index 7d2f6f71..13485dd8 100644 --- a/Core/MapperFactory.cpp +++ b/Core/MapperFactory.cpp @@ -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 MapperFactory::InitializeFromFile(string romFilename, stringstream *filestream, string ipsFilename) { - ROMLoader loader; + RomLoader loader; if(loader.LoadFile(romFilename, filestream, ipsFilename)) { - shared_ptr mapper(GetMapperFromID(loader)); + RomData romData = loader.GetRomData(); + shared_ptr mapper(GetMapperFromID(romData)); if(mapper) { - mapper->Initialize(loader); + mapper->Initialize(romData); return mapper; } } diff --git a/Core/MapperFactory.h b/Core/MapperFactory.h index fc9c1e90..55c46c55 100644 --- a/Core/MapperFactory.h +++ b/Core/MapperFactory.h @@ -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 InitializeFromFile(string romFilename, stringstream *filestream, string ipsFilename); }; diff --git a/Core/Movie.cpp b/Core/Movie.cpp index bd7047c4..6e278953 100644 --- a/Core/Movie.cpp +++ b/Core/Movie.cpp @@ -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); } diff --git a/Core/ROMLoader.h b/Core/ROMLoader.h index 45d4054d..4050b1e6 100644 --- a/Core/ROMLoader.h +++ b/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 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 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); }; - diff --git a/Core/RomData.h b/Core/RomData.h new file mode 100644 index 00000000..91765b9a --- /dev/null +++ b/Core/RomData.h @@ -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 PrgRom; + vector ChrRom; + vector> FdsDiskData; + + vector 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; + } + } +}; \ No newline at end of file diff --git a/Core/RomLoader.cpp b/Core/RomLoader.cpp new file mode 100644 index 00000000..92820170 --- /dev/null +++ b/Core/RomLoader.cpp @@ -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 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 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 validExtensions = { { "*.nes", "*.zip", "*.fds" } }; + vector 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 ""; +} \ No newline at end of file diff --git a/Core/iNesLoader.h b/Core/iNesLoader.h new file mode 100644 index 00000000..b328da9b --- /dev/null +++ b/Core/iNesLoader.h @@ -0,0 +1,30 @@ +#pragma once +#include "stdafx.h" + +class iNesLoader +{ +public: + RomData LoadRom(vector& 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; + } +}; \ No newline at end of file diff --git a/GUI.NET/Config/PreferenceInfo.cs b/GUI.NET/Config/PreferenceInfo.cs index c173a406..ac6af2c9 100644 --- a/GUI.NET/Config/PreferenceInfo.cs +++ b/GUI.NET/Config/PreferenceInfo.cs @@ -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); } } } diff --git a/GUI.NET/Forms/Config/frmPreferences.Designer.cs b/GUI.NET/Forms/Config/frmPreferences.Designer.cs index de04b5e7..76d5f6a9 100644 --- a/GUI.NET/Forms/Config/frmPreferences.Designer.cs +++ b/GUI.NET/Forms/Config/frmPreferences.Designer.cs @@ -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; } } \ No newline at end of file diff --git a/GUI.NET/Forms/Config/frmPreferences.cs b/GUI.NET/Forms/Config/frmPreferences.cs index d088748a..e9403ec2 100644 --- a/GUI.NET/Forms/Config/frmPreferences.cs +++ b/GUI.NET/Forms/Config/frmPreferences.cs @@ -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) diff --git a/GUI.NET/Forms/Config/frmPreferences.resx b/GUI.NET/Forms/Config/frmPreferences.resx index 1af7de15..8766f298 100644 --- a/GUI.NET/Forms/Config/frmPreferences.resx +++ b/GUI.NET/Forms/Config/frmPreferences.resx @@ -117,4 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + \ No newline at end of file diff --git a/GUI.NET/Forms/frmMain.Designer.cs b/GUI.NET/Forms/frmMain.Designer.cs index 922d98df..b07e702d 100644 --- a/GUI.NET/Forms/frmMain.Designer.cs +++ b/GUI.NET/Forms/frmMain.Designer.cs @@ -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; } } diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index 023f605e..edbd955f 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -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); + } + } + } + } } } diff --git a/GUI.NET/Forms/frmMain.resx b/GUI.NET/Forms/frmMain.resx index d4693c9f..9c8ed399 100644 --- a/GUI.NET/Forms/frmMain.resx +++ b/GUI.NET/Forms/frmMain.resx @@ -117,10 +117,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + 17, 17 + + 107, 17 + - 125, 17 + 215, 17 \ No newline at end of file diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 7a8f765e..acb249d0 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -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; } diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index 5c2716a9..17bbf540 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -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(); } } } \ No newline at end of file diff --git a/Utilities/IpsPatcher.cpp b/Utilities/IpsPatcher.cpp index 4b4fb8da..4302d9c0 100644 --- a/Utilities/IpsPatcher.cpp +++ b/Utilities/IpsPatcher.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include #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 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 &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 IpsPatcher::PatchBuffer(string ipsFilepath, vector 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 records; + vector 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 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 IpsPatcher::CreatePatch(vector originalData, vector newData) +{ + assert(originalData.size() == newData.size()); + + vector 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(&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; } \ No newline at end of file diff --git a/Utilities/IpsPatcher.h b/Utilities/IpsPatcher.h index 94f0e470..8aa08915 100644 --- a/Utilities/IpsPatcher.h +++ b/Utilities/IpsPatcher.h @@ -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 PatchBuffer(string ipsFilepath, vector input); + static vector CreatePatch(vector originalData, vector newData); }; \ No newline at end of file