Add IPS patching.

This commit is contained in:
Themaister 2012-03-20 23:08:34 +01:00
parent acdc6179bb
commit 48cddde84a
6 changed files with 126 additions and 8 deletions

View File

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

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

View File

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

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

View File

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

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