Author Topic: Guide to Getting Started with FF7 Engineering  (Read 4472 times)

FF7ExpNeg1

  • *
  • Posts: 16
    • View Profile
Guide to Getting Started with FF7 Engineering
« on: 2005-10-08 13:38:17 »
FF7 Engineering Part I

On the first PSX FF7 disc there is a file in the root drive named SYSTEM.CNF (the .CNF might not be visible due to Windows).  This is just a plain text file that we can open in Notepad.  The first line contains the following:
Code: [Select]
BOOT = cdrom:\SCUS_941.63;1

This tells us that the PlayStation will launch the file SCUS_941.63 on boot.  If we look on the root of the disc we can see this file.

SCUS_941.63 happens to be a binary file that we can't really look at using Notepad.  We will write a simple Java program to display the binary data in a more readable form.  This code does just that:
Code: [Select]

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class HexDump {
public static void main(String[] args) {
if (args.length < 3) {
System.out.print("USAGE: java HexDump [filename] [start hex address] [num lines]");
System.exit(1);
}
File f = new File(args[0]);
if (!f.exists()) {
System.out.println("ERROR: File not found!");
System.exit(1);
}
RandomAccessFile in = null;
try {
in = new RandomAccessFile(f, "r");
} catch (FileNotFoundException e) {e.printStackTrace(); System.exit(1);}
long startAddress = 0;
int numLines = 0;
try {
startAddress = Long.parseLong(args[1], 16); //base 16, hexadecimal
numLines = Integer.parseInt(args[2]);
} catch (NumberFormatException e) {e.printStackTrace(); System.exit(1);}
try {
in.seek(startAddress);
} catch (IOException e) {e.printStackTrace(); System.exit(1);}
byte[] buf = new byte[16]; //one line of hexadecimal values
try {
for (int i = 0; i < numLines; i++) {
in.read(buf);
System.out.printf("0x%08X  ", startAddress);
StringBuilder ascii = new StringBuilder();
for (int j = 0; j < 16; j++) {
System.out.printf("%02X ", buf[j]);
if (buf[j] < 32 || buf[j] > 127)
ascii.append('.');
else
ascii.append((char)buf[j]);
}
System.out.print(" ");
System.out.println(ascii.toString());
startAddress += 16;
}
in.read(buf);
} catch (IOException e) {e.printStackTrace(); System.exit(1);}
try {
in.close();
} catch (IOException e) {e.printStackTrace(); System.exit(1);}
}
}


The program above can be compiled on the command line:
Code: [Select]
javac HexDump.java

Now make sure the command prompt's current working directory contains the SCUS_941.63 file.  We can now run the above program and look at the first few bytes of the boot file:

Code: [Select]
java HexDump SCUS_941.63 0 10

This produces the following:
Code: [Select]

0x00000000  50 53 2D 58 20 45 58 45 00 00 00 00 00 00 00 00  PS-X EXE........
0x00000010  C0 10 01 80 00 00 00 00 00 00 01 80 00 08 06 00  ................
0x00000020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x00000030  F0 FF 1F 80 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x00000040  00 00 00 00 00 00 00 00 00 00 00 00 53 6F 6E 79  ............Sony
0x00000050  20 43 6F 6D 70 75 74 65 72 20 45 6E 74 65 72 74   Computer Entert
0x00000060  61 69 6E 6D 65 6E 74 20 49 6E 63 2E 20 66 6F 72  ainment Inc. for
0x00000070  20 4E 6F 72 74 68 20 41 6D 65 72 69 63 61 20 61   North America a
0x00000080  72 65 61 00 00 00 00 00 00 00 00 00 00 00 00 00  rea.............
0x00000090  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................


We can definitely see that this is a PlayStation executable by looking at the first line.  Lets now look at three important values on the second line (address 0x00000010).

The last four bytes are 00 08 06 00. All four byte integer values in this file are in little-endian format so to get a better look at a particular integer we need to byteswap it.  To do this we make the first byte the last, the second the third, the third the second, and the last the first.  Byteswapping the current four bytes we are looking at produces 00 06 08 00 or just 0x60800.  If we convert this to a decimal value we get the value 395,264.  

