mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-23 16:09:47 +00:00
Add IPS patching.
This commit is contained in:
parent
acdc6179bb
commit
48cddde84a
@ -192,6 +192,12 @@ If this flag is not specified, SSNES will look for a .ups file with same basenam
|
||||
Attempts to apply a BPS patch to the current ROM image. No files are altered.
|
||||
If this flag is not specified, SSNES will look for a .bps file with same basename as ROM specified.
|
||||
|
||||
.TP
|
||||
\fB--ips PATCH\fR
|
||||
Attempts to apply a IPS patch to the current ROM image. No files are altered.
|
||||
If this flag is not specified, SSNES will look for a .ips file with same basename as ROM specified.
|
||||
Note that SSNES cannot perform any error checking if patching was successful due to how IPS works.
|
||||
|
||||
.TP
|
||||
\fB--xml MAP, -X MAP\fR
|
||||
Specifies path to XML memory map for the given ROM.
|
||||
|
24
file.c
24
file.c
@ -150,24 +150,34 @@ static void patch_rom(uint8_t **buf, ssize_t *size)
|
||||
void *patch_data = NULL;
|
||||
bool success = false;
|
||||
|
||||
if (g_extern.ups_pref && g_extern.bps_pref)
|
||||
if (g_extern.ups_pref + g_extern.bps_pref + g_extern.ips_pref > 1)
|
||||
{
|
||||
SSNES_WARN("Both UPS and BPS patch explicitly defined, ignoring both ...\n");
|
||||
SSNES_WARN("Several patches are explicitly defined, ignoring all ...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_extern.bps_pref && *g_extern.ups_name && (patch_size = read_file(g_extern.ups_name, &patch_data)) >= 0)
|
||||
bool allow_bps = !g_extern.ups_pref && !g_extern.ips_pref;
|
||||
bool allow_ups = !g_extern.bps_pref && !g_extern.ips_pref;
|
||||
bool allow_ips = !g_extern.ups_pref && !g_extern.bps_pref;
|
||||
|
||||
if (allow_ups && *g_extern.ups_name && (patch_size = read_file(g_extern.ups_name, &patch_data)) >= 0)
|
||||
{
|
||||
patch_desc = "UPS";
|
||||
patch_path = g_extern.ups_name;
|
||||
func = ups_apply_patch;
|
||||
}
|
||||
else if (!g_extern.ups_pref && *g_extern.bps_name && (patch_size = read_file(g_extern.bps_name, &patch_data)) >= 0)
|
||||
else if (allow_bps && *g_extern.bps_name && (patch_size = read_file(g_extern.bps_name, &patch_data)) >= 0)
|
||||
{
|
||||
patch_desc = "BPS";
|
||||
patch_path = g_extern.bps_name;
|
||||
func = bps_apply_patch;
|
||||
}
|
||||
else if (allow_ips && *g_extern.ips_name && (patch_size = read_file(g_extern.ips_name, &patch_data)) >= 0)
|
||||
{
|
||||
patch_desc = "IPS";
|
||||
patch_path = g_extern.ips_name;
|
||||
func = ips_apply_patch;
|
||||
}
|
||||
else
|
||||
{
|
||||
SSNES_LOG("Did not find a valid ROM patch.\n");
|
||||
@ -279,6 +289,9 @@ static ssize_t read_rom_file(FILE* file, void** buf)
|
||||
ret_buf = (uint8_t*)rom_buf;
|
||||
}
|
||||
|
||||
// Attempt to apply a patch.
|
||||
patch_rom(&ret_buf, &ret);
|
||||
|
||||
// Remove copier header if present (512 first bytes).
|
||||
if ((ret & 0x7fff) == 512)
|
||||
{
|
||||
@ -286,9 +299,6 @@ static ssize_t read_rom_file(FILE* file, void** buf)
|
||||
ret -= 512;
|
||||
}
|
||||
|
||||
// Attempt to apply a patch :)
|
||||
patch_rom(&ret_buf, &ret);
|
||||
|
||||
g_extern.cart_crc = crc32_calculate(ret_buf, ret);
|
||||
#ifdef HAVE_XML
|
||||
sha256_hash(g_extern.sha256, ret_buf, ret);
|
||||
|
@ -288,8 +288,10 @@ struct global
|
||||
|
||||
bool ups_pref;
|
||||
bool bps_pref;
|
||||
bool ips_pref;
|
||||
char ups_name[PATH_MAX];
|
||||
char bps_name[PATH_MAX];
|
||||
char ips_name[PATH_MAX];
|
||||
|
||||
unsigned state_slot;
|
||||
|
||||
|
83
patch.c
83
patch.c
@ -15,11 +15,15 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// BPS/UPS/IPS implementation from bSNES (nall::).
|
||||
// Modified for SSNES.
|
||||
|
||||
#include "patch.h"
|
||||
#include "movie.h"
|
||||
#include "boolean.h"
|
||||
#include "msvc/msvc_compat.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
enum bps_mode
|
||||
{
|
||||
@ -316,3 +320,82 @@ patch_error_t ups_apply_patch(
|
||||
return PATCH_SOURCE_INVALID;
|
||||
}
|
||||
|
||||
patch_error_t ips_apply_patch(
|
||||
const uint8_t *patchdata, size_t patchlen,
|
||||
const uint8_t *sourcedata, size_t sourcelength,
|
||||
uint8_t *targetdata, size_t *targetlength)
|
||||
{
|
||||
if (patchlen < 8 ||
|
||||
patchdata[0] != 'P' ||
|
||||
patchdata[1] != 'A' ||
|
||||
patchdata[2] != 'T' ||
|
||||
patchdata[3] != 'C' ||
|
||||
patchdata[4] != 'H')
|
||||
return PATCH_PATCH_INVALID;
|
||||
|
||||
memcpy(targetdata, sourcedata, sourcelength);
|
||||
|
||||
uint32_t offset = 5;
|
||||
*targetlength = sourcelength;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (offset > patchlen - 3)
|
||||
break;
|
||||
|
||||
uint32_t address = patchdata[offset++] << 16;
|
||||
address |= patchdata[offset++] << 8;
|
||||
address |= patchdata[offset++] << 0;
|
||||
|
||||
if (address == 0x454f46) // EOF
|
||||
{
|
||||
if (offset == patchlen)
|
||||
return PATCH_SUCCESS;
|
||||
else if (offset == patchlen - 3)
|
||||
{
|
||||
uint32_t size = patchdata[offset++] << 16;
|
||||
size |= patchdata[offset++] << 8;
|
||||
size |= patchdata[offset++] << 0;
|
||||
*targetlength = size;
|
||||
return PATCH_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset > patchlen - 2)
|
||||
break;
|
||||
|
||||
unsigned length = patchdata[offset++] << 8;
|
||||
length |= patchdata[offset++] << 0;
|
||||
|
||||
if (length) // Copy
|
||||
{
|
||||
if (offset > patchlen - length)
|
||||
break;
|
||||
|
||||
while (length--)
|
||||
targetdata[address++] = patchdata[offset++];
|
||||
}
|
||||
else // RLE
|
||||
{
|
||||
if (offset > patchlen - 3)
|
||||
break;
|
||||
|
||||
length = patchdata[offset++] << 8;
|
||||
length |= patchdata[offset++] << 0;
|
||||
|
||||
if (length == 0) // Illegal
|
||||
break;
|
||||
|
||||
while (length--)
|
||||
targetdata[address++] = patchdata[offset];
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
if (address > *targetlength)
|
||||
*targetlength = address;
|
||||
}
|
||||
|
||||
return PATCH_PATCH_INVALID;
|
||||
}
|
||||
|
||||
|
9
patch.h
9
patch.h
@ -21,7 +21,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// BPS/UPS implementation from bSNES (nall::).
|
||||
// BPS/UPS/IPS implementation from bSNES (nall::).
|
||||
// Modified for SSNES.
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@ -51,4 +52,10 @@ patch_error_t ups_apply_patch(
|
||||
const uint8_t *source_data, size_t source_length,
|
||||
uint8_t *target_data, size_t *target_length);
|
||||
|
||||
|
||||
patch_error_t ips_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);
|
||||
|
||||
#endif
|
||||
|
10
ssnes.c
10
ssnes.c
@ -536,6 +536,7 @@ static void print_help(void)
|
||||
puts("\t-v/--verbose: Verbose logging.");
|
||||
puts("\t-U/--ups: Specifies path for UPS patch that will be applied to ROM.");
|
||||
puts("\t--bps: Specifies path for BPS patch that will be applied to ROM.");
|
||||
puts("\t--ips: Specifies path for IPS patch that will be applied to ROM.");
|
||||
puts("\t-X/--xml: Specifies path to XML memory map.");
|
||||
puts("\t-D/--detach: Detach SSNES from the running console. Not relevant for all platforms.\n");
|
||||
}
|
||||
@ -683,6 +684,7 @@ static void parse_input(int argc, char *argv[])
|
||||
#endif
|
||||
{ "ups", 1, NULL, 'U' },
|
||||
{ "bps", 1, &val, 'B' },
|
||||
{ "ips", 1, &val, 'I' },
|
||||
{ "xml", 1, NULL, 'X' },
|
||||
{ "detach", 0, NULL, 'D' },
|
||||
{ "features", 0, &val, 'f' },
|
||||
@ -909,6 +911,11 @@ static void parse_input(int argc, char *argv[])
|
||||
g_extern.bps_pref = true;
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
strlcpy(g_extern.ips_name, optarg, sizeof(g_extern.ips_name));
|
||||
g_extern.ips_pref = true;
|
||||
break;
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
case 's':
|
||||
{
|
||||
@ -1511,6 +1518,9 @@ static void fill_pathnames(void)
|
||||
if (!(*g_extern.bps_name))
|
||||
fill_pathname_noext(g_extern.bps_name, g_extern.basename, ".bps", sizeof(g_extern.bps_name));
|
||||
|
||||
if (!(*g_extern.ips_name))
|
||||
fill_pathname_noext(g_extern.ips_name, g_extern.basename, ".ips", sizeof(g_extern.ips_name));
|
||||
|
||||
if (!(*g_extern.xml_name))
|
||||
fill_pathname_noext(g_extern.xml_name, g_extern.basename, ".xml", sizeof(g_extern.xml_name));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user