Scripts
Required:
Files:
- Windhex
- Final Fantasy III (US, snes) rom.
- Final Fantasy III main tablefile.
- UHR - script-dumper/insertor
- MSI-install for Windows (optional)
- Snes emulator of your choice. Should support savestates (Snes9x does this).
In this tutorial:
- Scripts - introduction
- Setting up UHR
- Dumping scripts
- Edit scripts
- Inserting scripts
- Closing comments
Scripts - introduction
If you've visited romhacking-related messageboards or other communities, then you have probably heard of the term "script" by now.
A script, is simply a text-document, containing text (usually dialogue, hence the name "script") from a certain game.
Extracting such a script from a rom is called "dumping" a script. Inserting it back again is called...well..."inserting" a script. Easy stuff.
The reason why people bother to do this, is because scripts prove to be a very convenient method to alter a games text. Since the program (that does the inserting) has access to the entire script at once, it can optimize the number of DTE-combinations used. If the program also supports pointers, it will also enable you to freely rearrange the space every given block of text inside the script has.
Also, scripts require very little space to store, and little to no knowledge to edit (not every script, though). Suppose you wanted to translate a japanese Playstation ISO (ISO is the CD-equivalent of a rom). You don't know any japanese yourself, but you have a friend who does. Would you send him/her an entire rom (500Mb+) and ask him to hack it open and translate via Windhex? No you wouldn't.
Instead, you could locate the text, and dump it into a simple .txt-document. You could then send it to your friend, who would be able to edit it just like any other document. He could then send the translated script back to you, and you would later insert it back into the ISO. Or the rom, depending on what you're working with.
These are the benefits of working with scripts.
Now, before implying that this chapter contains everything you need to know about scripts, let me first say that the structure and layout of a script may vary depending on which program you use to dump/insert it, and what kind of game/platform you're working with.
Even so, scripts come in two basic shapes. Formatted and unformatted. Most (if not all) games use code inside/around the text in order to make the text display as it should (when you're playing, that is). These might be simple hexdecimals controlling basic behaviour of the text, like when to jump to a new line, or more advanced instructions. Either way, code such as this has to be included in the script as well, otherwise it won't automatically start working when you insert it (and in most cases, you'll want to avoid making any custom changes after an insertion). Bottom line is, you should be able to insert a script, and then be done with it.
Note: Despite this, most scripts tends to need some tweaking in the code (after being edited) before they're ready to be inserted. But this tweaking should be made before the insertion, not after.
We'll continue talking about some basic script formatting later in this chapter. Here's a short summary:
- A script is a simple text-file, that can be edited just like any other file.
- Dumping a script = extracting text from a rom into a text-file.
- Inserting a script = the exact opposite of dumping it.
- Formatting is when you edit the contents of a script, to make sure that the game functions as it should when you're done inserting it.
- Several tools to dump/insert exists.
Setting up UHR
Disclaimer
There's a great deal of dumpers/inserters on the web, take your pick. One example of this would be Atlas, a very flexible and versatile tool. On the other hand, it is also controlled completely through the Windows command-line (meaning it lacks a userfriendly GUI), which along with some complexity when it comes to formatting makes it less than ideal for a complete (and hopeless, I might add) newbie.
Because of that, we will stick with the French tickler UHR (stands for Ultimate Hacking Rom), which you can download from this page (see links at the top of the chapter, or the Misc-section in the tools menu). It can do both dumping and inserting, and supports tablefiles.
This program is entirely in French, so this chapter will use detailed descriptions on how to make use of the features we're after. Since it's French though, I won't go into details of the program, since I'm no expert on the language myself. But for now, think of:
"Annuler" = Close
"Enregistrer" = Confirm and close
"Appliquer" = Confirm only
Now then, start by creating a separate folder somewhere on your hard-drive, and name it "FF3". this is where we will put our roms, scripts and tablefiles. Inside the folder, create the folders "BU_rom", "tablefile", "scripts_dump", "scripts_insert", "DUM_rom, and "INS_rom". Like this:
FF3 (main folder)
-BU_rom (put a backup of your FF3 rom here)
-tablefile (store your tablefile here)
-scripts_dump (dumped scripts go here)
-script_insert (edited scripts go here before inserting them)
-INS_rom (the rom we insert scripts into will be placed here)
-DUM_rom (the rom you're dumping scripts from goes here)
The reason for this isn't because there's a dire need to organize your material, but you should always keep track of which files goes where, in case you decide to return to an old project.
Take a copy of FF3 (non-hacked rom) and put it into 'BU_rom'. Take your main tablefile (the one with DTE) and put it in 'tablefile' (download it from this page if you haven't gotten it yet). Take another copy of FF3 and put it into 'INS_rom' and 'DUM_rom'. Now it's time to duke it out with UHR.
First of all, you should install UHR (Dev-version 4, it's incomplete and discontinued as far as I know) onto your computer. If the program won't run (after installation), also try to install Windows MSI (also available from this page). If it still won't run, you'll have to look up the issue yourself. It does, however, run just fine on XP (not sure about Vista).
Once you have installed it and fired it up, you should be greeted by a "tip-of-the-day" function.
Click "Precedent" and "Suivant" to scroll through the 21 tips that you have available, or just "Annuler" to close the window. Make sure that "Afficher au démarage" isn't checked if you don't want to have this window appear in the future.
Before we move onto Dumping scripts, we'll set up a few basic options inside UHR. In the top-menu, go to "Outils > Options", or simply click "F7". In the following window, click "Répertoire" in the left menu:
This is where you specify the default maps that UHR will direct you to whenever you decide load/save a file. First, highlight "Roms" and click on "Choisir le Répertoire" (found above). Now browse your way to your FF3-folder (the main folder) and click OK. Do the same with "Textes". After that, do the same with "Table de Jeu", but browse all the way into your folder called 'tablefile'.
Now you're done, so click on "Enregistrer" at the bottom of the window. As you've probably guessed by now, you have defined your folder as the starting position of choice when it comes to loading roms, scripts and tablefiles.
Next, divert your attention to the menu on your left ("Principal"):
There's a bunch of links here too, but we will only use the top one, "Extraction/Insertion". This where the main dumping/inserting functions are controlled from. Click on the button, and you'll see a window asking you to load a rom. Browse your way into 'DUM_rom' and load the copy of FF3. When you click OK, you'll be promted to load a tablefile as well, so make sure that you load the main FF3 tablefile (since you specified 'tablefile' as a starting folder, you should be brought there at once).
When both files have been loaded, the following window will appear (never mind what is outside of it):
---

1) Insertion
16 bits (never mind). This is the button that prompts you to select a document for insertion. We'll go through this process later in this chapter.
2) Extraction
Same as 1), but extraction instead of insertion.
3) Profiles
Some games have profiles saved for them. If you load one of these, UHR automatically loads data needed to insert/extract text from whatever games profile you load. For some reason, UHR refuses to save any new profiles you might want to add (even though it asks you if you want to save a new profile every time you shut a rom down). This is probably some bug that was left unsolved.
Note: There's even a few profiles for FF3, but for now, we will input our own data. You're here to learn, after all.
4) Header corrections
UHR will automatically calculate pointers according to the format your rom is in. Here, you should specify how large the header of the rom is, if the rom has one.
5) High-rom or Low-rom
Err...just click "Detection Auto".
6) Adresse du Pointers
Specify the starting and ending offset of your pointer table (i.e the offsets of the first and last hexdecimal in the table).
"Debut" = Start
"Fin" = End
PS. The values that has been filled in from start comes from the profile that is automatically loaded. Ignore them.
7) Adresse du text
Same as with pointers, but with text this time.
"Debut" = Start
"Fin" = End
---
This window is the only one you'll need in order to dump/insert scripts. Keep it open, and move on to the next section.
Dumping Scripts
Before we actually dump our scripts, we need to gather some data first:
- Does the rom have a header?
- Which text do we want to dump?
- What is the starting offset for that text?
- What is the ending offset for the text?
- What is the starting offset for the texts pointers?
- What is the ending offset for the texts pointers?
We already know that our rom has a header of 200 bytes, since it's a Snes rom. To make things easy, we will attempt to dump the first part of dialogue from FF3 (the one we worked with in the Pointer-chapter). All we really need to do in order to find this text, is to search for it in Windhex, since we already have a tablefile ready. But for now, I'll tell you that it starts at offset 0D0200. In the last chapter, we also found that it had a pointer table located at offset 0CE804. Go the beginning of the pointer table and scroll down until you see the end of it.
As you can see, the end is located at 0CF44D (pointer EDFF). After that, the "count-up" restarts at 0C00, meaning we have the start of another pointer table.
Look at the last pointer in the table (EDFF), and calculate it "backwards":
- Break apart ( ED FF)
- Switch places (FFED)
- Add 200 for the header (FFED+200=101ED)
- Take the last 4 digits (01ED)
Ok, so we now know that the last pointer in the table points at 0X01ED (X is unknown). It couldn't be "0D", since that would be the beginning of the first textbank (our starting offset is 0D0200, remember). But what about "0E"? Scroll down to what we now believe is the end of the text, at offset "0E01ED".

