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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -46,6 +46,7 @@ struct GBAThread {
|
|||
struct ARMDebugger* debugger;
|
||||
int fd;
|
||||
int biosFd;
|
||||
int patchFd;
|
||||
const char* fname;
|
||||
int activeKeys;
|
||||
int frameskip;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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, ...);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -18,6 +18,7 @@ struct StartupOptions {
|
|||
int fd;
|
||||
const char* fname;
|
||||
int biosFd;
|
||||
int patchFd;
|
||||
int logLevel;
|
||||
int frameskip;
|
||||
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