IPS/UPS/PPF support

This commit is contained in:
dborth 2009-07-23 05:39:43 +00:00
parent 07e87efe38
commit 3c09a5fcc1
6 changed files with 641 additions and 2 deletions

View File

@ -49,8 +49,8 @@ bool FCEUD_ShouldDrawInputAids()
// General Logging
void FCEUD_PrintError(const char *errormsg)
{
if(GuiLoaded())
ErrorPrompt(errormsg);
//if(GuiLoaded())
// ErrorPrompt(errormsg);
}
void FCEUD_Message(const char *text)

View File

@ -32,6 +32,7 @@
#include "gcunzip.h"
#include "fceuram.h"
#include "fceustate.h"
#include "patch.h"
BROWSERINFO browser;
BROWSERENTRY * browserList = NULL; // list of files/folders in browser
@ -501,6 +502,9 @@ int BrowserLoadFile(int method)
}
else
{
// load UPS/IPS/PPF patch
filesize = LoadPatch(method, filesize);
if(GCMemROM(method, filesize) > 0)
{
romLoaded = true;

133
source/ngc/memfile.cpp Normal file
View File

@ -0,0 +1,133 @@
/****************************************************************************
* FCE Ultra
* Nintendo Wii/Gamecube Port
*
* Tantric October 2008
*
* memfile.cpp
*
* 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
***************************************************************************/
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include "memfile.h"
/****************************************************************************
* memfopen
* memory replacement for fopen
*
* Creates a memory-based file
***************************************************************************/
MFILE * memfopen(char * buffer, int size)
{
MFILE *f = (MFILE *)memalign(32, 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++];
}

35
source/ngc/memfile.h Normal file
View File

@ -0,0 +1,35 @@
/****************************************************************************
* FCE Ultra
* Nintendo Wii/Gamecube Port
*
* Tantric October 2008
*
* memfile.h
*
* 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
***************************************************************************/
#ifndef _MEMFILE_H_
#define _MEMFILE_H_
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);
#endif

448
source/ngc/patch.cpp Normal file
View File

@ -0,0 +1,448 @@
/****************************************************************************
* FCE Ultra
* Nintendo Wii/Gamecube Port
*
* Tantric October 2008
*
* patch.cpp
*
* IPS/UPS/PPF patch support
***************************************************************************/
#include <zlib.h>
#include "fceugx.h"
#include "fceusupport.h"
#include "menu.h"
#include "memfile.h"
#include "fileop.h"
#include "filebrowser.h"
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 NGC
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) {
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;
}
int LoadPatch(int method, int size)
{
int newsize = size;
int patchsize = 0;
int patchtype;
char patchpath[3][512];
AllocSaveBuffer ();
memset(patchpath, 0, sizeof(patchpath));
sprintf(patchpath[0], "%s/%s.ips", browser.dir, romFilename);
sprintf(patchpath[1], "%s/%s.ups", browser.dir, romFilename);
sprintf(patchpath[2], "%s/%s.ppf", browser.dir, romFilename);
for(patchtype=0; patchtype<3; patchtype++)
{
patchsize = LoadFile(patchpath[patchtype], method, SILENT);
if(patchsize)
break;
}
if(patchsize > 0)
{
// create memory file
MFILE * mf = memfopen((char *)savebuffer, patchsize);
if(patchtype == 0)
patchApplyIPS(mf, &nesrom, &newsize);
else if(patchtype == 1)
patchApplyUPS(mf, &nesrom, &newsize);
else
patchApplyPPF(mf, &nesrom, &newsize);
memfclose(mf); // close memory file
}
FreeSaveBuffer ();
return newsize;
}

19
source/ngc/patch.h Normal file
View File

@ -0,0 +1,19 @@
/****************************************************************************
* FCE Ultra
* Nintendo Wii/Gamecube Port
*
* Tantric October 2008
*
* patch.h
*
* IPS/UPS/PPF patch support
***************************************************************************/
#ifndef PATCH_H
#define PATCH_H
#include "memfile.h"
int LoadPatch(int method, int size);
#endif