Coincidentally, the beginning of a string of text can be found here (the DTE "Th" in "The Empire..."). Now, look at the offset of the next line ("3 cheers for the..."). It's offset is "0E020C". Do the snes-pointer-math, and you'll end up with "0C00", the very same pointer that we found at the beginning of the next pointer table.
With this evidence, it's now clear that "The Empire..." is the last line of text in the first text-bank. Look at the last offset in that line, and you'll see that it's 0E020B. Actually, this is the offset of the hexdecimal that tells the rom to stop reading the text (displayed by a '/' in Windhex), but since we want to include it in the script as well, we'll use its offset.
Now we know that:
- The pointer is 200 byte
- We're dumping the first text-bank in FF3
- Text starts at 0D0200
- Text ends at 0E020B
- Pointers starts at 0CE804
- Pointers end at 0CF44D
Create a new document in your main FF3 folder (name it FF3_info.txt or something). In this document, write down the following info:
FF3 text-bank 1
Header 200
Text: 0D0200 -> 0E020B
Pointers: 0CE804 -> 0CF44D
Since you need the EXACT same data to insert the script again, it's very important that you store your found information somewhere. 'Cuz you don't want to check all of this up once more, every time you decide to insert an updated version of text-bank 1's script. Keep it organized, that's all.
The dumping starts now. Return to your UHR-window and type in the information you just found, like this:

