IPS patch loading support

This commit is contained in:
Jeffrey Pfau 2014-07-06 23:39:27 -07:00
parent a6c8089601
commit f420232bbf
11 changed files with 171 additions and 3 deletions

View File

@ -82,6 +82,9 @@ void GBAMemoryInit(struct GBA* gba) {
void GBAMemoryDeinit(struct GBA* gba) {
mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM);
mappedMemoryFree(gba->memory.iwram, SIZE_WORKING_IRAM);
if (gba->memory.rom) {
mappedMemoryFree(gba->memory.rom, gba->memory.romSize);
}
GBASavedataDeinit(&gba->memory.savedata);
}

View File

@ -6,6 +6,8 @@
#include "debugger/debugger.h"
#include "util/patch.h"
#include <signal.h>
#ifdef USE_PTHREADS
@ -52,6 +54,7 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
struct GBA gba;
struct ARMCore cpu;
struct Patch patch;
struct GBAThread* threadContext = context;
struct ARMComponent* components[1] = {};
int numComponents = 0;
@ -110,6 +113,10 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
if (threadContext->biosFd >= 0) {
GBALoadBIOS(&gba, threadContext->biosFd);
}
if (threadContext->patchFd >= 0 && loadPatch(threadContext->patchFd, &patch)) {
GBAApplyPatch(&gba, &patch);
}
}
if (threadContext->debugger) {
@ -169,6 +176,7 @@ void GBAMapOptionsToContext(struct StartupOptions* opts, struct GBAThread* threa
threadContext->fd = opts->fd;
threadContext->fname = opts->fname;
threadContext->biosFd = opts->biosFd;
threadContext->patchFd = opts->patchFd;
threadContext->frameskip = opts->frameskip;
threadContext->logLevel = opts->logLevel;
threadContext->rewindBufferCapacity = opts->rewindBufferCapacity;

View File

@ -46,6 +46,7 @@ struct GBAThread {
struct ARMDebugger* debugger;
int fd;
int biosFd;
int patchFd;
const char* fname;
int activeKeys;
int frameskip;

View File

@ -6,6 +6,7 @@
#include "gba-thread.h"
#include "util/memory.h"
#include "util/patch.h"
#include <sys/stat.h>
@ -139,6 +140,10 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) {
}
void GBADestroy(struct GBA* gba) {
if (gba->pristineRom == gba->memory.rom) {
gba->memory.rom = 0;
}
mappedMemoryFree(gba->pristineRom, gba->pristineRomSize);
GBAMemoryDeinit(gba);
GBAVideoDeinit(&gba->video);
GBAAudioDeinit(&gba->audio);
@ -360,10 +365,12 @@ void GBADetachDebugger(struct GBA* gba) {
void GBALoadROM(struct GBA* gba, int fd, const char* fname) {
struct stat info;
gba->memory.rom = fileMemoryMap(fd, SIZE_CART0, MEMORY_READ);
gba->pristineRom = fileMemoryMap(fd, SIZE_CART0, MEMORY_READ);
gba->memory.rom = gba->pristineRom;
gba->activeFile = fname;
fstat(fd, &info);
gba->memory.romSize = info.st_size;
gba->pristineRomSize = info.st_size;
gba->memory.romSize = gba->pristineRomSize;
if (gba->savefile) {
GBASavedataInit(&gba->memory.savedata, gba->savefile);
}
@ -391,6 +398,18 @@ void GBALoadBIOS(struct GBA* gba, int fd) {
// TODO: error check
}
void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
size_t patchedSize = patch->outputSize(patch, gba->memory.romSize);
gba->memory.rom = anonymousMemoryMap(patchedSize);
memcpy(gba->memory.rom, gba->pristineRom, gba->memory.romSize > patchedSize ? patchedSize : gba->memory.romSize);
if (!patch->applyPatch(patch, gba->memory.rom, patchedSize)) {
mappedMemoryFree(gba->memory.rom, patchedSize);
gba->memory.rom = gba->pristineRom;
return;
}
gba->memory.romSize = patchedSize;
}
void GBATimerUpdateRegister(struct GBA* gba, int timer) {
struct GBATimer* currentTimer = &gba->timers[timer];
if (currentTimer->enable && !currentTimer->countUp) {

View File

@ -62,6 +62,8 @@ enum GBAKey {
struct GBARotationSource;
struct GBA;
struct Patch;
typedef void (*GBALogHandler)(struct GBA*, enum GBALogLevel, const char* format, va_list args);
struct GBA {
@ -95,6 +97,8 @@ struct GBA {
int* keySource;
struct GBARotationSource* rotationSource;
struct GBARumble* rumble;
void* pristineRom;
size_t pristineRomSize;
const char* activeFile;
const char* savefile;
@ -139,6 +143,7 @@ void GBADetachDebugger(struct GBA* gba);
void GBALoadROM(struct GBA* gba, int fd, const char* fname);
void GBALoadBIOS(struct GBA* gba, int fd);
void GBAApplyPatch(struct GBA* gba, struct Patch* patch);
__attribute__((format (printf, 3, 4)))
void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...);

View File

@ -25,6 +25,7 @@ static const char* _defaultFilename = "test.rom";
static const struct option _options[] = {
{ "bios", 1, 0, 'b' },
{ "patch", 1, 0, 'p' },
{ "frameskip", 1, 0, 's' },
#ifdef USE_CLI_DEBUGGER
{ "debug", 1, 0, 'd' },
@ -41,10 +42,11 @@ int parseCommandArgs(struct StartupOptions* opts, int argc, char* const* argv, s
memset(opts, 0, sizeof(*opts));
opts->fd = -1;
opts->biosFd = -1;
opts->patchFd = -1;
int ch;
char options[64] =
"b:l:s:"
"b:l:p:s:"
#ifdef USE_CLI_DEBUGGER
"d"
#endif
@ -80,6 +82,9 @@ int parseCommandArgs(struct StartupOptions* opts, int argc, char* const* argv, s
case 'l':
opts->logLevel = atoi(optarg);
break;
case 'p':
opts->patchFd = open(optarg, O_RDONLY);
break;
case 's':
opts->frameskip = atoi(optarg);
break;

View File

@ -18,6 +18,7 @@ struct StartupOptions {
int fd;
const char* fname;
int biosFd;
int patchFd;
int logLevel;
int frameskip;
int rewindBufferCapacity;

88
src/util/patch-ips.c Normal file
View File

@ -0,0 +1,88 @@
#include "util/patch-ips.h"
#include "util/patch.h"
#include <stdint.h>
#include <unistd.h>
static size_t _IPSOutputSize(struct Patch* patch, size_t inSize);
static int _IPSApplyPatch(struct Patch* patch, void* out, size_t outSize);
int loadPatchIPS(struct Patch* patch) {
lseek(patch->patchfd, 0, SEEK_SET);
char buffer[5];
if (read(patch->patchfd, buffer, 5) != 5) {
return 0;
}
if (memcmp(buffer, "PATCH", 5) != 0) {
return 0;
}
lseek(patch->patchfd, -3, SEEK_END);
if (read(patch->patchfd, buffer, 3) != 3) {
return 0;
}
if (memcmp(buffer, "EOF", 3) != 0) {
return 0;
}
patch->outputSize = _IPSOutputSize;
patch->applyPatch = _IPSApplyPatch;
return 1;
}
size_t _IPSOutputSize(struct Patch* patch, size_t inSize) {
(void) (patch);
return inSize;
}
int _IPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
if (lseek(patch->patchfd, 5, SEEK_SET) != 5) {
return 0;
}
uint8_t* buf = out;
while (1) {
uint32_t offset = 0;
uint16_t size = 0;
if (read(patch->patchfd, &offset, 3) != 3) {
return 0;
}
if (offset == 0x464F45) {
return 1;
}
offset = (offset >> 16) | (offset & 0xFF00) | ((offset << 16) & 0xFF0000);
if (read(patch->patchfd, &size, 2) != 2) {
return 0;
}
if (!size) {
// RLE chunk
if (read(patch->patchfd, &size, 2) != 2) {
return 0;
}
size = (size >> 8) | (size << 8);
uint8_t byte;
if (read(patch->patchfd, &byte, 1) != 1) {
return 0;
}
if (offset + size > outSize) {
return 0;
}
memset(&buf[offset], byte, size);
} else {
size = (size >> 8) | (size << 8);
if (offset + size > outSize) {
return 0;
}
if (read(patch->patchfd, &buf[offset], size) != size) {
return 0;
}
}
}
}

8
src/util/patch-ips.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef PATCH_IPS_H
#define PATCH_IPS_H
struct Patch;
int loadPatchIPS(struct Patch* patch);
#endif

15
src/util/patch.c Normal file
View File

@ -0,0 +1,15 @@
#include "util/patch.h"
#include "util/patch-ips.h"
int loadPatch(int patchfd, struct Patch* patch) {
patch->patchfd = patchfd;
if (loadPatchIPS(patch)) {
return 1;
}
patch->outputSize = 0;
patch->applyPatch = 0;
return 0;
}

15
src/util/patch.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef PATCH_H
#define PATCH_H
#include <string.h>
struct Patch {
int patchfd;
size_t (*outputSize)(struct Patch* patch, size_t inSize);
int (*applyPatch)(struct Patch* patch, void* out, size_t outSize);
};
int loadPatch(int patchfd, struct Patch* patch);
#endif