Patches by chrono:

[CORE] Add UPS/PPF patch support
[SDL] Add UPS/PPF patch support
[SDL] Fix memory leak
This commit is contained in:
spacy51 2008-10-16 13:56:49 +00:00
parent dcc2f30119
commit 89c0a7b049
5 changed files with 551 additions and 140 deletions

View File

@ -134,6 +134,7 @@ SET(SRC_MAIN
src/Mode3.cpp
src/Mode4.cpp
src/Mode5.cpp
src/Patch.cpp
src/pixel.cpp
src/RTC.cpp
src/scanline.cpp

464
src/Patch.cpp Normal file
View File

@ -0,0 +1,464 @@
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004-2006 Forgotten and the VBA development team
// Copyright (C) 2007-2008 VBA-M development team and Shay Green
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or(at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include "System.h"
// #include "NLS.h"
// #include "Util.h"
// #include "Flash.h"
// #include "agb/GBA.h"
// #include "Globals.h"
// #include "RTC.h"
// #include "Port.h"
#ifndef _MSC_VER
#define _stricmp strcasecmp
#endif // ! _MSC_VER
#if defined(__APPLE__) || defined (MACOSX)
#define fseeko64 fseeko
#define ftello64 ftello
typedef off_t __off64_t;
#endif /* __APPLE__ || MACOSX */
static int readInt2(FILE *f)
{
int res = 0;
int c = fgetc(f);
if(c == EOF)
return -1;
res = c;
c = fgetc(f);
if(c == EOF)
return -1;
return c + (res<<8);
}
static int readInt3(FILE *f)
{
int res = 0;
int c = fgetc(f);
if(c == EOF)
return -1;
res = c;
c = fgetc(f);
if(c == EOF)
return -1;
res = c + (res<<8);
c = fgetc(f);
if(c == EOF)
return -1;
return c + (res<<8);
}
static s64 readInt4(FILE *f)
{
s64 tmp, res = 0;
int c;
for (int i = 0; i < 4; i++) {
c = fgetc(f);
if (c == EOF)
return -1;
tmp = c;
res = res + (tmp << (i*8));
}
return res;
}
static s64 readInt8(FILE *f)
{
s64 tmp, res = 0;
int c;
for (int i = 0; i < 8; i++) {
c = fgetc(f);
if (c == EOF)
return -1;
tmp = c;
res = res + (tmp << (i*8));
}
return res;
}
static s64 readVarPtr(FILE *f)
{
s64 offset = 0, shift = 1;
for (;;) {
int c = fgetc(f);
if (c == EOF) return 0;
offset += (c & 0x7F) * shift;
if (c & 0x80) break;
shift <<= 7;
offset += shift;
}
return offset;
}
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
static uLong computePatchCRC(FILE *f, unsigned int size)
{
Bytef buf[4096];
long readed;
uLong crc = crc32(0L, Z_NULL, 0);
do {
readed = fread(buf, 1, MIN(size, sizeof(buf)), f);
crc = crc32(crc, buf, readed);
size -= readed;
} while (readed > 0);
return crc;
}
static bool patchApplyIPS(const char *patchname, u8 **r, int *s)
{
// from the IPS spec at http://zerosoft.zophar.net/ips.htm
FILE *f = fopen(patchname, "rb");
if(!f)
return false;
bool result = false;
u8 *rom = *r;
int size = *s;
if(fgetc(f) == 'P' &&
fgetc(f) == 'A' &&
fgetc(f) == 'T' &&
fgetc(f) == 'C' &&
fgetc(f) == 'H') {
int b;
int offset;
int len;
result = true;
for(;;) {
// read offset
offset = readInt3(f);
// if offset == EOF, end of patch
if(offset == 0x454f46 || offset == -1)
break;
// read length
len = readInt2(f);
if(!len) {
// len == 0, RLE block
len = readInt2(f);
// byte to fill
int c = fgetc(f);
if(c == -1)
break;
b = (u8)c;
} else
b= -1;
// check if we need to reallocate our ROM
if((offset + len) >= size) {
size *= 2;
rom = (u8 *)realloc(rom, size);
*r = rom;
*s = size;
}
if(b == -1) {
// normal block, just read the data
if(fread(&rom[offset], 1, len, f) != (size_t)len)
break;
} else {
// fill the region with the given byte
while(len--) {
rom[offset++] = b;
}
}
}
}
// close the file
fclose(f);
return result;
}
static bool patchApplyUPS(const char *patchname, u8 **rom, int *size)
{
s64 srcCRC, dstCRC, patchCRC;
FILE *f = fopen(patchname, "rb");
if (!f)
return false;
fseeko64(f, 0, SEEK_END);
__off64_t patchSize = ftello64(f);
if (patchSize < 20) {
fclose(f);
return false;
}
fseeko64(f, 0, SEEK_SET);
if(fgetc(f) != 'U' || fgetc(f) != 'P' || fgetc(f) != 'S' || fgetc(f) != '1') {
fclose(f);
return false;
}
fseeko64(f, -12, SEEK_END);
srcCRC = readInt4(f);
dstCRC = readInt4(f);
patchCRC = readInt4(f);
if (srcCRC == -1 || dstCRC == -1 || patchCRC == -1) {
fclose(f);
return false;
}
fseeko64(f, 0, SEEK_SET);
u32 crc = computePatchCRC(f, patchSize-4);
if (crc != patchCRC) {
fclose(f);
return false;
}
crc = crc32(0L, Z_NULL, 0);
crc = crc32(crc, *rom, *size);
fseeko64(f, 4, SEEK_SET);
s64 dataSize;
s64 srcSize = readVarPtr(f);
s64 dstSize = readVarPtr(f);
if (crc == srcCRC) {
dataSize = srcSize;
} else if (crc == dstCRC) {
dataSize = dstSize;
} else {
fclose(f);
return false;
}
if (dataSize != *size) {
fclose(f);
return false;
}
s64 relative = 0;
u8 *mem;
while(ftello64(f) < patchSize - 12) {
relative += readVarPtr(f);
if (relative > dataSize) continue;
mem = *rom + relative;
for(s64 i = relative; i < dataSize; i++) {
int x = fgetc(f);
relative++;
if (!x) break;
if (i < dataSize) {
*mem++ ^= x;
}
}
}
fclose(f);
return true;
}
static int ppfVersion(FILE *f)
{
fseeko64(f, 0, SEEK_SET);
if (fgetc(f) != 'P' || fgetc(f) != 'P' || fgetc(f) != 'F')
return 0;
switch(fgetc(f)){
case '1': return 1;
case '2': return 2;
case '3': return 3;
default: return 0;
}
}
int ppfFileIdLen(FILE *f, int version)
{
if (version == 2) {
fseeko64(f, -8, SEEK_END);
} else {
fseeko64(f, -6, SEEK_END);
}
if (fgetc(f) != '.' || fgetc(f) != 'D' || fgetc(f) != 'I' || fgetc(f) != 'Z')
return 0;
return (version == 2) ? readInt4(f) : readInt2(f);
}
static bool patchApplyPPF1(FILE *f, u8 **rom, int *size)
{
fseek(f, 0, SEEK_END);
int count = ftell(f);
if (count < 56)
return false;
count -= 56;
fseek(f, 56, SEEK_SET);
u8 *mem = *rom;
while (count > 0) {
int offset = readInt4(f);
if (offset == -1)
break;
int len = fgetc(f);
if (len == EOF)
break;
if (offset+len > *size)
break;
if (fread(&mem[offset], 1, len, f) != (size_t)len)
break;
count -= 4 + 1 + len;
}
return (count == 0);
}
static bool patchApplyPPF2(FILE *f, u8 **rom, int *size)
{
fseek(f, 0, SEEK_END);
int count = ftell(f);
if (count < 56+4+1024)
return false;
count -= 56+4+1024;
fseek(f, 56, SEEK_SET);
int datalen = readInt4(f);
if (datalen != *size)
return false;
u8 *mem = *rom;
u8 block[1024];
fread(&block, 1, 1024, f);
if (memcmp(&mem[0x9320], &block, 1024) != 0)
return false;
int idlen = ppfFileIdLen(f, 2);
if (idlen > 0)
count -= 16 + 16 + idlen;
fseek(f, 56+4+1024, SEEK_SET);
while (count > 0) {
int offset = readInt4(f);
if (offset == -1)
break;
int len = fgetc(f);
if (len == EOF)
break;
if (offset+len > *size)
break;
if (fread(&mem[offset], 1, len, f) != (size_t)len)
break;
count -= 4 + 1 + len;
}
return (count == 0);
}
static bool patchApplyPPF3(FILE *f, u8 **rom, int *size)
{
fseek(f, 0, SEEK_END);
int count = ftell(f);
if (count < 56+4+1024)
return false;
count -= 56+4;
fseek(f, 56, SEEK_SET);
int imagetype = fgetc(f);
int blockcheck = fgetc(f);
int undo = fgetc(f);
fgetc(f);
u8 *mem = *rom;
if (blockcheck) {
u8 block[1024];
fread(&block, 1, 1024, f);
if (memcmp(&mem[(imagetype == 0) ? 0x9320 : 0x80A0], &block, 1024) != 0)
return false;
count -= 1024;
}
int idlen = ppfFileIdLen(f, 2);
if (idlen > 0)
count -= 16 + 16 + idlen;
fseek(f, 56+4+(blockcheck ? 1024 : 0), SEEK_SET);
while (count > 0) {
__off64_t offset = readInt8(f);
if (offset == -1)
break;
int len = fgetc(f);
if (len == EOF)
break;
if (offset+len > *size)
break;
if (fread(&mem[offset], 1, len, f) != (size_t)len)
break;
if (undo) fseeko64(f, len, SEEK_CUR);
count -= 8 + 1 + len;
if (undo) count -= len;
}
return (count == 0);
}
static bool patchApplyPPF(const char *patchname, u8 **rom, int *size)
{
FILE *f = fopen(patchname, "rb");
if (!f)
return false;
bool res = false;
int version = ppfVersion(f);
switch (version) {
case 1: res = patchApplyPPF1(f, rom, size); break;
case 2: res = patchApplyPPF2(f, rom, size); break;
case 3: res = patchApplyPPF3(f, rom, size); break;
}
fclose(f);
return res;
}
bool applyPatch(const char *patchname, u8 **rom, int *size)
{
if (strlen(patchname) < 5)
return false;
const char * p = strrchr(patchname, '.');
if (p == NULL)
return false;
if (_stricmp(p, ".ips") == 0)
return patchApplyIPS(patchname, rom, size);
if (_stricmp(p, ".ups") == 0)
return patchApplyUPS(patchname, rom, size);
if (_stricmp(p, ".ppf") == 0)
return patchApplyPPF(patchname, rom, size);
return false;
}

27
src/Patch.h Normal file
View File

@ -0,0 +1,27 @@
// -*- C++ -*-
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004 Forgotten and the VBA development team
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or(at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef VBA_PATCH_H
#define VBA_PATCH_H
#include "System.h"
bool applyPatch(const char *patchname, u8 **rom, int *size);
#endif

View File

@ -34,6 +34,7 @@ extern "C" {
#include "agb/GBA.h"
#include "Globals.h"
#include "RTC.h"
#include "Patch.h"
#include "Port.h"
#include "fex.h"
@ -330,96 +331,9 @@ bool utilWriteBMPFile(const char *fileName, int w, int h, u8 *pix)
return true;
}
static int utilReadInt2(FILE *f)
{
int res = 0;
int c = fgetc(f);
if(c == EOF)
return -1;
res = c;
c = fgetc(f);
if(c == EOF)
return -1;
return c + (res<<8);
}
static int utilReadInt3(FILE *f)
{
int res = 0;
int c = fgetc(f);
if(c == EOF)
return -1;
res = c;
c = fgetc(f);
if(c == EOF)
return -1;
res = c + (res<<8);
c = fgetc(f);
if(c == EOF)
return -1;
return c + (res<<8);
}
void utilApplyIPS(const char *ips, u8 **r, int *s)
{
// from the IPS spec at http://zerosoft.zophar.net/ips.htm
FILE *f = fopen(ips, "rb");
if(!f)
return;
u8 *rom = *r;
int size = *s;
if(fgetc(f) == 'P' &&
fgetc(f) == 'A' &&
fgetc(f) == 'T' &&
fgetc(f) == 'C' &&
fgetc(f) == 'H') {
int b;
int offset;
int len;
for(;;) {
// read offset
offset = utilReadInt3(f);
// if offset == EOF, end of patch
if(offset == 0x454f46)
break;
// read length
len = utilReadInt2(f);
if(!len) {
// len == 0, RLE block
len = utilReadInt2(f);
// byte to fill
int c = fgetc(f);
if(c == -1)
break;
b = (u8)c;
} else
b= -1;
// check if we need to reallocate our ROM
if((offset + len) >= size) {
size *= 2;
rom = (u8 *)realloc(rom, size);
*r = rom;
*s = size;
}
if(b == -1) {
// normal block, just read the data
if(fread(&rom[offset], 1, len, f) != (size_t)len)
break;
} else {
// fill the region with the given byte
while(len--) {
rom[offset++] = b;
}
}
}
}
// close the file
fclose(f);
}
//TODO: Modify ZSNES code for this
void utilApplyUPS(const char *ips, u8 **r, int *s)
void utilApplyIPS(const char *ips, u8 **rom, int *size)
{
applyPatch(ips, rom, size);
}
extern bool cpuIsMultiBoot;

View File

@ -40,6 +40,7 @@
#include "../agb/GBA.h"
#include "../agb/agbprint.h"
#include "../Flash.h"
#include "../Patch.h"
#include "../RTC.h"
#include "../Sound.h"
#include "../Util.h"
@ -150,7 +151,6 @@ FilterFunc filterFunction = 0;
IFBFilterFunc ifbFunction = 0;
IFBFilter ifbType = kIFBNone;
char filename[2048];
char ipsname[2048];
char biosFileName[2048];
char gbBiosFileName[2048];
char captureDir[2048];
@ -185,10 +185,10 @@ static int saveSlotPosition = 0; // default is the slot from normal F1
static int sdlOpenglScale = 1;
// will scale window on init by this much
static int sdlSoundToggledOff = 0;
// allow up to 100 IPS patches given on commandline
#define IPS_MAX_NUM 100
int sdl_ips_num = 0;
char * (sdl_ips_names[IPS_MAX_NUM]) = { NULL }; // and so on
// allow up to 100 IPS/UPS/PPF patches given on commandline
#define PATCH_MAX_NUM 100
int sdl_patch_num = 0;
char * (sdl_patch_names[PATCH_MAX_NUM]) = { NULL }; // and so on
#define REWIND_NUM 8
#define REWIND_SIZE 400000
@ -214,7 +214,7 @@ bool debugger = false;
bool debuggerStub = false;
int fullscreen = 0;
int sdlFlashSize = 0;
int sdlAutoIPS = 1;
int sdlAutoPatch = 1;
int sdlRtcEnable = 0;
int sdlAgbPrint = 0;
int sdlMirroringEnable = 0;
@ -259,11 +259,11 @@ struct option sdlOptions[] = {
{ "fullscreen", no_argument, &fullscreen, 1 },
{ "gdb", required_argument, 0, 'G' },
{ "help", no_argument, &sdlPrintUsage, 1 },
{ "ips", required_argument, 0, 'i' },
{ "patch", required_argument, 0, 'i' },
{ "no-agb-print", no_argument, &sdlAgbPrint, 0 },
{ "no-auto-frameskip", no_argument, &autoFrameSkip, 0 },
{ "no-debug", no_argument, 0, 'N' },
{ "no-ips", no_argument, &sdlAutoIPS, 0 },
{ "no-patch", no_argument, &sdlAutoPatch, 0 },
{ "no-opengl", no_argument, &openGL, 0 },
{ "no-pause-when-inactive", no_argument, &pauseWhenInactive, 0 },
{ "no-rtc", no_argument, &sdlRtcEnable, 0 },
@ -1760,7 +1760,7 @@ Options:\n\
printf(" %d - %s\n", i, getFilterName((Filter)i));
printf("\
-h, --help Print this help\n\
-i, --ips=PATCH Apply given IPS patch\n\
-i, --patch=PATCH Apply given patch\n\
-p, --profile=[HERTZ] Enable profiling\n\
-s, --frameskip=FRAMESKIP Set frame skip (0...9)\n\
-t, --save-type=TYPE Set the available save type\n\
@ -1787,7 +1787,7 @@ Long options only:\n\
--auto-frameskip Enable auto frameskipping\n\
--no-agb-print Disable AGBPrint support\n\
--no-auto-frameskip Disable auto frameskipping\n\
--no-ips Do not apply IPS patch\n\
--no-patch Do not automatically apply patch\n\
--no-pause-when-inactive Don't pause when inactive\n\
--no-rtc Disable RTC support\n\
--no-show-speed Don't show emulation speed\n\
@ -1796,7 +1796,7 @@ Long options only:\n\
--rtc Enable RTC support\n\
--show-speed-normal Show emulation speed\n\
--show-speed-detailed Show detailed speed data\n\
--cheat 'CHEAT' add a cheat\n\
--cheat 'CHEAT' Add a cheat\n\
");
}
@ -1848,7 +1848,6 @@ int main(int argc, char **argv)
captureDir[0] = 0;
saveDir[0] = 0;
batteryDir[0] = 0;
ipsname[0] = 0;
int op = -1;
@ -1933,16 +1932,15 @@ int main(int argc, char **argv)
break;
case 'i':
if(optarg == NULL) {
fprintf(stderr, "Missing IPS name\n");
fprintf(stderr, "Missing patch name\n");
exit(-1);
}
// strcpy(ipsname, optarg);
if (sdl_ips_num >= IPS_MAX_NUM) {
fprintf(stderr, "Too many IPS patches given at %s (max is %d). Ignoring.\n", optarg, IPS_MAX_NUM);
if (sdl_patch_num >= PATCH_MAX_NUM) {
fprintf(stderr, "Too many patches given at %s (max is %d). Ignoring.\n", optarg, PATCH_MAX_NUM);
} else {
sdl_ips_names[sdl_ips_num] = (char *)malloc(1 + strlen(optarg));
strcpy(sdl_ips_names[sdl_ips_num], optarg);
sdl_ips_num++;
sdl_patch_names[sdl_patch_num] = (char *)malloc(1 + strlen(optarg));
strcpy(sdl_patch_names[sdl_patch_num], optarg);
sdl_patch_num++;
}
break;
case 'G':
@ -2110,14 +2108,26 @@ int main(int argc, char **argv)
if(p)
*p = 0;
// if(ipsname[0] == 0)
// sprintf(ipsname, "%s.ips", filename);
if (sdl_ips_num == 0)
if (sdlAutoPatch && sdl_patch_num == 0)
{
char * tmp;
// no patch given yet - look for ROMBASENAME.ips
sprintf(ipsname, "%s.ips", filename);
sdl_ips_names[0] = ipsname;
sdl_ips_num++;
tmp = (char *)malloc(strlen(filename) + 4 + 1);
sprintf(tmp, "%s.ips", filename);
sdl_patch_names[sdl_patch_num] = tmp;
sdl_patch_num++;
// no patch given yet - look for ROMBASENAME.ups
tmp = (char *)malloc(strlen(filename) + 4 + 1);
sprintf(tmp, "%s.ups", filename);
sdl_patch_names[sdl_patch_num] = tmp;
sdl_patch_num++;
// no patch given yet - look for ROMBASENAME.ppf
tmp = (char *)malloc(strlen(filename) + 4 + 1);
sprintf(tmp, "%s.ppf", filename);
sdl_patch_names[sdl_patch_num] = tmp;
sdl_patch_num++;
}
bool failed = false;
@ -2139,22 +2149,19 @@ int main(int argc, char **argv)
if (gbHardware & 5)
gbCPUInit(gbBiosFileName, useBios);
gbReset();
cartridgeType = IMAGE_GB;
emulator = GBSystem;
if(sdlAutoIPS) {
int size = gbRomSize, patchnum;
// utilApplyIPS(ipsname, &gbRom, &size);
for (patchnum = 0; patchnum < sdl_ips_num; patchnum++) {
fprintf(stdout, "Trying IPS patch %s.\n", sdl_ips_names[patchnum]);
utilApplyIPS(sdl_ips_names[patchnum], &gbRom, &size);
for (patchnum = 0; patchnum < sdl_patch_num; patchnum++) {
fprintf(stdout, "Trying patch %s%s\n", sdl_patch_names[patchnum],
applyPatch(sdl_patch_names[patchnum], &gbRom, &size) ? " [success]" : "");
}
if(size != gbRomSize) {
extern bool gbUpdateSizes();
gbUpdateSizes();
gbReset();
}
}
gbReset();
}
} else if(type == IMAGE_GBA) {
int size = CPULoadRom(szFile);
@ -2168,18 +2175,12 @@ int main(int argc, char **argv)
emulator = GBASystem;
CPUInit(biosFileName, useBios);
int patchnum;
for (patchnum = 0; patchnum < sdl_patch_num; patchnum++) {
fprintf(stdout, "Trying patch %s%s\n", sdl_patch_names[patchnum],
applyPatch(sdl_patch_names[patchnum], &rom, &size) ? " [success]" : "");
}
CPUReset();
if(sdlAutoIPS) {
int size = 0x2000000, patchnum;
// utilApplyIPS(ipsname, &rom, &size);
for (patchnum = 0; patchnum < sdl_ips_num; patchnum++) {
fprintf(stdout, "Trying IPS patch %s.\n", sdl_ips_names[patchnum]);
utilApplyIPS(sdl_ips_names[patchnum], &rom, &size);
}
if(size != 0x2000000) {
CPUReset();
}
}
}
}
@ -2361,6 +2362,10 @@ int main(int argc, char **argv)
filterPix = NULL;
}
for (int i = 0; i < sdl_patch_num; i++) {
free(sdl_patch_names[i]);
}
#if WITH_LIRC
StopLirc();
#endif