Redefine struct DSL
Still tab-separated, but types now require u/s (e.g. 8 is now u8) Array form changed from "type[x][y]" to "x of y of type" Array of array is now recursive so arbitrary nesting is allowed Also the new parser should be able to write as easily as it reads, hooray!
This commit is contained in:
parent
00496ed59f
commit
724f48a62e
|
@ -0,0 +1,161 @@
|
|||
struct Character
|
||||
u3 character_id # 0=Bartz, 1=Lenna, 2=Galuf, 3=Faris, 4=Krile, 5/6/7 unused
|
||||
u1 is_female # 0=male, 1=female
|
||||
u2 unk0 # Two unknown, possibly unused bits
|
||||
u1 is_absent # 0=present, 1=absent
|
||||
u1 is_back_row
|
||||
u8 current_job_id
|
||||
u8 level
|
||||
u24 experience
|
||||
u16 hp_current
|
||||
u16 hp_max
|
||||
u16 mp_current
|
||||
u16 mp_max
|
||||
u8 equipped_head
|
||||
u8 equipped_body
|
||||
u8 equipped_acc
|
||||
u8 equipped_rh_shield
|
||||
u8 equipped_lh_shield
|
||||
u8 equipped_rh_weapon
|
||||
u8 equipped_lh_weapon
|
||||
u8 caught_monster
|
||||
u8 ability_1
|
||||
u8 ability_2
|
||||
u8 ability_3
|
||||
u8 ability_4
|
||||
u8 status_1
|
||||
u8 status_2
|
||||
u8 status_3
|
||||
u8 status_4
|
||||
u8 action_flags
|
||||
u8 damage_mod
|
||||
u16 innates
|
||||
u8 magic_element_up
|
||||
u8 equip_weight
|
||||
u8 base_strength
|
||||
u8 base_agility
|
||||
u8 base_stamina
|
||||
u8 base_magic
|
||||
u8 current_strength
|
||||
u8 current_agility
|
||||
u8 current_stamina
|
||||
u8 current_magic
|
||||
u8 evasion
|
||||
u8 defense
|
||||
u8 magic_evasion
|
||||
u8 magic_defense
|
||||
u8 elemental_absorb
|
||||
u8 elemental_evade
|
||||
u8 elemental_immune
|
||||
u8 elemental_half
|
||||
u8 elemental_weakness
|
||||
u8 resistance_status_1
|
||||
u8 resistance_status_2
|
||||
u8 resistance_status_3
|
||||
u8 specialty_weapon
|
||||
u8 specialty_equipment
|
||||
u8 current_job_level
|
||||
u16 current_job_abp
|
||||
u8 spell_level_1
|
||||
u8 spell_level_2
|
||||
u8 spell_level_3
|
||||
u32 equipment_category
|
||||
u16 attack
|
||||
u8 attack_id_reaction_unused
|
||||
u8 unk1
|
||||
u8 unk2
|
||||
u8 unk3
|
||||
u8 freelancer_strength
|
||||
u8 freelancer_agility
|
||||
u8 freelancer_stamina
|
||||
u8 freelancer_magic
|
||||
u16 freelancer_innates
|
||||
|
||||
struct Job_progress
|
||||
u12 abp
|
||||
u4 level
|
||||
|
||||
struct Config
|
||||
u3 battle_speed # 0=1 in-game, ..., 5=6 in-game
|
||||
u1 is_wait_mode # 0=active, 1=wait???
|
||||
u3 message_speed # 0=1 in-game, ..., 5=6 in-game
|
||||
u1 command_set # 0=window, 1=shortcut
|
||||
u5 menu_color_r
|
||||
u5 menu_color_g
|
||||
u5 menu_color_b
|
||||
u1 padding
|
||||
u1 reequip_mode # 0=optimum, 1=empty
|
||||
u1 is_mono # 0=stereo, 1=mono
|
||||
u1 is_memory_cursor # 0=reset, 1=memory
|
||||
u4 unk0
|
||||
u1 hide_atb_gauge # 0=show, 1=hide
|
||||
u6 unk1
|
||||
u1 is_controller_custom # 0=no, 1=yes
|
||||
u1 is_controller_2p # 0=no, 1=yes
|
||||
u8 button_A # Bit of action
|
||||
u8 button_B # Bit of action
|
||||
u8 button_X # Bit of action
|
||||
u8 button_Y # Bit of action
|
||||
u8 button_L # Bit of action
|
||||
u8 button_R # Bit of action
|
||||
u8 button_Select # Bit of action
|
||||
4 of u8 character_player_nums # 0=controller 1, 1=controller 2
|
||||
4 of 4 of u8 character_shortcut_commands # 0=ability_1, 1=ability_2, 2=ability_3, 3=ability_4
|
||||
|
||||
struct Vehicle
|
||||
u2 mode_switching
|
||||
u3 movement_type
|
||||
u3 map_id
|
||||
u7 unk0
|
||||
u1 is_hidden # 0=show, 1=hide
|
||||
u8 x
|
||||
u8 y
|
||||
|
||||
struct Save_slot
|
||||
4 of Character characters
|
||||
256 of u8 inventory_item_ids
|
||||
256 of u8 inventory_item_qtys
|
||||
u24 unlocked_jobs
|
||||
4 of 22 of Job_progress character_jobs_progress
|
||||
4 of u8 character_abilities_learned_count
|
||||
4 of 20 of u8 character_abilities_learned
|
||||
u24 current_gil
|
||||
u32 game_time_frames
|
||||
u16 num_enemies_defeated
|
||||
32 of u8 magic_learned
|
||||
Config config
|
||||
5 of 6 of u8 character_names # Bartz, Lenna, Galuf, Faris, Krile. Dialog is hardcoded for everyone except Bartz's name anyway...
|
||||
6 of u8 unk0
|
||||
u8 magic_lamp_next_summon
|
||||
u8 num_battles_escaped # Brave Blade vs Chicken Knife
|
||||
u8 wonder_rod_magic
|
||||
9 of u8 unk1
|
||||
u16 num_total_battles
|
||||
u16 num_times_saved
|
||||
u8 last_battle_results # 0=victory, 1=game over, 2=escaped
|
||||
15 of u8 flags_battle_events
|
||||
32 of u8 flags_treasure_opened
|
||||
32 of u8 unk_probably_still_flags_treasure_opened
|
||||
96 of u8 flags_events # RAM map mentions $D8E000. This is likely critical to story progression and scripting.
|
||||
96 of u8 unk_probably_still_flags_events
|
||||
u16 map_id_inner
|
||||
u16 map_id_world
|
||||
u8 pos_x
|
||||
u8 pos_y
|
||||
u8 current_character_sprite
|
||||
u8 current_character_facing
|
||||
u8 current_vehicle
|
||||
Vehicle veh_chocobo
|
||||
Vehicle veh_black_chocobo
|
||||
Vehicle veh_hiryuu
|
||||
Vehicle veh_submarine
|
||||
Vehicle veh_steamship
|
||||
Vehicle veh_airship
|
||||
u16 teleport_map_id
|
||||
u8 teleport_map_x
|
||||
u8 teleport_map_y
|
||||
u8 initial_seed
|
||||
u8 walking_speed # 0=normal, 1=double (fast), 80=half (slow)
|
||||
u8 timed_event_active
|
||||
u16 timed_event_timer
|
||||
u16 timed_event_end
|
|
|
@ -1,161 +0,0 @@
|
|||
struct Character
|
||||
3 character_id # 0=Bartz, 1=Lenna, 2=Galuf, 3=Faris, 4=Krile, 5/6/7 unused
|
||||
1 is_female # 0=male, 1=female
|
||||
2 unk0 # Two unknown, possibly unused bits
|
||||
1 is_absent # 0=present, 1=absent
|
||||
1 is_back_row
|
||||
8 current_job_id
|
||||
8 level
|
||||
24 experience
|
||||
16 hp_current
|
||||
16 hp_max
|
||||
16 mp_current
|
||||
16 mp_max
|
||||
8 equipped_head
|
||||
8 equipped_body
|
||||
8 equipped_acc
|
||||
8 equipped_rh_shield
|
||||
8 equipped_lh_shield
|
||||
8 equipped_rh_weapon
|
||||
8 equipped_lh_weapon
|
||||
8 caught_monster
|
||||
8 ability_1
|
||||
8 ability_2
|
||||
8 ability_3
|
||||
8 ability_4
|
||||
8 status_1
|
||||
8 status_2
|
||||
8 status_3
|
||||
8 status_4
|
||||
8 action_flags
|
||||
8 damage_mod
|
||||
16 innates
|
||||
8 magic_element_up
|
||||
8 equip_weight
|
||||
8 base_strength
|
||||
8 base_agility
|
||||
8 base_stamina
|
||||
8 base_magic
|
||||
8 current_strength
|
||||
8 current_agility
|
||||
8 current_stamina
|
||||
8 current_magic
|
||||
8 evasion
|
||||
8 defense
|
||||
8 magic_evasion
|
||||
8 magic_defense
|
||||
8 elemental_absorb
|
||||
8 elemental_evade
|
||||
8 elemental_immune
|
||||
8 elemental_half
|
||||
8 elemental_weakness
|
||||
8 resistance_status_1
|
||||
8 resistance_status_2
|
||||
8 resistance_status_3
|
||||
8 specialty_weapon
|
||||
8 specialty_equipment
|
||||
8 current_job_level
|
||||
16 current_job_abp
|
||||
8 spell_level_1
|
||||
8 spell_level_2
|
||||
8 spell_level_3
|
||||
32 equipment_category
|
||||
16 attack
|
||||
8 attack_id_reaction_unused
|
||||
8 unk1
|
||||
8 unk2
|
||||
8 unk3
|
||||
8 freelancer_strength
|
||||
8 freelancer_agility
|
||||
8 freelancer_stamina
|
||||
8 freelancer_magic
|
||||
16 freelancer_innates
|
||||
|
||||
struct Job_progress
|
||||
12 abp
|
||||
4 level
|
||||
|
||||
struct Config
|
||||
3 battle_speed # 0=1 in-game, ..., 5=6 in-game
|
||||
1 is_wait_mode # 0=active, 1=wait???
|
||||
3 message_speed # 0=1 in-game, ..., 5=6 in-game
|
||||
1 command_set # 0=window, 1=shortcut
|
||||
5 menu_color_r
|
||||
5 menu_color_g
|
||||
5 menu_color_b
|
||||
1 padding
|
||||
1 reequip_mode # 0=optimum, 1=empty
|
||||
1 is_mono # 0=stereo, 1=mono
|
||||
1 is_memory_cursor # 0=reset, 1=memory
|
||||
4 unk0
|
||||
1 hide_atb_gauge # 0=show, 1=hide
|
||||
6 unk1
|
||||
1 is_controller_custom # 0=no, 1=yes
|
||||
1 is_controller_2p # 0=no, 1=yes
|
||||
8 button_A # Bit of action
|
||||
8 button_B # Bit of action
|
||||
8 button_X # Bit of action
|
||||
8 button_Y # Bit of action
|
||||
8 button_L # Bit of action
|
||||
8 button_R # Bit of action
|
||||
8 button_Select # Bit of action
|
||||
8[4] character_player_nums # 0=controller 1, 1=controller 2
|
||||
8[4][4] character_shortcut_commands # 0=ability_1, 1=ability_2, 2=ability_3, 3=ability_4
|
||||
|
||||
struct Vehicle
|
||||
2 mode_switching
|
||||
3 movement_type
|
||||
3 map_id
|
||||
7 unk0
|
||||
1 is_hidden # 0=show, 1=hide
|
||||
8 x
|
||||
8 y
|
||||
|
||||
struct Save_slot
|
||||
Character[4] characters
|
||||
8[256] inventory_item_ids
|
||||
8[256] inventory_item_qtys
|
||||
24 unlocked_jobs
|
||||
Job_progress[4][22] character_jobs_progress
|
||||
8[4] character_abilities_learned_count
|
||||
8[4][20] character_abilities_learned
|
||||
24 current_gil
|
||||
32 game_time_frames
|
||||
16 num_enemies_defeated
|
||||
8[32] magic_learned
|
||||
Config config
|
||||
8[5][6] character_names # Bartz, Lenna, Galuf, Faris, Krile. Dialog is hardcoded for everyone except Bartz's name anyway...
|
||||
8[6] unk0
|
||||
8 magic_lamp_next_summon
|
||||
8 num_battles_escaped # Brave Blade vs Chicken Knife
|
||||
8 wonder_rod_magic
|
||||
8[9] unk1
|
||||
16 num_total_battles
|
||||
16 num_times_saved
|
||||
8 last_battle_results # 0=victory, 1=game over, 2=escaped
|
||||
8[15] flags_battle_events
|
||||
8[32] flags_treasure_opened
|
||||
8[32] unk_probably_still_flags_treasure_opened
|
||||
8[96] flags_events # RAM map mentions $D8E000. This is likely critical to story progression and scripting.
|
||||
8[96] unk_probably_still_flags_events
|
||||
16 map_id_inner
|
||||
16 map_id_world
|
||||
8 pos_x
|
||||
8 pos_y
|
||||
8 current_character_sprite
|
||||
8 current_character_facing
|
||||
8 current_vehicle
|
||||
Vehicle veh_chocobo
|
||||
Vehicle veh_black_chocobo
|
||||
Vehicle veh_hiryuu
|
||||
Vehicle veh_submarine
|
||||
Vehicle veh_steamship
|
||||
Vehicle veh_airship
|
||||
16 teleport_map_id
|
||||
8 teleport_map_x
|
||||
8 teleport_map_y
|
||||
8 initial_seed
|
||||
8 walking_speed # 0=normal, 1=double (fast), 80=half (slow)
|
||||
8 timed_event_active
|
||||
16 timed_event_timer
|
||||
16 timed_event_end
|
|
@ -1,6 +1,7 @@
|
|||
extends Node
|
||||
const STRUCT := preload('res://scripts/struct.gd')
|
||||
const SLOT_IN_USE := 0xE41B
|
||||
var schema := {}
|
||||
var struct_types := STRUCT.get_base_structarraytypes()
|
||||
|
||||
# FFV SRAM is 4 slots of 0x700byte save files
|
||||
# $0000-$06FF - Save Slot 1
|
||||
|
@ -38,148 +39,13 @@ func get_slot_checksum(slot: StreamPeerBuffer) -> int:
|
|||
checksum += 1 # TODO: confirm this carry flag edge case is correct!
|
||||
return checksum
|
||||
|
||||
func get_character(slot: StreamPeerBuffer, character_slot_id: int) -> Dictionary:
|
||||
var character := {}
|
||||
slot.seek(0x50 * character_slot_id)
|
||||
var b := slot.get_u8()
|
||||
character['id'] = b & 0x07 # 0Bartz/1Lenna/2Galuf/3Faris/4Krile/5/6/7
|
||||
character['gender'] = (b >> 3) & 1 # 0 = male, 1 = female
|
||||
# Two unknown, possibly unused bits
|
||||
character['present'] = (b >> 6) & 1 # 0 = absent?
|
||||
character['back_row'] = (b >> 7) & 1
|
||||
character['job_id'] = slot.get_u8()
|
||||
character['level'] = slot.get_u8()
|
||||
character['experience'] = slot.get_u16() | (slot.get_u8() << 16)
|
||||
character['hp_current'] = slot.get_u16()
|
||||
character['hp_max'] = slot.get_u16()
|
||||
character['mp_current'] = slot.get_u16()
|
||||
character['mp_max'] = slot.get_u16()
|
||||
character['equipped_head'] = slot.get_u8()
|
||||
character['equipped_body'] = slot.get_u8()
|
||||
character['equipped_acc'] = slot.get_u8()
|
||||
character['equipped_rh_shield'] = slot.get_u8()
|
||||
character['equipped_lh_shield'] = slot.get_u8()
|
||||
character['equipped_rh_weapon'] = slot.get_u8()
|
||||
character['equipped_lh_weapon'] = slot.get_u8()
|
||||
character['caught_monster'] = slot.get_u8()
|
||||
character['ability_1'] = slot.get_u8()
|
||||
character['ability_2'] = slot.get_u8()
|
||||
character['ability_3'] = slot.get_u8()
|
||||
character['ability_4'] = slot.get_u8()
|
||||
character['action_flags'] = slot.get_u8()
|
||||
character['damage_mod'] = slot.get_u8()
|
||||
character['innates'] = slot.get_u16()
|
||||
character['magic_element_up'] = slot.get_u8()
|
||||
character['equip_weight'] = slot.get_u8()
|
||||
character['base_strength'] = slot.get_u8()
|
||||
character['base_agility'] = slot.get_u8()
|
||||
character['base_stamina'] = slot.get_u8()
|
||||
character['base_magic'] = slot.get_u8()
|
||||
character['current_strength'] = slot.get_u8()
|
||||
character['current_agility'] = slot.get_u8()
|
||||
character['current_stamina'] = slot.get_u8()
|
||||
character['current_magic'] = slot.get_u8()
|
||||
character['evasion'] = slot.get_u8()
|
||||
character['defense'] = slot.get_u8()
|
||||
character['magic_evasion'] = slot.get_u8()
|
||||
character['magic_defense'] = slot.get_u8()
|
||||
character['elemental_absorb'] = slot.get_u8()
|
||||
character['elemental_evade'] = slot.get_u8()
|
||||
character['elemental_immune'] = slot.get_u8()
|
||||
character['elemental_half'] = slot.get_u8()
|
||||
character['elemental_weakness'] = slot.get_u8()
|
||||
character['resistance_status_1'] = slot.get_u8()
|
||||
character['resistance_status_2'] = slot.get_u8()
|
||||
character['resistance_status_3'] = slot.get_u8()
|
||||
character['specialty_weapon'] = slot.get_u8()
|
||||
character['specialty_equipment'] = slot.get_u8()
|
||||
character['current_job_level'] = slot.get_u8()
|
||||
character['current_job_abp'] = slot.get_u16()
|
||||
character['spell_level_1'] = slot.get_u8()
|
||||
character['spell_level_2'] = slot.get_u8()
|
||||
character['spell_level_3'] = slot.get_u8()
|
||||
character['equipment_category'] = slot.get_u32()
|
||||
character['attack'] = slot.get_u16()
|
||||
character['attack_id_reaction_unused'] = slot.get_u8()
|
||||
character['unk1'] = slot.get_u8()
|
||||
character['unk2'] = slot.get_u8()
|
||||
character['unk3'] = slot.get_u8()
|
||||
character['freelancer_strength'] = slot.get_u8()
|
||||
character['freelancer_agility'] = slot.get_u8()
|
||||
character['freelancer_stamina'] = slot.get_u8()
|
||||
character['freelancer_magic'] = slot.get_u8()
|
||||
character['freelancer_innates'] = slot.get_u16()
|
||||
return character
|
||||
|
||||
func get_struct(buffer: StreamPeer, struct_name: String) -> Dictionary:
|
||||
# As this is recursive, it does not seek to the start of the buffer.
|
||||
# Does not seek to the start of the buffer.
|
||||
# Make sure it is set to where you want to start reading.
|
||||
if not (struct_name in schema):
|
||||
if not (struct_name in struct_types):
|
||||
print_debug('Attempted to get undeclared struct: "%s"' % struct_name)
|
||||
return {}
|
||||
var struct := {}
|
||||
var leftover_bits := 0
|
||||
var leftover_bits_value := 0
|
||||
for line in schema[struct_name]:
|
||||
var t: String = line[0]
|
||||
var k: String = line[1]
|
||||
match t:
|
||||
'8':
|
||||
struct[k] = buffer.get_u8()
|
||||
'16':
|
||||
struct[k] = buffer.get_u16()
|
||||
'24':
|
||||
struct[k] = buffer.get_u16() | (buffer.get_u8() << 16)
|
||||
'32':
|
||||
struct[k] = buffer.get_u32()
|
||||
_:
|
||||
var arr_split = t.split('[')
|
||||
match arr_split.size():
|
||||
1:
|
||||
# Check if it's an odd number of bits
|
||||
var b := int(t)
|
||||
if b > 0:
|
||||
# Check if we have leftover bits to fill this order
|
||||
while leftover_bits < b:
|
||||
leftover_bits_value |= buffer.get_u8() << leftover_bits
|
||||
leftover_bits += 8
|
||||
struct[k] = leftover_bits_value & ((1 << b)-1)
|
||||
leftover_bits_value = leftover_bits_value >> b
|
||||
leftover_bits -= b
|
||||
else:
|
||||
# It's a struct
|
||||
struct[k] = get_struct(buffer, t)
|
||||
2:
|
||||
var n0 := int(arr_split[1].trim_suffix(']'))
|
||||
var arr = []
|
||||
if arr_split[0] == '8':
|
||||
arr = PoolByteArray()
|
||||
for i in n0:
|
||||
arr.append(buffer.get_u8())
|
||||
else:
|
||||
for i in n0:
|
||||
arr.append(get_struct(buffer, arr_split[0]))
|
||||
struct[k] = arr
|
||||
3:
|
||||
var n0 := int(arr_split[1].trim_suffix(']'))
|
||||
var n1 := int(arr_split[2].trim_suffix(']'))
|
||||
var arr0 := []
|
||||
if arr_split[0] == '8':
|
||||
for i in n0:
|
||||
var arr1 := PoolByteArray()
|
||||
for j in n1:
|
||||
arr1.append(buffer.get_u8())
|
||||
arr0.append(arr1)
|
||||
else:
|
||||
for i in n0:
|
||||
var arr1 := []
|
||||
for j in n1:
|
||||
arr1.append(get_struct(buffer, arr_split[0]))
|
||||
arr0.append(arr1)
|
||||
struct[k] = arr0
|
||||
var s:
|
||||
print_debug('struct value array dims of %d are not yet supported (struct "%s")' % [s, struct_name])
|
||||
return struct
|
||||
return struct_types[struct_name].get_value(buffer, [0, 0])
|
||||
|
||||
func get_save_slot(sram: File, slot_id: int):
|
||||
var buffer := StreamPeerBuffer.new()
|
||||
|
@ -201,37 +67,24 @@ func delete_save_slot(sram: File, slot_id: int):
|
|||
|
||||
func _ready():
|
||||
var file := File.new()
|
||||
var error = file.open('res://data/SNES_save.txt', File.READ)
|
||||
var error = file.open('res://data/SNES_save.tsv', File.READ)
|
||||
if error == OK:
|
||||
var current_struct_name = null
|
||||
var current_struct = []
|
||||
var line_num := 0
|
||||
var current_struct: STRUCT.Struct
|
||||
var line_num := 0 # Currently only used for step-through debugging
|
||||
while file.get_position() < file.get_len():
|
||||
var line := file.get_csv_line('\t')
|
||||
line_num += 1
|
||||
var size = line.size()
|
||||
if size < 2:
|
||||
if current_struct_name:
|
||||
# Store struct we just finished declaring
|
||||
schema[current_struct_name] = current_struct
|
||||
current_struct = []
|
||||
current_struct_name = null
|
||||
continue
|
||||
# if size < 2:
|
||||
# print_debug('Malformed schema file: line %d - size %d - "%s"' % [line_num, line.size(), line])
|
||||
# continue
|
||||
# Size is at least 2
|
||||
if line[0] == 'struct':
|
||||
var type := line[0]
|
||||
var label := line[1]
|
||||
if type == 'struct':
|
||||
# New struct declaration
|
||||
if current_struct_name:
|
||||
# Store one we just finished declaring
|
||||
schema[current_struct_name] = current_struct
|
||||
current_struct = []
|
||||
current_struct_name = line[1]
|
||||
else:
|
||||
current_struct = STRUCT.Struct.new()
|
||||
struct_types[label] = current_struct
|
||||
elif type and label:
|
||||
# TODO: Maybe store the trailing comments somewhere?
|
||||
current_struct.append(line)
|
||||
# Make sure we saved the final struct
|
||||
if current_struct_name:
|
||||
schema[current_struct_name] = current_struct
|
||||
current_struct.members.append([label, STRUCT.get_structarraytype(type, struct_types)])
|
||||
file.close()
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
#warning-ignore-all:shadowed_variable
|
||||
#warning-ignore-all:unused_argument
|
||||
# leftover_bits is array of form [count, value]
|
||||
# array is used for reference semantics as get and put operations may mutate it
|
||||
class StructType:
|
||||
func get_value(buffer: StreamPeer, leftover_bits: Array):
|
||||
return
|
||||
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
|
||||
return
|
||||
|
||||
class U8 extends StructType:
|
||||
func get_value(buffer: StreamPeer, leftover_bits: Array):
|
||||
return buffer.get_u8()
|
||||
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
|
||||
buffer.put_u8(value)
|
||||
|
||||
class S8 extends StructType:
|
||||
func get_value(buffer: StreamPeer, leftover_bits: Array):
|
||||
return buffer.get_8()
|
||||
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
|
||||
buffer.put_8(value)
|
||||
|
||||
class U16 extends StructType:
|
||||
func get_value(buffer: StreamPeer, leftover_bits: Array):
|
||||
return buffer.get_u16()
|
||||
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
|
||||
buffer.put_u16(value)
|
||||
|
||||
class S16 extends StructType:
|
||||
func get_value(buffer: StreamPeer, leftover_bits: Array):
|
||||
return buffer.get_16()
|
||||
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
|
||||
buffer.put_16(value)
|
||||
|
||||
class U24 extends StructType:
|
||||
func get_value(buffer: StreamPeer, leftover_bits: Array):
|
||||
return buffer.get_u16() | (buffer.get_u8() << 16)
|
||||
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
|
||||
buffer.put_u16(value & 0xFFFF)
|
||||
buffer.put_u8(value >> 16)
|
||||
|
||||
class S24 extends StructType:
|
||||
func get_value(buffer: StreamPeer, leftover_bits: Array):
|
||||
var unsigned = buffer.get_u16() | (buffer.get_u8() << 16)
|
||||
return unsigned - (2 * (unsigned & 0x800000))
|
||||
|
||||
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
|
||||
var unsigned = value % 0x1000000
|
||||
buffer.put_u16(unsigned & 0xFFFF)
|
||||
buffer.put_u8(unsigned >> 16)
|
||||
|
||||
class U32 extends StructType:
|
||||
func get_value(buffer: StreamPeer, leftover_bits: Array):
|
||||
return buffer.get_u32()
|
||||
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
|
||||
buffer.put_u32(value)
|
||||
|
||||
class S32 extends StructType:
|
||||
func get_value(buffer: StreamPeer, leftover_bits: Array):
|
||||
return buffer.get_32()
|
||||
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
|
||||
buffer.put_32(value)
|
||||
|
||||
|
||||
class UBits extends StructType:
|
||||
var bits = 8
|
||||
|
||||
func _init(bits: int):
|
||||
self.bits = bits
|
||||
|
||||
func get_value(buffer: StreamPeer, leftover_bits: Array):
|
||||
while leftover_bits[0] < bits:
|
||||
leftover_bits[1] |= buffer.get_u8() << leftover_bits[0]
|
||||
leftover_bits[0] += 8
|
||||
var value = leftover_bits[1] & ((1 << bits)-1)
|
||||
leftover_bits[1] = leftover_bits[1] >> bits
|
||||
leftover_bits[0] -= bits
|
||||
return value
|
||||
|
||||
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
|
||||
leftover_bits[1] |= value << bits
|
||||
leftover_bits[0] += bits
|
||||
while leftover_bits[0] >= 8:
|
||||
buffer.put_8(leftover_bits[1] & 0xFF)
|
||||
leftover_bits[0] -= 8
|
||||
leftover_bits[1] = leftover_bits[1] >> 8
|
||||
|
||||
|
||||
class Struct extends StructType:
|
||||
var members := [] # Array of [name, StructType]
|
||||
func get_value(buffer: StreamPeer, leftover_bits: Array):
|
||||
var result = {}
|
||||
for member in members:
|
||||
var key: String = member[0]
|
||||
var structType: StructType = member[1]
|
||||
result[key] = structType.get_value(buffer, leftover_bits)
|
||||
return result
|
||||
|
||||
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
|
||||
for member in members:
|
||||
var key: String = member[0]
|
||||
var structType: StructType = member[1]
|
||||
if not (key in value):
|
||||
print_debug('Key "%s" missing from value supplied' % key)
|
||||
return
|
||||
structType.put_value(buffer, value[key], leftover_bits)
|
||||
|
||||
|
||||
class StructArrayType extends StructType:
|
||||
var count: int
|
||||
var structType: StructType
|
||||
|
||||
func _init(count, structType) -> void:
|
||||
self.count = count
|
||||
self.structType = structType
|
||||
|
||||
func get_value(buffer: StreamPeer, leftover_bits: Array):
|
||||
# Might be a bit too much branching but oh well
|
||||
if self.structType is U8:
|
||||
var result = PoolByteArray()
|
||||
# Slight optimization over calling the method
|
||||
for i in self.count:
|
||||
result.append(buffer.get_u8())
|
||||
return result
|
||||
|
||||
var result = []
|
||||
for i in self.count:
|
||||
result.append(self.structType.get_value(buffer, leftover_bits))
|
||||
return result
|
||||
|
||||
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
|
||||
if len(value) < self.count:
|
||||
print_debug('Not enough values supplied')
|
||||
return
|
||||
for i in self.count:
|
||||
self.structType.put_value(buffer, value[i], leftover_bits)
|
||||
|
||||
|
||||
static func get_base_structarraytypes() -> Dictionary:
|
||||
return {
|
||||
'u8': U8.new(),
|
||||
'u16': U16.new(),
|
||||
'u24': U24.new(),
|
||||
'u32': U32.new(),
|
||||
's8': S8.new(),
|
||||
's16': S16.new(),
|
||||
's24': S24.new(),
|
||||
's32': S32.new(),
|
||||
}
|
||||
|
||||
|
||||
static func get_structarraytype(type: String, existing_structs: Dictionary):
|
||||
var tokens := type.split(' ', false)
|
||||
var t: String = tokens[-1]
|
||||
var inner_type
|
||||
if t in existing_structs:
|
||||
inner_type = existing_structs[t]
|
||||
elif t[0] == 'u':
|
||||
var b := int(t.substr(1))
|
||||
if b > 0:
|
||||
inner_type = UBits.new(b)
|
||||
existing_structs['u%d'%b] = inner_type # Cache it for future use
|
||||
if not inner_type:
|
||||
print_debug('typestring "%s" has no matches for "%s" in existing structs' % [type, t])
|
||||
return
|
||||
|
||||
var l := len(tokens)
|
||||
if l == 1:
|
||||
return inner_type
|
||||
# Our parsing goal is to turn 'a of b of c of d' into StructArrayType<StructArrayType<StructArrayType<d, c>, b>, a>
|
||||
# Our strategy is to parse backwards over the tokens, changing inner_type at each point
|
||||
# a of b of c of (d)
|
||||
# a of b of (c of d)
|
||||
# a of (b of c of d)
|
||||
# (a of b of c of d)
|
||||
# done
|
||||
var i := l-2
|
||||
while i > -1:
|
||||
match tokens[i]:
|
||||
'of':
|
||||
i -= 1
|
||||
var l1 = int(tokens[i])
|
||||
if l1 > 1:
|
||||
inner_type = StructArrayType.new(l1, inner_type) # Might be worth caching these later on if we use them more
|
||||
i -= 1
|
||||
var k:
|
||||
print_debug('Invalid keyword used in type designator: "%s"' % k)
|
||||
return
|
||||
return inner_type
|
Loading…
Reference in New Issue