-- Exports an ini file for Dwarf Therapist. local utils = require 'utils' local ms = require 'memscan' -- Utility functions local globals = df.global local global_addr = dfhack.internal.getAddress local os_type = dfhack.getOSType() local rdelta = dfhack.internal.getRebaseDelta() local lines = {} local complete = true local function header(name) table.insert(lines, '') table.insert(lines, '['..name..']') end local function value(name,addr) local line if not addr then complete = false line = name..'=0x0' elseif addr < 0x10000 then line = string.format('%s=0x%04x',name,addr) else line = string.format('%s=0x%08x',name,addr) end table.insert(lines, line) end local function address(name,base,field,...) local addr if base == globals then addr = global_addr(field) if addr and select('#',...) > 0 then _,addr = df.sizeof(ms.field_ref(base,field,...)) end elseif base._kind == 'class-type' then -- field_offset crashes with classes due to vtable problems, -- so we have to create a real temporary object here. local obj = df.new(base) if obj then local _,a1 = df.sizeof(obj) local _,a2 = df.sizeof(ms.field_ref(obj,field,...)) addr = a2-a1 obj:delete() end else addr = ms.field_offset(base,field,...) end value(name, addr) end -- List of actual values header('addresses') address('translation_vector',globals,'world','raws','language','translations') address('language_vector',globals,'world','raws','language','words') address('creature_vector',globals,'world','units','all') address('active_creature_vector',globals,'world','units','active') address('dwarf_race_index',globals,'ui','race_id') address('squad_vector',globals,'world','squads','all') address('current_year',globals,'cur_year') address('cur_year_tick',globals,'cur_year_tick') address('dwarf_civ_index',globals,'ui','civ_id') address('races_vector',globals,'world','raws','creatures','all') address('reactions_vector',globals,'world','raws','reactions') address('events_vector',globals,'world','history','events') address('historical_figures_vector',globals,'world','history','figures') address('fake_identities_vector',globals,'world','identities','all') address('fortress_entity',globals,'ui','main','fortress_entity') address('historical_entities_vector',globals,'world','entities','all') address('itemdef_weapons_vector',globals,'world','raws','itemdefs','weapons') address('itemdef_trap_vector',globals,'world','raws','itemdefs','trapcomps') address('itemdef_toy_vector',globals,'world','raws','itemdefs','toys') address('itemdef_tool_vector',globals,'world','raws','itemdefs','tools') address('itemdef_instrument_vector',globals,'world','raws','itemdefs','instruments') address('itemdef_armor_vector',globals,'world','raws','itemdefs','armor') address('itemdef_ammo_vector',globals,'world','raws','itemdefs','ammo') address('itemdef_siegeammo_vector',globals,'world','raws','itemdefs','siege_ammo') address('itemdef_glove_vector',globals,'world','raws','itemdefs','gloves') address('itemdef_shoe_vector',globals,'world','raws','itemdefs','shoes') address('itemdef_shield_vector',globals,'world','raws','itemdefs','shields') address('itemdef_helm_vector',globals,'world','raws','itemdefs','helms') address('itemdef_pant_vector',globals,'world','raws','itemdefs','pants') address('itemdef_food_vector',globals,'world','raws','itemdefs','food') address('colors_vector',globals,'world','raws','language','colors') address('shapes_vector',globals,'world','raws','language','shapes') address('base_materials',globals,'world','raws','mat_table','builtin') address('inorganics_vector',globals,'world','raws','inorganics') address('plants_vector',globals,'world','raws','plants','all') address('material_templates_vector',globals,'world','raws','material_templates') address('all_syndromes_vector',globals,'world','raws','syndromes','all') address('world_data',globals,'world','world_data') address('active_sites_vector',df.world_data,'active_site') address('world_site_type',df.world_site,'type') address('weapons_vector',globals,'world','items','other','WEAPON') address('shields_vector',globals,'world','items','other', 'SHIELD') address('quivers_vector',globals,'world','items','other', 'QUIVER') address('crutches_vector',globals,'world','items','other', 'CRUTCH') address('backpacks_vector',globals,'world','items','other', 'BACKPACK') address('ammo_vector',globals,'world','items','other', 'AMMO') address('flasks_vector',globals,'world','items','other', 'FLASK') address('pants_vector',globals,'world','items','other', 'PANTS') address('armor_vector',globals,'world','items','other', 'ARMOR') address('shoes_vector',globals,'world','items','other', 'SHOES') address('helms_vector',globals,'world','items','other', 'HELM') address('gloves_vector',globals,'world','items','other', 'GLOVES') address('artifacts_vector',globals,'world','artifacts','all') header('offsets') address('word_table',df.language_translation,'words') value('string_buffer_offset', 0x0000) header('word_offsets') address('base',df.language_word,'word') address('noun_singular',df.language_word,'forms','Noun') address('noun_plural',df.language_word,'forms','NounPlural') address('adjective',df.language_word,'forms','Adjective') address('verb',df.language_word,'forms','Verb') address('present_simple_verb',df.language_word,'forms','Verb3rdPerson') address('past_simple_verb',df.language_word,'forms','VerbPast') address('past_participle_verb',df.language_word,'forms','VerbPassive') address('present_participle_verb',df.language_word,'forms','VerbGerund') address('words',df.language_name,'words') address('word_type',df.language_name,'parts_of_speech') address('language_id',df.language_name,'language') header('general_ref_offsets') --WARNING below value should be: "general_ref::vtable","1","0x8","0x4","vmethod","getType","general_ref_type","" value('ref_type',0x8) address('artifact_id',df.general_ref_artifact,'artifact_id') address('item_id',df.general_ref_item,'item_id') header('race_offsets') address('name_singular',df.creature_raw,'name',0) address('name_plural',df.creature_raw,'name',1) address('adjective',df.creature_raw,'name',2) address('baby_name_singular',df.creature_raw,'general_baby_name',0) address('baby_name_plural',df.creature_raw,'general_baby_name',1) address('child_name_singular',df.creature_raw,'general_child_name',0) address('child_name_plural',df.creature_raw,'general_child_name',1) address('pref_string_vector',df.creature_raw,'prefstring') address('castes_vector',df.creature_raw,'caste') address('pop_ratio_vector',df.creature_raw,'pop_ratio') address('materials_vector',df.creature_raw,'material') address('flags',df.creature_raw,'flags') address('tissues_vector',df.creature_raw,'tissue') header('caste_offsets') address('caste_name',df.caste_raw,'caste_name') address('caste_descr',df.caste_raw,'description') address('caste_trait_ranges',df.caste_raw,'personality','a') address('caste_phys_att_ranges',df.caste_raw,'attributes','phys_att_range') address('baby_age',df.caste_raw,'misc','baby_age') address('child_age',df.caste_raw,'misc','child_age') address('adult_size',df.caste_raw,'misc','adult_size') address('flags',df.caste_raw,'flags') address('body_info',df.caste_raw,'body_info') address('skill_rates',df.caste_raw,'skill_rates') address('caste_att_rates',df.caste_raw,'attributes','phys_att_rates') address('caste_att_caps',df.caste_raw,'attributes','phys_att_cap_perc') address('shearable_tissues_vector',df.caste_raw,'shearable_tissue_layer') address('extracts',df.caste_raw,'extracts','extract_matidx') header('hist_entity_offsets') address('beliefs',df.historical_entity,'resources','values') address('squads',df.historical_entity,'squads') address('positions',df.historical_entity,'positions','own') address('assignments',df.historical_entity,'positions','assignments') address('assign_hist_id',df.entity_position_assignment,'histfig') address('assign_position_id',df.entity_position_assignment,'position_id') address('position_id',df.entity_position,'id') address('position_name',df.entity_position,'name') address('position_female_name',df.entity_position,'name_female') address('position_male_name',df.entity_position,'name_male') header('hist_figure_offsets') address('hist_race',df.historical_figure,'race') address('hist_name',df.historical_figure,'name') address('id',df.historical_figure,'id') address('hist_fig_info',df.historical_figure,'info') address('reputation',df.historical_figure_info,'reputation') address('current_ident',df.historical_figure_info.T_reputation,'cur_identity') address('fake_name',df.identity,'name') address('fake_birth_year',df.identity,'birth_year') address('fake_birth_time',df.identity,'birth_second') address('kills',df.historical_figure_info,'kills') address('killed_race_vector',df.historical_kills,'killed_race') address('killed_undead_vector',df.historical_kills,'killed_undead') address('killed_counts_vector',df.historical_kills,'killed_count') header('hist_event_offsets') address('event_year',df.history_event,'year') address('id',df.history_event,'id') address('killed_hist_id',df.history_event_hist_figure_diedst,'victim_hf') header('item_offsets') if os_type == 'darwin' then value('item_type',0x4) else value('item_type',0x1) end address('item_def',df.item_ammost,'subtype') --currently same for all address('id',df.item,'id') address('general_refs',df.item,'general_refs') address('stack_size',df.item_actual,'stack_size') address('wear',df.item_actual,'wear') address('mat_type',df.item_crafted,'mat_type') address('mat_index',df.item_crafted,'mat_index') address('quality',df.item_crafted,'quality') header('item_subtype_offsets') address('sub_type',df.itemdef,'subtype') address('name',df.itemdef_armorst,'name') address('name_plural',df.itemdef_armorst,'name_plural') address('adjective',df.itemdef_armorst,'name_preplural') header('item_filter_offsets') address('item_subtype',df.item_filter_spec,'item_subtype') address('mat_class',df.item_filter_spec,'material_class') address('mat_type',df.item_filter_spec,'mattype') address('mat_index',df.item_filter_spec,'matindex') header('weapon_subtype_offsets') address('single_size',df.itemdef_weaponst,'two_handed') address('multi_size',df.itemdef_weaponst,'minimum_size') address('ammo',df.itemdef_weaponst,'ranged_ammo') address('melee_skill',df.itemdef_weaponst,'skill_melee') address('ranged_skill',df.itemdef_weaponst,'skill_ranged') header('armor_subtype_offsets') address('layer',df.armor_properties,'layer') address('mat_name',df.itemdef_armorst,'material_placeholder') address('other_armor_level',df.itemdef_helmst,'armorlevel') address('armor_adjective',df.itemdef_armorst,'adjective') address('armor_level',df.itemdef_armorst,'armorlevel') address('chest_armor_properties',df.itemdef_armorst,'props') address('pants_armor_properties',df.itemdef_pantsst,'props') address('other_armor_properties',df.itemdef_helmst,'props') header('material_offsets') address('solid_name',df.material_common,'state_name','Solid') address('liquid_name',df.material_common,'state_name','Liquid') address('gas_name',df.material_common,'state_name','Gas') address('powder_name',df.material_common,'state_name','Powder') address('paste_name',df.material_common,'state_name','Paste') address('pressed_name',df.material_common,'state_name','Pressed') address('flags',df.material_common,'flags') address('inorganic_materials_vector',df.inorganic_raw,'material') address('inorganic_flags',df.inorganic_raw,'flags') header('plant_offsets') address('name',df.plant_raw,'name') address('name_plural',df.plant_raw,'name_plural') address('name_leaf_plural',df.plant_raw,'leaves_plural') address('name_seed_plural',df.plant_raw,'seed_plural') address('materials_vector',df.plant_raw,'material') address('flags',df.plant_raw,'flags') header('descriptor_offsets') address('color_name',df.descriptor_color,'name') address('shape_name_plural',df.descriptor_shape,'name_plural') header('health_offsets') address('parent_id',df.body_part_raw,'con_part_id') address('layers_vector',df.body_part_raw,'layers') address('number',df.body_part_raw,'number') address('names_vector',df.body_part_raw,'name_singular') address('names_plural_vector',df.body_part_raw,'name_plural') address('layer_tissue',df.body_part_layer_raw,'tissue_id') address('layer_global_id',df.body_part_layer_raw,'layer_id') address('tissue_name',df.tissue_template,'tissue_name_singular') address('tissue_flags',df.tissue_template,'flags') header('dwarf_offsets') address('first_name',df.unit,'name','first_name') address('nick_name',df.unit,'name','nickname') address('last_name',df.unit,'name','words') address('custom_profession',df.unit,'custom_profession') address('profession',df.unit,'profession') address('race',df.unit,'race') address('flags1',df.unit,'flags1') address('flags2',df.unit,'flags2') address('flags3',df.unit,'flags3') address('caste',df.unit,'caste') address('sex',df.unit,'sex') address('id',df.unit,'id') address('animal_type',df.unit,'training_level') address('civ',df.unit,'civ_id') address('specific_refs',df.unit,'specific_refs') address('squad_id',df.unit,'military','squad_id') address('squad_position',df.unit,'military','squad_position') address('recheck_equipment',df.unit,'military','pickup_flags') address('mood',df.unit,'mood') address('birth_year',df.unit,'relations','birth_year') address('birth_time',df.unit,'relations','birth_time') address('pet_owner_id',df.unit,'relations','pet_owner_id') address('current_job',df.unit,'job','current_job') address('physical_attrs',df.unit,'body','physical_attrs') address('body_size',df.unit,'appearance','body_modifiers') address('size_info',df.unit,'body','size_info') address('curse',df.unit,'curse','name') address('curse_add_flags1',df.unit,'curse','add_tags1') address('turn_count',df.unit,'curse','time_on_site') address('souls',df.unit,'status','souls') address('states',df.unit,'status','misc_traits') address('labors',df.unit,'status','labors') address('hist_id',df.unit,'hist_figure_id') address('artifact_name',df.unit,'status','artifact_name') address('active_syndrome_vector',df.unit,'syndromes','active') address('syn_sick_flag',df.unit_syndrome,'flags') address('unit_health_info',df.unit,'health') address('temp_mood',df.unit,'counters','soldier_mood') address('counters1',df.unit,'counters','winded') address('counters2',df.unit, 'counters','pain') address('counters3',df.unit, 'counters2','paralysis') address('limb_counters',df.unit,'status2','limbs_stand_max') address('blood',df.unit,'body','blood_max') address('body_component_info',df.unit,'body','components') address('layer_status_vector',df.body_component_info,'layer_status') address('wounds_vector',df.unit,'body','wounds') address('mood_skill',df.unit,'job','mood_skill') address('used_items_vector',df.unit,'used_items') address('affection_level',df.unit_item_use,'affection_level') address('inventory',df.unit,'inventory') address('inventory_item_mode',df.unit_inventory_item,'mode') address('inventory_item_bodypart',df.unit_inventory_item,'body_part_id') header('syndrome_offsets') address('cie_effects',df.syndrome,'ce') address('cie_end',df.creature_interaction_effect,'end') address('cie_first_perc',df.creature_interaction_effect_phys_att_changest,'phys_att_perc') --same for mental address('cie_phys',df.creature_interaction_effect_phys_att_changest,'phys_att_add') address('cie_ment',df.creature_interaction_effect_ment_att_changest,'ment_att_add') address('syn_classes_vector',df.syndrome,'syn_class') address('trans_race_id',df.creature_interaction_effect_body_transformationst,'race') header('unit_wound_offsets') address('parts',df.unit_wound,'parts') address('id',df.unit_wound.T_parts,'body_part_id') address('layer',df.unit_wound.T_parts,'layer_idx') address('general_flags',df.unit_wound,'flags') address('flags1',df.unit_wound.T_parts,'flags1') address('flags2',df.unit_wound.T_parts,'flags2') address('effects_vector',df.unit_wound.T_parts,'effect_type') address('bleeding',df.unit_wound.T_parts,'bleeding') address('pain',df.unit_wound.T_parts,'pain') address('cur_pen',df.unit_wound.T_parts,'cur_penetration_perc') address('max_pen',df.unit_wound.T_parts,'max_penetration_perc') header('soul_details') address('name',df.unit_soul,'name') address('orientation',df.unit_soul,'orientation_flags') address('mental_attrs',df.unit_soul,'mental_attrs') address('skills',df.unit_soul,'skills') address('preferences',df.unit_soul,'preferences') address('personality',df.unit_soul,'personality') address('beliefs',df.unit_personality,'values') address('emotions',df.unit_personality,'emotions') address('goals',df.unit_personality,'dreams') address('goal_realized',df.unit_personality.T_dreams,'unk8') address('traits',df.unit_personality,'traits') address('stress_level',df.unit_personality,'stress_level') header('emotion_offsets') address('emotion_type',df.unit_personality.T_emotions,'type') address('strength',df.unit_personality.T_emotions,'strength') address('thought_id',df.unit_personality.T_emotions,'thought') address('sub_id',df.unit_personality.T_emotions,'subthought') address('level',df.unit_personality.T_emotions,'severity') address('year',df.unit_personality.T_emotions,'year') address('year_tick',df.unit_personality.T_emotions,'year_tick') header('job_details') address('id',df.job,'job_type') address('mat_type',df.job,'mat_type') address('mat_index',df.job,'mat_index') address('mat_category',df.job,'material_category') value('on_break_flag',df.misc_trait_type.OnBreak) address('sub_job_id',df.job,'reaction_name') address('reaction',df.reaction,'name') address('reaction_skill',df.reaction,'skill') header('squad_offsets') address('id',df.squad,'id') address('name',df.squad,'name') address('alias',df.squad,'alias') address('members',df.squad,'positions') address('carry_food',df.squad,'carry_food') address('carry_water',df.squad,'carry_water') address('ammunition',df.squad,'ammunition') address('quiver',df.squad_position,'quiver') address('backpack',df.squad_position,'backpack') address('flask',df.squad_position,'flask') address('armor_vector',df.squad_position,'uniform','body') address('helm_vector',df.squad_position,'uniform','head') address('pants_vector',df.squad_position,'uniform','pants') address('gloves_vector',df.squad_position,'uniform','gloves') address('shoes_vector',df.squad_position,'uniform','shoes') address('shield_vector',df.squad_position,'uniform','shield') address('weapon_vector',df.squad_position,'uniform','weapon') address('uniform_item_filter',df.squad_uniform_spec,'item_filter') address('uniform_indiv_choice',df.squad_uniform_spec,'indiv_choice') -- Final creation of the file local out = io.open('therapist.ini', 'w') out:write('[info]\n') -- TODO: add an api function to retrieve the checksum out:write('checksum=<>\n') out:write('version_name='..dfhack.getDFVersion()..'\n') out:write('complete='..(complete and 'true' or 'false')..'\n') for i,v in ipairs(lines) do out:write(v..'\n') end out:write[[ [valid_flags_2] size=0 [invalid_flags_1] size=10 1\name=a zombie 1\value=0x00001000 2\name=a skeleton 2\value=0x00002000 3\name=a merchant 3\value=0x00000040 4\name=outpost liason or diplomat 4\value=0x00000800 5\name=an invader or hostile 5\value=0x00020000 6\name=an invader or hostile 6\value=0x00080000 7\name=resident, invader or ambusher 7\value=0x00600000 8\name=part of a merchant caravan 8\value=0x00000080 9\name="Dead, Jim." 9\value=0x00000002 10\name=marauder 10\value=0x00000010 [invalid_flags_2] size=5 1\name="killed, Jim." 1\value=0x00000080 2\name=from the Underworld. SPOOKY! 2\value=0x00040000 3\name=resident 3\value=0x00080000 4\name=uninvited visitor 4\value=0x00400000 5\name=visitor 5\value=0x00800000 [invalid_flags_3] size=1 1\name=a ghost 1\value=0x00001000 ]] out:close()