Hmmm, this is quite interesting.  Why?  Take a look at the size of SCUS_941.63: 397,312.  This is exactly 2048 byte (2k) difference.  We can make the safe assumption that a PlayStation executable header is 2k in length.  We can then reason that the PlayStation loads the data starting at offset 2k and with a length of 0x60800.  This would take us to the end of the file.  But where does it load the data to?

To answer this question let us look at the second to last four bytes on line two: 00 00 01 80.  If we byteswap these we get 0x80010000, which is a valid memory location on the PlayStation.

Once the the data is loaded in memory, execution must begin at some address.  For this we turn to the first four bytes of line two (byteswapped of course): 0x800110C0.  This is where execution of Final Fantasy 7 begins (well, not really, there is a ton of startup and initialization code).

This concludes part I.  In the next installment we will go through dis-assembling the first few MIPS opcodes. Feedback appreciated!

dziugo

  • *
  • Posts: 1470
    • View Profile
    • A new copy of FF7 thanks to Salk. Pack (zip/rar/etc) your saved game before sending it to me.
Guide to Getting Started with FF7 Engineering
« Reply #1 on: 2005-10-08 13:51:34 »
The header looks like "our" PE-file's image-section combined with PE-header. You have the image size, image base, program entry point, and (probably) last initialised byte.

Can you check (somehow...) which part of this file is loaded into memory? Because header may not be exactly 2k in size...

dziugo

FF7ExpNeg1

  • *
  • Posts: 16
    • View Profile
Guide to Getting Started with FF7 Engineering
« Reply #2 on: 2005-10-08 14:01:00 »
For this particular file I am pretty sure it is from 0x800 (2k) to the end of the file.  The reason I say this is because most of the data between what I displayed above and 0x800 is zero and looking at the data beginning at 0x800 I see the following:

Code: [Select]

0x00000800  D4 15 01 80 9C 15 01 80 AC 15 01 80 BC 15 01 80  ................
0x00000810  CC 15 01 80 62 61 74 74 6C 65 2E 78 00 00 00 00  ....battle.x....
0x00000820  00 00 00 00 E0 01 D8 01 7C 1E 01 80 8C 1E 01 80  ........|.......
0x00000830  9C 21 01 80 8C 1E 01 80 28 22 01 80 08 25 01 80  .!......("...%..
0x00000840  4C 25 01 80 90 25 01 80 18 26 01 80 BC 26 01 80  L%...%...&...&..
0x00000850  5C 26 01 80 88 24 01 80 A0 23 01 80 D4 25 01 80  \&...$...#...%..
0x00000860  38 27 01 80 0C 22 01 80 BC 22 01 80 D4 22 01 80  8'..."..."..."..
0x00000870  FC 22 01 80 14 23 01 80 84 23 01 80 84 23 01 80  ."...#...#...#..
0x00000880  84 23 01 80 84 23 01 80 4C 23 01 80 84 23 01 80  .#...#..L#...#..
0x00000890  84 23 01 80 84 23 01 80 5C 23 01 80 74 23 01 80  .#...#..\#..t#..


A string 'battle.x' and a bunch of memory addresses. Thanks for the input!

dziugo

  • *
  • Posts: 1470
    • View Profile
    • A new copy of FF7 thanks to Salk. Pack (zip/rar/etc) your saved game before sending it to me.
Re: Guide to Getting Started with FF7 Engineering
« Reply #3 on: 2005-10-08 14:13:59 »
Ah yes... You have made a typo when converting 0x60800 so it wasn't 2048 to me (now it is :)). Sorry.

FF7ExpNeg1

  • *
  • Posts: 16
    • View Profile
Guide to Getting Started with FF7 Engineering
« Reply #4 on: 2005-10-08 14:17:11 »
Good catch, I switched the 2 and the 6, now fixed. Thanks!