mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-23 16:09:47 +00:00
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
|
||||
|
||||
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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
192
file.c
@ -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)
|
||||
{
|
||||
|
@ -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
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.");
|
||||
#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
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_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)
|
||||
|
Loading…
Reference in New Issue
Block a user