Copy Link
Add to Bookmark
Report

xPS-Apply: a small tool which can apply IPS and APS-patches

Nintendo64's profile picture
Published in 
N64 various
 · 31 Oct 2019

xPS-Apply Some notes


What is it ?
It is a small tool which can apply IPS and APS-patches.


Binary
...was compiled for x86/Linux. For other os/platform, see Source section.


Usage

 
XPSAPPLY rom patch [-]

b: Make a backup before patching
l: Create a logfile


* No wildcards are allowed.
* Options can be combined so "-bl" would be valid

E.g. "XPSAPPLY zelda.v64 zcrk.aps -l" would apply the APS-patch "zcrk.aps" and creates a log-file called "zelda.log".


Source-code
Compile with 'gcc -o xpsapply xPSapply.c'


The APS-draft 1.2
There is a little bug in the draft

 
wrong | BYTE 69-74 : Pad.
| BYTE 75-79 : Size of destination image.

fixed | BYTE 69-73 : Pad.
| BYTE 74-77 : Size of destination image.

Ravemax/Dextrose
Modded (fixed) for UNIX/Linux compatibility. Se Source Code section for details on how to compile.

Also modded such that posix details are provided on file errors.

-John '99

xPSapply.c


 
/***************************************************************************
* *
* xpsapply.c *
* *
* This program can be used to apply *
* *
* - IPS (International Patching System) *
* - APS (Advanced Patching System) *
* *
* Version 1.00 *
* Ravemax/Dextrose *
* *
* --------------------------------------------------------------------- *
* *
* You can customize xPSApply and you are allowed to spread the modified *
* versions, but please give me a credit (don't be a lamer). *
* *
***************************************************************************/


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

/*--------------------------------------------------------------------------
Types
--------------------------------------------------------------------------*/


typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;

typedef char bool;

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE (!FALSE)
#endif

/*--------------------------------------------------------------------------
Constant strings
--------------------------------------------------------------------------*/


const char *title_str =
"\nxPS-Apply v1.00 (c) Ravemax of Dextrose\n";

const char *usage_str =
"Usage: XPSAPPLY rom patch [-<option(s)>]\n\n"
"<Options>\n"
" b: Make a backup before patching\n"
" l: Create a logfile\n";

const char *logfile_hdr_str =
"Created with xPS-Apply\n"
" \n"
"[ Offset: ≥ RLE: ≥ Bytes: ]\n"
"⁄ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ≈ƒƒƒƒƒƒƒƒ≈ƒƒƒƒƒƒƒƒƒƒø\n";

const char *logfile_end_str =
"¿ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒŸ\n"
" \n"
"Modifications: %ld\n";

/*
* Extensions
*/


const char *log_ext_str =
".LOG";

const char *backup_ext_str =
".ORG";

/*--------------------------------------------------------------------------
Global variables
--------------------------------------------------------------------------*/


FILE *rom_file,*patch_file,*log_file;
bool opt_backup = FALSE,opt_log = FALSE;
long mod_cnt = 0;

/*--------------------------------------------------------------------------
ERROR_EXIT
--------------------------------------------------------------------------*/


#define ERROR_OPTION 0
#define ERROR_NOT_FOUND 1
#define ERROR_NO_PATCH 2
#define ERROR_NO_APS 3
#define ERROR_WRONG_ENDIANESS 4
#define ERROR_NOT_RIGHT_ROM 5

void error_exit(char num,char *param)
{
const char *error_base_str =
"- Error : %s %s !\n\n";

const char *error_msg_str[] = {
"contents a unknown option",
"not found",
"is no known patch-type (IPS or APS)",
"is not a N64-specific APS-file",
"has the wrong endianess (byte-swap)",
"is not the right ROM"
};

printf(error_base_str,param,error_msg_str[num]);
exit(num+2);
}

/*--------------------------------------------------------------------------
PARSE_CMDLINE
--------------------------------------------------------------------------*/


