Copy Link
Add to Bookmark
Report
xPS-Apply: a small tool which can apply IPS and APS-patches
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);
}