Then hit the "Extract-16bits" button (check the first image in case you've forgotten where it's at). Now UHR asks you to save the script, so browse into 'script_dump' and save it as "FF3_dialogue_01". For compatibilities sake, also change the file format from 'UHR (UHT)' to '.TXT'.
UHR will also ask if you want to open the script inside UHR, but we won't so simply close that window.
Edit scripts
Open your 'script_dump' folder (outside of UHR) and open FF3_dialogue_02.txt. If you did everything right up until now, it should look somethink like this (draft of the files beginning):
<PT0001>
VICKS: There's the town_<16><18><12><13>WEDGE: Hard to believe an Esper's been found intact there, 1000 years after the War of the Magi_<16><18><12><FIN>
<PT0002>
VICKS: Think it's still alive?
WEDGE: Probably_
_judging from the urgency of our orders. <16><18><12><FIN>
<PT0003>
VICKS: And this woman, this_sorcerer. Why's she here?<16><18><12><13>I heard she fried 50 of our Magitek Armored soldiers in under 3 minutes.<16><18><12><FIN>
<PT0004>
WEDGE: Not to worry. The Slave Crown on her head robs her of all conscious thought. She'll follow orders.<16><18><12><FIN>
<PT0005>
WEDGE: We'll approach from the east.
Move out!<16>MOG<12><FIN>
Let's start by explaining what we can see, and what we can't see.
---
- <PT0001>
This is a pointer. UHR will automatically redirect the first pointer in the pointer table (the one starting at 0CE804) to wherever this line of code is put within the script. Suppose you want to point it to the same location as the second pointer. All you have to do is:
VICKS: There's the town_<16><18><12>
WEDGE: Hard to believe an Esper's been found intact there, 1000 years after the War of the Magi_<16><18><12><FIN>
<PT0001><PT0002>
VICKS: Think it's still alive?
WEDGE: Probably_
_judging from the urgency of our orders. <16><18><12><FIN>
- <16><18><12>
These are hexdecimals. All the hexdecimals within the text that doesn't have a character defined (via the tablefile) will look like this. In other words, <16> is the hexdecimal "16". Since our tablefile is complete, they're most likely parts of code specifying what should happen next, once "VICKS: There's the town_" has been displayed on screen.
- _
Notice the '_' at the end of "VICKS: There's the town_"? The tablefile you're currently using has specified '_' as the character of choice instead of '...' (three dots). Whenever '_' turns up in the script, the actual game reads '...'. (Just to avoid some confusion.)
- *New line*
I'm sure you can see that some parts of the dialogue has a new line inserted, like:
WEDGE: Probably_*New line here*
_judging from the urgency of our orders. <16><18><12><FIN>
Check the tablefile:
*01
Typing the hexdecimal like this (instead of '01=*') is a means of telling the program that every time the hexdecimal <01> occurs, the script should start a new line. And that is pretty correct, seeing as FF3 uses <01> to do just that. If the tablefile hadn't been formatted like this, the above mentioned line would have read:
WEDGE: Probably_*_judging from the urgency of our orders. <16><18><12><FIN>
This way, the script will look clean, instead of being clustered with code. People who aren't familiar with hex, can now insert <01> simple by pressing 'Return' on the keyboard and make a new line.
- <16>MOG<12>
Notice the 'MOG'? Check the tablefile, and you'll see that '0C=MOG'. In other words, wherever you input <0C> into the script, the game will display the name 'MOG' (or whatever you decide to name him to). Likewise, wherever you insert 'MOG', UHR will insert <0C> upon insertion.
But, this also means that every hexdecimal 0C will be represented by 'MOG' in the script, even if the code (like the one above) isn't a part of an actual text.
- <FIN>
/00
The hexdecimal '00' is used in FF3 to tell the rom where to stop reading text (if it didn't, the game would keep reading until it reached the end of the text-bank. Pointers only decide where to start reading, remember?).
By typing '/00' into the tablefile, UHR will display <FIN> wherever a text ends, and insert a new line to make look clean.
---
Let's try some editing. Take the first paragraph of the script and type something else. Make sure that the text you write is considerably shorter than the original (you are NOT required to do this when editing scripts, but we'll do it for the sake of examples later on).
<PT0001>
VICKS: There's the town_<16><18><12><13>WEDGE: Hard to believe an Esper's been found intact there, 1000 years after the War of the Magi_<16><18><12><FIN>
Into:
<PT0001>
VICKS: Nice town_<16><18><12><13>WEDGE: Time for shopping, me thinks_ <16><18><12><FIN>
Save your edited copy into 'script_insert', and you're done. It isn't harder than this to edit a script, regardless of if you know romhacking or not.
Before you move on, scroll down to the bottom of the script:
<PT1573>
The Empire strings up anyone<PT0001>
who opposes it.<FIN>
You'll see that pointer 0001 somehow has ended up here as well. This because the last string of text spills over to text-bank 2. Even though Pointers can't point across different banks, the text can still spill between them, and since "who opposes it." starts at offset "0E[0200]", UHR believes that the roms first pointer (0000) points here as well.
To remedy this issue before moving on, simply delete <PT0001> from this text.
Inserting a script
This will be brief, really brief.
In the top menu of UHR, go to "Fichier > Ouvrir une rom". UHR will ask you if you want to close your current FF3 rom. Click NO, since you want to keep the settings you've entered so far.
Open the rom you've placed in 'INS_rom'.
Then, click the "Insert16-bit" button in UHR, and select the edited script that you saved in 'script_insert'.
By doing this, the script will automatically be inserted, and the rom will be saved. Open it up in Snes9x, and play until you reach the first dialogue in the game, with WEDGE, VICKS and TERRA on the cliff. It would seem that Wedgie and Vickie need to go over the mission briefing once more:

Closing comments
For the record, Final fantasy 3 is a prime example when it comes to script dumping/inserting. Not every game stores all of its pointers in a tidy pointer table, right above the text (which, conveniently, also is stored together).
In case a game has several blocks of text, with several pointer tables, you will most likely have to dump several scripts in order not to mess the rom up. If the script somehow looks messed up, it might be because you have inserted the wrong starting/ending point for the text or the pointers.
Also, UHR only supports some of the basic formats (Nes, Snes, Gameboy, Gameboy Color, N64 and Genesis/Megadrive). If you want to dump scripts from any other file, you'll have to use another program. Note that the "header+-" in UHR also can be used to other calculations needed to obtain the right pointer values for some games.
Finally, if you look at the beginning of FF3's dialogue in Windhex, you'll see that even though the text we inserted above were shorter then the original, there's no blank space between it and the next. That's because UHR simply inserts the rest of the text immediately after, and recalculates all the remaining pointers to make up for the lost space. Instead, the empty space is moved to the end of the first text-bank, and can be filled up in case you decide to insert a longer version of the script later on. This is how pointers are best used, as a script that doesn't support pointers will need a lot of tweaking until it can be inserted correctly.