void parse_cmdline(char start_from,int argc,char *argv[])
{
char *cptr;

while (1)
{
argc--;
if (argc < start_from)
break;
cptr = argv[argc]+1;

while (*cptr)
{
switch(tolower(*cptr))
{
case 'b' :
opt_backup = TRUE;
break;
case 'l' :
opt_log = TRUE;
break;
case '?' :
puts(usage_str);
exit(1);
default :
error_exit(ERROR_OPTION,argv[argc]);
}
cptr++;
}
}
}

/*--------------------------------------------------------------------------
OPEN_PATCH
--------------------------------------------------------------------------*/


bool open_patch(char *fname)
{
const char patch_id[2][5] = {
"PATCH",
"APS10"
};

char id_chk[5];

patch_file = fopen(fname,"rb");
if (patch_file == NULL)
{
printf("Couldn't open ROM: ");
perror("fopen");
exit(2);
}
fread(id_chk,1,5,patch_file);
if (!memcmp(id_chk,patch_id[0],5))
return(TRUE);
else if (!memcmp(id_chk,patch_id[1],5))
return(FALSE);
else
error_exit(ERROR_NO_PATCH,fname);
}

/*--------------------------------------------------------------------------
OPEN_WITH_EXT
--------------------------------------------------------------------------*/


FILE *open_with_ext(char *fname,const char *ext)
{
char ctemp[13];

strcpy(ctemp,fname);
if (strchr(ctemp,'.'))
memcpy(strchr(ctemp,'.'),ext,strlen(ext)+1);
else
strcat(ctemp,ext);

return(fopen(ctemp,"wb"));
}

/*--------------------------------------------------------------------------
CREATE_BACKUP
--------------------------------------------------------------------------*/


void create_backup(char *fname)
{
char *buffer;
size_t numread;
FILE *bakf;

puts("- Create backup");

buffer = (char *)malloc(32768);
bakf = open_with_ext(fname,backup_ext_str);
while (numread = fread(buffer,1,32768,rom_file))
fwrite(buffer,1,numread,bakf);
fclose(bakf);

free(buffer);
fseek(rom_file,0,SEEK_SET); // Some compiler doesnt support "rewind()"
}

/*--------------------------------------------------------------------------
FSIZE
--------------------------------------------------------------------------*/


long fsize(FILE *file)
{
long fs,fpos = ftell(file);

fseek(file,0,SEEK_END);
fs = ftell(file);
fseek(file,fpos,SEEK_SET);
return(fs);
}

/*--------------------------------------------------------------------------
SWAP_BYTES
--------------------------------------------------------------------------*/


void swap_bytes(u8 *data,size_t size)
{
u8 temp;

while (size > 1)
{
temp = *data;
*data = data[1];
data[1] = temp;

data += 2;
size -= 2;
}
}

/*--------------------------------------------------------------------------
VALIDATE_PATCH (only APS)
--------------------------------------------------------------------------*/


void validate_patch(char *rom_name,char *patch_name)
{
u8 cpuf[51];
bool z64_endianess;
long destsize;

if ((fgetc(patch_file) != 1) || (fgetc(patch_file) != 0))
error_exit(ERROR_NO_APS,patch_name);
fread(cpuf,1,50,patch_file);
cpuf[50] = 0;
printf("- Info : %s\n",cpuf);

/*
* Endianess
*/


z64_endianess = fgetc(patch_file);
*cpuf = fgetc(rom_file);

if ((z64_endianess && (*cpuf != 0x80)) ||
(!z64_endianess && (*cpuf != 0x37)))
error_exit(ERROR_WRONG_ENDIANESS,rom_name);

/*
* Header-data
*/


fread(cpuf,1,11,patch_file);
fseek(rom_file,0x3C,SEEK_SET); // Cart-ID+country-code
fread(cpuf+20,1,3,rom_file);
if (!z64_endianess)
cpuf[22] = fgetc(rom_file);

fseek(rom_file,0x10,SEEK_SET); // CRCs
fread(cpuf+23,1,8,rom_file);

if (!z64_endianess)
{
swap_bytes(&cpuf[20],2);
swap_bytes(&cpuf[23],8);
}
if (memcmp(cpuf,cpuf+20,11))
error_exit(ERROR_NOT_RIGHT_ROM,rom_name);

/*
* Filesize
*/


fseek(patch_file,74,SEEK_SET); // Skip future-expansion
fread(&destsize,4,1,patch_file);
if (destsize != fsize(rom_file))
error_exit(ERROR_NOT_RIGHT_ROM,rom_name);

fseek(rom_file,0,SEEK_SET);
}

