extends Node var SNES_block_addresses := Common.load_tsv('res://data/string_blocks.tsv') var tables_raw = {} var tables = {} var glyph_tables := { 'RPGe_dialog': Common.load_glyph_table('res://data/glyph_tables/Glyphs_dialog_RPGe.txt'), 'RPGe_small': Common.load_glyph_table('res://data/glyph_tables/Glyphs_small_RPGe.txt'), 'SNES_dialog': Common.load_glyph_table('res://data/glyph_tables/Glyphs_dialog_SNES.txt'), 'SNES_kanji': Common.load_glyph_table('res://data/glyph_tables/Glyphs_dialog_kanji_SNES.txt'), 'SNES_small': Common.load_glyph_table('res://data/glyph_tables/Glyphs_small_SNES.txt'), } static func decode_string(bytes, glyph_table: PoolStringArray, trim_trailing_whitespace: bool = true) -> String: # Trivial conversion for small text # Dialog requires multibyte handling including kanji and other macros var output = '' for c in bytes: output += glyph_table[c] return output.trim_suffix(' ') if trim_trailing_whitespace else output func decode_array(array, glyph_table, trim_trailing_whitespace: bool = true) -> PoolStringArray: if glyph_table is String: glyph_table = glyph_tables[glyph_table] var output = PoolStringArray() for s in array: output.append(decode_string(s, glyph_table, trim_trailing_whitespace)) return output func load_snes_rom(rom: File, is_RPGe: bool = false) -> void: for block_name in SNES_block_addresses: var block: Dictionary = SNES_block_addresses[block_name] var glyph_table_small: PoolStringArray = glyph_tables.RPGe_small if is_RPGe else glyph_tables.SNES_small var raw_strings := [] var strings := PoolStringArray() var num_entries: int = block.num_entries var l1_width: int = block.bytes if (not is_RPGe) and block.snes_bytes: l1_width = block.snes_bytes var l1_address: int = block.address if (not is_RPGe) and block.snes_address: l1_address = block.snes_address rom.seek(l1_address) var ptr_offset = block.rpge_ptr_offset if is_RPGe else block.snes_ptr_offset if ptr_offset is int: var ptrs = PoolIntArray() match l1_width: 1: for i in num_entries: ptrs.append((ptr_offset + rom.get_8()) & 0x3FFFFF) # Bank wrapping 2: for i in num_entries: ptrs.append((ptr_offset + rom.get_16()) & 0x3FFFFF) # Bank wrapping 3: for i in num_entries: ptrs.append((ptr_offset + rom.get_16() + (rom.get_8() << 16)) & 0x3FFFFF) # Bank wrapping _: assert(false, 'Indirect l1_width of %d is not possible' % l1_width) if block.null_terminated: for i in num_entries: rom.seek(ptrs[i]) var bytes = PoolByteArray() while true: var b = rom.get_8() if b == 0: break bytes.append(b) raw_strings.append(bytes) else: for i in num_entries-1: rom.seek(ptrs[i]) raw_strings.append(rom.get_buffer(ptrs[i+1] - ptrs[i])) else: # Get first level of data for i in num_entries: raw_strings.append(rom.get_buffer(l1_width)) # Decode if block.dialog: pass # TODO else: for raw in raw_strings: strings.append(decode_string(raw, glyph_table_small)) self.tables_raw[block_name] = raw_strings self.tables[block_name] = strings func get_ability_name(id: int) -> String: if id < 128: return self.tables.battle_commands[id] else: return self.tables.ability_names[id-128] func get_ability_desc(id: int) -> String: # TODO: revisit for GBA if id < 78: return self.tables.job_and_ability_descs[id+22] elif (id >= 128) and (id <= 161): return self.tables.job_and_ability_descs[(id-128)+100] else: assert(false, 'ability id %d out of description ranges' % id) return 'ability id %d out of description ranges' % id func get_job_name(id: int) -> String: return self.tables.job_names[id] func get_job_desc(id: int) -> String: return self.tables.job_and_ability_descs[id] func _ready() -> void: pass