2023-08-04 18:17:08 +09:30
|
|
|
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))
|
|
|
|
tables_raw[block_name] = raw_strings
|
|
|
|
tables[block_name] = strings
|
|
|
|
|
|
|
|
func get_ability_name(id: int) -> String:
|
2023-08-04 22:11:42 +09:30
|
|
|
if id < 128:
|
2023-08-04 18:17:08 +09:30
|
|
|
return tables.battle_commands[id]
|
|
|
|
else:
|
2023-08-04 22:11:42 +09:30
|
|
|
return tables.ability_names[id-128]
|
2023-08-04 18:17:08 +09:30
|
|
|
|
|
|
|
func _ready() -> void:
|
|
|
|
pass
|