Chrono Trigger Music Library rom hacking readme v1.0 (by yawnmoth) you will want to disable Word Wrap in Notepad (if that's what you're using) before viewing this... To begin, you must first understand how the fonts are stored. In this game, they are stored as 2bpp 8x12 fonts. That means that they support four colors. Each character within the font is 24 bytes long. The first 12 bytes represent the first two colors, and the second 12 bytes represent the last two colors. Each byte represents one row of 8 pixels, which is the width of most SNES fonts. Since one byte is equal to 8 bits, and one byte is equal to 8 horoz pixels, it should stand to reason that the fonts store the color by bit. This is why each font character is 24 bytes - because they have to do each bitplane twice, since each bitplane has only two possible values, 0 and 1. Other games may store fonts differently. To find out what format they store the fonts in, use tmod2. From there you can look in FDwR's document entitled "SNES and GB sprite storage formats" to figure out how the fonts work. The program I've included with this should be fairly easy to modify per the font style... So, the next step is to create our own set of fonts. These will be 4x12. We are going to fit two of these hacked fonts into one of the normal SNES fonts. The purpose behind this is so we can do a relative search later on to see where the the text strings begin. The C++ program should better explain how they work. If you are working with some font other than a 2bpp 8x12 font, then you'll have to make your own program to do something like this... If you can't make out the fonts, this might give a better example (this is only for the first two colors): _0__ 00__ 000_ 000_ 0_0_ 000_ 000_ 000_ _0__ 000_ _0__ 00__ _00_ 00__ 000_ 000_ 0_0_ _0__ __0_ __0_ 0_0_ 0___ 0___ __0_ 0_0_ 0_0_ 0_0_ 0_0_ 0___ 0_0_ 0___ 0___ 0_0_ _0__ __0_ __0_ 0_0_ 0___ 0___ __0_ 0_0_ 0_0_ 0_0_ 0_0_ 0___ 0_0_ 0___ 0___ 0_0_ _0__ __0_ __0_ 0_0_ 0___ 0___ __0_ 0_0_ 0_0_ 0_0_ 0_0_ 0___ 0_0_ 0___ 0___ 0_0_ _0__ __0_ __0_ 0_0_ 0___ 0___ __0_ 0_0_ 0_0_ 0_0_ 0_0_ 0___ 0_0_ 0___ 0___ 0-0_ _0__ 000_ 000_ 000_ 000_ 000_ _0__ _0__ 000_ 000_ 00__ 0___ 0_0_ 000_ 000_ 0_0_ _0__ 0___ __0_ __0_ __0_ 0_0_ _0__ 0_0_ __0_ 0_0_ 0_0_ 0___ 0_0_ 0___ 0___ 0_0_ _0__ 0___ __0_ __0_ __0_ 0_0_ _0__ 0_0_ __0_ 0_0_ 0_0_ 0___ 0_0_ 0___ 0___ 0_0_ _0__ 0___ __0_ __0_ __0_ 0_0_ _0__ 0_0_ __0_ 0_0_ 0_0_ 0___ 0_0_ 0___ 0___ 0_0_ _0__ 0___ __0_ __0_ __0_ 0_0_ _0__ 0_0_ __0_ 0_0_ 0_0_ 0___ 0_0_ 0___ 0___ 0_0_ _0__ 0___ __0_ __0_ __0_ 0_0_ _0__ 0_0_ __0_ 0_0_ 0_0_ 0___ 0_0_ 0___ 0___ _0__ 000_ 000_ 000_ __0_ 000_ 000_ _0__ _0__ 000_ 0_0_ 00__ _00_ 00__ 000_ 0___ However, when hacking the rom, the fonts have to be "mirror image'd", so... (the first two colors): 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: A: B: C: D: E: F: __0_ __00 _000 _000 _0_0 _000 _000 _000 __0_ _000 __0_ __00 _00_ __00 _000 _000 _0_0 __0_ _0__ _0__ _0_0 ___0 ___0 _0__ _0_0 _0_0 _0_0 _0_0 ___0 _0_0 ___0 ___0 _0_0 __0_ _0__ _0__ _0_0 ___0 ___0 _0__ _0_0 _0_0 _0_0 _0_0 ___0 _0_0 ___0 ___0 _0_0 __0_ _0__ _0__ _0_0 ___0 ___0 _0__ _0_0 _0_0 _0_0 _0_0 ___0 _0_0 ___0 ___0 _0_0 __0_ _0__ _0__ _0_0 ___0 ___0 _0__ _0_0 _0_0 _0_0 _0_0 ___0 _0_0 ___0 ___0 _0-0 __0_ _000 _000 _000 _000 _000 _0__ __0_ _000 _000 __00 ___0 _0_0 _000 _000 _0_0 __0_ ___0 _0__ _0__ _0__ _0_0 __0_ _0_0 _0__ _0_0 _0_0 ___0 _0_0 ___0 ___0 _0_0 __0_ ___0 _0__ _0__ _0__ _0_0 __0_ _0_0 _0__ _0_0 _0_0 ___0 _0_0 ___0 ___0 _0_0 __0_ ___0 _0__ _0__ _0__ _0_0 __0_ _0_0 _0__ _0_0 _0_0 ___0 _0_0 ___0 ___0 _0_0 __0_ ___0 _0__ _0__ _0__ _0_0 __0_ _0_0 _0__ _0_0 _0_0 ___0 _0_0 ___0 ___0 _0_0 __0_ ___0 _0__ _0__ _0__ _0_0 __0_ _0_0 _0__ _0_0 _0_0 ___0 _0_0 ___0 ___0 __0_ _000 _000 _000 _0__ _000 _000 __0_ __0_ _000 _0_0 __00 _00_ __00 _000 ___0 (the last two colors): 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: A: B: C: D: E: F: ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ In the program, the _'s are replaced with 0's, and the 0's are replaced with 1's. Here's the code: --- c++ source code for font generation --- #include #include // The hacked 4x12 fonts... // 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: A: B: C: D: E: F: char nums[12][64]={{0,0,1,0,0,0,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0,0,0,1,1,0,1,1,0,0,0,1,1,0,1,1,1,0,1,1,1}, //01 {0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1}, //02 {0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1}, //03 {0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1}, //04 {0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1}, //05 {0,1,0,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,0,0,0,1,0,0,1,1,1,0,1,1,1,0,0,1,1,0,0,0,1,0,1,0,1,0,1,1,1,0,1,1,1}, //06 {0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,1,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1}, //07 {0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,1,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1}, //08 {0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,1,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1}, //09 {0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,1,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1}, //10 {0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,1,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1}, //11 {0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,0,0,1,1,1,0,1,1,1,0,0,1,0,0,0,1,0,0,1,1,1,0,1,0,1,0,0,1,1,0,1,1,0,0,0,1,1,0,1,1,1,0,0,0,1}}; //12 char bin2dec(char a, char b, char c, char d, char e, char f, char g, char h) //convert two four bit segments (nibbles) to one byte { char temp=0, count, bin[8]={a,b,c,d,e,f,g,h}; for (count=0;count<8;count++) temp+=bin[count]*pow(2,count); return temp; } int main() { char cmax, imax, lmax; ofstream out_file; out_file.open("newfont.dat"); for (cmax=0;cmax<16;cmax++) for (imax=0;imax<16;imax++) { for (lmax=0;lmax<12;lmax++) //for the first two colors. out_file << bin2dec(nums[lmax][imax*4],nums[lmax][imax*4+1],nums[lmax][imax*4+2],nums[lmax][imax*4+3],nums[lmax][cmax*4],nums[lmax][cmax*4+1],nums[lmax][cmax*4+2],nums[lmax][cmax*4+3]); for (lmax=0;lmax<12;lmax++) //for the last two colors -- we really don't care about these right now... out_file << bin2dec(0,0,0,0,0,0,0,0); } out_file.close(); return 0; } --- end c++ source code --- Now... using tmod2, we see that the font begins 0x009000h . Open up Hex Workshop, and from Hex Workshop, open up newfont.dat, and the name of your rom, and paste the contents to the Chrono Trigger Music Library rom, for a length of 0x1800. The length of 0x1800 means that you are replacing 6144 bytes, which is consistant with the size Windows gives you - 6.00 kb. Once this is done, you will see that the there are more than 256 individual characters in the game, which means that each individual font pointer must be two bytes long, kinda like unicode. The next step now is to perform a relative search. The idea behind this is that, since rom's don't have to use a uniform code set for their fonts, the character A, for example, could be represented by anything. However, just about every custom code set, if not every code set has the characters placement relative to one another. So, if A is 23, then B would be 24, and C would be 25. If A were 77, then B would be 78, and C would be 79, and so on... What we have to do in order to find these values is by performing a relative search - you take a string from the game, like "It was a dark and stormy night", or whatever, and you find out where in the actual rom there is a string, whose relative spacing in between the letters is equal to the relative spaching in the search string. The longer the string, the less results will pop up in the search. We could search with mutliple strings to simulate a longer string, but that won't be necessary... What the font insertion program did was to assign a value to the first 256 characters in the rom, so we could see how far each japanese character is from the other. This was necessary because I myself, don't know which order the japanese character's come in, and also because after the hiragana, and katakatana characters have been used, kanji is next. Kanji has several thousand characters in it, and many roms don't fully implement the entire kanji character set because it would needlessly make the rom big. Anyhow, so that's why the above part was necessary. Now, on to the relative searching. I didn't find a utility that would perform relative searches on character sets with two bytes, so we'll make our own!: The search algorithim has been slightly modified from the typical one: Here are the search strings: 67,51,77,FA,11,53 (the title of song 1-3) 7D,55,93,09,77,FA,* (the title of song 1-4) We are now going to convert this to an ascii format. We could normally just convert each consecutive two hexadecimal values directly to ascii, but... the fifth character converts to a control character, so... we'll convert to decimal, and we'll "add" up the character codes through type casting. The result (before casting) is this: 103,81,119,250,17,83 125,85,147,9,119,250 Here's the code: --- c++ source code for relative searching on two byte character sets --- #include #include #include #include int main() { char rsearch01[6]={103,81,119,250,17,83}; //song 1-3 char rsearch02[6]={125,85,147,9,119,250}; //song 1-4 const int length=6; //length of rsearch01 char *ch; char byteso[length]; char diff[5]={0}; FILE *ptr; unsigned long len, num; int inum, size; ofstream out_file; out_file.open("output.txt"); ptr=fopen("chrono.smc","rb"); if (ptr) { fseek(ptr, 0, SEEK_END); len = ftell(ptr); fseek(ptr, 0, SEEK_SET); } ch=new char[len]; if (ch) fread(ch,1,len,ptr); fclose(ptr); for (inum=0;inum<5;inum++) diff[inum]=rsearch02[inum+1]-rsearch02[inum]; out_file << "Output generated by build 8\n"; size=ceil(3 * length / 2); for (num=0;num