Pointer tables and SNES rom expansion (AnusP)
==================================================
TABLE OF CONTENTS
===============================================
1- Introduction
2- How Text is Normally Stored
3- Headers
4- Pointer Tables
5- Moving Text in SNES Roms
6- Physical Rom Expansion
7- Expanding text past its bank
8- Conclusion
========================================
SECTION 1 INTRODUCTION
===================================================
1.0 General Stuff about this document
This document was created to help people who wish to learn about pointer tables and SNES rom expansion. This document may be very confusing but I tried to keep it as simple as possible! heh. I wrote in in one night! So I'll probably have to update it later to fix spelling errors, or make things easier to understand... but I'm nothing more than a lazy bastard, so my hacks come first! Anyways, if you do not know anything about rom-hacking, or you don't know much about it, then LEARN and get some experience! the only program you'll need to do the following is a hex editor. I recommend "Hex Workshop"! I also tried to write this document to be as universal as possible, so not every rom will be modified or expanded in the following ways. So if you have problems with this document, then you're either hacking an EVIL rom, or something I haven't seen yet. Or I might not have gotten my point accross.. Please, tell me how good this document is! I beg you!
1.1 History
version 1.2 (HTML) 03/25/98
Added stuff about moving text in high-roms. added information about expanding text past its bank. New E-Mail address! Check it out! I was damn lucky to get that domain name!
version 1.1 (HTML)
History section added! Colours changed around tables added for a more friendly environment added a link to Anus P's home page!
version 1.0 (HTML)
Ported to HTML by CataclysmX
version 1.0 (TEXT)
Info added into document about finding pointers and SNES rom expansion
==============================================
SECTION 2 HOW TEXT IS NORMALLY STORED
============================================
2.0 Variable Length Text
In rom's text is usually stored in clumps of text strings. For instance, one text string will appear after another, and are usually separated by a hex value which signifies the end of a string (not a line break!). Now the text in these clumps of text strings (I like to call them "text blocks") won't nessessarily have the text in the order that it appears in the game. In NES roms this is how all of the text is usually stored, with a "standard pointer table system". But in SNES roms, I have found other ways in which text is stored; Some times text will be stored in text blocks as mentioned above, but will include some coding in between each string instead of a standard hex to signify the end of a line. These text blocks usually don't have pointer tables and you should be able to change the lengths of individual strings without any problems! The only downfall to text stored like this is the fact that it makes it harder to dump text from it to get it translated. Examples of text that's stored like this are the Romancing Saga games.
2.1 Constant Length text
One other way that some text is sometimes stored in SNES roms is in a "constant length" format. This is usually how monster names, items, ect... are stored in RPGs. The Final Fantasy games store that kind of stuff in that format. What Constant Length text is; It's a bunch of text (items) that have a certain length and are not separated by hex values which signify the end of a string. They run right into one another... For example, the Items in Final Fantasy 4 are 9 characters long, and they follow one another like this:
Tent FenixDownPotion BlackSwrdIronArrow
as you can see there is no specific hex value separating them. Which means that you can use the extra space as you wish! Because if you look there are still 5 spaces to use for Tent, so you can make that name even longer of you wish! While this may be good news for some things, You wont be able to exceed the maximum amount of spaces used for each item. The only way to increase the amount of letters you can use for Constant Length text is to learn ASM!
2.2 Text that's just floating around
And finally, sometimes in SNES roms, there is just text floating around in the rom on its own, not in any specific "Text Blocks". The only way to expand these out is to move'em to somewhere in the rom where there is room to epand them out! (explained in more detail later on... section 5)
2.3 Something else you should know about the way SNES stores text.
Sometimes there are Text Blocks in the rom which do not use pointer tables. These can just simply be expanded out as you wish without any trouble just by moving the hex values which signify the end of a string. This is similar to the "RS" text blocks with the coding in between each string. just move the coding to fit text into rom. But one thing you should know is that there is sometimes text stored in the rom (in text blocks) that have to be expanded out in a similar mannar that the text floating around has to be. Basically this type of text is text just "floating around" in the rom, exept they follow one another like text would in a standard text block.
=======================================================
SECTION 3 HEADERS
============================================
3.0 NES Headers
The headers in NES roms are in the first line of an NES rom in a hex editor. the fist 16 bytes of the rom is its header. In other words the header covers "10" hex bytes. This is an important number to remember when editing pointer tables in NES roms.
the header usually looks something like this:
4E45 531A 1000 1300 0000 0000 0000 0000
You'll notice that the first three bytes are the ASCII standard for "NES"! Other info is stored here such as the mapper number, ect....
3.1 SNES Headers
The header in SNES roms is remarkably bigger than in NES roms. It covers 512 bytes. Or "200" hex bytes. This is also another important number to remember. Not as important for pointer tables though, but it is important for moving text blocks and stuff!
the header in an SNES rom looks similar to this:
8000000000000000AABB040000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
The information here is not really important.
But if you're rom does not have a header, then ADD ONE!
(This is sometime the cause of roms not working in some emulators)
To add a header to it, just copy a header from any SNES game that has one, and then "Insert" the copied header into the rom.
==========================================
SECTION 4 POINTER TABLES
=============================================
4.0 NES Pointer Tables
To find a pointer table for the text in your rom, it is nessessary that you know where the text is in the rom, what hex signifies the end of a line, and by knowing these you can then find the beginning of the text block. Once you find the beginning of the text block the pointer table should be IMMEDIATELY before the text! It will look like it has a pattern to it. Figuring out the pattern is mostly up to you, but here is pretty much how you figure out how it works. Take an address from the beginning of a line. (An example would be 012D63) and knock off the all extra letters except for the last 4 digits making it 2D63. Pointer tables store addresses in pairs so 2 hex values are needed... break up 2D63 into pairs and you got 2 hex values.. 2D, and 63! Now before you break up the hex values, you gotta subtract the header from the address. Since the NES header is 10 hex bytes, subtract 2D63 by 10 making it 2D53. Now break this up into pairs making it 2D 53, and then re-arrange it backwards! (thats how roms store addresses. so 2D 53, becomes 53 2D. This will tell you one of the hex values in each pointer in the pointer tables. You should be able to find 53 in there, but 2D might be something else.. So you're actually looking for 53xx (where xx is unknown). That's up to you to figure out!
an example of a pointer table followed by text in an NES rom:
019D30: 4D76 00F8 349D 3B9D 419D 449D 499D 4D9D
019D40: 529D 569D E5E3 F6CD 4ADA 70F8 F9FA FBF0
019D50: 704C FE70 4BDD CEF3 705C F64D 704C F6CF
019D60: CE70 F5DD CB70 4BF0 DDF1 F2DD F370 83B7
Explanation: Well, hex "70" signifies the end of a string so using that you find the begining of the text block which is at hex 019D44, and so everything before that is the pointer table! You'll notice that 9D repeats alot in the pointer table. Well, that is your unknown which you gotta figure out on your own!
4.1 SNES Pointer Tables
SNES pointer tables work in the same manner, except you don't subtract the header from it. But you gotta watch out, because sometimes you might get an evil rom in which you GOTTA subtract something, but thats very rare and easy to deal with once you get the hang of it! Now also keep in mind that sometimes text in SNES roms don't use a pointer table(Romancing SaGA). So if you can't find one before the text, then just experiment! Try changing the lengths of the strings by moving around the hex that signifies the end of a string and see if it works! But if it doesn't work, and there's no pointer table, then you're gonna have to learn how to move text within the rom! (explained in section 5)
================================
SECTION 5 MOVING TEXT IN SNES ROMS
==============================================
5.0 Moving Text Within Banks
Now this is more heavy stuff! You might have to do this if the text you have doesn't have a pointer table, and wont expand just by moving the hex to signify the end of a text string. You will also have to do this to expand text that's just floating around in the rom. Most of the time this text is moved within Banks. What's a bank? well... time for my explanation. A bank is a part of the rom in which info is stored. One bank in a low-rom is 32KB (8000 hex bytes) and in a high-rom its 64KB (10000 hex bytes). For instince, the adress $000200 to $0081FF is the first bank within a low-rom and $008200 to $0101FF is the 2nd bank, ect... And in a high-rom $000200 to $0101FF is the first bank and $020200 to $0301FF is the 2nd bank, ect... These addresses have the $200 header included if you notice! Oh, and did I forget to mention, the "$" before the address signifies that I'm talking about hex values! Anyways... Most of the time text that's just floating there, or whatever can only be moved within the bank that it's located in. To do this you gotta start converting the address into a pointer to that address you can find! First, lets take some text from FF4 that's on its own (not in any specific text block) at the address 09DEC6. Now subtract the header from this address ($200 in SNES roms, if the game has a header). So $09DEC6 - $200 = $09DCC6. Now, since you are just moving one small thing, chances are, it can only be moved within that bank! Which means that it has to be moved within $098000 to $09FFFF (without the header, ofcourse. also note that FF4 is a low-rom). Now the next step to do is to get rid of the 09 before the rest of the address, since jumps within banks are stored as 2 hex bytes. So 09DCC6 becomes DCC6. Now split it up and make it backwards so it becomes C6DC, and search for that number within the bank, and hopefully you'll find the pointer!
Another Note: The address you are searching for must always be between 8000 to FFFF in a low-rom, so if you're looking up a pointer for text at 0928B0, subtract the header, knock off the extra 09 (or whatever that may be), which makes it 26B0. Now before you split it up and make it backwards, you gotta att $8000 to it, to make the address between 8000 to FFFF, so $26B0 + $8000 becomes $A6B0. You then re-arrange it as B0A6 and search for that! But in a high-rom, the address can be anywhere between 0000 and FFFF since the banks are 64KB instead of 32KB.
5.1 Moving Large Text Blocks (and other stuff) To Different Banks!
Usually Large Blocks of text will have pointers that will anable you to move text to anywhere in the rom! This is where physical expansion comes in handy! (explained in section 6) What you will have to do is move text to a place where there is enough room to expand it out. First find the place where your text begins. (you'll have to move text and its pointer tables seperately... This may also apply for some text just floating in the rom, but they usally use pointers within banks to point to them.) Moving these is basically done in the same mannor as above, exept you gotta figure out what bank its in. The first thing to do is subtract the header, and this following chart shows the conversion between the actual address and the way the SNES will read the address.
this chart is for low-roms:
HEX Low-ROM SNES
000000-007FFF = 008000-00FFFF
008000-00FFFF = 018000-01FFFF
010000-017FFF = 028000-02FFFF
018000-01FFFF = 038000-03FFFF
020000-027FFF = 048000-04FFFF
028000-02FFFF = 058000-05FFFF
030000-037FFF = 068000-06FFFF
038000-03FFFF = 078000-07FFFF
040000-047FFF = 088000-08FFFF
048000-04FFFF = 098000-09FFFF
050000-057FFF = 0A8000-0AFFFF
058000-05FFFF = 0B8000-0BFFFF
060000-067FFF = 0C8000-0DFFFF
068000-06FFFF = 0D8000-0CFFFF
070000-077FFF = 0E8000-0EFFFF
078000-07FFFF = 0F8000-0FFFFF
080000-087FFF = 108000-10FFFF
088000-08FFFF = 118000-11FFFF
090000-097FFF = 128000-12FFFF
098000-09FFFF = 138000-13FFFF
0A0000-0A7FFF = 148000-14FFFF
0A8000-0AFFFF = 158000-15FFFF
0B0000-0B7FFF = 168000-16FFFF
0B8000-0BFFFF = 178000-17FFFF
0C0000-0C7FFF = 188000-18FFFF
0C8000-0CFFFF = 198000-19FFFF
0D0000-0D7FFF = 1A8000-1AFFFF
0D8000-0DFFFF = 1B8000-1BFFFF
0E0000-0E7FFF = 1C8000-1CFFFF
0E8000-0EFFFF = 1D8000-1DFFFF
0F0000-0F7FFF = 1E8000-1EFFFF
0F8000-0FFFFF = 1F8000-1FFFFF
100000-107FFF = 208000-20FFFF
108000-10FFFF = 218000-21FFFF
110000-117FFF = 228000-22FFFF
118000-11FFFF = 238000-23FFFF
120000-127FFF = 248000-24FFFF
128000-12FFFF = 258000-25FFFF
130000-137FFF = 268000-26FFFF
138000-13FFFF = 278000-27FFFF
140000-147FFF = 288000-28FFFF
148000-14FFFF = 298000-29FFFF
150000-157FFF = 2A8000-2AFFFF
158000-15FFFF = 2B8000-2BFFFF
160000-167FFF = 2C8000-2CFFFF
168000-16FFFF = 2D8000-2DFFFF
170000-177FFF = 2E8000-2EFFFF
178000-17FFFF = 2F8000-2FFFFF
180000-187FFF = 308000-30FFFF
I'm sure you see the pattern now. So lets say you have a text block located at $080600. Subtract the header, so it becomes $080400, and then look it up on the chart and find its new address. so $080400 is actually $108400. Now split it up into pairs -> 10 84 00, and re-arrange them backwards -> 008410. Now you'll have to search for that, and remember, it might appear more than once in the rom, so the best thing to do change all of these addresses that appear in the rom to the new one.
A little hint: These kind of just USUALLY have "BF" before them, so 008410, might look like BF008410.
Now for high-roms since the banks are in 64KB blocks the way the snes addresses them is different. They don't have to be between the 8000-FFFF crap! It just adds up like a normal adress exept it starts at C00000. So really, all you gotta do is add $C00000 to the hex address to get the High-rom SNES address. For example, take hex $1AE380, subtract the header so it becomes $1AE180 and now add $C00000 so it becomes $DAE180. And there you go! You got yourself your pointer!
===================================================
SECTION 6 PHYSICAL SNES ROM EXPANSION
===============================================
Now for the easy part! In SNES roms, all you gotta do to make the rom bigger, is just add more space to it! Of course the space that you add HAS to be a multiple of 32KB, or $8000 hex bytes, or 1 bank.
But the roms are usually more stable if you keep them at a muliple of 4mbits.
below is a small chart which shows stable file sizes to keep your rom at:
(the following hex addresses include the $200 header)
MBITs HEX BYTES ~ SIZE
4mbit $080200 0.5MB
8mbit $100200 1.0MB
12mbit $180200 1.5MB
16mbit $200200 2.0MB
20mbit $280200 2.5MB
24mbit $300200 3.0MB
yada yada yada... I'm sure you see the pattern...
well, that about does it! Don't ask me how to expand NES roms, because I don't know how, and from what I hear it's next to impossible!
================================================
SECTION 7 EXPANDING TEXT PAST ITS BANK
=============================================
What do I have to say about this? Well, unless you're prepared to do some ASM modifications, don't expand text past the bank that its in. What I mean by this is, if you have a low rom and you're expanding text, you can't have it exceed the 32KB that's allocated for the bank, or 64KB for high-roms. What tends to happen is the text that runs spills into the next bank tends not to be displayed on the screen. Now I'm not sure that this is the case with some roms, but I'm suspecting that it is because this has happened to us in our FF4 translation. Now there is another way to get around the problem, and that's to limit the player to using ZSNES 2.95 because of a bug in it! But I would not recommend using that as an alternative, because not everyone will want to use that old version of ZSNES.
===============================================
SECTION 8 CONCLUSION
====================================
Wow! I wrote all this in one night!?! Yep! Now I'm damn tired! Anyways, Most of the stuff in this document covers stuff on SNES roms, so if you're having trouble with NES roms, please don't contact me, because I probably won't know about it! I only know how to find NES pointer tables, and that's it! But SNES is a different story! I am constantly learning more and more, and so someday this document will probably be even better! But for now, it remains as it is.
I would like to thank the following people:
P-Funk: For helping me out with moving stuff within a rom
Dark Force: For helping me out with some technical stuff as well
Necrosaro: For creating his program to save time on adding up all of FF4's pointers!! (that would have taken a loooong time without him!)
CataclysmX: For originally porting this document into HTML and other stuff!
Any questions/comments? Send away to AnusP!
Any HTML comments or flames Send away to CaTaclysmX!
***Copyright Issues***
Anyone who distributes this file must not modify it in any way, without prior consent from the author. They must also have a link to J2E translations and Anus P's home page at the top or bottom of the page. Thank you for your cooperation.
J2E Translations! (http://www.members.aol.com/j2etrans/index.html)
Anus P. (C) 1998