137 lines
4.8 KiB
GDScript
137 lines
4.8 KiB
GDScript
extends Node
|
|
const ff5_dialog := preload('res://scripts/loaders/snes/ff5_dialog.gd')
|
|
|
|
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_block(block_name: String, buffer: StreamPeerBuffer, is_RPGe: bool = false) -> void:
|
|
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 glyph_table_dialog: PoolStringArray = glyph_tables.RPGe_dialog if is_RPGe else glyph_tables.SNES_dialog
|
|
var glyph_table_kanji: PoolStringArray = glyph_tables.SNES_kanji
|
|
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
|
|
buffer.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 + buffer.get_u8())
|
|
2:
|
|
for i in num_entries:
|
|
ptrs.append(ptr_offset + buffer.get_u16()) # Bank wrapping
|
|
3:
|
|
for i in num_entries:
|
|
ptrs.append(ptr_offset + buffer.get_u16() + (buffer.get_u8() << 16)) # Bank wrapping
|
|
_:
|
|
assert(false, 'Indirect l1_width of %d is not possible' % l1_width)
|
|
if block.null_terminated:
|
|
for i in num_entries:
|
|
buffer.seek(ptrs[i] & 0x3FFFFF) # Bank wrapping
|
|
var bytes = PoolByteArray()
|
|
while true:
|
|
var b = buffer.get_u8()
|
|
if b == 0:
|
|
break
|
|
bytes.append(b)
|
|
raw_strings.append(bytes)
|
|
else:
|
|
for i in num_entries-1:
|
|
var size: int = ptrs[i+1] - ptrs[i]
|
|
if size > 0:
|
|
buffer.seek(ptrs[i] & 0x3FFFFF) # Bank wrapping
|
|
raw_strings.append(buffer.get_data(size)[1])
|
|
elif size == 0:
|
|
raw_strings.append(PoolByteArray())
|
|
else:
|
|
print_debug('String pointer mismatch: "%s" index %d: 0x%06X:0x%06X, effective size of %d bytes' % [block_name, i, ptrs[i], ptrs[i+1], size])
|
|
break
|
|
else:
|
|
# Get first level of data
|
|
for i in num_entries:
|
|
raw_strings.append(buffer.get_data(l1_width)[1])
|
|
|
|
# Decode
|
|
if block.dialog:
|
|
for raw in raw_strings:
|
|
strings.append(ff5_dialog.decode_string_dialog(raw, glyph_table_dialog, glyph_table_kanji))
|
|
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 load_snes_rom(buffer: StreamPeerBuffer, is_RPGe: bool = false) -> void:
|
|
for block_name in SNES_block_addresses:
|
|
self._load_block(block_name, buffer, is_RPGe)
|
|
|
|
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_inventory_item_desc(id: int) -> String:
|
|
var desc_id := 0
|
|
if id < 0x80:
|
|
desc_id = RomLoader.snes_data.tbl_weapons[id].description
|
|
elif id < 0xE0:
|
|
desc_id = RomLoader.snes_data.tbl_armors[id-0x80].description
|
|
else:
|
|
desc_id = RomLoader.snes_data.tbl_items[id-0xE0].description
|
|
return self.tables.item_descriptions[desc_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
|