#include #include #include #include #include "Patch.h" #ifndef _MSC_VER #define _stricmp strcasecmp #endif // ! _MSC_VER 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) { #ifdef GEKKO size = offset + len; #else size *= 2; rom = (u8 *)realloc(rom, size); #endif *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) { if (srcSize != *size) { return false; } dataSize = dstSize; } else if (crc == dstCRC) { if (dstSize != *size) { return false; } dataSize = srcSize; } else { return false; } if (dataSize > *size) { *rom = (u8*)realloc(*rom, dataSize); memset(*rom + *size, 0, dataSize - *size); *size = dataSize; } 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') //-V501 return 0; switch(memfgetc(f)){ case '1': return 1; case '2': return 2; case '3': return 3; default: return 0; } } static 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; }