diff --git a/source/ngc/fileop.cpp b/source/ngc/fileop.cpp index 70d2867..6e86d9e 100644 --- a/source/ngc/fileop.cpp +++ b/source/ngc/fileop.cpp @@ -249,6 +249,15 @@ LoadFATSzFile(char * filepath, unsigned char * rbuffer) ***************************************************************************/ int LoadBufferFromFAT (char *filepath, bool silent) +{ + return LoadBufferFromFAT((char *)savebuffer, filepath, silent); +} + +/**************************************************************************** + * Load buffer from FAT file + ***************************************************************************/ +int +LoadBufferFromFAT (char * buffer, char *filepath, bool silent) { int size = 0; diff --git a/source/ngc/fileop.h b/source/ngc/fileop.h index a1a549c..ee7395e 100644 --- a/source/ngc/fileop.h +++ b/source/ngc/fileop.h @@ -27,6 +27,7 @@ int LoadFATFile (char * fbuffer, int length); int LoadFATSzFile(char * filepath, unsigned char * rbuffer); int SaveBufferToFAT (char *filepath, int datasize, bool silent); int LoadBufferFromFAT (char *filepath, bool silent); +int LoadBufferFromFAT (char *buffer, char *filepath, bool silent); extern char currFATdir[MAXPATHLEN]; extern FILE * fatfile; diff --git a/source/ngc/filesel.h b/source/ngc/filesel.h index 84cb3ae..31155d0 100644 --- a/source/ngc/filesel.h +++ b/source/ngc/filesel.h @@ -13,7 +13,7 @@ #include -#define SAVEBUFFERSIZE (512 * 1024) +#define SAVEBUFFERSIZE (1024 * 1024 * 2) #define MAXJOLIET 255 #define MAXDISPLAY 40 diff --git a/source/ngc/vbasupport.cpp b/source/ngc/vbasupport.cpp index 27ddf9c..b962e08 100644 --- a/source/ngc/vbasupport.cpp +++ b/source/ngc/vbasupport.cpp @@ -16,6 +16,7 @@ #include "unzip.h" #include "Util.h" #include "Flash.h" +#include "Patch.h" #include "Port.h" #include "RTC.h" #include "Sound.h" @@ -589,6 +590,80 @@ static void ApplyPerImagePreferences() } } +void LoadPatch(int method) +{ + int patchsize = 0; + int patchtype = -1; + + AllocSaveBuffer (); + + char patchpath[3][512]; + memset(patchpath, 0, sizeof(patchpath)); + sprintf(patchpath[0], "%s/%s.ips",currentdir,ROMFilename); + sprintf(patchpath[1], "%s/%s.ups",currentdir,ROMFilename); + sprintf(patchpath[2], "%s/%s.ppf",currentdir,ROMFilename); + + ShowAction((char *)"Loading patch..."); + + switch (method) + { + case METHOD_SD: + case METHOD_USB: + for(int i=0; i<3; i++) + { + patchsize = LoadBufferFromFAT (patchpath[i], SILENT); + + if(patchsize) + { + patchtype = i; + break; + } + } + break; + + case METHOD_SMB: + for(int i=0; i<3; i++) + { + patchsize = LoadBufferFromSMB (patchpath[i], SILENT); + + if(patchsize) + { + patchtype = i; + break; + } + } + } + + if(patchsize > 0) + { + // create memory file + MFILE * mf = memfopen((char *)savebuffer, patchsize); + + if(cartridgeType == 1) + { + if(patchtype == 0) + patchApplyIPS(mf, &gbRom, &gbRomSize); + else if(patchtype == 1) + patchApplyUPS(mf, &gbRom, &gbRomSize); + else + patchApplyPPF(mf, &gbRom, &gbRomSize); + } + else + { + if(patchtype == 0) + patchApplyIPS(mf, &rom, &GBAROMSize); + else if(patchtype == 1) + patchApplyUPS(mf, &rom, &GBAROMSize); + else + patchApplyPPF(mf, &rom, &GBAROMSize); + } + + memfclose(mf); // close memory file + } + + FreeSaveBuffer (); +} + extern bool gbUpdateSizes(); bool LoadGBROM(int method) @@ -721,7 +796,7 @@ bool LoadVBAROM(int method) // Setup GX GX_Render_Init( srcWidth, srcHeight, hAspect, vAspect ); - if ( cartridgeType == 1 ) + if (cartridgeType == 1) { gbGetHardwareType(); @@ -729,7 +804,7 @@ bool LoadVBAROM(int method) //if (gbHardware & 5) //gbCPUInit(gbBiosFileName, useBios); - //applyPatch(patch, &gbRom, &size); + LoadPatch(method); gbSoundReset(); gbSoundSetQuality(soundQuality); @@ -752,7 +827,7 @@ bool LoadVBAROM(int method) soundReset(); soundSetQuality(soundQuality); CPUInit("BIOS.GBA", 1); - //applyPatch(patch, &gbRom, &size); + LoadPatch(method); CPUReset(); } diff --git a/source/ngc/vmmem.cpp b/source/ngc/vmmem.cpp index 156cc63..1163a53 100644 --- a/source/ngc/vmmem.cpp +++ b/source/ngc/vmmem.cpp @@ -38,7 +38,7 @@ extern "C" { unsigned int MEM2Storage = 0x91000000; -static u32 GBAROMSize = 0; +int GBAROMSize = 0; #ifdef USE_VM //extern u32 loadtimeradjust; diff --git a/source/ngc/vmmem.h b/source/ngc/vmmem.h index 41bc271..c639e22 100644 --- a/source/ngc/vmmem.h +++ b/source/ngc/vmmem.h @@ -20,5 +20,7 @@ u16 VMRead16( u32 address ); u8 VMRead8( u32 address ); #endif +extern int GBAROMSize; + #endif diff --git a/source/vba/Patch.cpp b/source/vba/Patch.cpp new file mode 100644 index 0000000..a8dd215 --- /dev/null +++ b/source/vba/Patch.cpp @@ -0,0 +1,467 @@ +// 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 +#include +#include +#include + +#include "System.h" +#include "memfile.h" + +/* +#ifdef __GNUC__ +typedef off64_t __off64_t; +#endif +*/ +#ifndef _MSC_VER +#define _stricmp strcasecmp +#endif // ! _MSC_VER + +#ifdef _MSC_VER +#define fseeko64 _fseeki64 +#define ftello64 _ftelli64 +typedef __int64 __off64_t; +#endif + +#if defined(__APPLE__) || defined (MACOSX) +#define fseeko64 fseeko +#define ftello64 ftello +typedef off_t __off64_t; +#endif /* __APPLE__ || MACOSX */ + +static int readInt2(MFILE *f) { + int res = 0; + int c = memfgetc(f); + if (c == MEOF) + return -1; + res = c; + c = memfgetc(f); + if (c == MEOF) + return -1; + return c + (res << 8); +} + +static int readInt3(MFILE *f) { + int res = 0; + int c = memfgetc(f); + if (c == MEOF) + return -1; + res = c; + c = memfgetc(f); + if (c == MEOF) + return -1; + res = c + (res << 8); + c = memfgetc(f); + if (c == MEOF) + return -1; + return c + (res << 8); +} + +static s64 readInt4(MFILE *f) { + s64 tmp, res = 0; + int c; + + for (int i = 0; i < 4; i++) { + c = memfgetc(f); + if (c == MEOF) + return -1; + tmp = c; + res = res + (tmp << (i * 8)); + } + + return res; +} + +static s64 readInt8(MFILE *f) { + s64 tmp, res = 0; + int c; + + for (int i = 0; i < 8; i++) { + c = memfgetc(f); + if (c == MEOF) + return -1; + tmp = c; + res = res + (tmp << (i * 8)); + } + + return res; +} + +static s64 readVarPtr(MFILE *f) { + s64 offset = 0, shift = 1; + for (;;) { + int c = memfgetc(f); + if (c == MEOF) + 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(MFILE *f, unsigned int size) { + Bytef buf[4096]; + long readed; + + uLong crc = crc32(0L, Z_NULL, 0); + do { + readed = memfread(buf, 1, MIN(size, sizeof(buf)), f); + crc = crc32(crc, buf, readed); + size -= readed; + } while (readed > 0); + + return crc; +} + +bool patchApplyIPS(MFILE * f, u8 **r, int *s) { + // from the IPS spec at http://zerosoft.zophar.net/ips.htm + + bool result = false; + + u8 *rom = *r; + int size = *s; + if (memfgetc(f) == 'P' && memfgetc(f) == 'A' && memfgetc(f) == 'T' + && memfgetc(f) == 'C' && memfgetc(f) == 'H') { + int b; + int offset; + int len; + + result = true; + + for (;;) { + // read offset + offset = readInt3(f); + // if offset == MEOF, 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 = memfgetc(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 (memfread(&rom[offset], 1, len, f) != (size_t) len) + break; + } else { + // fill the region with the given byte + while (len--) { + rom[offset++] = b; + } + } + } + } + return result; +} + +bool patchApplyUPS(MFILE * f, u8 **rom, int *size) { + + s64 srcCRC, dstCRC, patchCRC; + + memfseek(f, 0, MSEEK_END); + long int patchSize = memftell(f); + if (patchSize < 20) { + return false; + } + + memfseek(f, 0, MSEEK_SET); + + if (memfgetc(f) != 'U' || memfgetc(f) != 'P' || memfgetc(f) != 'S' + || memfgetc(f) != '1') { + return false; + } + + memfseek(f, -12, MSEEK_END); + srcCRC = readInt4(f); + dstCRC = readInt4(f); + patchCRC = readInt4(f); + if (srcCRC == -1 || dstCRC == -1 || patchCRC == -1) { + return false; + } + + memfseek(f, 0, MSEEK_SET); + u32 crc = computePatchCRC(f, patchSize - 4); + + if (crc != patchCRC) { + return false; + } + + crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, *rom, *size); + + memfseek(f, 4, MSEEK_SET); + s64 dataSize; + s64 srcSize = readVarPtr(f); + s64 dstSize = readVarPtr(f); + + if (crc == srcCRC) { + dataSize = srcSize; + } else if (crc == dstCRC) { + dataSize = dstSize; + } else { + return false; + } + if (dataSize != *size) { + return false; + } + + s64 relative = 0; + u8 *mem; + while (memftell(f) < patchSize - 12) { + relative += readVarPtr(f); + if (relative > dataSize) + continue; + mem = *rom + relative; + for (s64 i = relative; i < dataSize; i++) { + int x = memfgetc(f); + relative++; + if (!x) + break; + if (i < dataSize) { + *mem++ ^= x; + } + } + } + return true; +} + +static int ppfVersion(MFILE *f) { + memfseek(f, 0, MSEEK_SET); + if (memfgetc(f) != 'P' || memfgetc(f) != 'P' || memfgetc(f) != 'F') + return 0; + switch (memfgetc(f)) { + case '1': + return 1; + case '2': + return 2; + case '3': + return 3; + default: + return 0; + } +} + +int ppfFileIdLen(MFILE *f, int version) { + if (version == 2) { + memfseek(f, -8, MSEEK_END); + } else { + memfseek(f, -6, MSEEK_END); + } + + if (memfgetc(f) != '.' || memfgetc(f) != 'D' || memfgetc(f) != 'I' + || memfgetc(f) != 'Z') + return 0; + + return (version == 2) ? readInt4(f) : readInt2(f); +} + +static bool patchApplyPPF1(MFILE *f, u8 **rom, int *size) { + memfseek(f, 0, MSEEK_END); + int count = memftell(f); + if (count < 56) + return false; + count -= 56; + + memfseek(f, 56, MSEEK_SET); + + u8 *mem = *rom; + + while (count > 0) { + int offset = readInt4(f); + if (offset == -1) + break; + int len = memfgetc(f); + if (len == MEOF) + break; + if (offset + len > *size) + break; + if (memfread(&mem[offset], 1, len, f) != (size_t) len) + break; + count -= 4 + 1 + len; + } + + return (count == 0); +} + +static bool patchApplyPPF2(MFILE *f, u8 **rom, int *size) { + memfseek(f, 0, MSEEK_END); + int count = memftell(f); + if (count < 56 + 4 + 1024) + return false; + count -= 56 + 4 + 1024; + + memfseek(f, 56, MSEEK_SET); + + int datalen = readInt4(f); + if (datalen != *size) + return false; + + u8 *mem = *rom; + + u8 block[1024]; + memfread(&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; + + memfseek(f, 56 + 4 + 1024, MSEEK_SET); + + while (count > 0) { + int offset = readInt4(f); + if (offset == -1) + break; + int len = memfgetc(f); + if (len == MEOF) + break; + if (offset + len > *size) + break; + if (memfread(&mem[offset], 1, len, f) != (size_t) len) + break; + count -= 4 + 1 + len; + } + + return (count == 0); +} + +static bool patchApplyPPF3(MFILE *f, u8 **rom, int *size) { + memfseek(f, 0, MSEEK_END); + int count = memftell(f); + if (count < 56 + 4 + 1024) + return false; + count -= 56 + 4; + + memfseek(f, 56, MSEEK_SET); + + int imagetype = memfgetc(f); + int blockcheck = memfgetc(f); + int undo = memfgetc(f); + memfgetc(f); + + u8 *mem = *rom; + + if (blockcheck) { + u8 block[1024]; + memfread(&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; + + memfseek(f, 56 + 4 + (blockcheck ? 1024 : 0), MSEEK_SET); + + while (count > 0) { + s64 offset = readInt8(f); + if (offset == -1) + break; + int len = memfgetc(f); + if (len == MEOF) + break; + if (offset + len > *size) + break; + if (memfread(&mem[offset], 1, len, f) != (size_t) len) + break; + if (undo) + memfseek(f, len, MSEEK_CUR); + count -= 8 + 1 + len; + if (undo) + count -= len; + } + + return (count == 0); +} + +bool patchApplyPPF(MFILE *f, u8 **rom, int *size) +{ + 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; + } + + return res; +} + +bool applyPatch(const char *patchname, u8 **rom, int *size) +{ + bool result = false; + + if (strlen(patchname) < 5) + return false; + const char * p = strrchr(patchname, '.'); + if (p == NULL) + return false; + + FILE *f = fopen(patchname, "rb"); + if (!f) + return false; + + // read in file + fseek(f, 0, SEEK_END); // go to end of file + int filesize = ftell(f); // get filesize + fseek(f, 0, SEEK_SET); // go to start of file + char * pbuffer = (char *) malloc(filesize); + fread (pbuffer, 1, filesize, f); + fclose(f); + + MFILE * mf = memfopen(pbuffer, filesize); // create memory file + + if (_stricmp(p, ".ips") == 0) + result = patchApplyIPS(mf, rom, size); + else if (_stricmp(p, ".ups") == 0) + result = patchApplyUPS(mf, rom, size); + else if (_stricmp(p, ".ppf") == 0) + result = patchApplyPPF(mf, rom, size); + + memfclose(mf); // close memory file + free(pbuffer); // free buffer + + return result; +} diff --git a/source/vba/Patch.h b/source/vba/Patch.h new file mode 100644 index 0000000..fa7a0fe --- /dev/null +++ b/source/vba/Patch.h @@ -0,0 +1,31 @@ +// -*- 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 "memfile.h" +#include "System.h" + +bool applyPatch(const char *patchname, u8 **rom, int *size); +bool patchApplyIPS(MFILE * f, u8 **r, int *s); +bool patchApplyUPS(MFILE * f, u8 **rom, int *size); +bool patchApplyPPF(MFILE *f, u8 **rom, int *size); + +#endif diff --git a/source/vba/memfile.cpp b/source/vba/memfile.cpp new file mode 100644 index 0000000..17b0dcf --- /dev/null +++ b/source/vba/memfile.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** + * Memory-based replacement for standard C file functions + * This allows standard file calls to be replaced by memory based ones + * With little modification required to the code + * + * Written By: Daryl Borth, October 2008 + * + * This code 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. + ***************************************************************************/ +#include +#include + +#include "memfile.h" + +/**************************************************************************** + * memfopen + * memory replacement for fopen + * + * Creates a memory-based file + ***************************************************************************/ +MFILE * memfopen(char * buffer, int size) +{ + MFILE *f = (MFILE *)malloc(sizeof(MFILE)); + + f->buffer = buffer; + f->offset = 0; + f->size = size; + + return f; +} + +/**************************************************************************** + * memfclose + * memory replacement for fclose + * + * 'Closes' the memory file specified + ***************************************************************************/ +int memfclose(MFILE * src) +{ + free(src); + return 0; +} + +/**************************************************************************** + * memfseek + * memory replacement for fseek + * + * Sets the position indicator associated with the stream to a new position + * defined by adding offset to a reference position specified by origin. + ***************************************************************************/ +int memfseek ( MFILE * m, long int offset, int origin ) +{ + int success = 0; // 0 indicates success + + switch(origin) + { + case MSEEK_SET: + if(offset >= 0 && offset <= m->size) + m->offset = offset; + else + success = 1; // failure + break; + case MSEEK_CUR: + if((m->offset + offset) >= 0 && (m->offset + offset) <= m->size) + m->offset += offset; + else + success = 1; // failure + break; + case MSEEK_END: + if((m->size + offset) >= 0 && (m->size + offset) <= m->size) + m->offset = m->size + offset; + else + success = 1; // failure + break; + } + return success; +} + +/**************************************************************************** + * memftell + * memory replacement for ftell + * + * Get current position in stream (offset + 1) + ***************************************************************************/ +long int memftell (MFILE * stream) +{ + return stream->offset; // to emulate ftell behavior +} + +/**************************************************************************** + * memfread + * memory replacement for fread + * + * Reads an array of count elements, each one with a size of size bytes, from + * the buffer and stores them in the block of memory specified by ptr. The + * postion indicator of the buffer is advanced by the total amount of bytes + * read. The total amount of bytes read if successful is (size * count). + ***************************************************************************/ +size_t memfread(void * dst, size_t size, size_t count, MFILE * src) +{ + if(src->offset >= src->size) // reached end of buffer + return 0; + + int numbytes = size*count; + + if((src->offset + numbytes) > src->size) // can't read full # requested + numbytes = src->size - src->offset; // do a partial read + + if(numbytes > 0) + memcpy(dst, src->buffer+src->offset, numbytes); + + src->offset += numbytes; + return numbytes; +} + +/**************************************************************************** + * memfgetc + * memory replacement for fgetc + * + * Returns the next character in the buffer specified, and advances the + * postion indicator of the buffer by 1. + ***************************************************************************/ +int memfgetc(MFILE * src) +{ + if(src->offset >= src->size) // reached end of buffer + return MEOF; + else + return src->buffer[src->offset++]; +} diff --git a/source/vba/memfile.h b/source/vba/memfile.h new file mode 100644 index 0000000..2c65628 --- /dev/null +++ b/source/vba/memfile.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * Memory-based replacement for standard C file functions + * This allows standard file calls to be replaced by memory based ones + * With little modification required to the code + * + * Written By: Daryl Borth, October 2008 + * + * This code 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. + ***************************************************************************/ + +typedef struct MFile { + char *buffer; // pointer to buffer memory + long int offset; // current position indicator in buffer + long int size; // total file (buffer) size +} MFILE; + +#define MSEEK_SET 0 +#define MSEEK_CUR 1 +#define MSEEK_END 2 +#define MEOF (-1) + +MFILE * memfopen(char * buffer, int size); +int memfclose(MFILE * src); +int memfseek ( MFILE * m, long int offset, int origin ); +long int memftell (MFILE * stream); +size_t memfread(void * dst, size_t size, size_t count, MFILE * src); +int memfgetc(MFILE * src);