IPS support

This commit is contained in:
Souryo 2015-12-27 18:41:38 -05:00
parent b1bccc47ce
commit 78081d7c69
14 changed files with 262 additions and 46 deletions

View File

@ -31,13 +31,13 @@ void Console::Release()
Console::Instance.reset(new Console());
}
void Console::Initialize(string filename, stringstream *filestream)
void Console::Initialize(string romFilename, stringstream *filestream, string ipsFilename)
{
MessageManager::SendNotification(ConsoleNotificationType::GameStopped);
shared_ptr<BaseMapper> mapper = MapperFactory::InitializeFromFile(filename, filestream);
shared_ptr<BaseMapper> mapper = MapperFactory::InitializeFromFile(romFilename, filestream, ipsFilename);
if(mapper) {
_romFilepath = filename;
_romFilepath = romFilename;
VideoDecoder::GetInstance()->StopThread();
@ -64,13 +64,20 @@ void Console::Initialize(string filename, stringstream *filestream)
VideoDecoder::GetInstance()->StartThread();
FolderUtilities::AddKnowGameFolder(FolderUtilities::GetFolderName(filename));
MessageManager::DisplayMessage("Game loaded", FolderUtilities::GetFilename(filename, false));
FolderUtilities::AddKnowGameFolder(FolderUtilities::GetFolderName(romFilename));
MessageManager::DisplayMessage("Game loaded", FolderUtilities::GetFilename(romFilename, false));
} else {
MessageManager::DisplayMessage("Error", string("Could not load file: ") + FolderUtilities::GetFilename(filename, true));
MessageManager::DisplayMessage("Error", string("Could not load file: ") + FolderUtilities::GetFilename(romFilename, true));
}
}
void Console::ApplyIpsPatch(string ipsFilename)
{
Console::Pause();
Instance->Initialize(GetROMPath(), nullptr, ipsFilename);
Console::Resume();
}
void Console::LoadROM(string filepath, stringstream *filestream)
{
Console::Pause();

View File

@ -35,7 +35,7 @@ class Console
bool _initialized = false;
void ResetComponents(bool softReset);
void Initialize(string filename, stringstream *filestream = nullptr);
void Initialize(string filename, stringstream *filestream = nullptr, string ipsFilename = "");
void UpdateNesModel(double &frameDelay, bool showMessage);
public:
@ -60,6 +60,7 @@ 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();

View File

@ -48,11 +48,11 @@ BaseMapper* MapperFactory::GetMapperFromID(uint8_t mapperID)
return nullptr;
}
shared_ptr<BaseMapper> MapperFactory::InitializeFromFile(string filename, stringstream *filestream)
shared_ptr<BaseMapper> MapperFactory::InitializeFromFile(string romFilename, stringstream *filestream, string ipsFilename)
{
ROMLoader loader;
if(loader.LoadFile(filename, filestream)) {
if(loader.LoadFile(romFilename, filestream, ipsFilename)) {
uint8_t mapperID = loader.GetMapperID();
shared_ptr<BaseMapper> mapper(GetMapperFromID(mapperID));

View File

@ -7,5 +7,5 @@ class MapperFactory
static BaseMapper* GetMapperFromID(uint8_t mapperID);
public:
static shared_ptr<BaseMapper> InitializeFromFile(string filename, stringstream *filestream);
static shared_ptr<BaseMapper> InitializeFromFile(string romFilename, stringstream *filestream, string ipsFilename);
};

View File

@ -5,6 +5,7 @@
#include "../Utilities/FolderUtilities.h"
#include "../Utilities/ZipReader.h"
#include "../Utilities/CRC32.h"
#include "../Utilities/IpsPatcher.h"
enum class MirroringType
{
@ -65,6 +66,7 @@ class ROMLoader
uint8_t* _prgRAM = nullptr;
uint8_t* _chrRAM = nullptr;
uint32_t _crc32;
string _ipsFilename;
bool LoadFromZip(stringstream &zipFile)
{
@ -128,6 +130,16 @@ class ROMLoader
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) {
memcpy((char*)&_header, buffer, sizeof(NESHeader));
@ -163,8 +175,10 @@ class ROMLoader
}
}
bool LoadFile(string filename, stringstream *filestream = nullptr)
bool LoadFile(string filename, stringstream *filestream = nullptr, string ipsFilename = "")
{
_ipsFilename = ipsFilename;
stringstream ss;
if(!filestream) {
ifstream file(filename, ios::in | ios::binary);

View File

@ -21,6 +21,7 @@ namespace Mesen.GUI.Config
public List<CheatInfo> Cheats;
public List<ControllerInfo> Controllers;
public bool ShowOnlyCheatsForCurrentGame;
public bool AutoLoadIpsPatches;
public NesModel Region;
public Configuration()

View File

@ -23,6 +23,7 @@
if(_debugger != null) {
_debugger.Close();
}
UpdateConfig();
StopEmu();
InteropEmu.Release();
base.Dispose(disposing);
@ -105,6 +106,8 @@
this.mnuTestRecordMovie = new System.Windows.Forms.ToolStripMenuItem();
this.dxViewer = new Mesen.GUI.Controls.DXViewer();
this.mnuTestRecordTest = new System.Windows.Forms.ToolStripMenuItem();
this.mnuAutoLoadIpsPatches = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem10 = new System.Windows.Forms.ToolStripSeparator();
this.menuStrip.SuspendLayout();
this.SuspendLayout();
//
@ -141,49 +144,49 @@
//
this.mnuOpen.Name = "mnuOpen";
this.mnuOpen.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O)));
this.mnuOpen.Size = new System.Drawing.Size(146, 22);
this.mnuOpen.Size = new System.Drawing.Size(152, 22);
this.mnuOpen.Text = "Open";
this.mnuOpen.Click += new System.EventHandler(this.mnuOpen_Click);
//
// toolStripMenuItem4
//
this.toolStripMenuItem4.Name = "toolStripMenuItem4";
this.toolStripMenuItem4.Size = new System.Drawing.Size(143, 6);
this.toolStripMenuItem4.Size = new System.Drawing.Size(149, 6);
//
// mnuSaveState
//
this.mnuSaveState.Name = "mnuSaveState";
this.mnuSaveState.Size = new System.Drawing.Size(146, 22);
this.mnuSaveState.Size = new System.Drawing.Size(152, 22);
this.mnuSaveState.Text = "Save State";
this.mnuSaveState.DropDownOpening += new System.EventHandler(this.mnuSaveState_DropDownOpening);
//
// mnuLoadState
//
this.mnuLoadState.Name = "mnuLoadState";
this.mnuLoadState.Size = new System.Drawing.Size(146, 22);
this.mnuLoadState.Size = new System.Drawing.Size(152, 22);
this.mnuLoadState.Text = "Load State";
this.mnuLoadState.DropDownOpening += new System.EventHandler(this.mnuLoadState_DropDownOpening);
//
// toolStripMenuItem7
//
this.toolStripMenuItem7.Name = "toolStripMenuItem7";
this.toolStripMenuItem7.Size = new System.Drawing.Size(143, 6);
this.toolStripMenuItem7.Size = new System.Drawing.Size(149, 6);
//
// mnuRecentFiles
//
this.mnuRecentFiles.Name = "mnuRecentFiles";
this.mnuRecentFiles.Size = new System.Drawing.Size(146, 22);
this.mnuRecentFiles.Size = new System.Drawing.Size(152, 22);
this.mnuRecentFiles.Text = "Recent Files";
//
// toolStripMenuItem6
//
this.toolStripMenuItem6.Name = "toolStripMenuItem6";
this.toolStripMenuItem6.Size = new System.Drawing.Size(143, 6);
this.toolStripMenuItem6.Size = new System.Drawing.Size(149, 6);
//
// mnuExit
//
this.mnuExit.Name = "mnuExit";
this.mnuExit.Size = new System.Drawing.Size(146, 22);
this.mnuExit.Size = new System.Drawing.Size(152, 22);
this.mnuExit.Text = "Exit";
this.mnuExit.Click += new System.EventHandler(this.mnuExit_Click);
//
@ -202,7 +205,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(152, 22);
this.mnuPause.Text = "Pause";
this.mnuPause.Click += new System.EventHandler(this.mnuPause_Click);
//
@ -210,7 +213,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(152, 22);
this.mnuReset.Text = "Reset";
this.mnuReset.Click += new System.EventHandler(this.mnuReset_Click);
//
@ -218,7 +221,7 @@
//
this.mnuStop.Enabled = false;
this.mnuStop.Name = "mnuStop";
this.mnuStop.Size = new System.Drawing.Size(129, 22);
this.mnuStop.Size = new System.Drawing.Size(152, 22);
this.mnuStop.Text = "Stop";
this.mnuStop.Click += new System.EventHandler(this.mnuStop_Click);
//
@ -227,6 +230,8 @@
this.mnuOptions.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuEmulationSpeed,
this.mnuShowFPS,
this.toolStripMenuItem10,
this.mnuAutoLoadIpsPatches,
this.toolStripMenuItem1,
this.mnuAudioConfig,
this.mnuInput,
@ -250,7 +255,7 @@
this.mnuEmuSpeedHalf,
this.mnuEmuSpeedQuarter});
this.mnuEmulationSpeed.Name = "mnuEmulationSpeed";
this.mnuEmulationSpeed.Size = new System.Drawing.Size(163, 22);
this.mnuEmulationSpeed.Size = new System.Drawing.Size(191, 22);
this.mnuEmulationSpeed.Text = "Emulation Speed";
//
// mnuEmuSpeedNormal
@ -328,26 +333,26 @@
this.mnuShowFPS.CheckOnClick = true;
this.mnuShowFPS.Name = "mnuShowFPS";
this.mnuShowFPS.ShortcutKeys = System.Windows.Forms.Keys.F10;
this.mnuShowFPS.Size = new System.Drawing.Size(163, 22);
this.mnuShowFPS.Size = new System.Drawing.Size(191, 22);
this.mnuShowFPS.Text = "Show FPS";
this.mnuShowFPS.Click += new System.EventHandler(this.mnuShowFPS_Click);
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(160, 6);
this.toolStripMenuItem1.Size = new System.Drawing.Size(188, 6);
//
// mnuAudioConfig
//
this.mnuAudioConfig.Name = "mnuAudioConfig";
this.mnuAudioConfig.Size = new System.Drawing.Size(163, 22);
this.mnuAudioConfig.Size = new System.Drawing.Size(191, 22);
this.mnuAudioConfig.Text = "Audio";
this.mnuAudioConfig.Click += new System.EventHandler(this.mnuAudioConfig_Click);
//
// mnuInput
//
this.mnuInput.Name = "mnuInput";
this.mnuInput.Size = new System.Drawing.Size(163, 22);
this.mnuInput.Size = new System.Drawing.Size(191, 22);
this.mnuInput.Text = "Input";
this.mnuInput.Click += new System.EventHandler(this.mnuInput_Click);
//
@ -358,7 +363,7 @@
this.mnuRegionNtsc,
this.mnuRegionPal});
this.mnuRegion.Name = "mnuRegion";
this.mnuRegion.Size = new System.Drawing.Size(163, 22);
this.mnuRegion.Size = new System.Drawing.Size(191, 22);
this.mnuRegion.Text = "Region";
//
// mnuRegionAuto
@ -385,7 +390,7 @@
// mnuVideoConfig
//
this.mnuVideoConfig.Name = "mnuVideoConfig";
this.mnuVideoConfig.Size = new System.Drawing.Size(163, 22);
this.mnuVideoConfig.Size = new System.Drawing.Size(191, 22);
this.mnuVideoConfig.Text = "Video";
this.mnuVideoConfig.Click += new System.EventHandler(this.mnuVideoConfig_Click);
//
@ -645,6 +650,18 @@
this.mnuTestRecordTest.Text = "Test";
this.mnuTestRecordTest.Click += new System.EventHandler(this.mnuTestRecordTest_Click);
//
// mnuAutoLoadIpsPatches
//
this.mnuAutoLoadIpsPatches.CheckOnClick = true;
this.mnuAutoLoadIpsPatches.Name = "mnuAutoLoadIpsPatches";
this.mnuAutoLoadIpsPatches.Size = new System.Drawing.Size(191, 22);
this.mnuAutoLoadIpsPatches.Text = "Auto-load IPS patches";
//
// toolStripMenuItem10
//
this.toolStripMenuItem10.Name = "toolStripMenuItem10";
this.toolStripMenuItem10.Size = new System.Drawing.Size(188, 6);
//
// frmMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -734,6 +751,8 @@
private System.Windows.Forms.ToolStripMenuItem mnuTestStopRecording;
private System.Windows.Forms.ToolStripMenuItem mnuTestRecordMovie;
private System.Windows.Forms.ToolStripMenuItem mnuTestRecordTest;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem10;
private System.Windows.Forms.ToolStripMenuItem mnuAutoLoadIpsPatches;
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@ -41,6 +42,8 @@ namespace Mesen.GUI.Forms
InitializeEmulationSpeedMenu();
UpdateVideoSettings();
this.mnuAutoLoadIpsPatches.Checked = ConfigManager.Config.AutoLoadIpsPatches;
InitializeEmu();
UpdateMenus();
@ -60,7 +63,7 @@ namespace Mesen.GUI.Forms
{
InteropEmu.InitializeEmu(ConfigManager.HomeFolder, this.Handle, this.dxViewer.Handle);
foreach(string romPath in ConfigManager.Config.RecentFiles) {
InteropEmu.AddKnowGameFolder(System.IO.Path.GetDirectoryName(romPath).ToLowerInvariant());
InteropEmu.AddKnowGameFolder(Path.GetDirectoryName(romPath).ToLowerInvariant());
}
ConfigManager.Config.ApplyConfig();
@ -139,8 +142,8 @@ namespace Mesen.GUI.Forms
{
SetEmulationSpeed((uint)(int)((ToolStripItem)sender).Tag);
}
void UpdateEmulationFlags()
private void UpdateEmulationFlags()
{
ConfigManager.Config.VideoInfo.ShowFPS = mnuShowFPS.Checked;
ConfigManager.ApplyChanges();
@ -148,19 +151,25 @@ namespace Mesen.GUI.Forms
VideoInfo.ApplyConfig();
}
void UpdateVideoSettings()
private void UpdateVideoSettings()
{
mnuShowFPS.Checked = ConfigManager.Config.VideoInfo.ShowFPS;
UpdateEmulationSpeedMenu();
dxViewer.Size = VideoInfo.GetViewerSize();
}
void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
private void UpdateConfig()
{
ConfigManager.Config.AutoLoadIpsPatches = this.mnuAutoLoadIpsPatches.Checked;
ConfigManager.ApplyChanges();
}
private void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
MessageBox.Show(e.Exception.ToString(), "Unexpected Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
void _notifListener_OnNotification(InteropEmu.NotificationEventArgs e)
private void _notifListener_OnNotification(InteropEmu.NotificationEventArgs e)
{
if(e.NotificationType == InteropEmu.ConsoleNotificationType.GameLoaded) {
CheatInfo.ApplyCheats();
@ -176,10 +185,43 @@ namespace Mesen.GUI.Forms
private void mnuOpen_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "All supported formats (*.nes, *.zip)|*.NES;*.ZIP|NES Roms (*.nes)|*.NES|ZIP Archives (*.zip)|*.ZIP|All (*.*)|*.*";
ofd.InitialDirectory = System.IO.Path.GetDirectoryName(ConfigManager.Config.RecentFiles[0]);
ofd.Filter = "All supported formats (*.nes, *.zip, *.ips)|*.NES;*.ZIP;*.IPS|NES Roms (*.nes)|*.NES|ZIP Archives (*.zip)|*.ZIP|IPS Patches (*.ips)|*.IPS|All (*.*)|*.*";
if(ConfigManager.Config.RecentFiles.Count > 0) {
ofd.InitialDirectory = Path.GetDirectoryName(ConfigManager.Config.RecentFiles[0]);
}
if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
LoadROM(ofd.FileName);
if(Path.GetExtension(ofd.FileName).ToLowerInvariant() == ".ips") {
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"));
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 (*.*)|*.*";
if(ConfigManager.Config.RecentFiles.Count > 0) {
ofd.InitialDirectory = Path.GetDirectoryName(ConfigManager.Config.RecentFiles[0]);
}
if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
LoadROM(ofd.FileName);
}
InteropEmu.ApplyIpsPatch(ipsFile);
}
} else if(MessageBox.Show("Patch and reset the current game?", string.Empty, MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.OK) {
InteropEmu.ApplyIpsPatch(ipsFile);
}
}
} else {
LoadROM(ofd.FileName);
if(this.mnuAutoLoadIpsPatches.Checked) {
string ipsFile = Path.Combine(Path.GetDirectoryName(ofd.FileName), Path.GetFileNameWithoutExtension(ofd.FileName)) + ".ips";
if(File.Exists(ipsFile)) {
InteropEmu.ApplyIpsPatch(ipsFile);
}
}
}
}
}
@ -196,7 +238,7 @@ namespace Mesen.GUI.Forms
if(this.InvokeRequired) {
this.BeginInvoke((MethodInvoker)(() => this.UpdateMenus()));
} else {
string romFilename = System.IO.Path.GetFileNameWithoutExtension(InteropEmu.GetROMPath());
string romFilename = Path.GetFileNameWithoutExtension(InteropEmu.GetROMPath());
if(string.IsNullOrWhiteSpace(romFilename)) {
this.Text = "Mesen";
} else {
@ -249,7 +291,7 @@ namespace Mesen.GUI.Forms
mnuRecentFiles.DropDownItems.Clear();
foreach(string filepath in ConfigManager.Config.RecentFiles) {
ToolStripMenuItem tsmi = new ToolStripMenuItem();
tsmi.Text = System.IO.Path.GetFileName(filepath);
tsmi.Text = Path.GetFileName(filepath);
tsmi.Click += (object sender, EventArgs args) => {
LoadROM(filepath);
};
@ -445,7 +487,7 @@ namespace Mesen.GUI.Forms
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "Movie files (*.mmo)|*.mmo|All (*.*)|*.*";
sfd.InitialDirectory = ConfigManager.MovieFolder;
sfd.FileName = System.IO.Path.GetFileNameWithoutExtension(InteropEmu.GetROMPath()) + ".mmo";
sfd.FileName = Path.GetFileNameWithoutExtension(InteropEmu.GetROMPath()) + ".mmo";
if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
InteropEmu.MovieRecord(sfd.FileName, resetEmu);
}
@ -493,9 +535,9 @@ namespace Mesen.GUI.Forms
bool result = InteropEmu.RomTestRun(filename);
if(result) {
passedTests.Add(System.IO.Path.GetFileNameWithoutExtension(filename));
passedTests.Add(Path.GetFileNameWithoutExtension(filename));
} else {
failedTests.Add(System.IO.Path.GetFileNameWithoutExtension(filename));
failedTests.Add(Path.GetFileNameWithoutExtension(filename));
}
}
@ -539,7 +581,7 @@ namespace Mesen.GUI.Forms
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "Test files (*.mtp)|*.mtp|All (*.*)|*.*";
sfd.InitialDirectory = ConfigManager.TestFolder;
sfd.FileName = System.IO.Path.GetFileNameWithoutExtension(InteropEmu.GetROMPath()) + ".mtp";
sfd.FileName = Path.GetFileNameWithoutExtension(InteropEmu.GetROMPath()) + ".mtp";
if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
InteropEmu.RomTestRecord(sfd.FileName, resetEmu);
}
@ -554,7 +596,7 @@ namespace Mesen.GUI.Forms
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "Test files (*.mtp)|*.mtp|All (*.*)|*.*";
sfd.InitialDirectory = ConfigManager.TestFolder;
sfd.FileName = System.IO.Path.GetFileNameWithoutExtension(ofd.FileName) + ".mtp";
sfd.FileName = Path.GetFileNameWithoutExtension(ofd.FileName) + ".mtp";
if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
InteropEmu.RomTestRecordFromMovie(sfd.FileName, ofd.FileName);
}
@ -571,7 +613,7 @@ namespace Mesen.GUI.Forms
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "Test files (*.mtp)|*.mtp|All (*.*)|*.*";
sfd.InitialDirectory = ConfigManager.TestFolder;
sfd.FileName = System.IO.Path.GetFileNameWithoutExtension(ofd.FileName) + ".mtp";
sfd.FileName = Path.GetFileNameWithoutExtension(ofd.FileName) + ".mtp";
if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
InteropEmu.RomTestRecordFromTest(sfd.FileName, ofd.FileName);
}

