SUPERIPS - ips file patching program

Posts 1–2 of 2 · Page 1 of 1
SUPERIPS - ips file patching program
Just thought I would release this program I thought up. It's a simple IPS patcher. Yeah, dozens of them already exist, but I decided to make one for fun.
For those of you who don't know, an IPS patch is typically used in emulation, for patching a rom. For example, a translation patch will translate a game from a language into another. IPS files are used for this most commonly.

This program does have it's limitations. First of all, it doesn't support RLE compression (although barely any ips patches I find use RLE compression, so that doesn't really matter) and it doesn't support files that are padded with 0s, which means it won't work with the RPGe translation for Final Fantasy V.

So far I confirmed it works with multiple translation patches for Final Fantasy III, demiforce's patch for Final Fantasy II, and Legend of Zelda Polish Translation patch

Attachment with download is provided. Virus Scan



Here is the source:
main.cpp
Code:
#include "fileIO.h"

/*	Usage goes as follows
	Argument 1 is the source file, this is the patch file
	Argument 2 is the destination file, this is the file to be patched

	IPS File format: First 5 bytes = 
					 "PATCH" in ascii
					 Multiple patch records
						3 bytes -  Big endian integer (other than 0x454F46) to give offset to patch in dest
						2 byte - Big endian integer (non zero) to give the size of the data to patch
						(size) bytes - the data to copy
					 Last 3 bytes = "EOF" in ascii
*/
int main(int argc, char* argv[])
{
	string clarg;
	switch(argc)
	{
		case 1: //no command
			printf("Parameters missing. For usage help use -help\n");
			return 0;
		case 2:
			clarg = argv[1];
			if(clarg == "-help")
			{
				printf("\nUsage:\nFirst Parameter - source patch file\n"
					"Second Parameter - destination file to be patched\n"
					"Example: superips rompatch.ips destrom.smc\n");
				return 0;
			}
			else
			{	
				printf("\nInvalid Command. For usage see -help\n");
				return 0;
			}
		case 3:
			break;	//leave the switch statement to allow normal control to continue outside of the switch
		default:
			printf("\nParameter error: see -help for usage instructions\n");
			return 0;
	}

	fstream fSource, fDest; //open source and dest files
	fSource.open(argv[1], ios::in | ios::out | ios::binary);
	fDest.open(argv[2], ios::in | ios::out | ios::binary);
	printf("\nUsing file %s as source and %s as destination.\nProceed? (y/n):",argv[1],argv[2]);
	char choice;
	scanf("%c",&choice);
	if(choice != 'y')
	{
		printf("\n**--__--EXITING--__--**\n");
		fSource.close();
		fDest.close();
		return 0;
	}

//===========================================================================================	
// Test if the file is a valid IPS file
	string sigCode;						//if this is a valid IPS file, it should store "PATCHEOF"
	for(int i = 0x0; i < 0x05; i++)
		sigCode += readData(fSource,i);
	fSource.seekg(0, fSource.end);		//get position of last byte in file
	int _pos_t = fSource.tellg();		//the last byte in the file
	int _pos_t2 = _pos_t - 0x03;		//temp to store offset of 3rd to last byte
										//had to do these gimmicks because I kept getting overload ambiguity compiler errors
	for(int i = _pos_t2; i < _pos_t; i++)
		sigCode += readData(fSource,i);
	
	if(sigCode != "PATCHEOF")
	{
		printf("Error: Source file is not a valid IPS file - Verify that you are using a valid file!\n");
		return 0;
	}
	printf("\nVerified that the source file is a valid IPS file\n");
//===========================================================================================
//Assemble records
	int offsetCounter = 0x05;	//initially start reading from right after the header
	int iRecords = 0;			//tally of amount of records
	record records[1000];			
	while(readData24(fSource,offsetCounter) != _32to24(swapEndian32(0x454F46))) //while not EOF
	{
		records[iRecords].offset = _32to24(swapEndian32(readData24(fSource,offsetCounter))); //Gets the offset where the data will be written
		offsetCounter+=0x03;																//increment to length section of the record
		records[iRecords].length = swapEndian16(readData16(fSource,offsetCounter));			//get the length
		offsetCounter+=0x02;																//increment to the data field

		records[iRecords].data = (byte*)malloc((sizeof(byte))*(records[iRecords].length));	

		for(int i = 0; i < records[iRecords].length; i++)
			records[iRecords].data[i] = readData(fSource,offsetCounter+i);
		offsetCounter+=(records[iRecords].length);											//increment to the start of the next record
		iRecords++;		
	}

	for(int i = 0; i < iRecords; i++)
		printf("Record [%d]\tOffset: 0x%X\t\tLength (bytes): %d\n",i,records[i].offset,records[i].length);

//========================================================================
//write to dest file

	for(int i = 0; i < iRecords; i++)
	{
		for(int j = 0; j < records[i].length; j++)
		{
			writeData(fDest,records[i].offset+j,records[i].data[j]);
		}
		printf("\nWrote %d bytes in dest file at: 0x%X",records[i].length,records[i].offset);
	}
	
	printf("\n\nReached EOF at location 0x%X in source file %s\nPatch Complete!\n",offsetCounter,argv[1]);

	for(int i = 0; i < iRecords; i++) //some final cleanup
		free(records[i].data);

	fSource.close();
	fDest.close();
	return 0;
}
fileIO.h
Code:
#ifndef FILEIO_H
#define FILEIO_H

#include <stdio.h>
#include <fstream>
#include <string>

using namespace std;
//Word of caution: this was made on a 32-bit system. 
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned int dword;

void writeData(fstream&, int, byte);
byte readData(fstream&, int);
word readData16(fstream&, int);
dword readData24(fstream&, int);

word swapEndian16(word x);
dword swapEndian32(dword x);
dword _32to24(dword x);

struct record
{
	dword offset; //where to write data in dest file
	word length; //how long the stream of data to write is
	byte* data; //the data to write
};

#endif
fileIO.cpp
Code:
#include "fileIO.h"

void writeData(fstream& File, int location, byte data)
{
	File.seekp(location); //point to the location to write
	File.write((char*)&data,sizeof(byte)); //writes data, specifies 1 byte to be written
}

byte readData(fstream& File, int location)
{
	byte value;
	File.seekp(location); //point to location to read from
	File.read((char*)&value,sizeof(byte));
	return value;
}
word readData16(fstream& File, int location)
{
	word value = 0;
	File.seekp(location); //point to location to read from
	File.read((char*)&value,sizeof(word));
	return value;
}
dword readData24(fstream& File, int location)
{
	dword value = 0;
	File.seekp(location); //point to location to read from
	File.read((char*)&value,3); //read 3 bytes (24 bit)
	return value;
}
word swapEndian16(word x)
{
	return (x >> 8) |
			(x << 8);
}
dword swapEndian32(dword x)
{
	return (x >> 24) |
		((x << 8) & 0x00FF0000) |
		((x >> 8) & 0x0000FF00) |
		(x << 24);
}
dword _32to24(dword x)
{
	return ((x >> 8) & 0x00FFFFFF);
}
superips_mpgh.net.zip6 KB · 6 downloads Clean
Posts 1–2 of 2 · Page 1 of 1

Post a Reply

Similar Threads

Tags for this Thread

None

Talk with us