impiaaa / smstools

Scripts for decoding data files from Super Mario Sunshine, Zelda: Wind Waker, and more
21 stars 3 forks source link

Bfn file of Twilight Princess #2

Open link8023 opened 1 year ago

link8023 commented 1 year ago

Can you ask the commands used by bfn's py script

impiaaa commented 1 year ago

Can you send an example?

link8023 commented 1 year ago

How can I use these scripts? How can I use bfn.py to extract files? My English is not very good.

impiaaa commented 1 year ago

In a command-line terminal, run python3 bfn.py <path-to-bfn-file>. It will print information about the font to the terminal window, and export a PNG file of the character map, next to the BFN file. You will need to have Python 3 and Pillow installed.

Darth-Koopa commented 1 year ago

Can you send an example?

Our goal is to port the Chinese font and text of Twilight Princess Nvidia Shield Version to the GameCube/Wii U version (currently we chose the Japanese version since a fan-translated version is also based on it). The issue is the NS version is encoded in UTF-16 BE which is multi-byte and the Japanese version is encoded in Shift-JIS which is single-byte. Here are some examples as references. BFN.zip

impiaaa commented 1 year ago

I pushed a small fix to bfn.py due to an array size error, but otherwise all of the examples work fine. I also added some information which might be interesting to you: the field I called fontType in the INF1 chunk determines the type of text encoding. 0 is for single-byte encodings, like ASCII. 1 is for 2-byte encodings, like UTF-16. 2 is for Shift-JIS, which is 2 bytes per character when the first byte is between 0x81 and 0xfc, and 1 byte otherwise. The Japanese and fan-translated examples use type 2, and the Nvidia Shield example uses type 1. Unless they removed the code for type 1, you should just be able to use the Nvidia Shield font as-is, with UTF-16 text.

Darth-Koopa commented 1 year ago

I pushed a small fix to bfn.py due to an array size error, but otherwise all of the examples work fine. I also added some information which might be interesting to you: the field I called fontType in the INF1 chunk determines the type of text encoding. 0 is for single-byte encodings, like ASCII. 1 is for 2-byte encodings, like UTF-16. 2 is for Shift-JIS, which is 2 bytes per character when the first byte is between 0x81 and 0xfc, and 1 byte otherwise. The Japanese and fan-translated examples use type 2, and the Nvidia Shield example uses type 1. Unless they removed the code for type 1, you should just be able to use the Nvidia Shield font as-is, with UTF-16 text.

Copy the Nvidia Shield font and text to the Japanese/English game will result in disorder codes. If anyone can hack the game and make it read UTF-16, it will resolve everything. As far as I know, only the Korean Wii version and the Nvidia Shield version supports UTF-16, but those two versions have their own defects. So hacking the Japanese font file is the only choice left for link8023, unless another hacker is willing for help.

impiaaa commented 1 year ago

Here's a version of the font from NVIDIA Shield version, modified to use Shift_JIS-2004: nvidia shield shift jis.zip. That means that a number of characters, that don't exist in Shift_JIS-2004, are not included. Here's the code I used to make it:

fin = open("Nvidia Shield.bfn", 'rb')
bfn = BFont()
bfn.read(fin)
fin.close()
newSpans = []
for charCode, glyph in zip(bfn.map1.spans[::2], bfn.map1.spans[1::2]):
    ch = chr(charCode)
    try:
        enc = ch.encode('shift_jis_2004')
    except UnicodeEncodeError:
        continue
    newCode, = struct.unpack('>H', enc)
    newSpans.append((newCode, glyph))

newSpans.sort(key=lambda a: a[0])
newSpans = [n for p in newSpans for n in p]
bfn.map1.spans = array.array('H')
bfn.map1.spans.fromlist(newSpans)
bfn.inf1.fontType = 2
bfn.write(open('nvidia shield shift-jis.bfn', 'wb'))

I think it would be much better to figure out why UTF-16 fonts don't work in the Japanese and English versions, so that you can use all of the available characters. From my reading of the Super Mario Sunshine code, it should work there. The function pointer for JUTResFont::isLeadByte is set in JUTResFont::setBlock to either isLeadByte_1Byte, isLeadByte_2Byte, or isLeadByte_ShiftJIS depending on the field in the INF1 chunk. It's possible that this code to support 2-bytes-per-character fonts was removed from Twilight Princess, but it shouldn't be hard to patch back in. I would also be cautious of encoding errors. I have heard that the NVIDIA Shield releases sometimes byte-swap resources back to little-endian. Also, fontType 1 only works with exactly 2 bytes per character, so it will break with any ASCII text or any characters with code point above U+FFFF.

Darth-Koopa commented 1 year ago

Can you give me your email for further coorperation?

impiaaa commented 1 year ago

My contact information is on my website. I can't help you with patching Twilight Princess.