/* ApplyPPF.c (Playstation Patch File) for Playstation Images.
 *
 * Modifications for Playstation Images by Icarus / Paradox 1999
 * Original APS Code by Silo / BlackBag 1998
 *
 * Version 1.0 990704
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>

#ifndef TRUE
#define TRUE  1
#define FALSE !TRUE
#endif

#define MESSAGE   "\nApplyPPF Version 1.0 / Build 990704\n"
#define COPYRIGHT "By Icarus/Paradox 1999 (More Infos at: http://www.paradogs.com)\nTaken from APS coded by Silo/Blackbag\n\n"

#define BUFFERSIZE 255

char Magic[]="PPF10";
#define MagicLength 5

#define TYPE_N64 1
unsigned char PatchType=TYPE_N64;

#define DESCRIPTION_LEN 50

#define ENCODINGMETHOD 0 /* Encodemethod for version 1.0 */

unsigned char EncodingMethod=ENCODINGMETHOD;

FILE	*APSFile;
FILE	*ORGFile;
int		Quiet=FALSE;

void syntax (void) {
	printf ("%s", MESSAGE);
	printf ("%s", COPYRIGHT);
	printf ("ApplyPPF <options> <Original File> <PPF File>\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 ("This is *NO* PPF-Patchfile...\n");
		fclose (ORGFile);
		fclose (APSFile);
		exit (1);
	}
	fread (&EncodingMethod,sizeof (EncodingMethod),1,APSFile);
	if (EncodingMethod != 0) /* Simple Encoding */
	{
		printf ("Unknown or New Encoding Method\nCheck out the PPF website for updates...");
		fclose (ORGFile);
		fclose (APSFile);
		exit (1);
	}

	fread (Description,1,DESCRIPTION_LEN,APSFile);
	Description[DESCRIPTION_LEN]=0;
	if (!Quiet)
	{
		printf ("Patch Description : %s\n",Description);
		fflush (stdout);
	}
}

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     mode = 0;
	int     i;

	if (argc < 3) {
		syntax();
		return (1);
	}

	for(i=1; i<argc;i++) {

	 switch(argv[i][0]) {
	   case '-': switch(argv[i][1]) {
					case 'q': Quiet=TRUE;
							break;
					default : printf("Unknown Option: %c\n",argv[i][1]);
							  syntax();
							  return(1);
				 }
				 break;
	   default : switch(mode) {
					case 0: strcpy(File1,argv[i]);
							mode++;
						  break;
					case 1: strcpy(File2,argv[i]);
							mode++;
						  break;
					default : printf("Too much Options!\n");
							  syntax();
							  return(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 ();

	if (!Quiet) { printf ("Done\nPatching file..."); fflush (stdout); }

	fseek (ORGFile,0,SEEK_SET);
	fseek(APSFile,56,SEEK_SET);

	ReadPatch ();

	fclose (ORGFile);
	fclose (APSFile);

	return (0);
}
