ref: 71b1c3c020f663a42eadba05437758932cda31cb
parent: aee281aa1fd1ec50acc1c1de00bb0c5c832fd68e
author: Snesrev <snesrev@protonmail.com>
date: Fri Mar 10 01:02:56 EST 2023
Add support for spanish, portuguese, polish, redux, dutch, swedish
--- a/messaging.c
+++ b/messaging.c
@@ -2220,7 +2220,7 @@
// EU encoding
if (a < 0x7f)
return TEXTCMD_MK(a, kTextCmd_IsLetter, 0);
- static const uint8 kSoundLut[1] = {45};
+ static const uint8 kSoundLut[] = {45};
static const uint8 kReturns_Simple[] = {
TEXTCMD_MK(0, kTextCmd_EndMessage, 0),
TEXTCMD_MK(0, kTextCmd_Scroll, 0),
@@ -2522,6 +2522,7 @@
const uint8 *kFontData = FindIndexInMemblk(g_zenv.dialogue_font_blk, 0).ptr;
uint8 width = FindIndexInMemblk(g_zenv.dialogue_font_blk, 1).ptr[c];
+ assert(width <= 8);
int i = vwf_var1++;
uint8 arrval = vwf_arr[i];
--- a/tables/compile_resources.py
+++ b/tables/compile_resources.py
@@ -119,25 +119,28 @@
add_asset_packed('kBgGfx', all)
def print_dialogue(args):
- from text_compression import kDialogueFilenames
+ from text_compression import dialogue_filename, kLanguages
languages = ['us']
if args.languages:
for a in args.languages.split(','):
- if a in languages or a not in kDialogueFilenames:
+ if a in languages or a not in kLanguages:
raise Exception(f'Language {a} is not valid')
- if not os.path.exists(kDialogueFilenames[a]):
- raise Exception(f'{kDialogueFilenames[a]} not found. You need to extract it with --extract-dialogue using the ROM of that language.')
+ name = dialogue_filename(a)
+ if not os.path.exists(name):
+ raise Exception(f'{name} not found. You need to extract it with --extract-dialogue using the ROM of that language.')
languages.append(a)
all_langs, all_fonts, mappings = [], [], []
for i, lang in enumerate(languages):
dict_packed = pack_arrays(text_compression.encode_dictionary(lang))
- dialogue_packed = pack_arrays(compress_dialogue(kDialogueFilenames[lang], lang))
+ dialogue_packed = pack_arrays(compress_dialogue(dialogue_filename(lang), lang))
all_langs.append(pack_arrays([dict_packed, dialogue_packed]))
font_data, font_width = sprite_sheets.encode_font_from_png(lang)
all_fonts.append(pack_arrays([font_data, font_width]))
- mappings.append(pack_arrays([lang.encode('utf8'), bytearray([i, i, i != 0])]))
+ flags = text_compression.uses_new_format(lang)
+ if i != 0: flags |= 2 # no us rom match?
+ mappings.append(pack_arrays([lang.encode('utf8'), bytearray([i, i, flags])]))
add_asset_packed('kDialogue', all_langs)
add_asset_packed('kDialogueFont', all_fonts)
add_asset_packed('kDialogueMap', mappings)
--- a/tables/extract_resources.py
+++ b/tables/extract_resources.py
@@ -240,7 +240,7 @@
print_overworld_area(i)
def print_dialogue():
- text_compression.print_strings(util.ROM, file = open(text_compression.kDialogueFilenames[util.ROM.language], 'w', encoding='utf8'))
+ text_compression.print_strings(util.ROM, file = open(text_compression.dialogue_filename(util.ROM.language), 'w', encoding='utf8'))
def decode_room_objects(p):
objs = []
--- a/tables/restool.py
+++ b/tables/restool.py
@@ -8,7 +8,7 @@
optional = parser.add_argument_group('Language settings')
optional.add_argument('--extract-dialogue', action='store_true', help = 'Extract dialogue from the german ROM')
-optional.add_argument('--languages', action='store', metavar='L1,L2', help = 'Comma separated list of additional languages to build (de,fr,fr-c,en).')
+optional.add_argument('--languages', action='store', metavar='L1,L2', help = 'Comma separated list of additional languages to build (de,fr,fr-c,en,es,pl,pt,redux,nl,sv).')
optional = parser.add_argument_group('Debug things')
optional.add_argument('--no-build', action='store_true', help="Don't actually build zelda3_assets.dat")
@@ -23,8 +23,8 @@
if args.extract_dialogue:
ROM = util.load_rom(args.rom, True)
import extract_resources, sprite_sheets
- extract_resources.print_dialogue()
sprite_sheets.decode_font()
+ extract_resources.print_dialogue()
sys.exit(0)
ROM = util.load_rom(args.rom)
--- a/tables/sprite_sheets.py
+++ b/tables/sprite_sheets.py
@@ -136,13 +136,29 @@
save_as_png((128, 64 * 3), dst, 'hud_icons.png', convert_snes_palette(get_hud_snes_palette()[:128]))
+def get_pt_remapper():
+ b = util.ROM.get_bytes(0x8EFC09, 121 * 3)
+ d = {}
+ for i in range(121):
+ ch = (i & 0xf) | (i << 1) & 0xe0
+ d[ch] = b[i*3+0]
+ d[ch|0x10] = b[i*3+1]
+ return d
+
kFontTypes = {
- 'us' : (0xe8000, 256, 'font.png', (0x8ECADF, 99)),
+ 'us' : (0x8e8000, 256, 'font.png', (0x8ECADF, 99)),
'de' : (0xCC6E8, 256, 'font_de.png', (0x8CDECF, 112)),
'fr' : (0xCC6E8, 256, 'font_fr.png', (0x8CDEAF, 112)),
'fr-c' : (0xCD078, 256, 'font_fr_c.png', (0x8CE83F, 112)),
'en' : (0x8E8000, 256, 'font_en.png', (0x8ECAFF, 102)),
+ 'es' : (0x8e8000, 256, 'font_es.png', (0x8ECADF, 99)),
+ 'pl' : (0x8e8000, 256, 'font_pl.png', (0x8ECADF, 99)),
+ 'pt' : (0x8e8000, 256, 'font_pt.png', (0x8ECADF, 121)),
+ 'redux': (0x8e8000, 256, 'font_redux.png', (0x8ECADF, 99)),
+ 'nl': (0x8e8000, 256, 'font_nl.png', (0x8ECADF, 99)),
+ 'sv': (0x8e8000, 256, 'font_sv.png', (0x8ECADF, 99)),
}
+
def decode_font():
lang = util.ROM.language
def decomp_one_spr_2bit(data, offs, target, toffs, pitch, palette_base):
@@ -152,17 +168,24 @@
t = ((d0 >> x) & 1) * 1 + ((d1 >> x) & 1) * 2
target[toffs + y * pitch + (7 - x)] = t + palette_base
ft = kFontTypes[lang]
- W = get_bytes(*ft[3])
+ if lang == 'pt':
+ W = util.ROM.get_bytes(0x8EFC09, 121 * 3)
+ W = [W[i*3+2] for i in range(121)]
+ remapper = get_pt_remapper()
+ else:
+ W = get_bytes(*ft[3])
+ remapper = {}
w = 128 + 15
hi = ft[1] // 32
h = hi * 17
data = get_bytes(ft[0], ft[1] * 16)
dst = bytearray(w * h)
+
for i in range(ft[1]):
x, y = i % 16, i // 16
pal_base = 6 * 16
base_offs = x * 9 + (y * 8 + (y >> 1)) * w
- decomp_one_spr_2bit(data, i * 16, dst, base_offs + w, w, pal_base)
+ decomp_one_spr_2bit(data, remapper.get(i, i) * 16, dst, base_offs + w, w, pal_base)
if (y & 1) == 0:
j = (y >> 1) * 16 + x
if j < len(W):
@@ -172,7 +195,8 @@
pal[0], pal[1], pal[2] = 192, 192, 192
pal[255*3+0], pal[255*3+1], pal[255*3+2] = 128, 128, 128
save_as_png((w, h), dst, ft[2], pal)
- assert (data, W) == encode_font_from_png(lang)
+ if lang != 'pt':
+ assert (data, W) == encode_font_from_png(lang)
def encode_font_from_png(lang):
font_data = Image.open(kFontTypes[lang][2]).tobytes()
--- a/tables/text_compression.py
+++ b/tables/text_compression.py
@@ -6,12 +6,10 @@
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
"w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
"-", ".", ",",
-
# 64 - 79
"[...]", ">", "(", ")",
"[Ankh]", "[Waves]", "[Snake]", "[LinkL]", "[LinkR]",
"\"", "[Up]", "[Down]", "[Left]",
-
# 80 - 95
"[Right]", "'", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
"[4HeartL]", "[4HeartR]", " ", "<", "[A]", "[B]", "[X]", "[Y]",
@@ -144,6 +142,58 @@
]
+# Uses the original format
+def org_encoder(cmd, param):
+ if cmd not in kText_CommandNames_US:
+ raise Exception(f'Invalid cmd {cmd}')
+ cmd_index = kText_CommandNames_US.index(cmd)
+ if kText_CommandLengths_US[cmd_index] != (1 if param == None else 2):
+ raise Exception(f'Invalid cmd params {cmd} {param}')
+ if param == None:
+ return [cmd_index + 0x67]
+ return [cmd_index + 0x67, int(param)]
+
+kCmdInfo = {
+ "Scroll" : (0x80, ),
+ "Waitkey" : (0x81, ),
+ "1" : (0x82, ),
+ "2" : (0x83, ),
+ "3" : (0x84, ),
+ "Name" : (0x85, ),
+ "Wait" : (0x87, {i:i+0x00 for i in range(16)}),
+ "Color" : (0x87, {i:i+0x10 for i in range(16)}),
+ "Number" : (0x87, {i:i+0x20 for i in range(16)}),
+ "Speed" : (0x87, {i:i+0x30 for i in range(16)}),
+ "Sound" : (0x87, {45 : 0x40, 64 : None}), # pt uses 64 for some reason
+ "Choose" : (0x87, 0x80),
+ "Choose2" : (0x87, 0x81),
+ "Choose3" : (0x87, 0x82),
+ "Selchg" : (0x87, 0x83),
+ "Item" : (0x87, 0x84),
+ "NextPic" : (0x87, 0x85),
+ "Window" : (0x87, {0 : None, 2 : 0x86}),
+ "Position" : (0x87, {0: 0x87, 1: 0x88}),
+ "ScrollSpd" : (0, {0 : None}),
+}
+
+# Uses another format where there's more bytes left for characters
+def new_encoder(cmd, param):
+ if cmd not in kCmdInfo:
+ raise Exception(f'Invalid cmd {cmd}')
+ info = kCmdInfo[cmd]
+ if len(info) <= 1 or isinstance(info[1], int):
+ if param != None:
+ raise Exception(f'Invalid cmd params {cmd} {param}')
+ return info
+ else:
+ if param == None or param not in info[1]:
+ raise Exception(f'Invalid cmd params {cmd} {param}')
+ r = info[1][param]
+ return (info[0], r) if r != None else ()
+
+kEncoders = {'org' : org_encoder, 'new' : new_encoder}
+
+
class LangUS:
alphabet = kTextAlphabet_US
dictionary = kTextDictionary_US
@@ -154,11 +204,8 @@
SWITCH_BANK = 0x80
FINISH = 0xff
DICT_BASE_ENC, DICT_BASE_DEC = 0x88, 0x88
- def encode_command(self, cmd_index, param):
- name = self.command_names[cmd_index]
- if param == None:
- return [cmd_index + self.COMMAND_START]
- return [cmd_index + self.COMMAND_START, int(param)]
+ ESCAPE_CHARACTER = None
+ encoder = 'org'
class LangEN(LangUS):
alphabet = kTextAlphabet_DE
@@ -165,6 +212,157 @@
dictionary = kTextDictionary_US
rom_addrs = [0x9c8000, 0x8edf60]
+class LangES(LangUS):
+ alphabet = [
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", # 0 - 15
+ "Q", "R", "S", "T", "U", "V", "W", "é", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
+ "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
+ "ó", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
+ "[Waves]", ".", ",",
+ # 64 - 79
+ "[...]", ">", "(", ")",
+ "ñ", "ú", "á", "[LinkL]", "[LinkR]", "\"", "[Up]", "[Down]", "[Left]",
+ # 80 - 95
+ "[Right]", "í", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
+ "[Ankh]", "[4HeartR]", " ", "[Snake]", "[A]", "[B]", "[X]", "[Y]", "[I]",
+ "¡", "¿", "Ñ"
+ ]
+ # "[Ankh]", "[Waves]", "[Snake]"
+
+ dictionary = [
+ ' ', ' ', ' ', ' en', ' la ', ' el ', ' de ', 'ien', 'tra', ' de',
+ 'te ', 'ar', 'a ', 'ada', 'es', 'as', 'o ', ' con', 'ero', 'ado',
+ 'e ', 'que', 'en', 'al', 'os ', 'ora', 'nte', ' al', 'lo ', 'or',
+ 'os', 'er', 'aci', 'res', ' que ', ' es', 'el', 'los ', 'tar', ' se',
+ ', ', 'ro', ' de l', ' est', 're', 'on', 'an', 'pued', ' del', 'ás ',
+ 'la', 'ti', 'la ', 'Es', 'to', 'ta', 'para', 'uer', 'ier', ' un ',
+ ' por', 'oder', 'da', 'in', 'cu', ' ha', 'per', 'ano', ' ve', 'cer',
+ 'lo', ' no ', 'ic', 'ra', 'ab', 'ir', ' una', 'undo', 'es ', 'as ',
+ 'con', 'a, ', 'te', ' m', 'gu', ' tu', 'ando', ' p', 'de', 'le',
+ 'ol', 'o, ', 'ten', 'lle', ' a ', 'aba', 'com',
+ ]
+ rom_addrs = [0x9c8000, 0x8edf40]
+
+
+class LangNL(LangUS):
+ alphabet = [
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", # 0 - 15
+ "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
+ "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
+ "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
+ "-", ".", ",", "[...]", ">", "(", ")", "[Ankh]",
+ "[Waves]", "[Snake]", "[LinkL]", "[LinkR]", "\"", "[Up]", "[Down]", "[Left]",
+ # 80 - 95
+ "[Right]", "'", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
+ "[4HeartL]", "[4HeartR]", " ", "<", "[A]", "[B]", "[X]", "[Y]",
+ ]
+
+ dictionary = [
+ ' ', ' ', ' ', "'s ", 'and ', 'are ', 'all ', 'ain', 'and', 'at ',
+ 'ast', 'an', 'at', 'ble', 'ba', 'be', 'bo', 'can ', 'che', 'com',
+ 'ck', 'des', 'di', 'do', 'en ', 'er ', 'ear', 'ent', 'ed ', 'en',
+ 'er', 'ev', 'for', 'fro', 'give ', 'get', 'go', 'have', 'has', 'her',
+ 'hi', 'ha', 'ight ', 'ing ', 'in', 'is', 'it', 'just', 'know', 'ly ',
+ 'la', 'lo', 'man', 'ma', 'me', 'mu', "n't ", 'non', 'not', 'open',
+ 'ound', 'out ', 'of', 'on', 'or', 'per', 'ple', 'pow', 'pro', 're ',
+ 're', 'some', 'se', 'sh', 'so', 'st', 'ter ', 'thin', 'ter', 'tha',
+ 'the', 'thi', 'to', 'tr', 'up', 'ver', 'with', 'wa', 'we', 'wh',
+ 'wi', 'you', 'Her', 'Tha', 'The', 'Thi', 'You',
+ ]
+ rom_addrs = [0x9c8000, 0x8edf40]
+
+
+
+class LangSV(LangUS):
+ alphabet = [
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "Ö", "P", # 0 - 15
+ "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
+ "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
+ "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
+ "å", ".", ",", "ä", ">", "(", ")","ö",
+ "Å", "Ä", "[LinkL]", "[LinkR]", "\"", "[Up]", "[Down]", "[Left]",
+ # 80 - 95
+ "[Right]", "'", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
+ "[4HeartL]", "[4HeartR]", " ", "<", "[Ankh]", "[Waves]", "[Snake]", "-", "[I]",
+ "[i]", "…", " ",
+ ]
+
+ dictionary = [
+ ' ', ' ', ' ', 'Du ', 'till', 'vill', 'bara', 'det', 'den', 'och',
+ 'en ', 'r ', 'n ', 'ett', 'en', ' d', 'a ', 'Hjäl', 'har', 'ter',
+ 't ', 'var', ' s', 'de', 'kan', 'med', 'som', 'för', 'att', 'ar',
+ ' h', 'er', 'jag', 'dig', 'öppna', 'mig', 'är', 'inte', 'hit', 'på ',
+ 'an', 'e ', 'rupie', '0kej', ' m', 'et', ', ', 'gång', 'måst', 'ten',
+ ' f', 'u ', 'men', 'te', 'tt', 'ka', 'vara', 'ken', '0m ', 'från',
+ 'myck', 'någo', 'in', ' k', ' i', 'vil', 'bar', 'ond', 'För', 'Jag',
+ 'ra', 'tack', 'll', 'g ', 'ta', 'om', 'anna', 'alla', 'en,', 'ber',
+ 'hem', 'han', 'st', 'ig', ' t', 'tro', 'kraf', 'ör', ' v', 'ag',
+ '… ', 'får', 'sin', 'mme', 'mma', 'en ', 'tat',
+ ]
+ rom_addrs = [0x9c8000, 0x8edf40]
+
+
+class LangPL(LangUS):
+ alphabet = [
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", # 0 - 15
+ "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
+ "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
+ "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
+ "-", ".", ",", "ć", "[Right]", "(", ")", "[Ankh]",
+ "[Waves]", "[Snake]", "[LinkL]", "[LinkR]","\"", "[Up]", "[Down]", "ę",
+
+ "ł", "ń", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
+ "ą", "[4HeartR]", " ", "[Left]", "ó", "ś", "ż", "ź", "Ł",
+ "Ś", "Ż", "Ź",
+ ] #
+ dictionary = ['Trój', '...', 'ść', 'Nie', ' nie', ' się', 'może', ' że', 'and', 'at ',
+ ' ty', 'an', 'at', 'kus', 'ba', 'be', 'bo', 'chce', 'che', 'ki ',
+ 'za', 'des', 'di', 'do', 'en ', 'er ', 'sz ', 'ent', 'ed ', 'en',
+ 'er', ' w', 'moc', 'zię', 'przez', 'ale', 'go', 'dzie', 'has', 'rze',
+ 'hi', 'ha', 'który', 'aby ', 'in', 'is', 'it', 'twoj', 'Może', 'łeś',
+ 'la', 'lo', 'czn', 'ma', 'me', 'mu', 'szcz', 'ska', 'śli', 'przy',
+ 'znaj', 'iecz', 'of', 'on', 'or', ' ', 'ple', 'pow', 'pro', 're ',
+ 're', 'mnie', 'se', ' z', 'so', 'st', 'któr', ' jak', 'ksz', 'sze',
+ 'coś', ' je', 'to', 'tr', 'up', 'kie', 'praw', 'wa', 'we', 'mi',
+ 'wi', 'szy', 'chc', 'pra', 'cie', ' i ', 'esz',
+ ]
+
+ rom_addrs = [0x9c8000, 0x8edf40]
+
+class LangPT(LangUS):
+ alphabet = [
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", # 0 - 15
+ "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
+ "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
+ "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
+ "-", ".", ",", "[...]", ">", "(", ")", "[Ankh]",
+ "[Waves]", "[Snake]", "[LinkL]", "[LinkR]", "\"", "[Up]", "[Down]", "[Left]",
+ # 80 - 95
+ "[Right]", "'", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
+ "[4HeartL]", "[4HeartR]", " ", "<", "[A]", "[B]", "[X]", "[Y]", "[I]",
+
+ "¡", "[!]", "Á", "À", "Â", "Ã", "É", "Ê", "Í", "Ó", "Ô", "Õ", "Ú", "á", "à", "â",
+ "ã", "é", "ê", "í", "ó", "ô", "õ", "ú", "ç",
+ ]
+ dictionary = [
+ ' ', ' ', ' ', ' ', 'o ', 'a ', 'e ', '..', 'de', 'ar',
+ 's ', 'ra', ' d', 'es', 'ocê ', 'do', ' a', ' p', 'er', ' e',
+ 'que', 'r ', 'os', 'te', ', ', 'as', 'or', 'm ', 'en', ' o',
+ 'nt', 're', ' s', 'co', 'da', 'se', 'st', ' c', ' m', 'em',
+ 'ma', 'ta', ' n', 'ad', 'on', 'al', 'ro', 'an', 'u ', 'nd',
+ ' um', 'pa', 'ca', 'el', ' f', 'to', 'in', ' t', 'ou', 'ei',
+ 'ss', 'ir', 'no', 'ri', 'tr', 'me', 'la', 'ia', 'le', 've',
+ 'is', 'sa', 'eu', 'pe', 'a.', 'na', 'so', 'mo', 'ga', 'o.',
+ 'á ', 'lo', 'ha', 'pr', 'ua', ' l', '! ', 'ui', 'am', 'ti',
+ 'io', 'gu', 'i ', 'di', 'nh', ' i', 'id',
+ ]
+ ESCAPE_CHARACTER = 0x62
+ rom_addrs = [0x9c8000, 0x8edf40]
+ encoder = 'new'
+
+ def __init__(self):
+ assert len(self.alphabet) == 121
+
class LangEU:
command_lengths = kText_CommandLengths_EU
command_names = kText_CommandNames_EU
@@ -172,41 +370,9 @@
SWITCH_BANK = 0x88
FINISH = 0x8f
DICT_BASE_ENC, DICT_BASE_DEC = 0x88, 0x90
- US = False
+ ESCAPE_CHARACTER = None
+ encoder = 'new'
- kCmdInfo = {
- "Scroll" : (0x80, ),
- "Waitkey" : (0x81, ),
- "1" : (0x82, ),
- "2" : (0x83, ),
- "3" : (0x84, ),
- "Name" : (0x85, ),
- "Wait" : (0x87, {i:i+0x00 for i in range(16)}),
- "Color" : (0x87, {i:i+0x10 for i in range(16)}),
- "Number" : (0x87, {i:i+0x20 for i in range(16)}),
- "Speed" : (0x87, {i:i+0x30 for i in range(16)}),
- "Sound" : (0x87, {45 : 0x40}),
- "Choose" : (0x87, 0x80),
- "Choose2" : (0x87, 0x81),
- "Choose3" : (0x87, 0x82),
- "Selchg" : (0x87, 0x83),
- "Item" : (0x87, 0x84),
- "NextPic" : (0x87, 0x85),
- "Window" : (0x87, {0 : None, 2 : 0x86}),
- "Position" : (0x87, {0: 0x87, 1: 0x88}),
- "ScrollSpd" : (0, {0 : None}),
- }
-
- def encode_command(self, cmd_index, param):
- info = self.kCmdInfo[self.command_names[cmd_index]]
- if len(info) <= 1 or isinstance(info[1], int):
- assert param == None
- return info
- else:
- assert param != None
- r = info[1][param]
- return (info[0], r) if r != None else ()
-
class LangDE(LangEU):
alphabet = kTextAlphabet_DE
dictionary = kTextDictionary_DE
@@ -222,8 +388,6 @@
dictionary = kTextDictionary_FR
rom_addrs = [0x9c8000, 0x8CF150]
-
-
kLanguages = {
'us' : LangUS(),
'de' : LangDE(),
@@ -230,16 +394,21 @@
'fr' : LangFR(),
'fr-c' : LangFR_C(),
'en' : LangEN(),
+ 'es' : LangES(),
+ 'pl' : LangPL(),
+ 'pt' : LangPT(),
+ 'redux' : LangUS(),
+ 'nl' : LangNL(),
+ 'sv' : LangSV(),
}
-kDialogueFilenames = {
- 'us' : 'dialogue.txt',
- 'de' : 'dialogue_de.txt',
- 'fr' : 'dialogue_fr.txt',
- 'fr-c' : 'dialogue_fr_c.txt',
- 'en' : 'dialogue_en.txt',
-}
+def dialogue_filename(s):
+ if s == 'us': return 'dialogue.txt'
+ return f"dialogue_{s.replace('-', '_')}.txt"
+def uses_new_format(lang):
+ return kLanguages[lang].encoder == 'new'
+
dict_expansion = []
def decode_strings_generic(get_byte, lang):
@@ -257,6 +426,9 @@
if c == 0x7f: # EndMessage
break
if c < info.COMMAND_START:
+ if c == info.ESCAPE_CHARACTER:
+ c = get_byte(p); p += 1
+ srcdata.append(c)
s += info.alphabet[c]
elif c < info.SWITCH_BANK:
if l == 2:
@@ -270,12 +442,20 @@
p = info.rom_addrs[rom_idx]; rom_idx += 1
s, srcdata = '', []
elif c < info.SWITCH_BANK + 8:
- assert 0
+ if lang != 'pt':
+ assert 0
else:
s += info.dictionary[c - info.DICT_BASE_DEC]
dict_expansion.append(len(info.dictionary[c - info.DICT_BASE_DEC]))
- result.append((s, srcdata))
+ result.append((s, srcdata))
+ if len(result) >= 397 and lang == 'pt':
+ return result
+# print(len(result), s)
+# if len(result) == 89:
+# print(s)
+# sys.exit(0)
+
def print_strings(rom, file = None):
texts = decode_strings_generic(rom.get_byte, rom.language)
@@ -302,12 +482,7 @@
if ' ' in cmd:
cmd, param = cmd.split(' ', 1)
param = int(param)
- if cmd not in info.command_names:
- raise Exception(f'Invalid cmd {cmd}')
- i = info.command_names.index(cmd)
- if info.command_lengths[i] != (1 if param == None else 2):
- raise Exception(f'Invalid cmd params {cmd} {param}')
- return info.encode_command(i, param), cmdlen + 2
+ return kEncoders[info.encoder](cmd, param), cmdlen + 2
else:
return [a2i[a[0]]], 1
--- a/tables/util.py
+++ b/tables/util.py
@@ -19,6 +19,12 @@
'170d4963a3f5854b2ab3b8203a75ee93034e8f2fff8ff587d9c1bdef9bd04984' : 'fr-c', # French Canadian
ZELDA3_SHA256_US : 'us',
'b7640393ff2cb47ec7c4c061b78f9f34df14663f1d52e171697661a542d01d0d' : 'en', # English PAL ROM
+ '0793101a75f493453f9412b700dc1076a31fc8079665c7af28257f975002d5dd' : 'es', # Spanish rom https://www.romhacking.net/translations/2195/
+ '773856c208a83b0d2c653675e33436040073915d2087f3803e25eb1506f27512' : 'pl', # Polish https://www.romhacking.net/translations/5760/
+ 'e414324d8c2e2dcdbbf033b3e028897554df0d13affe5ee4484439a3e8d79b18' : 'pt', # Portuguese https://www.romhacking.net/translations/6530/
+ '813bf9e5460a74aff2b2fdc5bc6ab14821ff0212c7546885e678737de969e5e2' : 'redux', # English Redux - https://www.romhacking.net/translations/6657/
+ '28275ab10572e93717e8a77a65baae33577cd04ef95f7268d21028b16deb376c' : 'nl', # Netherlands/Dutch - https://www.romhacking.net/translations/1124/
+ 'cf86ba00690cc6290ed3a229aa2bb460274b822de39eef694cda3576518184e3' : 'sv', # Sweden/Swedish - https://www.romhacking.net/translations/982/
}
def load_rom(filename, support_multilanguage = False):
@@ -58,7 +64,7 @@
hash = hashlib.sha256(self.ROM).hexdigest()
self.language = ZELDA3_SHA256.get(hash)
- if len(self.ROM) == 1049088:
+ if len(self.ROM) == 1049088 or self.language in ('sv',):
self.ROM = self.ROM[0x200:]
if support_multilanguage: