mirror of https://github.com/mgba-emu/mgba.git
IPS patch loading support
This commit is contained in:
parent
a6c8089601
commit
f420232bbf
|
@ -82,6 +82,9 @@ void GBAMemoryInit(struct GBA* gba) {
|
||||||
void GBAMemoryDeinit(struct GBA* gba) {
|
void GBAMemoryDeinit(struct GBA* gba) {
|
||||||
mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM);
|
mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM);
|
||||||
mappedMemoryFree(gba->memory.iwram, SIZE_WORKING_IRAM);
|
mappedMemoryFree(gba->memory.iwram, SIZE_WORKING_IRAM);
|
||||||
|
if (gba->memory.rom) {
|
||||||
|
mappedMemoryFree(gba->memory.rom, gba->memory.romSize);
|
||||||
|
}
|
||||||
GBASavedataDeinit(&gba->memory.savedata);
|
GBASavedataDeinit(&gba->memory.savedata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include "debugger/debugger.h"
|
#include "debugger/debugger.h"
|
||||||
|
|
||||||
|
#include "util/patch.h"
|
||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
#ifdef USE_PTHREADS
|
#ifdef USE_PTHREADS
|
||||||
|
@ -52,6 +54,7 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
||||||
|
|
||||||
struct GBA gba;
|
struct GBA gba;
|
||||||
struct ARMCore cpu;
|
struct ARMCore cpu;
|
||||||
|
struct Patch patch;
|
||||||
struct GBAThread* threadContext = context;
|
struct GBAThread* threadContext = context;
|
||||||
struct ARMComponent* components[1] = {};
|
struct ARMComponent* components[1] = {};
|
||||||
int numComponents = 0;
|
int numComponents = 0;
|
||||||
|
@ -110,6 +113,10 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
||||||
if (threadContext->biosFd >= 0) {
|
if (threadContext->biosFd >= 0) {
|
||||||
GBALoadBIOS(&gba, threadContext->biosFd);
|
GBALoadBIOS(&gba, threadContext->biosFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (threadContext->patchFd >= 0 && loadPatch(threadContext->patchFd, &patch)) {
|
||||||
|
GBAApplyPatch(&gba, &patch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (threadContext->debugger) {
|
if (threadContext->debugger) {
|
||||||
|
@ -169,6 +176,7 @@ void GBAMapOptionsToContext(struct StartupOptions* opts, struct GBAThread* threa
|
||||||
threadContext->fd = opts->fd;
|
threadContext->fd = opts->fd;
|
||||||
threadContext->fname = opts->fname;
|
threadContext->fname = opts->fname;
|
||||||
threadContext->biosFd = opts->biosFd;
|
threadContext->biosFd = opts->biosFd;
|
||||||
|
threadContext->patchFd = opts->patchFd;
|
||||||
threadContext->frameskip = opts->frameskip;
|
threadContext->frameskip = opts->frameskip;
|
||||||
threadContext->logLevel = opts->logLevel;
|
threadContext->logLevel = opts->logLevel;
|
||||||
threadContext->rewindBufferCapacity = opts->rewindBufferCapacity;
|
threadContext->rewindBufferCapacity = opts->rewindBufferCapacity;
|
||||||
|
|
|
@ -46,6 +46,7 @@ struct GBAThread {
|
||||||
struct ARMDebugger* debugger;
|
struct ARMDebugger* debugger;
|
||||||
int fd;
|
int fd;
|
||||||
int biosFd;
|
int biosFd;
|
||||||
|
int patchFd;
|
||||||
const char* fname;
|
const char* fname;
|
||||||
int activeKeys;
|
int activeKeys;
|
||||||
int frameskip;
|
int frameskip;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "gba-thread.h"
|
#include "gba-thread.h"
|
||||||
|
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
#include "util/patch.h"
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
@ -139,6 +140,10 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBADestroy(struct GBA* gba) {
|
void GBADestroy(struct GBA* gba) {
|
||||||
|
if (gba->pristineRom == gba->memory.rom) {
|
||||||
|
gba->memory.rom = 0;
|
||||||
|
}
|
||||||
|
mappedMemoryFree(gba->pristineRom, gba->pristineRomSize);
|
||||||
GBAMemoryDeinit(gba);
|
GBAMemoryDeinit(gba);
|
||||||
GBAVideoDeinit(&gba->video);
|
GBAVideoDeinit(&gba->video);
|
||||||
GBAAudioDeinit(&gba->audio);
|
GBAAudioDeinit(&gba->audio);
|
||||||
|
@ -360,10 +365,12 @@ void GBADetachDebugger(struct GBA* gba) {
|
||||||
|
|
||||||
void GBALoadROM(struct GBA* gba, int fd, const char* fname) {
|
void GBALoadROM(struct GBA* gba, int fd, const char* fname) {
|
||||||
struct stat info;
|
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;
|
gba->activeFile = fname;
|
||||||
fstat(fd, &info);
|
fstat(fd, &info);
|
||||||
gba->memory.romSize = info.st_size;
|
gba->pristineRomSize = info.st_size;
|
||||||
|
gba->memory.romSize = gba->pristineRomSize;
|
||||||
if (gba->savefile) {
|
if (gba->savefile) {
|
||||||
GBASavedataInit(&gba->memory.savedata, gba->savefile);
|
GBASavedataInit(&gba->memory.savedata, gba->savefile);
|
||||||
}
|
}
|
||||||
|
@ -391,6 +398,18 @@ void GBALoadBIOS(struct GBA* gba, int fd) {
|
||||||
// TODO: error check
|
// 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) {
|
void GBATimerUpdateRegister(struct GBA* gba, int timer) {
|
||||||
struct GBATimer* currentTimer = &gba->timers[timer];
|
struct GBATimer* currentTimer = &gba->timers[timer];
|
||||||
if (currentTimer->enable && !currentTimer->countUp) {
|
if (currentTimer->enable && !currentTimer->countUp) {
|
||||||
|
|
|
@ -62,6 +62,8 @@ enum GBAKey {
|
||||||
|
|
||||||
struct GBARotationSource;
|
struct GBARotationSource;
|
||||||
struct GBA;
|
struct GBA;
|
||||||
|
struct Patch;
|
||||||
|
|
||||||
typedef void (*GBALogHandler)(struct GBA*, enum GBALogLevel, const char* format, va_list args);
|
typedef void (*GBALogHandler)(struct GBA*, enum GBALogLevel, const char* format, va_list args);
|
||||||
|
|
||||||
struct GBA {
|
struct GBA {
|
||||||
|
@ -95,6 +97,8 @@ struct GBA {
|
||||||
int* keySource;
|
int* keySource;
|
||||||
struct GBARotationSource* rotationSource;
|
struct GBARotationSource* rotationSource;
|
||||||
struct GBARumble* rumble;
|
struct GBARumble* rumble;
|
||||||
|
void* pristineRom;
|
||||||
|
size_t pristineRomSize;
|
||||||
|
|
||||||
const char* activeFile;
|
const char* activeFile;
|
||||||
const char* savefile;
|
const char* savefile;
|
||||||
|
@ -139,6 +143,7 @@ void GBADetachDebugger(struct GBA* gba);
|
||||||
|
|
||||||
void GBALoadROM(struct GBA* gba, int fd, const char* fname);
|
void GBALoadROM(struct GBA* gba, int fd, const char* fname);
|
||||||
void GBALoadBIOS(struct GBA* gba, int fd);
|
void GBALoadBIOS(struct GBA* gba, int fd);
|
||||||
|
void GBAApplyPatch(struct GBA* gba, struct Patch* patch);
|
||||||
|
|
||||||
__attribute__((format (printf, 3, 4)))
|
__attribute__((format (printf, 3, 4)))
|
||||||
void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...);
|
void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...);
|
||||||
|
|
|
@ -25,6 +25,7 @@ static const char* _defaultFilename = "test.rom";
|
||||||
|
|
||||||
static const struct option _options[] = {
|
static const struct option _options[] = {
|
||||||
{ "bios", 1, 0, 'b' },
|
{ "bios", 1, 0, 'b' },
|
||||||
|
{ "patch", 1, 0, 'p' },
|
||||||
{ "frameskip", 1, 0, 's' },
|
{ "frameskip", 1, 0, 's' },
|
||||||
#ifdef USE_CLI_DEBUGGER
|
#ifdef USE_CLI_DEBUGGER
|
||||||
{ "debug", 1, 0, 'd' },
|
{ "debug", 1, 0, 'd' },
|
||||||
|
@ -41,10 +42,11 @@ int parseCommandArgs(struct StartupOptions* opts, int argc, char* const* argv, s
|
||||||
memset(opts, 0, sizeof(*opts));
|
memset(opts, 0, sizeof(*opts));
|
||||||
opts->fd = -1;
|
opts->fd = -1;
|
||||||
opts->biosFd = -1;
|
opts->biosFd = -1;
|
||||||
|
opts->patchFd = -1;
|
||||||
|
|
||||||
int ch;
|
int ch;
|
||||||
char options[64] =
|
char options[64] =
|
||||||
"b:l:s:"
|
"b:l:p:s:"
|
||||||
#ifdef USE_CLI_DEBUGGER
|
#ifdef USE_CLI_DEBUGGER
|
||||||
"d"
|
"d"
|
||||||
#endif
|
#endif
|
||||||
|
@ -80,6 +82,9 @@ int parseCommandArgs(struct StartupOptions* opts, int argc, char* const* argv, s
|
||||||
case 'l':
|
case 'l':
|
||||||
opts->logLevel = atoi(optarg);
|
opts->logLevel = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'p':
|
||||||
|
opts->patchFd = open(optarg, O_RDONLY);
|
||||||
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
opts->frameskip = atoi(optarg);
|
opts->frameskip = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -18,6 +18,7 @@ struct StartupOptions {
|
||||||
int fd;
|
int fd;
|
||||||
const char* fname;
|
const char* fname;
|
||||||
int biosFd;
|
int biosFd;
|
||||||
|
int patchFd;
|
||||||
int logLevel;
|
int logLevel;
|
||||||
int frameskip;
|
int frameskip;
|
||||||
int rewindBufferCapacity;
|
int rewindBufferCapacity;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef PATCH_IPS_H
|
||||||
|
#define PATCH_IPS_H
|
||||||
|
|
||||||
|
struct Patch;
|
||||||
|
|
||||||
|
int loadPatchIPS(struct Patch* patch);
|
||||||
|
|
||||||
|
#endif
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
Loading…
Reference in New Issue