/*--------------------------------------------------------------------------
MAKE_CHANGES
--------------------------------------------------------------------------*/


#define UNCOMPRESSED 0
#define IPS_RLE 1
#define APS_RLE 2

void make_changes(long offset,u16 num,char type)
{
u8 modval;
u16 loop;

/*
* Patch
*/


fseek(rom_file,offset,SEEK_SET);
if (type == UNCOMPRESSED)
{
for (loop = num; loop; loop--)
fputc(fgetc(patch_file),rom_file);
}
else
{
if (type == IPS_RLE)
modval = fgetc(patch_file);
else
{
modval = (u8)num;
num = ((u8 *)&num)[1];
}

for (loop = num; loop; loop--)
fputc(modval,rom_file);
}

/*
* Log
*/


mod_cnt++;
printf("- Offset : 0x%lX",offset);
if (opt_log)
fprintf(log_file,"≥ 0x%.8lX ≥ %s ≥ %5d ≥\n",
offset,(type) ? "Yes" : "No ",num);
}

/*--------------------------------------------------------------------------
HANDLE_IPS
--------------------------------------------------------------------------*/


void handle_ips(void)
{
#define BYTE3_TO_LONG(bp) \
(((long)(bp)[0] << 16) | ((long)(bp)[1] << 8) | (long)(bp)[2])

#define BYTE2_TO_WORD(bp) \
(((bp)[0] << 8) | (bp)[1])

const u8 end_of_ips[3] = "EOF";

u8 puffer[5];
long offset;
u16 count;

while (1)
{
fread(puffer,1,3,patch_file);
if (!memcmp(puffer,end_of_ips,3))
break;
offset = BYTE3_TO_LONG(puffer);
fread(puffer,2,1,patch_file);
count = BYTE2_TO_WORD(puffer);
if (count)
make_changes(offset,count,UNCOMPRESSED);
else
{
fread(puffer,1,2,patch_file);
make_changes(offset,BYTE2_TO_WORD(puffer),IPS_RLE);
}
}
}

/*--------------------------------------------------------------------------
HANDLE_APS
--------------------------------------------------------------------------*/


void handle_aps(void)
{
long offset,romsize;
u8 count;
u16 num;

romsize = fsize(rom_file);
while (fread(&offset,4,1,patch_file))
{
if (romsize < offset)
break;

if (count = fgetc(patch_file))
make_changes(offset,(u16)count,UNCOMPRESSED);
else
{
fread(&num,1,2,patch_file);
make_changes(offset,(u16)num,APS_RLE);
}
}
}

/*--------------------------------------------------------------------------
MAIN
--------------------------------------------------------------------------*/


int main(int argc,char *argv[])
{
puts(title_str);

/*
* Commandline
*/


if (argc < 3)
{
puts(usage_str);
return(1);
}

parse_cmdline(3,argc,argv);

/*
* Open/creates files
*/


rom_file = fopen(argv[1],"rb+");
if (rom_file == NULL)
{
printf("Couldn't open ROM: ");
perror("fopen");
exit(2);
}

if (opt_backup)
create_backup(argv[1]);

if (opt_log)
{
log_file = open_with_ext(argv[1],log_ext_str);
fputs(logfile_hdr_str,log_file);
}

/*
* Handle patch
*/


if (open_patch(argv[2]))
handle_ips();
else
{
validate_patch(argv[1],argv[2]);
handle_aps();
}

/*
* Thats all folks
*/


if (opt_log)
fprintf(log_file,logfile_end_str,mod_cnt);

puts("\n\nDone ;-)\n");
exit(0);
}

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT