From e484e22e2feb966f67cb27695a31e1f99da38720 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 23 Mar 2011 23:31:33 +0100 Subject: [PATCH] Fix up UPS :) Should work fine now at least for .sfc base roms. --- Makefile | 2 +- Makefile.win32 | 2 +- docs/ssnes.1 | 5 ++ file.c | 192 ++++++++++++++++++++++++++++--------------------- general.h | 1 + ssnes.c | 11 ++- ups.c | 45 ++++++------ ups.h | 2 +- 8 files changed, 156 insertions(+), 104 deletions(-) diff --git a/Makefile b/Makefile index 1a063bb234..e2b76747a5 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ include config.mk TARGET = ssnes tools/ssnes-joyconfig -OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o +OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o HEADERS = $(wildcard */*.h) $(wildcard *.h) diff --git a/Makefile.win32 b/Makefile.win32 index ff7b4e4a4e..9bf4394f3d 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -1,6 +1,6 @@ TARGET = ssnes.exe JTARGET = ssnes-joyconfig.exe -OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o +OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o CC = gcc diff --git a/docs/ssnes.1 b/docs/ssnes.1 index 205b217109..e68a884a8f 100644 --- a/docs/ssnes.1 +++ b/docs/ssnes.1 @@ -126,5 +126,10 @@ Set FRAMES to 0 to have perfect sync. 0 frames is only suitable for LAN. Default \fB--port PORT\fR Network port used for netplay. This defaults to 55435. This option affects both TCP and UDP. +.TP +\fB--ups PATCH, -U PATCH\fR +Attempts to apply an UPS patch to the current ROM image. No files are altered. +If this flag is not specified, SSNES will look for a .ups file with same basename as ROM specified. + .SH "SEE ALSO" \fBssnes-joyconfig\fR(1) diff --git a/file.c b/file.c index 815046ef01..a50905db34 100644 --- a/file.c +++ b/file.c @@ -23,92 +23,13 @@ #include #include "dynamic.h" #include "movie.h" +#include "ups.h" #ifdef _WIN32 #include #include #endif -// Load SNES rom only. Applies a hack for headered ROMs. -static ssize_t read_rom_file(FILE* file, void** buf) -{ - ssize_t ret; - if (file == NULL) // stdin - { -#ifdef _WIN32 - setmode(0, O_BINARY); -#endif - - SSNES_LOG("Reading ROM from stdin ...\n"); - size_t buf_size = 0xFFFFF; // Some initial guesstimate. - size_t buf_ptr = 0; - char *rom_buf = malloc(buf_size); - if (rom_buf == NULL) - { - SSNES_ERR("Couldn't allocate memory!\n"); - return -1; - } - - for(;;) - { - size_t ret = fread(rom_buf + buf_ptr, 1, buf_size - buf_ptr, stdin); - buf_ptr += ret; - - // We've reached the end - if (buf_ptr < buf_size) - break; - - rom_buf = realloc(rom_buf, buf_size * 2); - if (rom_buf == NULL) - { - SSNES_ERR("Couldn't allocate memory!\n"); - return -1; - } - - buf_size *= 2; - } - - if ((buf_ptr & 0x7fff) == 512) - { - memmove(rom_buf, rom_buf + 512, buf_ptr - 512); - buf_ptr -= 512; - } - - *buf = rom_buf; - ret = buf_ptr; - } - else - { - fseek(file, 0, SEEK_END); - long length = ftell(file); - rewind(file); - if ((length & 0x7fff) == 512) - { - length -= 512; - fseek(file, 512, SEEK_SET); - } - - void *rom_buf = malloc(length); - if ( rom_buf == NULL ) - { - SSNES_ERR("Couldn't allocate memory!\n"); - return -1; - } - - if ( fread(rom_buf, 1, length, file) < length ) - { - SSNES_ERR("Didn't read whole file.\n"); - free(rom_buf); - return -1; - } - *buf = rom_buf; - ret = length; - } - - g_extern.cart_crc = crc32_calculate(*buf, ret); - return ret; -} - // Generic file loader. static ssize_t read_file(const char *path, void **buf) { @@ -143,6 +64,117 @@ error: return -1; } +// Load SNES rom only. Applies a hack for headered ROMs. +static ssize_t read_rom_file(FILE* file, void** buf) +{ + ssize_t ret = 0; + uint8_t *ret_buf = NULL; + + if (file == NULL) // stdin + { +#ifdef _WIN32 + setmode(0, O_BINARY); +#endif + + SSNES_LOG("Reading ROM from stdin ...\n"); + size_t buf_size = 0xFFFFF; // Some initial guesstimate. + size_t buf_ptr = 0; + uint8_t *rom_buf = malloc(buf_size); + if (rom_buf == NULL) + { + SSNES_ERR("Couldn't allocate memory!\n"); + return -1; + } + + for(;;) + { + size_t ret = fread(rom_buf + buf_ptr, 1, buf_size - buf_ptr, stdin); + buf_ptr += ret; + + // We've reached the end + if (buf_ptr < buf_size) + break; + + rom_buf = realloc(rom_buf, buf_size * 2); + if (rom_buf == NULL) + { + SSNES_ERR("Couldn't allocate memory!\n"); + return -1; + } + + buf_size *= 2; + } + + ret_buf = rom_buf; + ret = buf_ptr; + } + else + { + fseek(file, 0, SEEK_END); + ret = ftell(file); + rewind(file); + + void *rom_buf = malloc(ret); + if (rom_buf == NULL) + { + SSNES_ERR("Couldn't allocate memory!\n"); + return -1; + } + + if (fread(rom_buf, 1, ret, file) < ret) + { + SSNES_ERR("Didn't read whole file.\n"); + free(rom_buf); + return -1; + } + + ret_buf = rom_buf; + } + + // Patch with UPS. + ssize_t ups_patch_size; + void *ups_patch = NULL; + if (*g_extern.ups_name && (ups_patch_size = read_file(g_extern.ups_name, &ups_patch)) >= 0) + { + SSNES_LOG("Found UPS file in \"%s\", attempting to patch ...\n", g_extern.ups_name); + + size_t target_size = ret * 4; // Just to be sure ... + uint8_t *patched_rom = malloc(target_size); + if (patched_rom) + { + ups_error_t err = ups_apply_patch(ups_patch, ups_patch_size, ret_buf, ret, patched_rom, &target_size); + if (err == UPS_SUCCESS) + { + free(ret_buf); + ret_buf = patched_rom; + ret = target_size; + SSNES_LOG("ROM patched successfully (UPS)!\n"); + } + else + { + free(patched_rom); + SSNES_LOG("ROM failed to patch (UPS).\n"); + } + + free(ups_patch); + } + } + else if (*g_extern.ups_name) + SSNES_LOG("Could not find UPS patch.\n"); + + // Remove SMC header if present. + if ((ret & 0x7fff) == 512) + { + memmove(ret_buf, ret_buf + 512, ret - 512); + ret -= 512; + } + + g_extern.cart_crc = crc32_calculate(ret_buf, ret); + *buf = ret_buf; + return ret; +} + + // Dump stuff to file. static bool dump_to_file(const char *path, const void *data, size_t size) { diff --git a/general.h b/general.h index b3cc5d8e9b..b7e452f9fc 100644 --- a/general.h +++ b/general.h @@ -162,6 +162,7 @@ struct global char savefile_name_asrm[512]; char savefile_name_bsrm[512]; char savestate_name[256]; + char ups_name[512]; unsigned state_slot; diff --git a/ssnes.c b/ssnes.c index 761379c942..6a5b9344b3 100644 --- a/ssnes.c +++ b/ssnes.c @@ -307,6 +307,7 @@ static void print_help(void) puts("\t-r/--record: Path to record video file. Settings for video/audio codecs are found in config file."); #endif puts("\t-v/--verbose: Verbose logging"); + puts("\t-U/--ups: Specifies path for UPS patch that will be applied to ROM.\n"); print_features(); } @@ -357,6 +358,7 @@ static void parse_input(int argc, char *argv[]) { "connect", 1, NULL, 'C' }, { "frames", 1, NULL, 'F' }, { "port", 1, &val, 'p' }, + { "ups", 1, NULL, 'U' }, { NULL, 0, NULL, 0 } }; @@ -374,7 +376,7 @@ static void parse_input(int argc, char *argv[]) #define CONFIG_FILE_ARG #endif - char optstring[] = "hs:vS:m:p4jJg:b:B:Y:Z:P:HC:F:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG; + char optstring[] = "hs:vS:m:p4jJg:b:B:Y:Z:P:HC:F:U:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG; for(;;) { val = 0; @@ -489,6 +491,10 @@ static void parse_input(int argc, char *argv[]) g_extern.netplay_sync_frames = 32; break; + case 'U': + strncpy(g_extern.ups_name, optarg, sizeof(g_extern.ups_name) - 1); + break; + case 0: switch (val) { @@ -872,6 +878,9 @@ static void fill_pathnames(void) if (!g_extern.bsv_movie_playback) fill_pathname(g_extern.bsv_movie_path, g_extern.savefile_name_srm, ""); + + if (!(*g_extern.ups_name) && *g_extern.basename) + fill_pathname(g_extern.ups_name, g_extern.basename, ".ups"); } // Save or load state here. diff --git a/ups.c b/ups.c index 734ace36a6..78a24b3510 100644 --- a/ups.c +++ b/ups.c @@ -56,16 +56,18 @@ static void target_write(struct ups_data *data, uint8_t n) data->target_data[data->target_offset] = n; data->target_checksum = crc32_adjust(data->target_checksum, n); } + + data->target_offset++; } static uint64_t decode(struct ups_data *data) { uint64_t offset = 0, shift = 1; - while(true) + while (true) { uint8_t x = patch_read(data); offset += (x & 0x7f) * shift; - if(x & 0x80) + if (x & 0x80) break; shift <<= 7; offset += shift; @@ -73,24 +75,24 @@ static uint64_t decode(struct ups_data *data) return offset; } -ups_error_t ups_patch( - const uint8_t *patch_data, size_t patch_length, - const uint8_t *source_data, size_t source_length, - uint8_t *target_data, size_t *target_length) +ups_error_t ups_apply_patch( + const uint8_t *patchdata, size_t patchlength, + const uint8_t *sourcedata, size_t sourcelength, + uint8_t *targetdata, size_t *targetlength) { struct ups_data data = { - .patch_data = patch_data, - .source_data = source_data, - .target_data = target_data, - .patch_length = patch_length, - .source_length = source_length, - .target_length = *target_length, + .patch_data = patchdata, + .source_data = sourcedata, + .target_data = targetdata, + .patch_length = patchlength, + .source_length = sourcelength, + .target_length = *targetlength, .patch_checksum = ~0, .source_checksum = ~0, .target_checksum = ~0 }; - if (patch_length < 18) + if (data.patch_length < 18) return UPS_PATCH_INVALID; if (patch_read(&data) != 'U') return UPS_PATCH_INVALID; @@ -104,12 +106,12 @@ ups_error_t ups_patch( unsigned source_read_length = decode(&data); unsigned target_read_length = decode(&data); - if (source_length != source_read_length && source_length != target_read_length) + if (data.source_length != source_read_length && data.source_length != target_read_length) return UPS_SOURCE_INVALID; - *target_length = (data.source_length == source_read_length ? target_read_length : source_read_length); - if (data.target_length < *target_length) + *targetlength = (data.source_length == source_read_length ? target_read_length : source_read_length); + if (data.target_length < *targetlength) return UPS_TARGET_TOO_SMALL; - data.target_length = *target_length; + data.target_length = *targetlength; while (data.patch_offset < data.patch_length - 12) { @@ -123,20 +125,23 @@ ups_error_t ups_patch( if (patch_xor == 0) break; } } + while (data.source_offset < data.source_length) target_write(&data, source_read(&data)); while (data.target_offset < data.target_length) target_write(&data, source_read(&data)); uint32_t patch_read_checksum = 0, source_read_checksum = 0, target_read_checksum = 0; - for(unsigned i = 0; i < 4; i++) + for (unsigned i = 0; i < 4; i++) source_read_checksum |= patch_read(&data) << (i * 8); - for(unsigned i = 0; i < 4; i++) + for (unsigned i = 0; i < 4; i++) target_read_checksum |= patch_read(&data) << (i * 8); + uint32_t patch_result_checksum = ~data.patch_checksum; data.source_checksum = ~data.source_checksum; data.target_checksum = ~data.target_checksum; - for(unsigned i = 0; i < 4; i++) + + for (unsigned i = 0; i < 4; i++) patch_read_checksum |= patch_read(&data) << (i * 8); if (patch_result_checksum != patch_read_checksum) diff --git a/ups.h b/ups.h index 56441569e3..7b217e5a65 100644 --- a/ups.h +++ b/ups.h @@ -36,7 +36,7 @@ typedef enum ups_error UPS_TARGET_CHECKSUM_INVALID } ups_error_t; -ups_error_t ups_patch( +ups_error_t ups_apply_patch( const uint8_t *patch_data, size_t patch_length, const uint8_t *source_data, size_t source_length, uint8_t *target_data, size_t *target_length);