View File

@ -16,6 +16,7 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void Release();
[DllImport(DLLPath)] public static extern void LoadROM([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename);
[DllImport(DLLPath)] public static extern void ApplyIpsPatch([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename);
[DllImport(DLLPath)] public static extern void AddKnowGameFolder([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string folder);
[DllImport(DLLPath)] public static extern void AddKeyMappings(int port, KeyMapping mapping);

View File

@ -62,6 +62,7 @@ namespace InteropEmu {
}
DllExport void __stdcall LoadROM(char* filename) { Console::LoadROM(filename); }
DllExport void __stdcall ApplyIpsPatch(char* filename) { Console::ApplyIpsPatch(filename); }
DllExport void __stdcall AddKnowGameFolder(char* folder) { FolderUtilities::AddKnowGameFolder(folder); }
DllExport void __stdcall AddKeyMappings(uint32_t port, KeyMapping mapping) { _inputDevices[port]->AddKeyMappings(mapping); }

113
Utilities/IpsPatcher.cpp Normal file
View File

@ -0,0 +1,113 @@
#include "stdafx.h"
#include "IpsPatcher.h"
class IpsRecord
{
public:
uint32_t Address = 0;
uint16_t Length = 0;
uint8_t* Replacement = nullptr;
//For RLE records (when length == 0)
uint16_t RepeatCount = 0;
uint8_t Value = 0;
bool ReadRecord(ifstream &ipsFile)
{
uint8_t buffer[3];
ipsFile.read((char*)buffer, 3);
if(memcmp(buffer, "EOF", 3) == 0) {
//EOF reached
return false;
} else {
Address = buffer[2] | (buffer[1] << 8) | (buffer[0] << 16);
ipsFile.read((char*)buffer, 2);
Length = buffer[1] | (buffer[0] << 8);
if(Length == 0) {
//RLE record
ipsFile.read((char*)buffer, 3);
RepeatCount = buffer[1] | (buffer[0] << 8);
Value = buffer[2];
} else {
Replacement = new uint8_t[Length];
ipsFile.read((char*)Replacement, Length);
}
return true;
}
}
~IpsRecord()
{
if(Replacement != nullptr) {
delete[] Replacement;
}
}
};
bool IpsPatcher::PatchBuffer(string ipsFilepath, uint8_t* inputBuffer, size_t inputBufferSize, uint8_t** outputBuffer, size_t &outputBufferSize)
{
ifstream ipsFile(ipsFilepath, std::ios::in | std::ios::binary);
if(ipsFile) {
char header[5];
ipsFile.read((char*)&header, 5);
if(memcmp((char*)&header, "PATCH", 5) != 0) {
//Invalid ips file
return false;
}
vector<IpsRecord*> records;
int32_t truncateOffset = -1;
size_t maxOutputSize = inputBufferSize;
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;
}
records.push_back(record);
} else {
//EOF, try to read truncate offset record if it exists
uint8_t buffer[3];
ipsFile.read((char*)buffer, 3);
if(!ipsFile.eof()) {
truncateOffset = buffer[2] | (buffer[1] << 8) | (buffer[0] << 16);
}
break;
}
}
outputBufferSize = maxOutputSize;
uint8_t *output = new uint8_t[outputBufferSize];
memset(output, 0, outputBufferSize);
memcpy(output, inputBuffer, inputBufferSize);
for(IpsRecord *record : records) {
if(record->Length == 0) {
memset(output+record->Address, record->Value, record->RepeatCount);
} else {
memcpy(output+record->Address, record->Replacement, record->Length);
}
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;
}
ipsFile.close();
return true;
}
return false;
}

9
Utilities/IpsPatcher.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include "stdafx.h"
class IpsPatcher
{
public:
static bool PatchBuffer(string ipsFilepath, uint8_t* inputBuffer, size_t inputBufferSize, uint8_t** outputBuffer, size_t &outputBufferSize);
};

View File

@ -230,6 +230,7 @@
<ItemGroup>
<ClInclude Include="CRC32.h" />
<ClInclude Include="FolderUtilities.h" />
<ClInclude Include="IpsPatcher.h" />
<ClInclude Include="md5.h" />
<ClInclude Include="miniz.h" />
<ClInclude Include="AutoResetEvent.h" />
@ -247,6 +248,7 @@
<ItemGroup>
<ClCompile Include="CRC32.cpp" />
<ClCompile Include="FolderUtilities.cpp" />
<ClCompile Include="IpsPatcher.cpp" />
<ClCompile Include="md5.cpp" />
<ClCompile Include="miniz.cpp" />
<ClCompile Include="PNGHelper.cpp" />

View File

@ -56,6 +56,9 @@
<ClInclude Include="ZipReader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IpsPatcher.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@ -100,5 +103,8 @@
<ClCompile Include="ZipReader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="IpsPatcher.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>