Fix up UPS :) Should work fine now at least for .sfc base roms.
This commit is contained in:
parent
8ef54de812
commit
e484e22e2f
2
Makefile
2
Makefile
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
192
file.c
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
11
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.");
|
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
45
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_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
2
ups.h
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue