// This is free and unencumbered software released into the public domain. Made my ArC.
// ips_patcher.c
// gcc -o ips_patcher ips_patcher.c
// ./ips_patcher sforce1.bin patch.ips sforce1_patched.bin
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define IPS_HEADER "PATCH"
#define IPS_EOF "EOF"
static int verbose = 0;
void logmsg(const char *msg) {
if (verbose) {
printf("[DEBUG] %s\n", msg);
}
}
uint8_t *read_file(const char *filename, long *size_out) {
FILE *f = fopen(filename, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long size = ftell(f);
rewind(f);
uint8_t *data = malloc(size);
if (!data) {
fclose(f);
return NULL;
}
fread(data, 1, size, f);
fclose(f);
*size_out = size;
return data;
}
int write_file(const char *filename, uint8_t *data, long size) {
FILE *f = fopen(filename, "wb");
if (!f) return 0;
fwrite(data, 1, size, f);
fclose(f);
return 1;
}
int apply_ips_patch(const char *rom_path, const char *ips_path, const char *out_path) {
long rom_size, ips_size;
logmsg(" Reading ROM file...");
uint8_t *rom = read_file(rom_path, &rom_size);
if (!rom) {
printf("

Error: Could not read ROM file.\n");
return 0;
}
logmsg("Reading IPS patch file...");
uint8_t *ips = read_file(ips_path, &ips_size);
if (!ips) {
printf("

Error: Could not read IPS file.\n");
free(rom);
return 0;
}
if (ips_size < 5 || memcmp(ips, IPS_HEADER, 5) != 0) {
printf("

Error: Invalid IPS header.\n");
free(rom);
free(ips);
return 0;
}
logmsg(" Valid IPS header found.");
long offset = 5;
int patch_count = 0;
while (offset < ips_size) {
if (offset + 3 <= ips_size && memcmp(&ips[offset], IPS_EOF, 3) == 0) {
logmsg(" Found EOF marker.");
break;
}
if (offset + 5 > ips_size) break;
// 3‑byte address (big endian)
uint32_t addr = (ips[offset] << 16) | (ips[offset+1] << 8) | ips[offset+2];
offset += 3;
// 2‑byte length (big endian)
uint16_t length = (ips[offset] << 8) | ips[offset+1];
offset += 2;
if (length == 0) {
// RLE record
if (offset + 3 > ips_size) break;
uint16_t rle_len = (ips[offset] << 8) | ips[offset+1];
uint8_t rle_byte = ips[offset+2];
offset += 3;
char dbg[128];
sprintf(dbg, " RLE patch at 0x%06X: %u bytes of 0x%02X", addr, rle_len, rle_byte);
logmsg(dbg);
// Extend ROM if needed
if (addr + rle_len > rom_size) {
long new_size = addr + rle_len;
rom = realloc(rom, new_size);
memset(rom + rom_size, 0, new_size - rom_size);
rom_size = new_size;
}
for (int i = 0; i < rle_len; i++) {
rom[addr + i] = rle_byte;
}
patch_count++;
} else {
// Normal patch
if (offset + length > ips_size) break;
char dbg[128];
sprintf(dbg, " Normal patch at 0x%06X: %u bytes", addr, length);
logmsg(dbg);
// Extend ROM if needed
if (addr + length > rom_size) {
long new_size = addr + length;
rom = realloc(rom, new_size);
memset(rom + rom_size, 0, new_size - rom_size);
rom_size = new_size;
}
memcpy(&rom[addr], &ips[offset], length);
offset += length;
patch_count++;
}
}
printf(" Applied %d patch segments.\n", patch_count);
logmsg(" Writing patched ROM...");
int ok = write_file(out_path, rom, rom_size);
free(rom);
free(ips);
return ok;
}
int main(int argc, char **argv) {
if (argc < 4) {
printf("Usage: %s [-v] rom.bin patch.ips output.bin\n", argv[0]);
return 1;
}
int argi = 1;
if (strcmp(argv[argi], "-v") == 0) {
verbose = 1;
argi++;
}
const char *rom = argv[argi++];
const char *ips = argv[argi++];
const char *out = argv[argi++];
printf("Applying IPS patch...\n");
printf("ROM: %s\n", rom);
printf("Patch: %s\n", ips);
printf("Out: %s\n", out);
if (apply_ips_patch(rom, ips, out)) {
printf(" Patch applied successfully.\n");
return 0;
} else {
printf("

Failed to apply patch.\n");
return 1;
}
}