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
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)

View File

@ -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

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
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)

192
file.c
View File

@ -23,92 +23,13 @@
#include <string.h>
#include "dynamic.h"
#include "movie.h"
#include "ups.h"
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#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)
{

View File

@ -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;

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.");
#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.

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_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)

2
ups.h
View File

@ -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);