Fix up UPS :) Should work fine now at least for .sfc base roms.

This commit is contained in:
Themaister 2011-03-23 23:31:33 +01:00
parent 8ef54de812
commit e484e22e2f
8 changed files with 156 additions and 104 deletions

View File

@ -2,7 +2,7 @@ include config.mk
TARGET = ssnes tools/ssnes-joyconfig 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 JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o
HEADERS = $(wildcard */*.h) $(wildcard *.h) HEADERS = $(wildcard */*.h) $(wildcard *.h)

View File

@ -1,6 +1,6 @@
TARGET = ssnes.exe TARGET = ssnes.exe
JTARGET = ssnes-joyconfig.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 JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o
CC = gcc CC = gcc

View File

@ -126,5 +126,10 @@ Set FRAMES to 0 to have perfect sync. 0 frames is only suitable for LAN. Default
\fB--port PORT\fR \fB--port PORT\fR
Network port used for netplay. This defaults to 55435. This option affects both TCP and UDP. 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" .SH "SEE ALSO"
\fBssnes-joyconfig\fR(1) \fBssnes-joyconfig\fR(1)

192
file.c
View File

@ -23,92 +23,13 @@
#include <string.h> #include <string.h>
#include "dynamic.h" #include "dynamic.h"
#include "movie.h" #include "movie.h"
#include "ups.h"
#ifdef _WIN32 #ifdef _WIN32
#include <io.h> #include <io.h>
#include <fcntl.h> #include <fcntl.h>
#endif #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. // Generic file loader.
static ssize_t read_file(const char *path, void **buf) static ssize_t read_file(const char *path, void **buf)
{ {
@ -143,6 +64,117 @@ error:
return -1; 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. // Dump stuff to file.
static bool dump_to_file(const char *path, const void *data, size_t size) static bool dump_to_file(const char *path, const void *data, size_t size)
{ {

View File

@ -162,6 +162,7 @@ struct global
char savefile_name_asrm[512]; char savefile_name_asrm[512];
char savefile_name_bsrm[512]; char savefile_name_bsrm[512];
char savestate_name[256]; char savestate_name[256];
char ups_name[512];
unsigned state_slot; unsigned state_slot;

11
ssnes.c
View File

@ -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."); puts("\t-r/--record: Path to record video file. Settings for video/audio codecs are found in config file.");
#endif #endif
puts("\t-v/--verbose: Verbose logging"); puts("\t-v/--verbose: Verbose logging");
puts("\t-U/--ups: Specifies path for UPS patch that will be applied to ROM.\n");
print_features(); print_features();
} }
@ -357,6 +358,7 @@ static void parse_input(int argc, char *argv[])
{ "connect", 1, NULL, 'C' }, { "connect", 1, NULL, 'C' },
{ "frames", 1, NULL, 'F' }, { "frames", 1, NULL, 'F' },
{ "port", 1, &val, 'p' }, { "port", 1, &val, 'p' },
{ "ups", 1, NULL, 'U' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
@ -374,7 +376,7 @@ static void parse_input(int argc, char *argv[])
#define CONFIG_FILE_ARG #define CONFIG_FILE_ARG
#endif #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(;;) for(;;)
{ {
val = 0; val = 0;
@ -489,6 +491,10 @@ static void parse_input(int argc, char *argv[])
g_extern.netplay_sync_frames = 32; g_extern.netplay_sync_frames = 32;
break; break;
case 'U':
strncpy(g_extern.ups_name, optarg, sizeof(g_extern.ups_name) - 1);
break;
case 0: case 0:
switch (val) switch (val)
{ {
@ -872,6 +878,9 @@ static void fill_pathnames(void)
if (!g_extern.bsv_movie_playback) if (!g_extern.bsv_movie_playback)
fill_pathname(g_extern.bsv_movie_path, g_extern.savefile_name_srm, ""); 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. // Save or load state here.

45
ups.c
View File

@ -56,16 +56,18 @@ static void target_write(struct ups_data *data, uint8_t n)
data->target_data[data->target_offset] = n; data->target_data[data->target_offset] = n;
data->target_checksum = crc32_adjust(data->target_checksum, n); data->target_checksum = crc32_adjust(data->target_checksum, n);
} }
data->target_offset++;
} }
static uint64_t decode(struct ups_data *data) static uint64_t decode(struct ups_data *data)
{ {
uint64_t offset = 0, shift = 1; uint64_t offset = 0, shift = 1;
while(true) while (true)
{ {
uint8_t x = patch_read(data); uint8_t x = patch_read(data);
offset += (x & 0x7f) * shift; offset += (x & 0x7f) * shift;
if(x & 0x80) if (x & 0x80)
break; break;
shift <<= 7; shift <<= 7;
offset += shift; offset += shift;
@ -73,24 +75,24 @@ static uint64_t decode(struct ups_data *data)
return offset; return offset;
} }
ups_error_t ups_patch( ups_error_t ups_apply_patch(
const uint8_t *patch_data, size_t patch_length, const uint8_t *patchdata, size_t patchlength,
const uint8_t *source_data, size_t source_length, const uint8_t *sourcedata, size_t sourcelength,
uint8_t *target_data, size_t *target_length) uint8_t *targetdata, size_t *targetlength)
{ {
struct ups_data data = { struct ups_data data = {
.patch_data = patch_data, .patch_data = patchdata,
.source_data = source_data, .source_data = sourcedata,
.target_data = target_data, .target_data = targetdata,
.patch_length = patch_length, .patch_length = patchlength,
.source_length = source_length, .source_length = sourcelength,
.target_length = *target_length, .target_length = *targetlength,
.patch_checksum = ~0, .patch_checksum = ~0,
.source_checksum = ~0, .source_checksum = ~0,
.target_checksum = ~0 .target_checksum = ~0
}; };
if (patch_length < 18) if (data.patch_length < 18)
return UPS_PATCH_INVALID; return UPS_PATCH_INVALID;
if (patch_read(&data) != 'U') if (patch_read(&data) != 'U')
return UPS_PATCH_INVALID; return UPS_PATCH_INVALID;
@ -104,12 +106,12 @@ ups_error_t ups_patch(
unsigned source_read_length = decode(&data); unsigned source_read_length = decode(&data);
unsigned target_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; return UPS_SOURCE_INVALID;
*target_length = (data.source_length == source_read_length ? target_read_length : source_read_length); *targetlength = (data.source_length == source_read_length ? target_read_length : source_read_length);
if (data.target_length < *target_length) if (data.target_length < *targetlength)
return UPS_TARGET_TOO_SMALL; return UPS_TARGET_TOO_SMALL;
data.target_length = *target_length; data.target_length = *targetlength;
while (data.patch_offset < data.patch_length - 12) while (data.patch_offset < data.patch_length - 12)
{ {
@ -123,20 +125,23 @@ ups_error_t ups_patch(
if (patch_xor == 0) break; if (patch_xor == 0) break;
} }
} }
while (data.source_offset < data.source_length) while (data.source_offset < data.source_length)
target_write(&data, source_read(&data)); target_write(&data, source_read(&data));
while (data.target_offset < data.target_length) while (data.target_offset < data.target_length)
target_write(&data, source_read(&data)); target_write(&data, source_read(&data));
uint32_t patch_read_checksum = 0, source_read_checksum = 0, target_read_checksum = 0; 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); 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); target_read_checksum |= patch_read(&data) << (i * 8);
uint32_t patch_result_checksum = ~data.patch_checksum; uint32_t patch_result_checksum = ~data.patch_checksum;
data.source_checksum = ~data.source_checksum; data.source_checksum = ~data.source_checksum;
data.target_checksum = ~data.target_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); patch_read_checksum |= patch_read(&data) << (i * 8);
if (patch_result_checksum != patch_read_checksum) if (patch_result_checksum != patch_read_checksum)

2
ups.h
View File

@ -36,7 +36,7 @@ typedef enum ups_error
UPS_TARGET_CHECKSUM_INVALID UPS_TARGET_CHECKSUM_INVALID
} ups_error_t; } 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 *patch_data, size_t patch_length,
const uint8_t *source_data, size_t source_length, const uint8_t *source_data, size_t source_length,
uint8_t *target_data, size_t *target_length); uint8_t *target_data, size_t *target_length);