Copy Link
Add to Bookmark
Report
Advanced Patching System Version 0.1
Advanced Patching System Version 0.1
Authors
Silo (silo@blackbag.org)
Fractal (fractal@blackbag.org)
APS has been designed with several features as standard.
- No file limitations.
With the ever expanding file sizes that are appearing ips files have problems with patching files that are greater than 16Mb.
APS does not have this problem and should facilitate patching of files up to 2Gb. - Target file verification.
APS will be capable of target verification. In the case of type 1 (N64 patching) the patch will contain the CRC, Territory code and Cart ID. This will allow the patching program to make sure that the file about to be patched is the intended target. - Internal Descriptors.
A small text field allowing APS authors to describe precisely what the patch does. - Future expansion through different patch types and encoding methods.
Detailed File Structure
Standard Header
BYTE 0-4 : Magic ("APS10")
BYTE 5 : Patch Type : -
0 for a Simple Patch
1 for a N64 Specific Patch
This method allows future expansion for other definable patch types.
BYTE 6 : Encoding Method
0 for Simple Encoding (Similar to IPS & Defined Below)
(Again allowing for future expansion)
BYTE 7-56 : Description
Space padded free text for patch information.
Type 0 : Simple Patching Header
BYTE 57-61 : Size of destination image.
Type 1 : N64 Header
Header specific information for a type 1 (N64 specific) patch.
BYTE 57 : Original image file format
0 = Doctor V64
1 = CD64/Z64/Wc/SP
BYTE 58-59 : CartID
This is the two bytes of Cart ID taken directly from
the original image. Stored in Motorola (human readable) endian.
BYTE 60 : Country
The original image's country code.Stored in Motorola
(human readable) endian.
BYTE 61-68 : CRC
The original image's CRC taken directly out of the
original image. Stored in Motorola (human readable) endian.
BYTE 69-74 : Pad.
For future expansion.
BYTE 75-79 : Size of destination image.
The Patch Itself
Encoding Method #0. (offset 0x6 within the standard header)
Format : xxxx,y,zzzz
xxxx = 4 byte file offset.
y = Number of bytes that will be changed.
zzzz = New data to be written ('y' number of bytes).
If paramter 'y' is set to zero (0) then paramter 'z' will be a two (2) byte field. Byte zero (0) will be the data and byte one (1) will be the number of repetitions.
Examples
Starting from File Offset 0x0015F9D0 replace 3 bytes with 01,02,03
D0 F9 15 00 03 01 02 03
Starting from File Offset 0x0015F9D0 replace 0x10 bytes with 0xFF
D0 F9 15 00 00 FF 10
APS is dedicated to CZN :) - keep the faith guys.
n64aps.c
/* Apply an APS (Advanced Patch System) File for N64 Images
* (C)1998 Silo / BlackBag
*
* Version 1.2 981217
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#ifndef TRUE
#define TRUE 1
#define FALSE !TRUE
#endif
#define MESSAGE "\nN64APS v1.2 (BETA) Build 981217\n"
#define COPYRIGHT "(C)1998 Silo/BlackBag (Silo@BlackBag.org)\n\n"
#define BUFFERSIZE 255
char Magic[]="APS10";
#define MagicLength 5
#define TYPE_N64 1
unsigned char PatchType=TYPE_N64;
#define DESCRIPTION_LEN 50
#define ENCODINGMETHOD 0 // Very Simplistic Method
unsigned char EncodingMethod=ENCODINGMETHOD;
FILE *APSFile;
FILE *ORGFile;
FILE *NEWFile;
int Quiet=FALSE;
void
syntax (void)
{
printf ("%s", MESSAGE);
printf ("%s", COPYRIGHT);
printf ("N64APS <options> <Original File> <APS File>\n");
printf (" -f : Force Patching Over Incorrect Image\n");
printf (" -q : Quiet Mode\n");
fflush (stdout);
}
int CheckFile (char *Filename,char *mode)
{
FILE *fp;
fp=fopen (Filename,mode);
if (fp == NULL)
return (FALSE);
else
{
fclose (fp);
if (mode[0] == 'w')
unlink (Filename);
return (TRUE);
}
}
void
ReadStdHeader ()
{
char aMagic[MagicLength];
char Description[DESCRIPTION_LEN+1];
fread (aMagic,1,MagicLength,APSFile);
if (strncmp (aMagic,Magic,MagicLength) != 0)
{
printf ("Not a Valid Patch File\n");
fclose (ORGFile);
fclose (APSFile);
exit (1);
}
fread (&PatchType,sizeof (PatchType),1,APSFile);
if (PatchType != 1) // N64 Patch
{
printf ("Unable to Process Patch File\n");
fclose (ORGFile);
fclose (APSFile);
exit (1);
}
fread (&EncodingMethod,sizeof (EncodingMethod),1,APSFile);
if (EncodingMethod != 0) // Simple Encoding
{
printf ("Unknown or New Encoding Method\n");
fclose (ORGFile);
fclose (APSFile);
exit (1);
}
fread (Description,1,DESCRIPTION_LEN,APSFile);
Description[DESCRIPTION_LEN]=0;
if (!Quiet)
{
printf ("Description : %s\n",Description);
fflush (stdout);
}
}
void
ReadN64Header (int Force)
{
unsigned long MagicTest;
unsigned char Buffer[8];
unsigned char APSBuffer[8];
int c;
unsigned char CartID[2],Temp;
unsigned char Teritory,APSTeritory;
unsigned char APSFormat;
fseek (ORGFile,0,SEEK_SET);
fread (&MagicTest,sizeof (MagicTest),1,ORGFile);
fread (Buffer,1,1,APSFile);
APSFormat=Buffer[0];
if (((MagicTest == 0x12408037) && (Buffer[0]==1)) || ((MagicTest != 0x12408037 && (Buffer[0]==0))))// 0 for Doctor Format, 1 for Everything Else
{
printf ("Image is in the wrong format\n");
fclose (ORGFile);
fclose (APSFile);
exit (1);
}
fseek (ORGFile,60, SEEK_SET); // Cart ID
fread (CartID,1,2,ORGFile);
fread (Buffer,1,2,APSFile);
if (MagicTest == 0x12408037) // Doc
{
Temp=CartID[0];
CartID[0]=CartID[1];
CartID[1]=Temp;
}
if ((Buffer[0] != CartID[0]) || (Buffer[1] != CartID[1]))
{
printf ("Not the Same Image\n");
fclose (ORGFile);
fclose (APSFile);
exit (1);
}
if (MagicTest == 0x12408037)
fseek (ORGFile,63,SEEK_SET); // Teritory
else
fseek (ORGFile,62,SEEK_SET);
fread (&Teritory,sizeof (Teritory),1,ORGFile);
fread (&APSTeritory,sizeof (APSTeritory),1,APSFile);
if (Teritory != APSTeritory)
{
printf ("Wrong Country\n");
if (!Force)
{
fclose (ORGFile);
fclose (APSFile);
exit (1);
}
}
fseek (ORGFile,0x10, SEEK_SET); // CRC Header Position
fread (Buffer,1,8,ORGFile);
fread (APSBuffer,1,8,APSFile);
if (MagicTest == 0x12408037) // Doc
{
Temp=Buffer[0];
Buffer[0]=Buffer[1];
Buffer[1]=Temp;
Temp=Buffer[2];
Buffer[2]=Buffer[3];
Buffer[3]=Temp;
Temp=Buffer[4];
Buffer[4]=Buffer[5];
Buffer[5]=Temp;
Temp=Buffer[6];
Buffer[6]=Buffer[7];
Buffer[7]=Temp;
}
if ((APSBuffer[0] != Buffer[0]) || (APSBuffer[1] != Buffer[1]) ||
(APSBuffer[2] != Buffer[2]) || (APSBuffer[3] != Buffer[3]) ||
(APSBuffer[4] != Buffer[4]) || (APSBuffer[5] != Buffer[5]) ||
(APSBuffer[6] != Buffer[6]) || (APSBuffer[7] != Buffer[7]))
{
if (!Quiet) { printf ("Incorrect Image\n"); fflush (stdout); }
if (!Force)
{
fclose (ORGFile);
fclose (APSFile);
exit (1);
}
}
fseek (ORGFile,0,SEEK_SET);
c=fgetc(APSFile);
c=fgetc(APSFile);
c=fgetc(APSFile);
c=fgetc(APSFile);
c=fgetc(APSFile);
}
void
ReadSizeHeader (char *File1)
{
long OrigSize;
long APSOrigSize;
unsigned char t;
long i;
fseek (ORGFile,0,SEEK_END);
OrigSize = ftell (ORGFile);
fread (&APSOrigSize,sizeof (APSOrigSize),1,APSFile);
if (OrigSize != APSOrigSize) // Do File Resize
{
if (APSOrigSize < OrigSize)
{
int x;
fclose (ORGFile);
x = open (File1, O_WRONLY);
if (ftruncate (x, APSOrigSize) != 0)
{
printf ("Trunacte Failed\n");
}
close (x);
ORGFile = fopen (File1,"rb+");
}
else
{
t = 0;
for (i=0;i<(APSOrigSize-OrigSize);i++) fputc (t,ORGFile);
}
}
fseek (ORGFile,0,SEEK_SET);
}
void
ReadPatch ()
{
int APSReadLen;
int Finished=FALSE;
unsigned char Buffer[256];
long Offset;
unsigned char Size;
while (!Finished)
{
APSReadLen = fread (&Offset,sizeof (Offset),1,APSFile);
if (APSReadLen == 0)
{
Finished=TRUE;
}
else
{
fread (&Size,sizeof (Size),1,APSFile);
if (Size != 0)
{
fread (Buffer,1,Size,APSFile);
if ((fseek (ORGFile,Offset,SEEK_SET)) != 0)
{
printf ("Seek Failed\n");
fflush (stdout);
exit (1);
}
fwrite (Buffer,1,Size,ORGFile);
}
else
{
unsigned char data;
unsigned char len;
int i;
fread (&data,sizeof(data),1,APSFile);
fread (&len,sizeof(data),1,APSFile);
if ((fseek (ORGFile,Offset,SEEK_SET)) != 0)
{
printf ("Seek Failed\n");
fflush (stdout);
exit (1);
}
for (i=0;i<len;i++) fputc (data,ORGFile);
}
}
}
}
int
main (int argc, char *argv[])
{
int Result;
char File1[256];
char File2[256];
int Force=FALSE;
while ((Result = getopt (argc, argv, "1:2:fq")) != -1)
{
switch (Result)
{
case 'f': // Force
Force=TRUE;
break;
case 'q': // Quiet
Quiet=TRUE;
break;
case '?':
printf ("Unknown option %c\n", Result);
syntax();
return (1);
break;
}
}
if (argc - optind != 2)
{
syntax();
return (1);
}
strcpy (File1,argv[optind]);
strcpy (File2,argv[optind+1]);
if (!CheckFile (File1,"rb+")) return(1);
if (!CheckFile (File2,"rb")) return(1);
ORGFile = fopen (File1,"rb+");
APSFile = fopen (File2,"rb");
if (!Quiet)
{
printf ("%s", MESSAGE);
printf ("%s", COPYRIGHT);
fflush (stdout);
}
ReadStdHeader ();
ReadN64Header (Force);
ReadSizeHeader (File1);
ReadPatch ();
fclose (NEWFile);
fclose (ORGFile);
fclose (APSFile);
return (0);
}
n64caps.c
/* Create APS (Advanced Patch System) for N64 Images
* (C)1998 Silo / BlackBag
*
* Version 1.2 981217
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef TRUE
#define TRUE 1
#define FALSE !TRUE
#endif
#define MESSAGE "\nN64CAPS v1.2 (BETA) Build 981217\n"
#define COPYRIGHT "(C)1998 Silo/BlackBag (Silo@BlackBag.org)\n\n"
#define BUFFERSIZE 255
char Magic[]="APS10";
#define MagicLength 5
#define TYPE_N64 1
unsigned char PatchType=TYPE_N64;
#define DESCRIPTION_LEN 50
#define ENCODINGMETHOD 0 // Very Simplistic Method
unsigned char EncodingMethod=ENCODINGMETHOD;
FILE *APSFile;
FILE *ORGFile;
FILE *NEWFile;
int Quiet=FALSE;
int ChangeFound;
void
syntax (void)
{
printf ("%s", MESSAGE);
printf ("%s", COPYRIGHT);
printf ("N64CAPS <options> <Original File> <Modified File> <Output APS File>\n");
printf (" -d %c<Image Title>%c : Description\n", 34, 34);
printf (" -q : Quiet Mode\n");
fflush (stdout);
}
int CheckFile (char *Filename,char *mode,int Image)
{
FILE *fp;
unsigned long MagicTest;
fp=fopen (Filename,mode);
if (fp == NULL)
return (FALSE);
else
{
if (Image)
{
fread (&MagicTest,sizeof (MagicTest),1,fp);
fclose (fp);
if (mode[0] == 'w')
unlink (Filename);
if (MagicTest != 0x12408037 && MagicTest != 0x40123780) // Invalid Image
{
if (!Quiet)
{
printf ("%s is an Invalid Image\n",Filename);
fflush (stdout);
}
return (FALSE);
}
return (TRUE);
}
}
return (TRUE);
}
void
WriteStdHeader (char *Desc)
{
char Description[DESCRIPTION_LEN];
fwrite (Magic,1,MagicLength,APSFile);
fwrite (&PatchType,sizeof (PatchType),1,APSFile);
fwrite (&EncodingMethod,sizeof (EncodingMethod),1,APSFile);
memset (Description,' ',DESCRIPTION_LEN);
strcpy (Description,Desc);
Description[strlen(Desc)]=' ';
fwrite (Description,1,DESCRIPTION_LEN,APSFile);
}
void
WriteN64Header ()
{
unsigned long MagicTest;
unsigned char Buffer[8];
unsigned char Teritory;
unsigned char CartID[2],Temp;
fread (&MagicTest,sizeof (MagicTest),1,ORGFile);
if (MagicTest == 0x12408037) // 0 for Doctor Format, 1 for Everything Else
fputc (0,APSFile);
else
fputc (1,APSFile);
fseek (ORGFile,60,SEEK_SET);
fread (CartID,1,2,ORGFile);
if (MagicTest == 0x12408037) // Doc
{
Temp=CartID[0];
CartID[0]=CartID[1];
CartID[1]=Temp;
}
fwrite (CartID,1,2,APSFile);
if (MagicTest == 0x12408037) // Doc
fseek (ORGFile,63,SEEK_SET);
else
fseek (ORGFile,62,SEEK_SET);
fread (&Teritory,sizeof (Teritory),1,ORGFile);
fwrite (&Teritory,sizeof (Teritory),1,APSFile);
fseek (ORGFile,0x10, SEEK_SET); // CRC Header Position
fread (Buffer,1,8,ORGFile);
if (MagicTest == 0x12408037) // Doc
{
Temp=Buffer[0];
Buffer[0]=Buffer[1];
Buffer[1]=Temp;
Temp=Buffer[2];
Buffer[2]=Buffer[3];
Buffer[3]=Temp;
Temp=Buffer[4];
Buffer[4]=Buffer[5];
Buffer[5]=Temp;
Temp=Buffer[6];
Buffer[6]=Buffer[7];
Buffer[7]=Temp;
}
fwrite (Buffer,1,8,APSFile);
fseek (ORGFile,0,SEEK_SET);
fputc (0,APSFile); // PAD
fputc (0,APSFile); // PAD
fputc (0,APSFile); // PAD
fputc (0,APSFile); // PAD
fputc (0,APSFile); // PAD
}
void
WriteSizeHeader ()
{
long NEWSize,ORGSize;
fseek (NEWFile,0,SEEK_END);
fseek (ORGFile,0,SEEK_END);
NEWSize = ftell (NEWFile);
ORGSize = ftell (ORGFile);
fwrite (&NEWSize,sizeof (NEWSize),1,APSFile);
if (ORGSize != NEWSize) ChangeFound=TRUE;
fseek (NEWFile,0,SEEK_SET);
fseek (ORGFile,0,SEEK_SET);
}
void
WritePatch ()
{
long ORGReadLen;
long NEWReadLen;
int Finished=FALSE;
unsigned char ORGBuffer[BUFFERSIZE];
unsigned char NEWBuffer[BUFFERSIZE];
long FilePos;
int i;
long ChangedStart=0;
long ChangedOffset=0;
int ChangedLen=0;
int State;
fseek (ORGFile,0,SEEK_SET);
fseek (NEWFile,0,SEEK_SET);
FilePos = 0;
while (!Finished)
{
ORGReadLen = fread (ORGBuffer,1,BUFFERSIZE,ORGFile);
NEWReadLen = fread (NEWBuffer,1,BUFFERSIZE,NEWFile);
if (ORGReadLen != NEWReadLen)
{
int a;
for (a=ORGReadLen;a<NEWReadLen;a++) ORGBuffer[a]=0;
}
i=0;
State=0;
if (NEWReadLen != 0)
{
while (i<NEWReadLen)
{
switch (State)
{
case 0:
if (NEWBuffer[i] != ORGBuffer[i])
{
State=1;
ChangedStart=FilePos+i;
ChangedOffset=i;
ChangedLen=1;
ChangeFound=TRUE;
}
i++;
break;
case 1:
if (NEWBuffer[i] != ORGBuffer[i])
{
ChangedLen++;
i++;
}
else
State=2;
break;
case 2:
fwrite (&ChangedStart,sizeof (ChangedStart),1,APSFile);
fputc ((ChangedLen&0xff),APSFile);
fwrite (NEWBuffer+(ChangedOffset),1,ChangedLen,APSFile);
State=0;
break;
}
}
}
if (State!=0)
{
if (ChangedLen==0) ChangedLen=255;
fwrite (&ChangedStart,sizeof (ChangedStart),1,APSFile);
fputc ((ChangedLen&0xff),APSFile);
fwrite (NEWBuffer+(ChangedOffset),1,ChangedLen,APSFile);
}
if (NEWReadLen==0) Finished=TRUE;
FilePos+=NEWReadLen;
}
}
int
main (int argc, char *argv[])
{
int Result;
char File1[256];
char File2[256];
char OutFile[256];
char Description[81];
ChangeFound = FALSE;
Description[0]=0;
while ((Result = getopt (argc, argv, "d:q")) != -1)
{
switch (Result)
{
case 'd': // Description
strcpy (Description, optarg);
break;
case 'q': // Quiet
Quiet=TRUE;
break;
case '?':
printf ("Unknown option %c\n", Result);
syntax();
return (1);
break;
}
}
if ((argc - optind) != 3)
{
syntax();
return (1);
}
strcpy (File1,argv[optind]);
strcpy (File2,argv[optind+1]);
strcpy (OutFile,argv[optind+2]);
if (!CheckFile (File1,"rb",TRUE)) return(1);
if (!CheckFile (File2,"rb",TRUE)) return(1);
if (!CheckFile (OutFile,"wb",FALSE)) return(1);
APSFile = fopen (OutFile,"wb");
ORGFile = fopen (File1,"rb");
NEWFile = fopen (File2,"rb");
if (!Quiet)
{
printf ("%s", MESSAGE);
printf ("%s", COPYRIGHT);
printf ("Writing Headers...");
fflush (stdout);
}
WriteStdHeader (Description);
WriteN64Header ();
WriteSizeHeader ();
if (!Quiet) { printf ("Done\nFinding Changes..."); fflush (stdout); }
WritePatch ();
if (!Quiet) { printf ("Done\n"); fflush (stdout); }
fclose (NEWFile);
fclose (ORGFile);
fclose (APSFile);
if (!Quiet && !ChangeFound)
{
printf ("No Changes Found\n");
fflush (stdout);
unlink (OutFile);
}
return (0);
}