Ipsy’s documentation¶
Ipsy is a tool for applying IPS (International/Internal Patch System) files. They are typically used for distributing emulator ROM changes as distributing the patched ROM would violate copyright law. IPS has a rather strait forward format expalined below.
Using ipsy¶
You can run Ipsy without installing by passing it into python with the ‘-m’ flag from the root directory of the package.
python3 -m ipsy [Ipsy Arguments]
Install as you would any other python program. You shouldn’t use sudo to install Ipsy unless you have reviewed the source code.
pip3 install --user .
Ipsy has three major options: Patch, Diff, and Merge.
If you have to files ‘game.rom’ and ‘patch_for_game.ips’ you can patch the game.
ipsy patch ./game.rom ./patch_for_game.ips -o new_rom
This will generate a file named ‘new_rom’ or, if no output name is given, ‘game_patched.rom’. The ‘o’ flag always supersedes any name that might be assigned by Ipsy regardless of the operation.
If you want to generate a patch use...
ipsy diff ./game.rom ./edited_game.ips
This will generate a file named ‘patch.ips’
When diffing you can also enable Run Length Encoding (RLE)
ipsy diff ./game.rom ./edited_game.ips -rle
RLE finds groups of edits where the same value is written is succession and replaces them with the value and the number of times that value should be written.
Lastly, you can merge multiple IPS files using the merge option
ipsy merge patchOne patchTwo patchThree patchFour
Results in a single file named after the first patch.
Ipsy’s help output:
usage: ipsy [-h] {patch,diff,merge} ... [output]
Apply an IPS patch, Diff two files to generate a patch, or Merge multiple IPS
files.
positional arguments:
{patch,diff,merge} Options for Ipsy...
patch Apply a patch to an unpatched file.
diff Generate an IPS file by diffing the unpatched and
patched versions.
merge Combine several IPS files into one.
output Name for the new, patched, file.
optional arguments:
-h, --help show this help message and exit
Ipsy API¶
-
class
ipsy.
IpsRecord
[source]¶ Data container for one record of an IPS file.
Parameters: - offset – offset in first 3 bytes of the record, stored as int
- size – size in the next 2 bytes, stored as int
- rle_size – size in the next 2 bytes if previous was 0, stored as int
- data – bytes object of data with length ‘size’ or ‘rle_size’
-
compress
()[source]¶ Attempts to RLE compress the record into a single, smaller, record. Makes No attempt to spilt the record up into multiple.
Returns: self or compressed IpsRecord
-
exception
ipsy.
IpsyError
[source]¶ Logged by
ips_read()
when IPS corruption is found.
-
ipsy.
cleanup_records
(ips_records, path_dst)[source]¶ Removes useless records and cobines records when possible. This function creates and deletes two temp files in the calling directory.
Parameters: - ips_records – List of
IpsRecord
- path_dst – Path to file that these pathes are inteded to be used on.
Returns: List of
IpsRecord
, simplified where possible.- ips_records – List of
-
ipsy.
diff
(fhsrc, fhdst, fhpatch=None, rle=False)[source]¶ Diff two files, attempt RLE compression, and write the IPS patch to a file. Assumes both files are the same size.
Parameters: - fhsrc – File handler of orignal file
- fhdst – File handler of the patched file
- fhpatch – File handler for IPS file
- rle – True if RLE compression should be used
-
ipsy.
eof_check
(fhpatch)[source]¶ Reviews an IPS patch to insure it has only one EOF marker.
Parameters: fhpatch – File handler of IPS patch Returns: True if exactly one marker, else False
-
ipsy.
ips_read
(fhpatch, EOFcontinue=False)[source]¶ Read in an IPS file to a list of
IpsRecord
Parameters: - fhpatch – File handler for IPS patch
- EOFcontinue – Continue processing until the real EOF is found (last 3 bytes of file)
Returns: List of
IpsRecord
-
ipsy.
ips_write
(fhpatch, records)[source]¶ Writes out a list of
IpsRecord
to a fileParameters: - fhpatch – File handler of the new patch file
- records – List of
IpsRecord
-
ipsy.
merge
(fhpatch, *fhpatches, path_dst=None)[source]¶ Turns several IPS patches into one larger patch. The order that the patches are applied in is preserved. If the destination file is provided then further simplifications can be made.
Parameters: - fhpatch – File Handler for resulting IPS file
- fhpatches – list of File Handlers for IPS files to merge
- path_dst – Path to file that these pathes are inteded to be used on.
-
ipsy.
patch
(fhdest, fhpatch, EOFcontinue=False)[source]¶ Apply an IPS patch to a file. Destructive processes.
Parameters: - fhdest – File handler to-be-patched
- fhpatch – File handler of the patch
- EOFcontinue – Continue processing until the real EOF is found (last 3 bytes of file)
Returns: Number of records applied by the patch
-
ipsy.
patch_from_records
(fhdest, records)[source]¶ Apply an list of
IpsRecord
a file. Destructive processes.Parameters: - fhdest – File handler to-be-patched
- fhpatch – File handler of the patch
Returns: Number of records applied by the patch
-
ipsy.
rle_compress
(records)[source]¶ Attempt to RLE compress a collection of IPS records.
Parameters: records – List of IpsRecord
to compressReturns: RLE compressed list of IpsRecord
IPS File Format¶
IPS files consist of a header, a list of records, and a footer. All IPS files are big-endian. Due to fixed length of the offset value, IPS files can’t be used to patch a target larger that 2^24-1 bytes (16 MB). The minimum viable IPS file (1 record changing 1 byte of data) is 14 bytes in size. There are a handfull of concerns with the format. A literal ‘EOF’ marker is used that is intended to be found instead of an offset value, however if the offset value is RAM address 0x454f46 then we have a problem. Ipsy, and most differs, resolves this by checking the offset and, if needed, decriments it while increamenting the size of the record and pre-appending the previous byte’s value to data.
Section | Size in Bytes | Description |
---|---|---|
Header | 5 | String literal ‘PATCH’ (Hex: 50 41 54 43 48) |
Record(s) | See Below | Repeatable section that contains describes a change to be made |
Footer | 3 | String literal ‘EOF’ (Hex: 45 4f 46) |
Each record has the format ...
Section | Size in Bytes | Description |
---|---|---|
Offset | 3 | Zero index offset that the change should be applied at |
Size | 2 | Size of the ‘data’ section in bytes (See below if 0) |
Data | size | Data to be written |
There is one exception to the format of a record. Records with size 0 are Run Length Encoded (RLE). This means that the record represents a one byte value that is to be written many times starting at the offset. The format for an RLE Record is below.
Section | Size in Bytes | Description |
---|---|---|
Offset | 3 | Zero index offset that the change should be applied at |
Size | 2 | 0 (See above if not) |
RLE_Size | 2 | Number of times to repeat the below one-byte value |
Data | 1 | Value to be repeated |