From 9adf310d7f162ba58da764bed2c918487c692f6b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 22 May 2012 12:31:37 +0400 Subject: [PATCH 01/60] Update Units::isCitizen after looking at game-over detection code. --- LUA_API.rst | 13 +++++-- Lua API.html | 11 ++++-- library/LuaApi.cpp | 2 ++ library/modules/Units.cpp | 71 ++++++++++++++++++++++++++++++++++----- library/xml | 2 +- 5 files changed, 86 insertions(+), 13 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index b0f1a9f31..9fb37375d 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -726,7 +726,7 @@ Units module * ``dfhack.units.isDead(unit)`` - The unit is completely dead and passive. + The unit is completely dead and passive, or a ghost. * ``dfhack.units.isAlive(unit)`` @@ -734,7 +734,16 @@ Units module * ``dfhack.units.isSane(unit)`` - The unit is capable of rational action, i.e. not dead, insane or zombie. + The unit is capable of rational action, i.e. not dead, insane, zombie, or active werewolf. + +* ``dfhack.units.isDwarf(unit)`` + + The unit is of the correct race of the fortress. + +* ``dfhack.units.isCitizen(unit)`` + + The unit is an alive sane citizen of the fortress; wraps the + same checks the game uses to decide game-over by extinction. * ``dfhack.units.getAge(unit[,true_age])`` diff --git a/Lua API.html b/Lua API.html index 4b763af25..1576652d6 100644 --- a/Lua API.html +++ b/Lua API.html @@ -960,13 +960,20 @@ a lua list containing them.

Returns the nemesis record of the unit if it has one, or nil.

  • dfhack.units.isDead(unit)

    -

    The unit is completely dead and passive.

    +

    The unit is completely dead and passive, or a ghost.

  • dfhack.units.isAlive(unit)

    The unit isn't dead or undead.

  • dfhack.units.isSane(unit)

    -

    The unit is capable of rational action, i.e. not dead, insane or zombie.

    +

    The unit is capable of rational action, i.e. not dead, insane, zombie, or active werewolf.

    +
  • +
  • dfhack.units.isDwarf(unit)

    +

    The unit is of the correct race of the fortress.

    +
  • +
  • dfhack.units.isCitizen(unit)

    +

    The unit is an alive sane citizen of the fortress; wraps the +same checks the game uses to decide game-over by extinction.

  • dfhack.units.getAge(unit[,true_age])

    Returns the age of the unit in years as a floating-point value. diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 6a550db87..8e8a3c4e3 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -715,6 +715,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, isDead), WRAPM(Units, isAlive), WRAPM(Units, isSane), + WRAPM(Units, isDwarf), + WRAPM(Units, isCitizen), WRAPM(Units, getAge), WRAPM(Units, getProfessionName), WRAPM(Units, getCasteProfessionName), diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index dd26109e7..ee383cc07 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -613,12 +613,45 @@ df::nemesis_record *Units::getNemesis(df::unit *unit) return NULL; } +static bool casteFlagSet(int race, int caste, df::caste_raw_flags flag) +{ + auto creature = df::creature_raw::find(race); + if (!creature) + return false; + + auto craw = vector_get(creature->caste, caste); + if (!craw) + return false; + + return craw->flags.is_set(flag); +} + +static bool isCrazed(df::unit *unit) +{ + if (unit->flags3.bits.scuttle) + return false; + if (unit->curse.rem_tags1.bits.CRAZED) + return false; + if (unit->curse.add_tags1.bits.CRAZED) + return true; + return casteFlagSet(unit->race, unit->caste, caste_raw_flags::CRAZED); +} + +static bool isOpposedToLife(df::unit *unit) +{ + if (unit->curse.rem_tags1.bits.OPPOSED_TO_LIFE) + return false; + if (unit->curse.add_tags1.bits.OPPOSED_TO_LIFE) + return true; + return casteFlagSet(unit->race, unit->caste, caste_raw_flags::CANNOT_UNDEAD); +} bool DFHack::Units::isDead(df::unit *unit) { CHECK_NULL_POINTER(unit); - return unit->flags1.bits.dead; + return unit->flags1.bits.dead || + unit->flags3.bits.ghostly; } bool DFHack::Units::isAlive(df::unit *unit) @@ -636,8 +669,11 @@ bool DFHack::Units::isSane(df::unit *unit) if (unit->flags1.bits.dead || unit->flags3.bits.ghostly || - unit->curse.add_tags1.bits.OPPOSED_TO_LIFE || - unit->curse.add_tags1.bits.CRAZED) + isOpposedToLife(unit) || + unit->unknown8.unk2) + return false; + + if (unit->unknown8.normal_race == unit->unknown8.were_race && isCrazed(unit)) return false; switch (unit->mood) @@ -657,19 +693,38 @@ bool DFHack::Units::isCitizen(df::unit *unit) { CHECK_NULL_POINTER(unit); + // Copied from the conditions used to decide game over, + // except that the game appears to let melancholy/raving + // dwarves count as citizens. + + if (!isDwarf(unit) || !isSane(unit)) + return false; + + if (unit->flags1.bits.marauder || + unit->flags1.bits.invader_origin || + unit->flags1.bits.active_invader || + unit->flags1.bits.forest || + unit->flags1.bits.merchant || + unit->flags1.bits.diplomat) + return false; + + if (unit->flags1.bits.tame) + return true; + return unit->civ_id == ui->civ_id && - !unit->flags1.bits.merchant && - !unit->flags1.bits.diplomat && + unit->civ_id != -1 && + !unit->flags2.bits.underworld && !unit->flags2.bits.resident && - !unit->flags1.bits.dead && - !unit->flags3.bits.ghostly; + !unit->flags2.bits.visitor_uninvited && + !unit->flags2.bits.visitor; } bool DFHack::Units::isDwarf(df::unit *unit) { CHECK_NULL_POINTER(unit); - return unit->race == ui->race_id; + return unit->race == ui->race_id || + unit->unknown8.normal_race == ui->race_id; } double DFHack::Units::getAge(df::unit *unit, bool true_age) diff --git a/library/xml b/library/xml index 234d0f57a..d991d47b0 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 234d0f57a927f306f2052fc2f45d38b3201ddee6 +Subproject commit d991d47b0f6709205c4e333bb86edb3dcf656429 From 65349039f0f735224982bece5ac294300915cb8d Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 22 May 2012 17:50:48 +0400 Subject: [PATCH 02/60] Fix workflow bug: properly deduce the output material of BrewDrink jobs. --- plugins/workflow.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 1092c86ae..dbf546070 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -965,6 +965,9 @@ static void compute_job_outputs(color_ostream &out, ProtectedJob *pj) mat.decode(mat.plant->material_defs.type_##tag, \ mat.plant->material_defs.idx_##tag); \ else mat.decode(-1); + case BrewDrink: + PLANT_PROCESS_MAT(DRINK, drink); + break; case MillPlants: PLANT_PROCESS_MAT(MILL, mill); break; From 705b60ca274ba5a4cb916a93aa439fd75b2f6874 Mon Sep 17 00:00:00 2001 From: Rinin Date: Tue, 22 May 2012 19:22:36 +0400 Subject: [PATCH 03/60] Fixed BrewDrink products counting --- plugins/workflow.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 1092c86ae..97be009de 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -968,6 +968,9 @@ static void compute_job_outputs(color_ostream &out, ProtectedJob *pj) case MillPlants: PLANT_PROCESS_MAT(MILL, mill); break; + case BrewDrink: + PLANT_PROCESS_MAT(DRINK, drink); + break; case ProcessPlants: PLANT_PROCESS_MAT(THREAD, thread); break; From 2ab4e32a53df755a17e69434461490ecfd56a88d Mon Sep 17 00:00:00 2001 From: warmist Date: Thu, 24 May 2012 14:03:05 +0300 Subject: [PATCH 04/60] Edited use_mode in moveToBuilding. It looks like df uses either 0 or 2 and 2 is structural. --- scripts/putontable.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/putontable.lua b/scripts/putontable.lua index 8156a54be..5002c8e6b 100644 --- a/scripts/putontable.lua +++ b/scripts/putontable.lua @@ -23,5 +23,5 @@ if #items==0 then error("No items found!") end for k,v in pairs(items) do - dfhack.items.moveToBuilding(v,build,1) + dfhack.items.moveToBuilding(v,build,0) end From 800cbca578a08dd7bc70441ecd5a01a380bd7118 Mon Sep 17 00:00:00 2001 From: Quietust Date: Fri, 25 May 2012 21:31:47 -0500 Subject: [PATCH 05/60] Some improvements to showmood - show relevant skill, cleanup messages --- plugins/showmood.cpp | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp index 7407e6af1..7926e2ac5 100644 --- a/plugins/showmood.cpp +++ b/plugins/showmood.cpp @@ -100,37 +100,37 @@ command_result df_showmood (color_ostream &out, vector & parameters) switch (job->job_type) { case job_type::StrangeMoodCrafter: - out.print("become a Craftsdwarf (or Engraver)"); + out.print("claim a Craftsdwarf's Workshop"); break; case job_type::StrangeMoodJeweller: - out.print("become a Jeweler"); + out.print("claim a Jeweler's Workshop"); break; case job_type::StrangeMoodForge: - out.print("become a Metalworker"); + out.print("claim a Metalsmith's Forge"); break; case job_type::StrangeMoodMagmaForge: - out.print("become a Metalworker using a Magma Forge"); + out.print("claim a Magma Forge"); break; case job_type::StrangeMoodCarpenter: - out.print("become a Carpenter"); + out.print("claim a Carpenter's Workshop"); break; case job_type::StrangeMoodMason: - out.print("become a Mason (or Miner)"); + out.print("claim a Mason's Workshop"); break; case job_type::StrangeMoodBowyer: - out.print("become a Bowyer"); + out.print("claim a Boywer's Workshop"); break; case job_type::StrangeMoodTanner: - out.print("become a Leatherworker (or Tanner)"); + out.print("claim a Leather Works"); break; case job_type::StrangeMoodWeaver: - out.print("become a Clothier (or Weaver)"); + out.print("claim a Clothier's Shop"); break; case job_type::StrangeMoodGlassmaker: - out.print("become a Glassmaker"); + out.print("claim a Glass Furnace"); break; case job_type::StrangeMoodMechanics: - out.print("become a Mechanic"); + out.print("claim a Mechanic's Workshop"); break; case job_type::StrangeMoodBrooding: out.print("enter a macabre mood?"); @@ -142,20 +142,28 @@ command_result df_showmood (color_ostream &out, vector & parameters) out.print("do something else..."); break; } + out.print(" and become a legendary %s", ENUM_ATTR_STR(job_skill, caption_noun, unit->job.mood_skill)); + if (unit->mood == mood_type::Possessed) + out.print(" (but not really)"); break; - default: out.print("insane?"); break; } + out.print(".\n"); + if (unit->sex) + out.print("He has "); + else + out.print("She has "); if (building) { string name; building->getName(&name); - out.print(" and has claimed a %s\n", name.c_str()); + out.print("claimed a %s and wants", name.c_str()); } else - out.print(" and has not yet claimed a workshop\n"); + out.print("not yet claimed a workshop but will want"); + out.print(" the following items:\n"); for (size_t i = 0; i < job->job_items.size(); i++) { From e72bf1ac9aa4d9c56f8435c5b86f538c06d651b9 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 26 May 2012 14:49:27 +0400 Subject: [PATCH 06/60] Sync to changes in df-structures. --- library/modules/Materials.cpp | 4 ++-- library/xml | 2 +- plugins/devel/counters.cpp | 2 +- plugins/forceequip.cpp | 22 +++++++++++----------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index f49dd82e2..f8f99f81e 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -761,8 +761,8 @@ bool Materials::ReadCreatureTypesEx (void) { df::body_part_raw *bp = ca->body_info.body_parts[k]; t_bodypart part; - part.id = bp->part_code; - part.category = bp->part_name; + part.id = bp->token; + part.category = bp->category; caste.bodypart.push_back(part); } using namespace df::enums::mental_attribute_type; diff --git a/library/xml b/library/xml index d991d47b0..7d5c349a4 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit d991d47b0f6709205c4e333bb86edb3dcf656429 +Subproject commit 7d5c349a40dc1f211de2d37aeece4a3d9d631ed2 diff --git a/plugins/devel/counters.cpp b/plugins/devel/counters.cpp index 66d8c63a4..332945677 100644 --- a/plugins/devel/counters.cpp +++ b/plugins/devel/counters.cpp @@ -25,7 +25,7 @@ command_result df_counters (color_ostream &out, vector & parameters) for (size_t i = 0; i < counters.size(); i++) { auto counter = counters[i]; - out.print("%i (%s): %i\n", counter->id, ENUM_KEY_STR(unit_misc_trait::T_id, counter->id).c_str(), counter->value); + out.print("%i (%s): %i\n", counter->id, ENUM_KEY_STR(misc_trait_type, counter->id).c_str(), counter->value); } return CR_OK; diff --git a/plugins/forceequip.cpp b/plugins/forceequip.cpp index 0d493143b..723f29474 100644 --- a/plugins/forceequip.cpp +++ b/plugins/forceequip.cpp @@ -292,7 +292,7 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u else if (bpIndex < unit->body.body_plan->body_parts.size()) { // The current body part is not the one that was specified in the function call, but we can keep searching - if (verbose) { Core::printerr("Found bodypart %s; not a match; continuing search.\n", currPart->part_code.c_str()); } + if (verbose) { Core::printerr("Found bodypart %s; not a match; continuing search.\n", currPart->token.c_str()); } continue; } else @@ -302,35 +302,35 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u return false; } - if (verbose) { Core::print("Inspecting bodypart %s.\n", currPart->part_code.c_str()); } + if (verbose) { Core::print("Inspecting bodypart %s.\n", currPart->token.c_str()); } // Inspect the current bodypart if (item->getType() == df::enums::item_type::GLOVES && currPart->flags.is_set(df::body_part_template_flags::GRASP) && ((item->getGloveHandedness() == const_GloveLeftHandedness && currPart->flags.is_set(df::body_part_template_flags::LEFT)) || (item->getGloveHandedness() == const_GloveRightHandedness && currPart->flags.is_set(df::body_part_template_flags::RIGHT)))) { - if (verbose) { Core::print("Hand found (%s)...", currPart->part_code.c_str()); } + if (verbose) { Core::print("Hand found (%s)...", currPart->token.c_str()); } } else if (item->getType() == df::enums::item_type::HELM && currPart->flags.is_set(df::body_part_template_flags::HEAD)) { - if (verbose) { Core::print("Head found (%s)...", currPart->part_code.c_str()); } + if (verbose) { Core::print("Head found (%s)...", currPart->token.c_str()); } } else if (item->getType() == df::enums::item_type::ARMOR && currPart->flags.is_set(df::body_part_template_flags::UPPERBODY)) { - if (verbose) { Core::print("Upper body found (%s)...", currPart->part_code.c_str()); } + if (verbose) { Core::print("Upper body found (%s)...", currPart->token.c_str()); } } else if (item->getType() == df::enums::item_type::PANTS && currPart->flags.is_set(df::body_part_template_flags::LOWERBODY)) { - if (verbose) { Core::print("Lower body found (%s)...", currPart->part_code.c_str()); } + if (verbose) { Core::print("Lower body found (%s)...", currPart->token.c_str()); } } else if (item->getType() == df::enums::item_type::SHOES && currPart->flags.is_set(df::body_part_template_flags::STANCE)) { - if (verbose) { Core::print("Foot found (%s)...", currPart->part_code.c_str()); } + if (verbose) { Core::print("Foot found (%s)...", currPart->token.c_str()); } } else if (targetBodyPart && ignoreRestrictions) { // The BP in question would normally be considered ineligible for equipment. But since it was deliberately specified by the user, we'll proceed anyways. - if (verbose) { Core::print("Non-standard bodypart found (%s)...", targetBodyPart->part_code.c_str()); } + if (verbose) { Core::print("Non-standard bodypart found (%s)...", targetBodyPart->token.c_str()); } } else if (targetBodyPart) { @@ -538,16 +538,16 @@ command_result df_forceequip(color_ostream &out, vector & parameters) { // Tentatively assume that the part is a match targetBodyPart = targetUnit->body.body_plan->body_parts.at(bpIndex); - if (targetBodyPart->part_code.compare(targetBodyPartCode) == 0) + if (targetBodyPart->token.compare(targetBodyPartCode) == 0) { // It is indeed a match; exit the loop (while leaving the variable populated) - if (verbose) { out.print("Matching bodypart (%s) found.\n", targetBodyPart->part_name.c_str()); } + if (verbose) { out.print("Matching bodypart (%s) found.\n", targetBodyPart->token.c_str()); } break; } else { // Not a match; nullify the variable (it will get re-populated on the next pass through the loop) - if (verbose) { out.printerr("Bodypart \"%s\" does not match \"%s\".\n", targetBodyPart->part_code.c_str(), targetBodyPartCode.c_str()); } + if (verbose) { out.printerr("Bodypart \"%s\" does not match \"%s\".\n", targetBodyPart->token.c_str(), targetBodyPartCode.c_str()); } targetBodyPart = NULL; } } From f71a843c6e00705bb2031643e1d93e6fd7442063 Mon Sep 17 00:00:00 2001 From: Quietust Date: Mon, 28 May 2012 11:06:23 -0500 Subject: [PATCH 07/60] Don't throw a fatal exception just because os-type isn't something we recognize - ignore it and move on --- library/VersionInfoFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/VersionInfoFactory.cpp b/library/VersionInfoFactory.cpp index 36dbd9aae..972ab31b4 100644 --- a/library/VersionInfoFactory.cpp +++ b/library/VersionInfoFactory.cpp @@ -107,7 +107,7 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem) } else { - throw Error::SymbolsXmlBadAttribute("os-type"); + return; // ignore it if it's invalid } // process additional entries From bcb698a5b45ce40c29215f0e38f36b22122ff969 Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 29 May 2012 16:10:33 +0200 Subject: [PATCH 08/60] ruby: have codegen compute struct offsets directly --- plugins/ruby/CMakeLists.txt | 18 +- plugins/ruby/codegen.pl | 461 ++++++++++++++++++++---------------- 2 files changed, 256 insertions(+), 223 deletions(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index 7057cb2dc..f28f269df 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -1,23 +1,9 @@ find_package(Ruby) if(RUBY_FOUND) - ADD_CUSTOM_COMMAND( - OUTPUT ruby-autogen.cpp - COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.cpp - DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl - ) - ADD_EXECUTABLE(ruby-autogen ruby-autogen.cpp) - if(CMAKE_COMPILER_IS_GNUCC) - set_target_properties (ruby-autogen PROPERTIES COMPILE_FLAGS "-Wno-invalid-offsetof") - endif(CMAKE_COMPILER_IS_GNUCC) - ADD_CUSTOM_COMMAND( - OUTPUT ruby-autogen.offsets - COMMAND ruby-autogen ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets - DEPENDS ruby-autogen - ) ADD_CUSTOM_COMMAND( OUTPUT ruby-autogen.rb - COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets ${CMAKE_CURRENT_SOURCE_DIR}/ruby-memstruct.rb - DEPENDS ruby-autogen.offsets ruby-memstruct.rb + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb ${CMAKE_CURRENT_SOURCE_DIR}/ruby-memstruct.rb + DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl ) ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb) include_directories("${dfhack_SOURCE_DIR}/depends/tthread" ${RUBY_INCLUDE_PATH}) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 5c60fcb41..53c67fd99 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -6,9 +6,13 @@ use warnings; use XML::LibXML; our @lines_rb; -my @lines_cpp; -my @include_cpp; -my %offsets; + +my $os; +if ($^O =~ /linux/i) { + $os = 'linux'; +} else { + $os = 'windows'; +} sub indent_rb(&) { my ($sub) = @_; @@ -39,6 +43,7 @@ my %item_renderer = ( ); my %global_types; +our $current_typename; sub render_global_enum { my ($name, $type) = @_; @@ -112,10 +117,6 @@ sub render_enum_fields { sub render_global_bitfield { my ($name, $type) = @_; - push @lines_cpp, "}" if @include_cpp; - push @lines_cpp, "void cpp_$name(FILE *fout) {"; - push @include_cpp, $name; - my $rbname = rb_ucase($name); push @lines_rb, "class $rbname < MemHack::Compound"; indent_rb { @@ -128,7 +129,7 @@ sub render_bitfield_fields { push @lines_rb, "field(:_whole, 0) {"; indent_rb { - render_item_number($type, ''); + render_item_number($type); }; push @lines_rb, "}"; @@ -152,25 +153,8 @@ sub render_bitfield_fields { } -sub render_global_struct { - my ($name, $type) = @_; - - my $rbname = rb_ucase($name); - - my $cppns = "df::$name"; - push @lines_cpp, "}" if @include_cpp; - push @lines_cpp, "void cpp_$name(FILE *fout) {"; - push @include_cpp, $name; - - push @lines_rb, "class $rbname < MemHack::Compound"; - indent_rb { - my $sz = query_cpp("sizeof($cppns)"); - push @lines_rb, "sizeof $sz"; - render_struct_fields($type, "$cppns"); - }; - push @lines_rb, "end\n"; -} my %seen_class; +our $compound_off; sub render_global_class { my ($name, $type) = @_; @@ -183,8 +167,12 @@ sub render_global_class { return if $seen_class{$name}; $seen_class{$name}++; + local $compound_off = 0; + my $rtti_name; - if ($type->getAttribute('ld:meta') eq 'class-type') { + my $meta = $type->getAttribute('ld:meta'); + if ($meta eq 'class-type') { + $compound_off = 4; $rtti_name = $type->getAttribute('original-name') || $type->getAttribute('type-name') || $name; @@ -192,39 +180,42 @@ sub render_global_class { my $rbparent = ($parent ? rb_ucase($parent) : 'MemHack::Compound'); - my $cppns = "df::$name"; - push @lines_cpp, "}" if @include_cpp; - push @lines_cpp, "void cpp_$name(FILE *fout) {"; - push @include_cpp, $name; + $compound_off = sizeof($global_types{$parent}) if $parent; + local $current_typename = $rbname; push @lines_rb, "class $rbname < $rbparent"; indent_rb { - my $sz = query_cpp("sizeof($cppns)"); + my $sz = sizeof($type); + # see comment is sub sizeof ; but gcc has sizeof(cls) aligned + $sz = align_field($sz, 4) if $os eq 'linux' and $meta eq 'class-type'; push @lines_rb, "sizeof $sz"; push @lines_rb, "rtti_classname :$rtti_name" if $rtti_name; - render_struct_fields($type, "$cppns"); + render_struct_fields($type); my $vms = $type->findnodes('child::virtual-methods')->[0]; render_class_vmethods($vms) if $vms; }; push @lines_rb, "end\n"; } sub render_struct_fields { - my ($type, $cppns) = @_; + my ($type) = @_; + my $isunion = $type->getAttribute('is-union'); for my $field ($type->findnodes('child::ld:field')) { my $name = $field->getAttribute('name'); $name = $field->getAttribute('ld:anon-name') if (!$name); if (!$name and $field->getAttribute('ld:anon-compound')) { - render_struct_fields($field, $cppns); + render_struct_fields($field); + } else { + $compound_off = align_field($compound_off, get_field_align($field)); + if ($name) { + push @lines_rb, "field(:$name, $compound_off) {"; + indent_rb { + render_item($field); + }; + push @lines_rb, "}"; + } } - next if (!$name); - my $offset = get_offset($cppns, $name); - - push @lines_rb, "field(:$name, $offset) {"; - indent_rb { - render_item($field, "$cppns"); - }; - push @lines_rb, "}"; + $compound_off += sizeof($field) if (!$isunion); } } sub render_class_vmethods { @@ -255,11 +246,10 @@ sub render_class_vmethods { push @lines_rb, 'end'; } # on linux, the destructor uses 2 entries - $voff += 4 if $meth->getAttribute('is-destructor') and $^O =~ /linux/i; + $voff += 4 if $meth->getAttribute('is-destructor') and $os eq 'linux'; $voff += 4; } } - sub render_class_vmethod_ret { my ($call, $ret) = @_; if (!$ret) { @@ -283,9 +273,9 @@ sub render_class_vmethod_ret { if ($retsubtype eq 'bool') { push @lines_rb, "(val & 1) != 0"; } elsif ($ret->getAttribute('ld:unsigned')) { - push @lines_rb, "val & ((1 << $retbits) - 1)"; + push @lines_rb, "val & ((1 << $retbits) - 1)"; # if $retbits != 32; } else { # signed - push @lines_rb, "val &= ((1 << $retbits) - 1)"; + push @lines_rb, "val &= ((1 << $retbits) - 1)"; # if $retbits != 32; push @lines_rb, "((val >> ($retbits-1)) & 1) == 0 ? val : val - (1 << $retbits)"; } } elsif ($retmeta eq 'pointer') { @@ -308,9 +298,8 @@ sub render_global_objects { my $sname = 'global_objects'; my $rbname = rb_ucase($sname); - push @lines_cpp, "}" if @include_cpp; - push @lines_cpp, "void cpp_$sname(FILE *fout) {"; - push @include_cpp, $sname; + local $compound_off = 0; + local $current_typename = 'Global'; push @lines_rb, "class $rbname < MemHack::Compound"; indent_rb { @@ -323,7 +312,7 @@ sub render_global_objects { push @lines_rb, "field(:$oname, addr) {"; my $item = $obj->findnodes('child::ld:item')->[0]; indent_rb { - render_item($item, 'df::global'); + render_item($item); }; push @lines_rb, "}"; }; @@ -344,28 +333,209 @@ sub render_global_objects { } +sub align_field { + my ($off, $fldalign) = @_; + my $dt = $off % $fldalign; + $off += $fldalign - $dt if $dt > 0; + return $off; +} + +my %align_cache; +my %sizeof_cache; +sub get_field_align { + my ($field) = @_; + my $al = 4; + my $meta = $field->getAttribute('ld:meta'); + if ($meta eq 'number') { + $al = $field->getAttribute('ld:bits')/8; + $al = 4 if $al > 4; + } elsif ($meta eq 'global') { + my $typename = $field->getAttribute('type-name'); + return $align_cache{$typename} if $align_cache{$typename}; + my $g = $global_types{$typename}; + my $st = $field->getAttribute('ld:subtype') || ''; + if ($st eq 'bitfield' or $st eq 'enum' or $g->getAttribute('ld:meta') eq 'bitfield-type') { + my $base = $field->getAttribute('base-type') || $g->getAttribute('base-type') || 'uint32_t'; + print "$st type $base\n" if $base !~ /int(\d+)_t/; + # dont cache, field->base-type may differ + return $1/8; + } + $al = 1; + for my $gf ($g->findnodes('child::ld:field')) { + my $fal = get_field_align($gf); + $al = $fal if $fal > $al; + } + $align_cache{$typename} = $al; + } elsif ($meta eq 'compound') { + my $st = $field->getAttribute('ld:subtype') || ''; + if ($st eq 'bitfield' or $st eq 'enum') { + my $base = $field->getAttribute('base-type') || 'uint32_t'; + print "$st type $base\n" if $base !~ /int(\d+)_t/; + return $1/8; + } + $al = 1; + for my $f ($field->findnodes('child::ld:field')) { + my $fal = get_field_align($f); + $al = $fal if $fal > $al; + } + } elsif ($meta eq 'static-array') { + my $tg = $field->findnodes('child::ld:item')->[0]; + $al = get_field_align($tg); + } elsif ($meta eq 'bytes') { + $al = $field->getAttribute('alignment') || 1; + } + return $al; +} + +sub sizeof { + my ($field) = @_; + my $meta = $field->getAttribute('ld:meta'); + if ($meta eq 'number') { + return $field->getAttribute('ld:bits')/8; + } elsif ($meta eq 'pointer') { + return 4; + } elsif ($meta eq 'static-array') { + my $count = $field->getAttribute('count'); + my $tg = $field->findnodes('child::ld:item')->[0]; + return $count * sizeof($tg); + } elsif ($meta eq 'bitfield-type' or $meta eq 'enum-type') { + my $base = $field->getAttribute('base-type') || 'uint32_t'; + print "$meta type $base\n" if $base !~ /int(\d+)_t/; + return $1/8; + } elsif ($meta eq 'global') { + my $typename = $field->getAttribute('type-name'); + return $sizeof_cache{$typename} if $sizeof_cache{$typename}; + my $g = $global_types{$typename}; + my $st = $field->getAttribute('ld:subtype') || ''; + if ($st eq 'bitfield' or $st eq 'enum' or $g->getAttribute('ld:meta') eq 'bitfield-type') { + my $base = $field->getAttribute('base-type') || $g->getAttribute('base-type') || 'uint32_t'; + print "$st type $base\n" if $base !~ /int(\d+)_t/; + return $1/8; + } + return sizeof($g); + } elsif ($meta eq 'class-type') { + my $typename = $field->getAttribute('type-name'); + return $sizeof_cache{$typename} if $sizeof_cache{$typename}; + my $off = 4; + my $parent = $field->getAttribute('inherits-from'); + $off = sizeof($global_types{$parent}) if ($parent); + for my $f ($field->findnodes('child::ld:field')) { + $off = align_field($off, get_field_align($f)); + $off += sizeof($f); + } + # GCC: class a { vtable; char; } ; class b:a { char c2; } -> c2 has offset 5 (Windows MSVC: offset 8) + $off = align_field($off, 4) if $os ne 'linux'; + $sizeof_cache{$typename} = $off; + return $off; + } elsif ($meta eq 'struct-type' or $meta eq 'compound') { + my $typename = $field->getAttribute('type-name'); + return $sizeof_cache{$typename} if $typename and $sizeof_cache{$typename}; + my $st = $field->getAttribute('ld:subtype') || ''; + if ($st eq 'bitfield' or $st eq 'enum') { + my $base = $field->getAttribute('base-type') || 'uint32_t'; + print "$st type $base\n" if $base !~ /int(\d+)_t/; + $sizeof_cache{$typename} = $1/8 if $typename; + return $1/8; + } + if ($field->getAttribute('is-union')) { + my $sz = 0; + for my $f ($field->findnodes('child::ld:field')) { + my $fsz = sizeof($f); + $sz = $fsz if $fsz > $sz; + } + return $sz; + } + my $off = 0; + my $parent = $field->getAttribute('inherits-from'); + $off = sizeof($global_types{$parent}) if ($parent); + my $al = 1; + for my $f ($field->findnodes('child::ld:field')) { + my $fa = get_field_align($f); + $al = $fa if $fa > $al; + $off = align_field($off, $fa); + $off += sizeof($f); + } + $off = align_field($off, $al); + $sizeof_cache{$typename} = $off if $typename; + return $off; + } elsif ($meta eq 'container') { + my $subtype = $field->getAttribute('ld:subtype'); + if ($subtype eq 'stl-vector') { + if ($os eq 'linux') { + return 12; + } elsif ($os eq 'windows') { + return 16; + } else { + print "sizeof stl-vector on $os\n"; + } + } elsif ($subtype eq 'stl-bit-vector') { + if ($os eq 'linux') { + return 20; + } elsif ($os eq 'windows') { + return 20; + } else { + print "sizeof stl-bit-vector on $os\n"; + } + } elsif ($subtype eq 'stl-deque') { + if ($os eq 'linux') { + return 40; + } elsif ($os eq 'windows') { + return 24; + } else { + print "sizeof stl-deque on $os\n"; + } + } elsif ($subtype eq 'df-linked-list') { + return 12; + } elsif ($subtype eq 'df-flagarray') { + return 8; + } elsif ($subtype eq 'df-array') { + return 8; # XXX 6 ? + } else { + print "sizeof container $subtype\n"; + } + } elsif ($meta eq 'primitive') { + my $subtype = $field->getAttribute('ld:subtype'); + if ($subtype eq 'stl-string') { + if ($os eq 'linux') { + return 4; + } elsif ($os eq 'windows') { + return 28; + } else { + print "sizeof stl-string on $os\n"; + } + print "sizeof stl-string\n"; + } else { + print "sizeof primitive $subtype\n"; + } + } elsif ($meta eq 'bytes') { + return $field->getAttribute('size'); + } else { + print "sizeof $meta\n"; + } +} + sub render_item { - my ($item, $pns) = @_; + my ($item) = @_; return if (!$item); my $meta = $item->getAttribute('ld:meta'); my $renderer = $item_renderer{$meta}; if ($renderer) { - $renderer->($item, $pns); + $renderer->($item); } else { print "no render item $meta\n"; } } sub render_item_global { - my ($item, $pns) = @_; + my ($item) = @_; my $typename = $item->getAttribute('type-name'); my $subtype = $item->getAttribute('ld:subtype'); if ($subtype and $subtype eq 'enum') { - render_item_number($item, $pns); + render_item_number($item); } else { my $rbname = rb_ucase($typename); push @lines_rb, "global :$rbname"; @@ -373,7 +543,7 @@ sub render_item_global { } sub render_item_number { - my ($item, $pns) = @_; + my ($item, $classname) = @_; my $subtype = $item->getAttribute('ld:subtype'); my $meta = $item->getAttribute('ld:meta'); @@ -381,13 +551,13 @@ sub render_item_number { my $typename = $item->getAttribute('type-name'); undef $typename if ($meta and $meta eq 'bitfield-type'); $typename = rb_ucase($typename) if $typename; - $typename = $pns if (!$typename and $subtype and $subtype eq 'enum'); # compound enum + $typename = $classname if (!$typename and $subtype and $subtype eq 'enum'); # compound enum $initvalue = 1 if ($initvalue and $initvalue eq 'true'); $initvalue = ":$initvalue" if ($initvalue and $typename and $initvalue =~ /[a-zA-Z]/); $initvalue ||= 'nil' if $typename; - $subtype = $item->getAttribute('base-type') if (!$subtype or $subtype eq 'enum' or $subtype eq 'bitfield'); + $subtype = $item->getAttribute('base-type') if (!$subtype or $subtype eq 'bitfield' or $subtype eq 'enum'); $subtype = 'int32_t' if (!$subtype); if ($subtype eq 'int64_t') { @@ -418,20 +588,20 @@ sub render_item_number { } sub render_item_compound { - my ($item, $pns) = @_; + my ($item) = @_; - my $cppns = $pns . '::' . $item->getAttribute('ld:typedef-name'); my $subtype = $item->getAttribute('ld:subtype'); - my @namecomponents = split('::', $cppns); - shift @namecomponents; - my $classname = join('_', map { rb_ucase($_) } @namecomponents); + local $compound_off = 0; + my $classname = $current_typename . '_' . rb_ucase($item->getAttribute('ld:typedef-name')); + local $current_typename = $classname; if (!$subtype || $subtype eq 'bitfield') { push @lines_rb, "compound(:$classname) {"; + #push @lines_rb, 'sizeof'; indent_rb { if (!$subtype) { - render_struct_fields($item, $cppns); + render_struct_fields($item); } else { render_bitfield_fields($item); } @@ -453,7 +623,7 @@ sub render_item_compound { } sub render_item_container { - my ($item, $pns) = @_; + my ($item) = @_; my $subtype = $item->getAttribute('ld:subtype'); my $rbmethod = join('_', split('-', $subtype)); @@ -463,11 +633,11 @@ sub render_item_container { if ($rbmethod eq 'df_linked_list') { push @lines_rb, "$rbmethod {"; } else { - my $tglen = get_tglen($tg, $pns); + my $tglen = sizeof($tg) if $tg; push @lines_rb, "$rbmethod($tglen) {"; } indent_rb { - render_item($tg, $pns); + render_item($tg); }; push @lines_rb, "}"; } elsif ($indexenum) { @@ -479,28 +649,28 @@ sub render_item_container { } sub render_item_pointer { - my ($item, $pns) = @_; + my ($item) = @_; my $tg = $item->findnodes('child::ld:item')->[0]; my $ary = $item->getAttribute('is-array'); if ($ary and $ary eq 'true') { - my $tglen = get_tglen($tg, $pns); + my $tglen = sizeof($tg) if $tg; push @lines_rb, "pointer_ary($tglen) {"; } else { push @lines_rb, "pointer {"; } indent_rb { - render_item($tg, $pns); + render_item($tg); }; push @lines_rb, "}"; } sub render_item_staticarray { - my ($item, $pns) = @_; + my ($item) = @_; my $count = $item->getAttribute('count'); my $tg = $item->findnodes('child::ld:item')->[0]; - my $tglen = get_tglen($tg, $pns); + my $tglen = sizeof($tg) if $tg; my $indexenum = $item->getAttribute('index-enum'); if ($indexenum) { $indexenum = rb_ucase($indexenum); @@ -509,13 +679,13 @@ sub render_item_staticarray { push @lines_rb, "static_array($count, $tglen) {"; } indent_rb { - render_item($tg, $pns); + render_item($tg); }; push @lines_rb, "}"; } sub render_item_primitive { - my ($item, $pns) = @_; + my ($item) = @_; my $subtype = $item->getAttribute('ld:subtype'); if ($subtype eq 'stl-string') { @@ -526,7 +696,7 @@ sub render_item_primitive { } sub render_item_bytes { - my ($item, $pns) = @_; + my ($item) = @_; my $subtype = $item->getAttribute('ld:subtype'); if ($subtype eq 'padding') { @@ -538,113 +708,10 @@ sub render_item_bytes { } } -sub get_offset { - my ($cppns, $fname) = @_; - - return query_cpp("offsetof($cppns, $fname)"); -} - -sub get_tglen { - my ($tg, $cppns) = @_; - - if (!$tg) { - return 'nil'; - } - - my $meta = $tg->getAttribute('ld:meta'); - if ($meta eq 'number') { - return $tg->getAttribute('ld:bits')/8; - } elsif ($meta eq 'pointer') { - return 4; - } elsif ($meta eq 'container') { - my $subtype = $tg->getAttribute('ld:subtype'); - if ($subtype eq 'stl-vector') { - return query_cpp("sizeof(std::vector)"); - } elsif ($subtype eq 'df-linked-list') { - return 12; - } else { - print "cannot tglen container $subtype\n"; - } - } elsif ($meta eq 'compound') { - my $cname = $tg->getAttribute('ld:typedef-name'); - return query_cpp("sizeof(${cppns}::$cname)"); - } elsif ($meta eq 'static-array') { - my $count = $tg->getAttribute('count'); - my $ttg = $tg->findnodes('child::ld:item')->[0]; - my $ttgl = get_tglen($ttg, $cppns); - if ($ttgl =~ /^\d+$/) { - return $count * $ttgl; - } else { - return "$count*$ttgl"; - } - } elsif ($meta eq 'global') { - my $typename = $tg->getAttribute('type-name'); - my $subtype = $tg->getAttribute('ld:subtype'); - if ($subtype and $subtype eq 'enum') { - my $base = $tg->getAttribute('base-type') || 'int32_t'; - if ($base eq 'int32_t') { - return 4; - } elsif ($base eq 'int16_t') { - return 2; - } elsif ($base eq 'int8_t') { - return 1; - } else { - print "cannot tglen enum $base\n"; - } - } else { - return query_cpp("sizeof(df::$typename)"); - } - } elsif ($meta eq 'primitive') { - my $subtype = $tg->getAttribute('ld:subtype'); - if ($subtype eq 'stl-string') { - return query_cpp("sizeof(std::string)"); - } else { - print "cannot tglen primitive $subtype\n"; - } - } else { - print "cannot tglen $meta\n"; - } -} - -my %query_cpp_cache; -sub query_cpp { - my ($query) = @_; - - my $ans = $offsets{$query}; - return $ans if (defined($ans)); - - my $cached = $query_cpp_cache{$query}; - return $cached if (defined($cached)); - $query_cpp_cache{$query} = 1; - - push @lines_cpp, " fprintf(fout, \"%s = %d\\n\", \"$query\", $query);"; - return "'$query'"; -} - - - my $input = $ARGV[0] || '../../library/include/df/codegen.out.xml'; -# run once with output = 'ruby-autogen.cpp' -# compile -# execute, save output to 'ruby-autogen.offsets' -# re-run this script with output = 'ruby-autogen.rb' and offsetfile = 'ruby-autogen.offsets' -# delete binary -# delete offsets my $output = $ARGV[1] or die "need output file"; -my $offsetfile = $ARGV[2]; -my $memstruct = $ARGV[3]; - -if ($offsetfile) { - open OF, "<$offsetfile"; - while (my $line = ) { - chomp($line); - my ($key, $val) = split(' = ', $line); - $offsets{$key} = $val; - } - close OF; -} - +my $memstruct = $ARGV[2]; my $doc = XML::LibXML->new()->parse_file($input); $global_types{$_->getAttribute('type-name')} = $_ foreach $doc->findnodes('/ld:data-definition/ld:global-type'); @@ -663,9 +730,7 @@ for my $name (sort { $a cmp $b } keys %global_types) { for my $name (@nonenums) { my $type = $global_types{$name}; my $meta = $type->getAttribute('ld:meta'); - if ($meta eq 'struct-type') { - render_global_struct($name, $type); - } elsif ($meta eq 'class-type') { + if ($meta eq 'struct-type' or $meta eq 'class-type') { render_global_class($name, $type); } elsif ($meta eq 'bitfield-type') { render_global_bitfield($name, $type); @@ -679,30 +744,12 @@ render_global_objects($doc->findnodes('/ld:data-definition/ld:global-object')); open FH, ">$output"; -if ($output =~ /\.cpp$/) { - print FH "#include \"DataDefs.h\"\n"; - print FH "#include \"df/$_.h\"\n" for @include_cpp; - print FH "#include \n"; - print FH "#include \n"; - print FH "$_\n" for @lines_cpp; - print FH "}\n"; - print FH "int main(int argc, char **argv) {\n"; - print FH " FILE *fout;\n"; - print FH " if (argc < 2) return 1;\n"; - print FH " fout = fopen(argv[1], \"w\");\n"; - print FH " cpp_$_(fout);\n" for @include_cpp; - print FH " fclose(fout);\n"; - print FH " return 0;\n"; - print FH "}\n"; - -} else { - if ($memstruct) { - open MH, "<$memstruct"; - print FH "$_" while(); - close MH; - } - print FH "module DFHack\n"; - print FH "$_\n" for @lines_rb; - print FH "end\n"; +if ($memstruct) { + open MH, "<$memstruct"; + print FH "$_" while(); + close MH; } +print FH "module DFHack\n"; +print FH "$_\n" for @lines_rb; +print FH "end\n"; close FH; From 3e61452f157fd9e8d29eb28106a2c9ee8b5bde60 Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 29 May 2012 17:28:51 +0200 Subject: [PATCH 09/60] ruby: codegen code cleanup, move ruby-memstruct in ruby.rb, handle bool struct fields, rename Enum.to_sym/to_i to sym()/int(), define nested compound sizeof() --- plugins/ruby/CMakeLists.txt | 2 +- plugins/ruby/README | 59 +-- plugins/ruby/codegen.pl | 499 +++++++++++++------- plugins/ruby/plugins/building.rb | 1 - plugins/ruby/ruby-memstruct.rb | 747 ------------------------------ plugins/ruby/ruby.cpp | 2 +- plugins/ruby/ruby.rb | 758 ++++++++++++++++++++++++++++++- 7 files changed, 1108 insertions(+), 960 deletions(-) delete mode 100644 plugins/ruby/ruby-memstruct.rb diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index f28f269df..440f3a249 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -2,7 +2,7 @@ find_package(Ruby) if(RUBY_FOUND) ADD_CUSTOM_COMMAND( OUTPUT ruby-autogen.rb - COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb ${CMAKE_CURRENT_SOURCE_DIR}/ruby-memstruct.rb + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl ) ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb) diff --git a/plugins/ruby/README b/plugins/ruby/README index 7238c161d..25b2781bd 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -3,7 +3,7 @@ This plugins embeds a ruby interpreter inside DFHack (ie inside Dwarf Fortress). The plugin maps all the structures available in library/xml/ to ruby objects. These objects are described in ruby-autogen.rb, they are all in the DFHack:: -module. The toplevel 'df' method returs the DFHack module. +module. The toplevel 'df' method is a shortcut to the DFHack module. The plugin does *not* map most of dfhack methods (MapCache, ...) ; only direct access to the raw DF data structures in memory is provided. @@ -12,7 +12,7 @@ Some library methods are stored in the ruby.rb file, with shortcuts to read a map block, find an unit or an item, etc. Global objects are stored in the GlobalObjects class ; each object accessor is -mirrored as a DFHack module method. +mirrored as a DFHack module method (eg df.world). The ruby plugin defines 2 dfhack console commands: rb_load ; load a ruby script. Ex: rb_load hack/plants.rb (no quotes) @@ -23,10 +23,12 @@ and removed by the dfhack console. The plugin also interfaces with dfhack 'onupdate' hook. To register ruby code to be run every graphic frame, use: - handle = df.onupdate_register { puts 'i love flood' } + handle = df.onupdate_register { puts 'i love flooding the console' } To stop being called, use: df.onupdate_unregister handle +The same mechanism is available for onstatechange. + Exemples -------- @@ -42,6 +44,9 @@ Set a custom nickname to unit with id '123' Show current unit profession p df.find_unit.profession +Change current unit profession + df.find_unit.profession = :MASON + Center the screen on unit '123' df.center_viewscreen(df.find_unit(123)) @@ -49,54 +54,42 @@ Find an item at a given position, show its C++ classname df.find_item(df.cursor)._rtti_classname Find the raws name of the plant under cursor - plant = df.world.plants.all.find { |p| df.at_cursor?(p) } - df.world.raws.plants.all[plant.mat_index].id + plant = df.world.plants.all.find { |plt| df.at_cursor?(plt) } + p df.world.raws.plants.all[plant.mat_index].id Dig a channel under the cursor - df.map_designation_at(df.cursor).dig = TileDigDesignation::Channel + df.map_designation_at(df.cursor).dig = :Channel df.map_block_at(df.cursor).flags.designated = true Compilation ----------- -The plugin consists of the ruby.rb file including user comfort functions ; -ruby-memstruct.rb describing basic classes used by the autogenerated code, and -embedded at the beginnig of ruby-autogen.rb, and the generated code. +The plugin consists of the ruby.rb file including user comfort functions and +describing basic classes used by the autogenerated code, and ruby-autogen.rb, +the auto-generated code. The generated code is generated by codegen.pl, which takes the codegen.out.xml file as input. -One of the limitations of the xml file is that it does not include structure -offsets, as they depend on the compiler. To overcome that, codegen runs in two -passes. The first pass generates a ruby-autogen.cpp file, that will output the -structure offsets ; the second pass will generate the ruby-autogen.rb using the -output of the compiled ruby-autogen.cpp. - -For exemple, from +For exemple, -We generate the cpp - printf("%s = %d", "offsetof(df::unit, language_name)", offsetof(df::unit, language_name)); - printf("%s = %d", "offsetof(df::unit, custom_profession)", offsetof(df::unit, custom_profession)); - printf("%s = %d", "offsetof(df::unit, profession)", offsetof(df::unit, profession)); - -Which generates (on linux) - offsetof(df::unit, name) = 0 - offsetof(df::unit, custom_profession) = 60 - offsetof(df::unit, profession) = 64 - -Which generates +Will generate class Unit < MemHack::Compound field(:name, 0) { global :LanguageName } field(:custom_profession, 60) { stl_string } field(:profession, 64) { number 16, true } -The field method has 2 arguments: the name of the method and the member offset ; -the block specifies the member type. See ruby-memstruct.rb for more information. +The syntax for the 'field' method is: +1st argument = name of the method +2nd argument = offset of this field from the beginning of the struct. + +The block argument describes the type of the field: uint32, ptr to global... + Primitive type access is done through native methods in ruby.cpp (vector length, raw memory access, etc) @@ -104,10 +97,4 @@ MemHack::Pointers are automatically dereferenced ; so a vector of pointer to Units will yield Units directly. Null pointers yield the 'nil' value. This allows to use code such as 'df.world.units.all[0].pos', with 'all' being -really a vector of pointer. - - -Todo ----- - -Correct c++ object (de)allocation (call ctor etc) ; ability to call vtable methods +in fact a vector of *pointers* to DFHack::Unit objects. diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 53c67fd99..5cdeeedd9 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -31,6 +31,7 @@ sub rb_ucase { return join("", map { ucfirst $_ } (split('_', $name))); } + my %item_renderer = ( 'global' => \&render_item_global, 'number' => \&render_item_number, @@ -42,6 +43,7 @@ my %item_renderer = ( 'bytes' => \&render_item_bytes, ); + my %global_types; our $current_typename; @@ -55,62 +57,88 @@ sub render_global_enum { }; push @lines_rb, "end\n"; } + sub render_enum_fields { my ($type) = @_; - my $value = -1; push @lines_rb, "ENUM = Hash.new"; push @lines_rb, "NUME = Hash.new"; my %attr_type; my %attr_list; - for my $attr ($type->findnodes('child::enum-attr')) { + render_enum_initattrs($type, \%attr_type, \%attr_list); + + my $value = -1; + for my $item ($type->findnodes('child::enum-item')) + { + $value = $item->getAttribute('value') || ($value+1); + my $elemname = $item->getAttribute('name'); # || "unk_$value"; + + if ($elemname) + { + my $rbelemname = rb_ucase($elemname); + push @lines_rb, "ENUM[$value] = :$rbelemname ; NUME[:$rbelemname] = $value"; + for my $iattr ($item->findnodes('child::item-attr')) + { + my $attr = render_enum_attr($rbelemname, $iattr, \%attr_type, \%attr_list); + $lines_rb[$#lines_rb] .= ' ; ' . $attr; + } + } + } +} + +sub render_enum_initattrs { + my ($type, $attr_type, $attr_list) = @_; + + for my $attr ($type->findnodes('child::enum-attr')) + { my $rbattr = rb_ucase($attr->getAttribute('name')); my $typeattr = $attr->getAttribute('type-name'); # find how we need to encode the attribute values: string, symbol (for enums), raw (number, bool) if ($typeattr) { if ($global_types{$typeattr}) { - $attr_type{$rbattr} = 'symbol'; + $attr_type->{$rbattr} = 'symbol'; } else { - $attr_type{$rbattr} = 'naked'; + $attr_type->{$rbattr} = 'naked'; } } else { - $attr_type{$rbattr} = 'quote'; + $attr_type->{$rbattr} = 'quote'; } my $def = $attr->getAttribute('default-value'); - if ($attr->getAttribute('is-list')) { + if ($attr->getAttribute('is-list')) + { push @lines_rb, "$rbattr = Hash.new { |h, k| h[k] = [] }"; - $attr_list{$rbattr} = 1; - } elsif ($def) { - $def = ":$def" if ($attr_type{$rbattr} eq 'symbol'); - $def =~ s/'/\\'/g if ($attr_type{$rbattr} eq 'quote'); - $def = "'$def'" if ($attr_type{$rbattr} eq 'quote'); + $attr_list->{$rbattr} = 1; + } + elsif ($def) + { + $def = ":$def" if ($attr_type->{$rbattr} eq 'symbol'); + $def =~ s/'/\\'/g if ($attr_type->{$rbattr} eq 'quote'); + $def = "'$def'" if ($attr_type->{$rbattr} eq 'quote'); push @lines_rb, "$rbattr = Hash.new($def)"; - } else { + } + else + { push @lines_rb, "$rbattr = Hash.new"; } } +} - for my $item ($type->findnodes('child::enum-item')) { - $value = $item->getAttribute('value') || ($value+1); - my $elemname = $item->getAttribute('name'); # || "unk_$value"; +sub render_enum_attr { + my ($rbelemname, $iattr, $attr_type, $attr_list) = @_; - if ($elemname) { - my $rbelemname = rb_ucase($elemname); - push @lines_rb, "ENUM[$value] = :$rbelemname ; NUME[:$rbelemname] = $value"; - for my $iattr ($item->findnodes('child::item-attr')) { - my $ian = $iattr->getAttribute('name'); - my $iav = $iattr->getAttribute('value'); - my $rbattr = rb_ucase($ian); - my $op = ($attr_list{$rbattr} ? '<<' : '='); - $iav = ":$iav" if ($attr_type{$rbattr} eq 'symbol'); - $iav =~ s/'/\\'/g if ($attr_type{$rbattr} eq 'quote'); - $iav = "'$iav'" if ($attr_type{$rbattr} eq 'quote'); - $lines_rb[$#lines_rb] .= " ; ${rbattr}[:$rbelemname] $op $iav"; - } - } - } + my $ian = $iattr->getAttribute('name'); + my $iav = $iattr->getAttribute('value'); + my $rbattr = rb_ucase($ian); + + my $op = ($attr_list->{$rbattr} ? '<<' : '='); + + $iav = ":$iav" if ($attr_type->{$rbattr} eq 'symbol'); + $iav =~ s/'/\\'/g if ($attr_type->{$rbattr} eq 'quote'); + $iav = "'$iav'" if ($attr_type->{$rbattr} eq 'quote'); + + return "${rbattr}[:$rbelemname] $op $iav"; } @@ -124,6 +152,7 @@ sub render_global_bitfield { }; push @lines_rb, "end\n"; } + sub render_bitfield_fields { my ($type) = @_; @@ -134,20 +163,26 @@ sub render_bitfield_fields { push @lines_rb, "}"; my $shift = 0; - for my $field ($type->findnodes('child::ld:field')) { + for my $field ($type->findnodes('child::ld:field')) + { my $count = $field->getAttribute('count') || 1; my $name = $field->getAttribute('name'); my $type = $field->getAttribute('type-name'); my $enum = rb_ucase($type) if ($type and $global_types{$type}); $name = $field->getAttribute('ld:anon-name') if (!$name); print "bitfield $name !number\n" if (!($field->getAttribute('ld:meta') eq 'number')); - if ($count == 1) { - push @lines_rb, "field(:$name, 0) { bit $shift }" if ($name); - } elsif ($enum) { - push @lines_rb, "field(:$name, 0) { bits $shift, $count, $enum }" if ($name); - } else { - push @lines_rb, "field(:$name, 0) { bits $shift, $count }" if ($name); + + if ($name) + { + if ($count == 1) { + push @lines_rb, "field(:$name, 0) { bit $shift }"; + } elsif ($enum) { + push @lines_rb, "field(:$name, 0) { bits $shift, $count, $enum }"; + } else { + push @lines_rb, "field(:$name, 0) { bits $shift, $count }"; + } } + $shift += $count; } } @@ -155,9 +190,12 @@ sub render_bitfield_fields { my %seen_class; our $compound_off; +our $compound_pointer; + sub render_global_class { my ($name, $type) = @_; + my $meta = $type->getAttribute('ld:meta'); my $rbname = rb_ucase($name); # ensure pre-definition of ancestors @@ -168,46 +206,55 @@ sub render_global_class { $seen_class{$name}++; local $compound_off = 0; + $compound_off = 4 if ($meta eq 'class-type'); + $compound_off = sizeof($global_types{$parent}) if $parent; + local $current_typename = $rbname; my $rtti_name; - my $meta = $type->getAttribute('ld:meta'); - if ($meta eq 'class-type') { - $compound_off = 4; + if ($meta eq 'class-type') + { $rtti_name = $type->getAttribute('original-name') || - $type->getAttribute('type-name') || - $name; + $type->getAttribute('type-name') || + $name; } my $rbparent = ($parent ? rb_ucase($parent) : 'MemHack::Compound'); - - $compound_off = sizeof($global_types{$parent}) if $parent; - local $current_typename = $rbname; - push @lines_rb, "class $rbname < $rbparent"; indent_rb { my $sz = sizeof($type); # see comment is sub sizeof ; but gcc has sizeof(cls) aligned $sz = align_field($sz, 4) if $os eq 'linux' and $meta eq 'class-type'; - push @lines_rb, "sizeof $sz"; - push @lines_rb, "rtti_classname :$rtti_name" if $rtti_name; + push @lines_rb, "sizeof $sz\n"; + + push @lines_rb, "rtti_classname :$rtti_name\n" if $rtti_name; + render_struct_fields($type); + my $vms = $type->findnodes('child::virtual-methods')->[0]; render_class_vmethods($vms) if $vms; }; push @lines_rb, "end\n"; } + sub render_struct_fields { my ($type) = @_; my $isunion = $type->getAttribute('is-union'); - for my $field ($type->findnodes('child::ld:field')) { + + for my $field ($type->findnodes('child::ld:field')) + { my $name = $field->getAttribute('name'); $name = $field->getAttribute('ld:anon-name') if (!$name); - if (!$name and $field->getAttribute('ld:anon-compound')) { + + if (!$name and $field->getAttribute('ld:anon-compound')) + { render_struct_fields($field); - } else { + } + else + { $compound_off = align_field($compound_off, get_field_align($field)); - if ($name) { + if ($name) + { push @lines_rb, "field(:$name, $compound_off) {"; indent_rb { render_item($field); @@ -215,27 +262,39 @@ sub render_struct_fields { push @lines_rb, "}"; } } + $compound_off += sizeof($field) if (!$isunion); } } + sub render_class_vmethods { my ($vms) = @_; my $voff = 0; - for my $meth ($vms->findnodes('child::vmethod')) { + + for my $meth ($vms->findnodes('child::vmethod')) + { my $name = $meth->getAttribute('name'); - if ($name) { + + if ($name) + { my @argnames; my @argargs; - for my $arg ($meth->findnodes('child::ld:field')) { + + # check if arguments need special treatment (eg auto-convert from symbol to enum value) + for my $arg ($meth->findnodes('child::ld:field')) + { my $nr = $#argnames + 1; my $argname = lcfirst($arg->getAttribute('name') || "arg$nr"); push @argnames, $argname; + if ($arg->getAttribute('ld:meta') eq 'global' and $arg->getAttribute('ld:subtype') eq 'enum') { - push @argargs, rb_ucase($arg->getAttribute('type-name')) . ".to_i($argname)"; + push @argargs, rb_ucase($arg->getAttribute('type-name')) . ".int($argname)"; } else { push @argargs, $argname; } } + + # write vmethod ruby wrapper push @lines_rb, "def $name(" . join(', ', @argnames) . ')'; indent_rb { my $args = join('', map { ", $_" } @argargs); @@ -245,47 +304,74 @@ sub render_class_vmethods { }; push @lines_rb, 'end'; } + # on linux, the destructor uses 2 entries $voff += 4 if $meth->getAttribute('is-destructor') and $os eq 'linux'; $voff += 4; } } + sub render_class_vmethod_ret { my ($call, $ret) = @_; - if (!$ret) { + + if (!$ret) + { + # method returns void, hide return value push @lines_rb, "$call ; nil"; return; } + my $retmeta = $ret->getAttribute('ld:meta') || ''; - if ($retmeta eq 'global') { # enum + if ($retmeta eq 'global') + { + # method returns an enum value: auto-convert to symbol my $retname = $ret->getAttribute('type-name'); if ($retname and $global_types{$retname} and - $global_types{$retname}->getAttribute('ld:meta') eq 'enum-type') { - push @lines_rb, rb_ucase($retname) . ".to_sym($call)"; - } else { + $global_types{$retname}->getAttribute('ld:meta') eq 'enum-type') + { + push @lines_rb, rb_ucase($retname) . ".sym($call)"; + } + else + { print "vmethod global nonenum $call\n"; push @lines_rb, $call; } - } elsif ($retmeta eq 'number') { + + } + elsif ($retmeta eq 'number') + { + # raw method call returns an int32, mask according to actual return type my $retsubtype = $ret->getAttribute('ld:subtype'); my $retbits = $ret->getAttribute('ld:bits'); push @lines_rb, "val = $call"; - if ($retsubtype eq 'bool') { + if ($retsubtype eq 'bool') + { push @lines_rb, "(val & 1) != 0"; - } elsif ($ret->getAttribute('ld:unsigned')) { - push @lines_rb, "val & ((1 << $retbits) - 1)"; # if $retbits != 32; - } else { # signed - push @lines_rb, "val &= ((1 << $retbits) - 1)"; # if $retbits != 32; + } + elsif ($ret->getAttribute('ld:unsigned')) + { + push @lines_rb, "val & ((1 << $retbits) - 1)"; + } + elsif ($retbits != 32) + { + # manual sign extension + push @lines_rb, "val &= ((1 << $retbits) - 1)"; push @lines_rb, "((val >> ($retbits-1)) & 1) == 0 ? val : val - (1 << $retbits)"; } - } elsif ($retmeta eq 'pointer') { + + } + elsif ($retmeta eq 'pointer') + { + # method returns a pointer to some struct, create the correct ruby wrapper push @lines_rb, "ptr = $call"; push @lines_rb, "class << self"; indent_rb { render_item($ret->findnodes('child::ld:item')->[0]); }; push @lines_rb, "end._at(ptr) if ptr != 0"; - } else { + } + else + { print "vmethod unkret $call\n"; push @lines_rb, $call; } @@ -295,16 +381,17 @@ sub render_global_objects { my (@objects) = @_; my @global_objects; - my $sname = 'global_objects'; - my $rbname = rb_ucase($sname); - local $compound_off = 0; local $current_typename = 'Global'; - push @lines_rb, "class $rbname < MemHack::Compound"; + # define all globals as 'fields' of a virtual globalobject wrapping the whole address space + push @lines_rb, 'class GlobalObjects < MemHack::Compound'; indent_rb { - for my $obj (@objects) { + for my $obj (@objects) + { my $oname = $obj->getAttribute('name'); + + # check if the symbol is defined in xml to avoid NULL deref my $addr = "DFHack.get_global_address('$oname')"; push @lines_rb, "addr = $addr"; push @lines_rb, "if addr != 0"; @@ -323,9 +410,11 @@ sub render_global_objects { }; push @lines_rb, "end"; + # define friendlier accessors, eg df.world -> DFHack::GlobalObjects.new._at(0).world indent_rb { push @lines_rb, "Global = GlobalObjects.new._at(0)"; - for my $obj (@global_objects) { + for my $obj (@global_objects) + { push @lines_rb, "def self.$obj ; Global.$obj ; end"; push @lines_rb, "def self.$obj=(v) ; Global.$obj = v ; end"; } @@ -333,6 +422,9 @@ sub render_global_objects { } +my %align_cache; +my %sizeof_cache; + sub align_field { my ($off, $fldalign) = @_; my $dt = $off % $fldalign; @@ -340,126 +432,116 @@ sub align_field { return $off; } -my %align_cache; -my %sizeof_cache; sub get_field_align { my ($field) = @_; my $al = 4; my $meta = $field->getAttribute('ld:meta'); + if ($meta eq 'number') { $al = $field->getAttribute('ld:bits')/8; $al = 4 if $al > 4; } elsif ($meta eq 'global') { - my $typename = $field->getAttribute('type-name'); - return $align_cache{$typename} if $align_cache{$typename}; - my $g = $global_types{$typename}; - my $st = $field->getAttribute('ld:subtype') || ''; - if ($st eq 'bitfield' or $st eq 'enum' or $g->getAttribute('ld:meta') eq 'bitfield-type') { - my $base = $field->getAttribute('base-type') || $g->getAttribute('base-type') || 'uint32_t'; - print "$st type $base\n" if $base !~ /int(\d+)_t/; - # dont cache, field->base-type may differ - return $1/8; - } - $al = 1; - for my $gf ($g->findnodes('child::ld:field')) { - my $fal = get_field_align($gf); - $al = $fal if $fal > $al; - } - $align_cache{$typename} = $al; + $al = get_global_align($field); } elsif ($meta eq 'compound') { - my $st = $field->getAttribute('ld:subtype') || ''; - if ($st eq 'bitfield' or $st eq 'enum') { - my $base = $field->getAttribute('base-type') || 'uint32_t'; - print "$st type $base\n" if $base !~ /int(\d+)_t/; - return $1/8; - } - $al = 1; - for my $f ($field->findnodes('child::ld:field')) { - my $fal = get_field_align($f); - $al = $fal if $fal > $al; - } + $al = get_compound_align($field); } elsif ($meta eq 'static-array') { my $tg = $field->findnodes('child::ld:item')->[0]; $al = get_field_align($tg); } elsif ($meta eq 'bytes') { $al = $field->getAttribute('alignment') || 1; } + + return $al; +} + +sub get_global_align { + my ($field) = @_; + + my $typename = $field->getAttribute('type-name'); + return $align_cache{$typename} if $align_cache{$typename}; + + my $g = $global_types{$typename}; + + my $st = $field->getAttribute('ld:subtype') || ''; + if ($st eq 'bitfield' or $st eq 'enum' or $g->getAttribute('ld:meta') eq 'bitfield-type') + { + my $base = $field->getAttribute('base-type') || $g->getAttribute('base-type') || 'uint32_t'; + print "$st type $base\n" if $base !~ /int(\d+)_t/; + # dont cache, field->base-type may differ + return $1/8; + } + + my $al = 1; + for my $gf ($g->findnodes('child::ld:field')) { + my $fld_al = get_field_align($gf); + $al = $fld_al if $fld_al > $al; + } + $align_cache{$typename} = $al; + + return $al; +} + +sub get_compound_align { + my ($field) = @_; + + my $st = $field->getAttribute('ld:subtype') || ''; + if ($st eq 'bitfield' or $st eq 'enum') + { + my $base = $field->getAttribute('base-type') || 'uint32_t'; + print "$st type $base\n" if $base !~ /int(\d+)_t/; + return $1/8; + } + + my $al = 1; + for my $f ($field->findnodes('child::ld:field')) { + my $fal = get_field_align($f); + $al = $fal if $fal > $al; + } + return $al; } sub sizeof { my ($field) = @_; my $meta = $field->getAttribute('ld:meta'); + if ($meta eq 'number') { return $field->getAttribute('ld:bits')/8; + } elsif ($meta eq 'pointer') { return 4; + } elsif ($meta eq 'static-array') { my $count = $field->getAttribute('count'); my $tg = $field->findnodes('child::ld:item')->[0]; return $count * sizeof($tg); + } elsif ($meta eq 'bitfield-type' or $meta eq 'enum-type') { my $base = $field->getAttribute('base-type') || 'uint32_t'; print "$meta type $base\n" if $base !~ /int(\d+)_t/; return $1/8; + } elsif ($meta eq 'global') { my $typename = $field->getAttribute('type-name'); return $sizeof_cache{$typename} if $sizeof_cache{$typename}; + my $g = $global_types{$typename}; my $st = $field->getAttribute('ld:subtype') || ''; - if ($st eq 'bitfield' or $st eq 'enum' or $g->getAttribute('ld:meta') eq 'bitfield-type') { + if ($st eq 'bitfield' or $st eq 'enum' or $g->getAttribute('ld:meta') eq 'bitfield-type') + { my $base = $field->getAttribute('base-type') || $g->getAttribute('base-type') || 'uint32_t'; print "$st type $base\n" if $base !~ /int(\d+)_t/; return $1/8; } + return sizeof($g); - } elsif ($meta eq 'class-type') { - my $typename = $field->getAttribute('type-name'); - return $sizeof_cache{$typename} if $sizeof_cache{$typename}; - my $off = 4; - my $parent = $field->getAttribute('inherits-from'); - $off = sizeof($global_types{$parent}) if ($parent); - for my $f ($field->findnodes('child::ld:field')) { - $off = align_field($off, get_field_align($f)); - $off += sizeof($f); - } - # GCC: class a { vtable; char; } ; class b:a { char c2; } -> c2 has offset 5 (Windows MSVC: offset 8) - $off = align_field($off, 4) if $os ne 'linux'; - $sizeof_cache{$typename} = $off; - return $off; - } elsif ($meta eq 'struct-type' or $meta eq 'compound') { - my $typename = $field->getAttribute('type-name'); - return $sizeof_cache{$typename} if $typename and $sizeof_cache{$typename}; - my $st = $field->getAttribute('ld:subtype') || ''; - if ($st eq 'bitfield' or $st eq 'enum') { - my $base = $field->getAttribute('base-type') || 'uint32_t'; - print "$st type $base\n" if $base !~ /int(\d+)_t/; - $sizeof_cache{$typename} = $1/8 if $typename; - return $1/8; - } - if ($field->getAttribute('is-union')) { - my $sz = 0; - for my $f ($field->findnodes('child::ld:field')) { - my $fsz = sizeof($f); - $sz = $fsz if $fsz > $sz; - } - return $sz; - } - my $off = 0; - my $parent = $field->getAttribute('inherits-from'); - $off = sizeof($global_types{$parent}) if ($parent); - my $al = 1; - for my $f ($field->findnodes('child::ld:field')) { - my $fa = get_field_align($f); - $al = $fa if $fa > $al; - $off = align_field($off, $fa); - $off += sizeof($f); - } - $off = align_field($off, $al); - $sizeof_cache{$typename} = $off if $typename; - return $off; + + } elsif ($meta eq 'class-type' or $meta eq 'struct-type' or $meta eq 'compound') { + return sizeof_compound($field); + } elsif ($meta eq 'container') { my $subtype = $field->getAttribute('ld:subtype'); + if ($subtype eq 'stl-vector') { if ($os eq 'linux') { return 12; @@ -493,10 +575,11 @@ sub sizeof { } else { print "sizeof container $subtype\n"; } + } elsif ($meta eq 'primitive') { my $subtype = $field->getAttribute('ld:subtype'); - if ($subtype eq 'stl-string') { - if ($os eq 'linux') { + + if ($subtype eq 'stl-string') { if ($os eq 'linux') { return 4; } elsif ($os eq 'windows') { return 28; @@ -507,6 +590,7 @@ sub sizeof { } else { print "sizeof primitive $subtype\n"; } + } elsif ($meta eq 'bytes') { return $field->getAttribute('size'); } else { @@ -514,6 +598,59 @@ sub sizeof { } } +sub sizeof_compound { + my ($field) = @_; + + my $typename = $field->getAttribute('type-name'); + return $sizeof_cache{$typename} if $typename and $sizeof_cache{$typename}; + + my $meta = $field->getAttribute('ld:meta'); + + my $st = $field->getAttribute('ld:subtype') || ''; + if ($st eq 'bitfield' or $st eq 'enum') + { + my $base = $field->getAttribute('base-type') || 'uint32_t'; + print "$st type $base\n" if $base !~ /int(\d+)_t/; + $sizeof_cache{$typename} = $1/8 if $typename; + return $1/8; + } + + if ($field->getAttribute('is-union')) + { + my $sz = 0; + for my $f ($field->findnodes('child::ld:field')) + { + my $fsz = sizeof($f); + $sz = $fsz if $fsz > $sz; + } + return $sz; + } + + my $parent = $field->getAttribute('inherits-from'); + my $off = 0; + $off = 4 if ($meta eq 'class-type'); + $off = sizeof($global_types{$parent}) if ($parent); + + my $al = 1; + $al = 4 if ($meta eq 'class-type'); + + for my $f ($field->findnodes('child::ld:field')) + { + my $fa = get_field_align($f); + $al = $fa if $fa > $al; + $off = align_field($off, $fa); + $off += sizeof($f); + } + + # GCC: class a { vtable; char; } ; class b:a { char c2; } -> c2 has offset 5 (Windows MSVC: offset 8) + $al = 1 if ($meta eq 'class-type' and $os eq 'linux'); + $off = align_field($off, $al); + $sizeof_cache{$typename} = $off if $typename; + + return $off; +} + + sub render_item { my ($item) = @_; return if (!$item); @@ -576,6 +713,8 @@ sub render_item_number { push @lines_rb, 'number 8, false'; } elsif ($subtype eq 'bool') { push @lines_rb, 'number 8, true'; + $initvalue ||= 'nil'; + $typename ||= 'BooleanEnum'; } elsif ($subtype eq 's-float') { push @lines_rb, 'float'; return; @@ -583,6 +722,7 @@ sub render_item_number { print "no render number $subtype\n"; return; } + $lines_rb[$#lines_rb] .= ", $initvalue" if ($initvalue); $lines_rb[$#lines_rb] .= ", $typename" if ($typename); } @@ -596,18 +736,26 @@ sub render_item_compound { my $classname = $current_typename . '_' . rb_ucase($item->getAttribute('ld:typedef-name')); local $current_typename = $classname; - if (!$subtype || $subtype eq 'bitfield') { + if (!$subtype || $subtype eq 'bitfield') + { push @lines_rb, "compound(:$classname) {"; - #push @lines_rb, 'sizeof'; indent_rb { + # declare sizeof() only for useful compound, eg the one behind pointers + # that the user may want to allocate + my $sz = sizeof($item); + push @lines_rb, "sizeof $sz\n" if $compound_pointer; + if (!$subtype) { + local $compound_pointer = 0; render_struct_fields($item); } else { render_bitfield_fields($item); } }; push @lines_rb, "}" - } elsif ($subtype eq 'enum') { + } + elsif ($subtype eq 'enum') + { push @lines_rb, "class ::DFHack::$classname < MemHack::Enum"; indent_rb { # declare constants @@ -617,7 +765,9 @@ sub render_item_compound { # actual field render_item_number($item, $classname); - } else { + } + else + { print "no render compound $subtype\n"; } } @@ -629,7 +779,8 @@ sub render_item_container { my $rbmethod = join('_', split('-', $subtype)); my $tg = $item->findnodes('child::ld:item')->[0]; my $indexenum = $item->getAttribute('index-enum'); - if ($tg) { + if ($tg) + { if ($rbmethod eq 'df_linked_list') { push @lines_rb, "$rbmethod {"; } else { @@ -640,10 +791,14 @@ sub render_item_container { render_item($tg); }; push @lines_rb, "}"; - } elsif ($indexenum) { + } + elsif ($indexenum) + { $indexenum = rb_ucase($indexenum); push @lines_rb, "$rbmethod($indexenum)"; - } else { + } + else + { push @lines_rb, "$rbmethod"; } } @@ -652,14 +807,16 @@ sub render_item_pointer { my ($item) = @_; my $tg = $item->findnodes('child::ld:item')->[0]; - my $ary = $item->getAttribute('is-array'); - if ($ary and $ary eq 'true') { + my $ary = $item->getAttribute('is-array') || ''; + + if ($ary eq 'true') { my $tglen = sizeof($tg) if $tg; push @lines_rb, "pointer_ary($tglen) {"; } else { push @lines_rb, "pointer {"; } indent_rb { + local $compound_pointer = 1; render_item($tg); }; push @lines_rb, "}"; @@ -672,6 +829,7 @@ sub render_item_staticarray { my $tg = $item->findnodes('child::ld:item')->[0]; my $tglen = sizeof($tg) if $tg; my $indexenum = $item->getAttribute('index-enum'); + if ($indexenum) { $indexenum = rb_ucase($indexenum); push @lines_rb, "static_array($count, $tglen, $indexenum) {"; @@ -708,16 +866,18 @@ sub render_item_bytes { } } -my $input = $ARGV[0] || '../../library/include/df/codegen.out.xml'; + +my $input = $ARGV[0] or die "need input xml"; my $output = $ARGV[1] or die "need output file"; -my $memstruct = $ARGV[2]; my $doc = XML::LibXML->new()->parse_file($input); $global_types{$_->getAttribute('type-name')} = $_ foreach $doc->findnodes('/ld:data-definition/ld:global-type'); +# render enums first, this allows later code to refer to them directly my @nonenums; -for my $name (sort { $a cmp $b } keys %global_types) { +for my $name (sort { $a cmp $b } keys %global_types) +{ my $type = $global_types{$name}; my $meta = $type->getAttribute('ld:meta'); if ($meta eq 'enum-type') { @@ -727,7 +887,9 @@ for my $name (sort { $a cmp $b } keys %global_types) { } } -for my $name (@nonenums) { +# render other structs/bitfields/classes +for my $name (@nonenums) +{ my $type = $global_types{$name}; my $meta = $type->getAttribute('ld:meta'); if ($meta eq 'struct-type' or $meta eq 'class-type') { @@ -739,16 +901,11 @@ for my $name (@nonenums) { } } - +# render globals render_global_objects($doc->findnodes('/ld:data-definition/ld:global-object')); open FH, ">$output"; -if ($memstruct) { - open MH, "<$memstruct"; - print FH "$_" while(); - close MH; -} print FH "module DFHack\n"; print FH "$_\n" for @lines_rb; print FH "end\n"; diff --git a/plugins/ruby/plugins/building.rb b/plugins/ruby/plugins/building.rb index 15212092d..5dfbcdacd 100644 --- a/plugins/ruby/plugins/building.rb +++ b/plugins/ruby/plugins/building.rb @@ -2,7 +2,6 @@ module DFHack # allocate a new building object def self.building_alloc(type, subtype=-1, custom=-1) - type = BuildingType.to_sym(type) cls = rtti_n2c[BuildingType::Classname[type].to_sym] raise "invalid building type #{type.inspect}" if not cls bld = cls.cpp_new diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb deleted file mode 100644 index 40f35bb42..000000000 --- a/plugins/ruby/ruby-memstruct.rb +++ /dev/null @@ -1,747 +0,0 @@ -module DFHack -module MemHack -INSPECT_SIZE_LIMIT=16384 -class MemStruct - attr_accessor :_memaddr - def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end - def _get ; self ; end - def _cpp_init ; end -end - -class Compound < MemStruct - class << self - attr_accessor :_fields, :_rtti_classname, :_sizeof - def field(name, offset) - struct = yield - return if not struct - @_fields ||= [] - @_fields << [name, offset, struct] - define_method(name) { struct._at(@_memaddr+offset)._get } - define_method("#{name}=") { |v| struct._at(@_memaddr+offset)._set(v) } - end - def _fields_ancestors - if superclass.respond_to?(:_fields_ancestors) - superclass._fields_ancestors + _fields.to_a - else - _fields.to_a - end - end - - def number(bits, signed, initvalue=nil, enum=nil) - Number.new(bits, signed, initvalue, enum) - end - def float - Float.new - end - def bit(shift) - BitField.new(shift, 1) - end - def bits(shift, len, enum=nil) - BitField.new(shift, len, enum) - end - def pointer - Pointer.new((yield if block_given?)) - end - def pointer_ary(tglen) - PointerAry.new(tglen, yield) - end - def static_array(len, tglen, indexenum=nil) - StaticArray.new(tglen, len, indexenum, yield) - end - def static_string(len) - StaticString.new(len) - end - - def stl_vector(tglen=nil) - tg = yield if tglen - case tglen - when 1; StlVector8.new(tg) - when 2; StlVector16.new(tg) - else StlVector32.new(tg) - end - end - def stl_string - StlString.new - end - def stl_bit_vector - StlBitVector.new - end - def stl_deque(tglen) - StlDeque.new(tglen, yield) - end - - def df_flagarray(indexenum=nil) - DfFlagarray.new(indexenum) - end - def df_array(tglen) - DfArray.new(tglen, yield) - end - def df_linked_list - DfLinkedList.new(yield) - end - - def global(glob) - Global.new(glob) - end - def compound(name=nil, &b) - m = Class.new(Compound) - DFHack.const_set(name, m) if name - m.instance_eval(&b) - m.new - end - def rtti_classname(n) - DFHack.rtti_register(n, self) - @_rtti_classname = n - end - def sizeof(n) - @_sizeof = n - end - - # allocate a new c++ object, return its ruby wrapper - def cpp_new - ptr = DFHack.malloc(_sizeof) - if _rtti_classname and vt = DFHack.rtti_getvtable(_rtti_classname) - DFHack.memory_write_int32(ptr, vt) - # TODO call constructor - end - o = new._at(ptr) - o._cpp_init - o - end - end - def _cpp_init - _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_init } - end - def _set(h) - case h - when Hash; h.each { |k, v| send("_#{k}=", v) } - when Array; names = _field_names ; raise 'bad size' if names.length != h.length ; names.zip(h).each { |n, a| send("#{n}=", a) } - when Compound; _field_names.each { |n| send("#{n}=", h.send(n)) } - else raise 'wut?' - end - end - def _fields ; self.class._fields.to_a ; end - def _fields_ancestors ; self.class._fields_ancestors.to_a ; end - def _field_names ; _fields_ancestors.map { |n, o, s| n } ; end - def _rtti_classname ; self.class._rtti_classname ; end - def _sizeof ; self.class._sizeof ; end - @@inspecting = {} # avoid infinite recursion on mutually-referenced objects - def inspect - cn = self.class.name.sub(/^DFHack::/, '') - cn << ' @' << ('0x%X' % _memaddr) if cn != '' - out = "#<#{cn}" - return out << ' ...>' if @@inspecting[_memaddr] - @@inspecting[_memaddr] = true - _fields_ancestors.each { |n, o, s| - out << ' ' if out.length != 0 and out[-1, 1] != ' ' - if out.length > INSPECT_SIZE_LIMIT - out << '...' - break - end - out << inspect_field(n, o, s) - } - out.chomp!(' ') - @@inspecting.delete _memaddr - out << '>' - end - def inspect_field(n, o, s) - if s.kind_of?(BitField) and s._len == 1 - send(n) ? n.to_s : '' - elsif s.kind_of?(Pointer) - "#{n}=#{s._at(@_memaddr+o).inspect}" - elsif n == :_whole - "_whole=0x#{_whole.to_s(16)}" - else - v = send(n).inspect - "#{n}=#{v}" - end - rescue - "#{n}=ERR(#{$!})" - end -end - -class Enum - # number -> symbol - def self.enum - # ruby weirdness, needed to make the constants 'virtual' - @enum ||= const_get(:ENUM) - end - # symbol -> number - def self.nume - @nume ||= const_get(:NUME) - end - - def self.to_i(i) - nume[i] || i - end - def self.to_sym(i) - enum[i] || i - end -end - -class Number < MemStruct - attr_accessor :_bits, :_signed, :_initvalue, :_enum - def initialize(bits, signed, initvalue, enum) - @_bits = bits - @_signed = signed - @_initvalue = initvalue - @_enum = enum - end - - def _get - v = case @_bits - when 32; DFHack.memory_read_int32(@_memaddr) - when 16; DFHack.memory_read_int16(@_memaddr) - when 8; DFHack.memory_read_int8( @_memaddr) - when 64;(DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + (DFHack.memory_read_int32(@_memaddr+4) << 32) - end - v &= (1 << @_bits) - 1 if not @_signed - v = @_enum.to_sym(v) if @_enum - v - end - - def _set(v) - v = @_enum.to_i(v) if @_enum - case @_bits - when 32; DFHack.memory_write_int32(@_memaddr, v) - when 16; DFHack.memory_write_int16(@_memaddr, v) - when 8; DFHack.memory_write_int8( @_memaddr, v) - when 64; DFHack.memory_write_int32(@_memaddr, v & 0xffffffff) ; DFHack.memory_write_int32(@memaddr+4, v>>32) - end - end - - def _cpp_init - _set(@_initvalue) if @_initvalue - end -end -class Float < MemStruct - def _get - DFHack.memory_read_float(@_memaddr) - end - - def _set(v) - DFHack.memory_write_float(@_memaddr, v) - end - - def _cpp_init - _set(0.0) - end -end -class BitField < MemStruct - attr_accessor :_shift, :_len, :_enum - def initialize(shift, len, enum=nil) - @_shift = shift - @_len = len - @_enum = enum - end - def _mask - (1 << @_len) - 1 - end - - def _get - v = DFHack.memory_read_int32(@_memaddr) >> @_shift - if @_len == 1 - ((v & 1) == 0) ? false : true - else - v &= _mask - v = @_enum.to_sym(v) if @_enum - v - end - end - - def _set(v) - if @_len == 1 - # allow 'bit = 0' - v = (v && v != 0 ? 1 : 0) - end - v = @_enum.to_i(v) if @_enum - v = (v & _mask) << @_shift - - ori = DFHack.memory_read_int32(@_memaddr) & 0xffffffff - DFHack.memory_write_int32(@_memaddr, ori - (ori & ((-1 & _mask) << @_shift)) + v) - end -end - -class Pointer < MemStruct - attr_accessor :_tg - def initialize(tg) - @_tg = tg - end - - def _getp - DFHack.memory_read_int32(@_memaddr) & 0xffffffff - end - - def _get - addr = _getp - return if addr == 0 - @_tg._at(addr)._get - end - - # XXX shaky... - def _set(v) - if v.kind_of?(Pointer) - DFHack.memory_write_int32(@_memaddr, v._getp) - elsif v.kind_of?(MemStruct) - DFHack.memory_write_int32(@_memaddr, v._memaddr) - else - _get._set(v) - end - end - - def inspect - ptr = _getp - if ptr == 0 - 'NULL' - else - cn = '' - cn = @_tg.class.name.sub(/^DFHack::/, '').sub(/^MemHack::/, '') if @_tg - cn = @_tg._glob if cn == 'Global' - "#" - end - end -end -class PointerAry < MemStruct - attr_accessor :_tglen, :_tg - def initialize(tglen, tg) - @_tglen = tglen - @_tg = tg - end - - def _getp(i=0) - delta = (i != 0 ? i*@_tglen : 0) - (DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + delta - end - - def _get - addr = _getp - return if addr == 0 - self - end - - def [](i) - addr = _getp(i) - return if addr == 0 - @_tg._at(addr)._get - end - def []=(i, v) - addr = _getp(i) - raise 'null pointer' if addr == 0 - @_tg._at(addr)._set(v) - end - - def inspect ; ptr = _getp ; (ptr == 0) ? 'NULL' : "#" ; end -end -module Enumerable - include ::Enumerable - attr_accessor :_indexenum - def each ; (0...length).each { |i| yield self[i] } ; end - def inspect - out = '[' - each_with_index { |e, idx| - out << ', ' if out.length > 1 - if out.length > INSPECT_SIZE_LIMIT - out << '...' - break - end - out << "#{_indexenum.to_sym(idx)}=" if _indexenum - out << e.inspect - } - out << ']' - end - def empty? ; length == 0 ; end - def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end -end -class StaticArray < MemStruct - attr_accessor :_tglen, :_length, :_indexenum, :_tg - def initialize(tglen, length, indexenum, tg) - @_tglen = tglen - @_length = length - @_indexenum = indexenum - @_tg = tg - end - def _set(a) - a.each_with_index { |v, i| self[i] = v } - end - def _cpp_init - _length.times { |i| _tgat(i)._cpp_init } - end - alias length _length - alias size _length - def _tgat(i) - @_tg._at(@_memaddr + i*@_tglen) if i >= 0 and i < @_length - end - def [](i) - i = _indexenum.to_i(i) if _indexenum - i += @_length if i < 0 - _tgat(i)._get - end - def []=(i, v) - i = _indexenum.to_i(i) if _indexenum - i += @_length if i < 0 - _tgat(i)._set(v) - end - - include Enumerable -end -class StaticString < MemStruct - attr_accessor :_length - def initialize(length) - @_length = length - end - def _get - DFHack.memory_read(@_memaddr, @_length) - end - def _set(v) - DFHack.memory_write(@_memaddr, v[0, @_length]) - end -end - -class StlVector32 < MemStruct - attr_accessor :_tg - def initialize(tg) - @_tg = tg - end - - def length - DFHack.memory_vector32_length(@_memaddr) - end - def size ; length ; end # alias wouldnt work for subclasses - def valueptr_at(idx) - DFHack.memory_vector32_ptrat(@_memaddr, idx) - end - def insert_at(idx, val) - DFHack.memory_vector32_insert(@_memaddr, idx, val) - end - def delete_at(idx) - DFHack.memory_vector32_delete(@_memaddr, idx) - end - - def _set(v) - delete_at(length-1) while length > v.length # match lengthes - v.each_with_index { |e, i| self[i] = e } # patch entries - end - - def _cpp_init - DFHack.memory_vector_init(@_memaddr) - end - - def clear - delete_at(length-1) while length > 0 - end - def [](idx) - idx += length if idx < 0 - @_tg._at(valueptr_at(idx))._get if idx >= 0 and idx < length - end - def []=(idx, v) - idx += length if idx < 0 - if idx >= length - insert_at(idx, 0) - elsif idx < 0 - raise 'invalid idx' - end - @_tg._at(valueptr_at(idx))._set(v) - end - def push(v) - self[length] = v - self - end - def <<(v) ; push(v) ; end - def pop - l = length - if l > 0 - v = self[l-1] - delete_at(l-1) - end - v - end - - include Enumerable - # do a binary search in an ordered vector for a specific target attribute - # ex: world.history.figures.binsearch(unit.hist_figure_id) - def binsearch(target, field=:id) - o_start = 0 - o_end = length - 1 - while o_end >= o_start - o_half = o_start + (o_end-o_start)/2 - obj = self[o_half] - oval = obj.send(field) - if oval == target - return obj - elsif oval < target - o_start = o_half+1 - else - o_end = o_half-1 - end - end - end -end -class StlVector16 < StlVector32 - def length - DFHack.memory_vector16_length(@_memaddr) - end - def valueptr_at(idx) - DFHack.memory_vector16_ptrat(@_memaddr, idx) - end - def insert_at(idx, val) - DFHack.memory_vector16_insert(@_memaddr, idx, val) - end - def delete_at(idx) - DFHack.memory_vector16_delete(@_memaddr, idx) - end -end -class StlVector8 < StlVector32 - def length - DFHack.memory_vector8_length(@_memaddr) - end - def valueptr_at(idx) - DFHack.memory_vector8_ptrat(@_memaddr, idx) - end - def insert_at(idx, val) - DFHack.memory_vector8_insert(@_memaddr, idx, val) - end - def delete_at(idx) - DFHack.memory_vector8_delete(@_memaddr, idx) - end -end -class StlBitVector < StlVector32 - def initialize ; end - def length - DFHack.memory_vectorbool_length(@_memaddr) - end - def insert_at(idx, val) - DFHack.memory_vectorbool_insert(@_memaddr, idx, val) - end - def delete_at(idx) - DFHack.memory_vectorbool_delete(@_memaddr, idx) - end - def [](idx) - idx += length if idx < 0 - DFHack.memory_vectorbool_at(@_memaddr, idx) if idx >= 0 and idx < length - end - def []=(idx, v) - idx += length if idx < 0 - if idx >= length - insert_at(idx, v) - elsif idx < 0 - raise 'invalid idx' - else - DFHack.memory_vectorbool_setat(@_memaddr, idx, v) - end - end -end -class StlString < MemStruct - def _get - DFHack.memory_read_stlstring(@_memaddr) - end - - def _set(v) - DFHack.memory_write_stlstring(@_memaddr, v) - end - - def _cpp_init - DFHack.memory_stlstring_init(@_memaddr) - end -end -class StlDeque < MemStruct - attr_accessor :_tglen, :_tg - def initialize(tglen, tg) - @_tglen = tglen - @_tg = tg - end - # XXX DF uses stl::deque, so to have a C binding we'd need to single-case every - # possible struct size, like for StlVector. Just ignore it for now, deque are rare enough. - def inspect ; "#" ; end -end - -class DfFlagarray < MemStruct - attr_accessor :_indexenum - def initialize(indexenum) - @_indexenum = indexenum - end - def length - DFHack.memory_bitarray_length(@_memaddr) - end - # TODO _cpp_init - def size ; length ; end - def resize(len) - DFHack.memory_bitarray_resize(@_memaddr, len) - end - def [](idx) - idx = _indexenum.to_i(idx) if _indexenum - idx += length if idx < 0 - DFHack.memory_bitarray_isset(@_memaddr, idx) if idx >= 0 and idx < length - end - def []=(idx, v) - idx = _indexenum.to_i(idx) if _indexenum - idx += length if idx < 0 - if idx >= length or idx < 0 - raise 'invalid idx' - else - DFHack.memory_bitarray_set(@_memaddr, idx, v) - end - end - - include Enumerable -end -class DfArray < Compound - attr_accessor :_tglen, :_tg - def initialize(tglen, tg) - @_tglen = tglen - @_tg = tg - end - - field(:_ptr, 0) { number 32, false } - field(:_length, 4) { number 16, false } - - def length ; _length ; end - def size ; _length ; end - # TODO _cpp_init - def _tgat(i) - @_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length - end - def [](i) - i += _length if i < 0 - _tgat(i)._get - end - def []=(i, v) - i += _length if i < 0 - _tgat(i)._set(v) - end - def _set(a) - a.each_with_index { |v, i| self[i] = v } - end - - include Enumerable -end -class DfLinkedList < Compound - attr_accessor :_tg - def initialize(tg) - @_tg = tg - end - - field(:_ptr, 0) { number 32, false } - field(:_prev, 4) { number 32, false } - field(:_next, 8) { number 32, false } - - def item - # With the current xml structure, currently _tg designate - # the type of the 'next' and 'prev' fields, not 'item'. - # List head has item == NULL, so we can safely return nil. - - #addr = _ptr - #return if addr == 0 - #@_tg._at(addr)._get - end - - def item=(v) - #addr = _ptr - #raise 'null pointer' if addr == 0 - #@_tg.at(addr)._set(v) - raise 'null pointer' - end - - def prev - addr = _prev - return if addr == 0 - @_tg._at(addr)._get - end - - def next - addr = _next - return if addr == 0 - @_tg._at(addr)._get - end - - include Enumerable - def each - o = self - while o - yield o.item if o.item - o = o.next - end - end - def inspect ; "#" ; end -end - -class Global < MemStruct - attr_accessor :_glob - def initialize(glob) - @_glob = glob - end - def _at(addr) - g = DFHack.const_get(@_glob) - g = DFHack.rtti_getclassat(g, addr) - g.new._at(addr) - end - def inspect ; "#<#{@_glob}>" ; end -end -end # module MemHack - - -# cpp rtti name -> rb class -@rtti_n2c = {} -@rtti_c2n = {} - -# cpp rtti name -> vtable ptr -@rtti_n2v = {} -@rtti_v2n = {} - -def self.rtti_n2c ; @rtti_n2c ; end -def self.rtti_c2n ; @rtti_c2n ; end -def self.rtti_n2v ; @rtti_n2v ; end -def self.rtti_v2n ; @rtti_v2n ; end - -# register a ruby class with a cpp rtti class name -def self.rtti_register(cppname, cls) - @rtti_n2c[cppname] = cls - @rtti_c2n[cls] = cppname -end - -# return the ruby class to use for the cpp object at address if rtti info is available -def self.rtti_getclassat(cls, addr) - if addr != 0 and @rtti_c2n[cls] - # rtti info exist for class => cpp object has a vtable - @rtti_n2c[rtti_readclassname(get_vtable_ptr(addr))] || cls - else - cls - end -end - -# try to read the rtti classname from an object vtable pointer -def self.rtti_readclassname(vptr) - unless n = @rtti_v2n[vptr] - n = @rtti_v2n[vptr] = get_rtti_classname(vptr).to_sym - @rtti_n2v[n] = vptr - end - n -end - -# return the vtable pointer from the cpp rtti name -def self.rtti_getvtable(cppname) - unless v = @rtti_n2v[cppname] - v = get_vtable(cppname.to_s) - @rtti_n2v[cppname] = v - @rtti_v2n[v] = cppname if v != 0 - end - v if v != 0 -end - -def self.vmethod_call(obj, voff, a0=0, a1=0, a2=0, a3=0, a4=0) - vmethod_do_call(obj._memaddr, voff, vmethod_arg(a0), vmethod_arg(a1), vmethod_arg(a2), vmethod_arg(a3)) -end - -def self.vmethod_arg(arg) - case arg - when nil, false; 0 - when true; 1 - when Integer; arg - #when String; [arg].pack('p').unpack('L')[0] # raw pointer to buffer - when MemHack::Compound; arg._memaddr - else raise "bad vmethod arg #{arg.class}" - end -end - -end - - diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 76d6a0431..c6053900e 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -641,7 +641,7 @@ static VALUE rb_dfvcall(VALUE self, VALUE cppobj, VALUE cppvoff, VALUE a0, VALUE int ret; fptr = (decltype(fptr))*(void**)(*that + rb_num2ulong(cppvoff)); ret = fptr(that, rb_num2ulong(a0), rb_num2ulong(a1), rb_num2ulong(a2), rb_num2ulong(a3)); - return rb_uint2inum(ret); + return rb_int2inum(ret); } diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 8c218eb63..fd597e43f 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -1,5 +1,3 @@ -require 'hack/ruby-autogen' - module DFHack class << self # update the ruby.cpp version to accept a block @@ -298,5 +296,759 @@ def df DFHack end -# load user-specified startup file +# following: definitions used by ruby-autogen.rb +module DFHack +module MemHack +INSPECT_SIZE_LIMIT=16384 +class MemStruct + attr_accessor :_memaddr + def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end + def _get ; self ; end + def _cpp_init ; end +end + +class Compound < MemStruct + class << self + attr_accessor :_fields, :_rtti_classname, :_sizeof + def field(name, offset) + struct = yield + return if not struct + @_fields ||= [] + @_fields << [name, offset, struct] + define_method(name) { struct._at(@_memaddr+offset)._get } + define_method("#{name}=") { |v| struct._at(@_memaddr+offset)._set(v) } + end + def _fields_ancestors + if superclass.respond_to?(:_fields_ancestors) + superclass._fields_ancestors + _fields.to_a + else + _fields.to_a + end + end + + def number(bits, signed, initvalue=nil, enum=nil) + Number.new(bits, signed, initvalue, enum) + end + def float + Float.new + end + def bit(shift) + BitField.new(shift, 1) + end + def bits(shift, len, enum=nil) + BitField.new(shift, len, enum) + end + def pointer + Pointer.new((yield if block_given?)) + end + def pointer_ary(tglen) + PointerAry.new(tglen, yield) + end + def static_array(len, tglen, indexenum=nil) + StaticArray.new(tglen, len, indexenum, yield) + end + def static_string(len) + StaticString.new(len) + end + + def stl_vector(tglen=nil) + tg = yield if tglen + case tglen + when 1; StlVector8.new(tg) + when 2; StlVector16.new(tg) + else StlVector32.new(tg) + end + end + def stl_string + StlString.new + end + def stl_bit_vector + StlBitVector.new + end + def stl_deque(tglen) + StlDeque.new(tglen, yield) + end + + def df_flagarray(indexenum=nil) + DfFlagarray.new(indexenum) + end + def df_array(tglen) + DfArray.new(tglen, yield) + end + def df_linked_list + DfLinkedList.new(yield) + end + + def global(glob) + Global.new(glob) + end + def compound(name=nil, &b) + m = Class.new(Compound) + DFHack.const_set(name, m) if name + m.instance_eval(&b) + m.new + end + def rtti_classname(n) + DFHack.rtti_register(n, self) + @_rtti_classname = n + end + def sizeof(n) + @_sizeof = n + end + + # allocate a new c++ object, return its ruby wrapper + def cpp_new + ptr = DFHack.malloc(_sizeof) + if _rtti_classname and vt = DFHack.rtti_getvtable(_rtti_classname) + DFHack.memory_write_int32(ptr, vt) + # TODO call constructor + end + o = new._at(ptr) + o._cpp_init + o + end + end + def _cpp_init + _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_init } + end + def _set(h) + case h + when Hash; h.each { |k, v| send("_#{k}=", v) } + when Array; names = _field_names ; raise 'bad size' if names.length != h.length ; names.zip(h).each { |n, a| send("#{n}=", a) } + when Compound; _field_names.each { |n| send("#{n}=", h.send(n)) } + else raise 'wut?' + end + end + def _fields ; self.class._fields.to_a ; end + def _fields_ancestors ; self.class._fields_ancestors.to_a ; end + def _field_names ; _fields_ancestors.map { |n, o, s| n } ; end + def _rtti_classname ; self.class._rtti_classname ; end + def _sizeof ; self.class._sizeof ; end + @@inspecting = {} # avoid infinite recursion on mutually-referenced objects + def inspect + cn = self.class.name.sub(/^DFHack::/, '') + cn << ' @' << ('0x%X' % _memaddr) if cn != '' + out = "#<#{cn}" + return out << ' ...>' if @@inspecting[_memaddr] + @@inspecting[_memaddr] = true + _fields_ancestors.each { |n, o, s| + out << ' ' if out.length != 0 and out[-1, 1] != ' ' + if out.length > INSPECT_SIZE_LIMIT + out << '...' + break + end + out << inspect_field(n, o, s) + } + out.chomp!(' ') + @@inspecting.delete _memaddr + out << '>' + end + def inspect_field(n, o, s) + if s.kind_of?(BitField) and s._len == 1 + send(n) ? n.to_s : '' + elsif s.kind_of?(Pointer) + "#{n}=#{s._at(@_memaddr+o).inspect}" + elsif n == :_whole + "_whole=0x#{_whole.to_s(16)}" + else + v = send(n).inspect + "#{n}=#{v}" + end + rescue + "#{n}=ERR(#{$!})" + end +end + +class Enum + # number -> symbol + def self.enum + # ruby weirdness, needed to make the constants 'virtual' + @enum ||= const_get(:ENUM) + end + # symbol -> number + def self.nume + @nume ||= const_get(:NUME) + end + + def self.int(i) + nume[i] || i + end + def self.sym(i) + enum[i] || i + end +end + +class Number < MemStruct + attr_accessor :_bits, :_signed, :_initvalue, :_enum + def initialize(bits, signed, initvalue, enum) + @_bits = bits + @_signed = signed + @_initvalue = initvalue + @_enum = enum + end + + def _get + v = case @_bits + when 32; DFHack.memory_read_int32(@_memaddr) + when 16; DFHack.memory_read_int16(@_memaddr) + when 8; DFHack.memory_read_int8( @_memaddr) + when 64;(DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + (DFHack.memory_read_int32(@_memaddr+4) << 32) + end + v &= (1 << @_bits) - 1 if not @_signed + v = @_enum.sym(v) if @_enum + v + end + + def _set(v) + v = @_enum.int(v) if @_enum + case @_bits + when 32; DFHack.memory_write_int32(@_memaddr, v) + when 16; DFHack.memory_write_int16(@_memaddr, v) + when 8; DFHack.memory_write_int8( @_memaddr, v) + when 64; DFHack.memory_write_int32(@_memaddr, v & 0xffffffff) ; DFHack.memory_write_int32(@memaddr+4, v>>32) + end + end + + def _cpp_init + _set(@_initvalue) if @_initvalue + end +end +class Float < MemStruct + def _get + DFHack.memory_read_float(@_memaddr) + end + + def _set(v) + DFHack.memory_write_float(@_memaddr, v) + end + + def _cpp_init + _set(0.0) + end +end +class BitField < MemStruct + attr_accessor :_shift, :_len, :_enum + def initialize(shift, len, enum=nil) + @_shift = shift + @_len = len + @_enum = enum + end + def _mask + (1 << @_len) - 1 + end + + def _get + v = DFHack.memory_read_int32(@_memaddr) >> @_shift + if @_len == 1 + ((v & 1) == 0) ? false : true + else + v &= _mask + v = @_enum.sym(v) if @_enum + v + end + end + + def _set(v) + if @_len == 1 + # allow 'bit = 0' + v = (v && v != 0 ? 1 : 0) + end + v = @_enum.int(v) if @_enum + v = (v & _mask) << @_shift + + ori = DFHack.memory_read_int32(@_memaddr) & 0xffffffff + DFHack.memory_write_int32(@_memaddr, ori - (ori & ((-1 & _mask) << @_shift)) + v) + end +end + +class Pointer < MemStruct + attr_accessor :_tg + def initialize(tg) + @_tg = tg + end + + def _getp + DFHack.memory_read_int32(@_memaddr) & 0xffffffff + end + + def _get + addr = _getp + return if addr == 0 + @_tg._at(addr)._get + end + + # XXX shaky... + def _set(v) + if v.kind_of?(Pointer) + DFHack.memory_write_int32(@_memaddr, v._getp) + elsif v.kind_of?(MemStruct) + DFHack.memory_write_int32(@_memaddr, v._memaddr) + else + _get._set(v) + end + end + + def inspect + ptr = _getp + if ptr == 0 + 'NULL' + else + cn = '' + cn = @_tg.class.name.sub(/^DFHack::/, '').sub(/^MemHack::/, '') if @_tg + cn = @_tg._glob if cn == 'Global' + "#" + end + end +end +class PointerAry < MemStruct + attr_accessor :_tglen, :_tg + def initialize(tglen, tg) + @_tglen = tglen + @_tg = tg + end + + def _getp(i=0) + delta = (i != 0 ? i*@_tglen : 0) + (DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + delta + end + + def _get + addr = _getp + return if addr == 0 + self + end + + def [](i) + addr = _getp(i) + return if addr == 0 + @_tg._at(addr)._get + end + def []=(i, v) + addr = _getp(i) + raise 'null pointer' if addr == 0 + @_tg._at(addr)._set(v) + end + + def inspect ; ptr = _getp ; (ptr == 0) ? 'NULL' : "#" ; end +end +module Enumerable + include ::Enumerable + attr_accessor :_indexenum + def each ; (0...length).each { |i| yield self[i] } ; end + def inspect + out = '[' + each_with_index { |e, idx| + out << ', ' if out.length > 1 + if out.length > INSPECT_SIZE_LIMIT + out << '...' + break + end + out << "#{_indexenum.sym(idx)}=" if _indexenum + out << e.inspect + } + out << ']' + end + def empty? ; length == 0 ; end + def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end +end +class StaticArray < MemStruct + attr_accessor :_tglen, :_length, :_indexenum, :_tg + def initialize(tglen, length, indexenum, tg) + @_tglen = tglen + @_length = length + @_indexenum = indexenum + @_tg = tg + end + def _set(a) + a.each_with_index { |v, i| self[i] = v } + end + def _cpp_init + _length.times { |i| _tgat(i)._cpp_init } + end + alias length _length + alias size _length + def _tgat(i) + @_tg._at(@_memaddr + i*@_tglen) if i >= 0 and i < @_length + end + def [](i) + i = _indexenum.int(i) if _indexenum + i += @_length if i < 0 + _tgat(i)._get + end + def []=(i, v) + i = _indexenum.int(i) if _indexenum + i += @_length if i < 0 + _tgat(i)._set(v) + end + + include Enumerable +end +class StaticString < MemStruct + attr_accessor :_length + def initialize(length) + @_length = length + end + def _get + DFHack.memory_read(@_memaddr, @_length) + end + def _set(v) + DFHack.memory_write(@_memaddr, v[0, @_length]) + end +end + +class StlVector32 < MemStruct + attr_accessor :_tg + def initialize(tg) + @_tg = tg + end + + def length + DFHack.memory_vector32_length(@_memaddr) + end + def size ; length ; end # alias wouldnt work for subclasses + def valueptr_at(idx) + DFHack.memory_vector32_ptrat(@_memaddr, idx) + end + def insert_at(idx, val) + DFHack.memory_vector32_insert(@_memaddr, idx, val) + end + def delete_at(idx) + DFHack.memory_vector32_delete(@_memaddr, idx) + end + + def _set(v) + delete_at(length-1) while length > v.length # match lengthes + v.each_with_index { |e, i| self[i] = e } # patch entries + end + + def _cpp_init + DFHack.memory_vector_init(@_memaddr) + end + + def clear + delete_at(length-1) while length > 0 + end + def [](idx) + idx += length if idx < 0 + @_tg._at(valueptr_at(idx))._get if idx >= 0 and idx < length + end + def []=(idx, v) + idx += length if idx < 0 + if idx >= length + insert_at(idx, 0) + elsif idx < 0 + raise 'invalid idx' + end + @_tg._at(valueptr_at(idx))._set(v) + end + def push(v) + self[length] = v + self + end + def <<(v) ; push(v) ; end + def pop + l = length + if l > 0 + v = self[l-1] + delete_at(l-1) + end + v + end + + include Enumerable + # do a binary search in an ordered vector for a specific target attribute + # ex: world.history.figures.binsearch(unit.hist_figure_id) + def binsearch(target, field=:id) + o_start = 0 + o_end = length - 1 + while o_end >= o_start + o_half = o_start + (o_end-o_start)/2 + obj = self[o_half] + oval = obj.send(field) + if oval == target + return obj + elsif oval < target + o_start = o_half+1 + else + o_end = o_half-1 + end + end + end +end +class StlVector16 < StlVector32 + def length + DFHack.memory_vector16_length(@_memaddr) + end + def valueptr_at(idx) + DFHack.memory_vector16_ptrat(@_memaddr, idx) + end + def insert_at(idx, val) + DFHack.memory_vector16_insert(@_memaddr, idx, val) + end + def delete_at(idx) + DFHack.memory_vector16_delete(@_memaddr, idx) + end +end +class StlVector8 < StlVector32 + def length + DFHack.memory_vector8_length(@_memaddr) + end + def valueptr_at(idx) + DFHack.memory_vector8_ptrat(@_memaddr, idx) + end + def insert_at(idx, val) + DFHack.memory_vector8_insert(@_memaddr, idx, val) + end + def delete_at(idx) + DFHack.memory_vector8_delete(@_memaddr, idx) + end +end +class StlBitVector < StlVector32 + def initialize ; end + def length + DFHack.memory_vectorbool_length(@_memaddr) + end + def insert_at(idx, val) + DFHack.memory_vectorbool_insert(@_memaddr, idx, val) + end + def delete_at(idx) + DFHack.memory_vectorbool_delete(@_memaddr, idx) + end + def [](idx) + idx += length if idx < 0 + DFHack.memory_vectorbool_at(@_memaddr, idx) if idx >= 0 and idx < length + end + def []=(idx, v) + idx += length if idx < 0 + if idx >= length + insert_at(idx, v) + elsif idx < 0 + raise 'invalid idx' + else + DFHack.memory_vectorbool_setat(@_memaddr, idx, v) + end + end +end +class StlString < MemStruct + def _get + DFHack.memory_read_stlstring(@_memaddr) + end + + def _set(v) + DFHack.memory_write_stlstring(@_memaddr, v) + end + + def _cpp_init + DFHack.memory_stlstring_init(@_memaddr) + end +end +class StlDeque < MemStruct + attr_accessor :_tglen, :_tg + def initialize(tglen, tg) + @_tglen = tglen + @_tg = tg + end + # XXX DF uses stl::deque, so to have a C binding we'd need to single-case every + # possible struct size, like for StlVector. Just ignore it for now, deque are rare enough. + def inspect ; "#" ; end +end + +class DfFlagarray < MemStruct + attr_accessor :_indexenum + def initialize(indexenum) + @_indexenum = indexenum + end + def length + DFHack.memory_bitarray_length(@_memaddr) + end + # TODO _cpp_init + def size ; length ; end + def resize(len) + DFHack.memory_bitarray_resize(@_memaddr, len) + end + def [](idx) + idx = _indexenum.int(idx) if _indexenum + idx += length if idx < 0 + DFHack.memory_bitarray_isset(@_memaddr, idx) if idx >= 0 and idx < length + end + def []=(idx, v) + idx = _indexenum.int(idx) if _indexenum + idx += length if idx < 0 + if idx >= length or idx < 0 + raise 'invalid idx' + else + DFHack.memory_bitarray_set(@_memaddr, idx, v) + end + end + + include Enumerable +end +class DfArray < Compound + attr_accessor :_tglen, :_tg + def initialize(tglen, tg) + @_tglen = tglen + @_tg = tg + end + + field(:_ptr, 0) { number 32, false } + field(:_length, 4) { number 16, false } + + def length ; _length ; end + def size ; _length ; end + # TODO _cpp_init + def _tgat(i) + @_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length + end + def [](i) + i += _length if i < 0 + _tgat(i)._get + end + def []=(i, v) + i += _length if i < 0 + _tgat(i)._set(v) + end + def _set(a) + a.each_with_index { |v, i| self[i] = v } + end + + include Enumerable +end +class DfLinkedList < Compound + attr_accessor :_tg + def initialize(tg) + @_tg = tg + end + + field(:_ptr, 0) { number 32, false } + field(:_prev, 4) { number 32, false } + field(:_next, 8) { number 32, false } + + def item + # With the current xml structure, currently _tg designate + # the type of the 'next' and 'prev' fields, not 'item'. + # List head has item == NULL, so we can safely return nil. + + #addr = _ptr + #return if addr == 0 + #@_tg._at(addr)._get + end + + def item=(v) + #addr = _ptr + #raise 'null pointer' if addr == 0 + #@_tg.at(addr)._set(v) + raise 'null pointer' + end + + def prev + addr = _prev + return if addr == 0 + @_tg._at(addr)._get + end + + def next + addr = _next + return if addr == 0 + @_tg._at(addr)._get + end + + include Enumerable + def each + o = self + while o + yield o.item if o.item + o = o.next + end + end + def inspect ; "#" ; end +end + +class Global < MemStruct + attr_accessor :_glob + def initialize(glob) + @_glob = glob + end + def _at(addr) + g = DFHack.const_get(@_glob) + g = DFHack.rtti_getclassat(g, addr) + g.new._at(addr) + end + def inspect ; "#<#{@_glob}>" ; end +end +end # module MemHack + +class BooleanEnum + def self.int(v) ; ((v == true) || (v == 1)) ? 1 : 0 ; end + def self.sym(v) ; (!v || (v == 0)) ? false : true ; end +end + +# cpp rtti name -> rb class +@rtti_n2c = {} +@rtti_c2n = {} + +# cpp rtti name -> vtable ptr +@rtti_n2v = {} +@rtti_v2n = {} + +def self.rtti_n2c ; @rtti_n2c ; end +def self.rtti_c2n ; @rtti_c2n ; end +def self.rtti_n2v ; @rtti_n2v ; end +def self.rtti_v2n ; @rtti_v2n ; end + +# register a ruby class with a cpp rtti class name +def self.rtti_register(cppname, cls) + @rtti_n2c[cppname] = cls + @rtti_c2n[cls] = cppname +end + +# return the ruby class to use for the cpp object at address if rtti info is available +def self.rtti_getclassat(cls, addr) + if addr != 0 and @rtti_c2n[cls] + # rtti info exist for class => cpp object has a vtable + @rtti_n2c[rtti_readclassname(get_vtable_ptr(addr))] || cls + else + cls + end +end + +# try to read the rtti classname from an object vtable pointer +def self.rtti_readclassname(vptr) + unless n = @rtti_v2n[vptr] + n = @rtti_v2n[vptr] = get_rtti_classname(vptr).to_sym + @rtti_n2v[n] = vptr + end + n +end + +# return the vtable pointer from the cpp rtti name +def self.rtti_getvtable(cppname) + unless v = @rtti_n2v[cppname] + v = get_vtable(cppname.to_s) + @rtti_n2v[cppname] = v + @rtti_v2n[v] = cppname if v != 0 + end + v if v != 0 +end + +def self.vmethod_call(obj, voff, a0=0, a1=0, a2=0, a3=0, a4=0) + vmethod_do_call(obj._memaddr, voff, vmethod_arg(a0), vmethod_arg(a1), vmethod_arg(a2), vmethod_arg(a3)) +end + +def self.vmethod_arg(arg) + case arg + when nil, false; 0 + when true; 1 + when Integer; arg + #when String; [arg].pack('p').unpack('L')[0] # raw pointer to buffer + when MemHack::Compound; arg._memaddr + else raise "bad vmethod arg #{arg.class}" + end +end + +end + +# load autogen'd file +require 'hack/ruby-autogen' + +# load optional user-specified startup file load 'ruby_custom.rb' if File.exist?('ruby_custom.rb') From 246df435117a6ae4da08dae8cf015be6d2d4e5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 30 May 2012 01:08:07 +0200 Subject: [PATCH 10/60] Track clsocket --- depends/clsocket | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/clsocket b/depends/clsocket index 49fa80061..54cad7ca5 160000 --- a/depends/clsocket +++ b/depends/clsocket @@ -1 +1 @@ -Subproject commit 49fa800615a4e5c872164bcb4122030d2ebda9cf +Subproject commit 54cad7ca5431c8e27cfb90f51fdfaf69211f0a4f From 0749233ed1cab8eb53216df8a074bb989c4af04b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 30 May 2012 01:12:29 +0200 Subject: [PATCH 11/60] And again... --- depends/clsocket | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/clsocket b/depends/clsocket index 54cad7ca5..c85e9fb35 160000 --- a/depends/clsocket +++ b/depends/clsocket @@ -1 +1 @@ -Subproject commit 54cad7ca5431c8e27cfb90f51fdfaf69211f0a4f +Subproject commit c85e9fb35d3510c5dcc367056cda3237d77a7add From 884cf53b0bee55ccdcf2ee98ba9cc30ea9236e4e Mon Sep 17 00:00:00 2001 From: Quietust Date: Wed, 30 May 2012 08:02:53 -0500 Subject: [PATCH 12/60] body_part_raw has its own set of flags which do NOT match body_part_template --- plugins/forceequip.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/forceequip.cpp b/plugins/forceequip.cpp index 723f29474..abf4a95db 100644 --- a/plugins/forceequip.cpp +++ b/plugins/forceequip.cpp @@ -28,7 +28,7 @@ using namespace std; #include "df/body_part_raw.h" #include "MiscUtils.h" #include "df/unit_inventory_item.h" -#include "df/body_part_template_flags.h" +#include "df/body_part_raw_flags.h" #include "df/creature_raw.h" #include "df/caste_raw.h" #include "df/body_detail_plan.h" @@ -305,25 +305,25 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u if (verbose) { Core::print("Inspecting bodypart %s.\n", currPart->token.c_str()); } // Inspect the current bodypart - if (item->getType() == df::enums::item_type::GLOVES && currPart->flags.is_set(df::body_part_template_flags::GRASP) && - ((item->getGloveHandedness() == const_GloveLeftHandedness && currPart->flags.is_set(df::body_part_template_flags::LEFT)) || - (item->getGloveHandedness() == const_GloveRightHandedness && currPart->flags.is_set(df::body_part_template_flags::RIGHT)))) + if (item->getType() == df::enums::item_type::GLOVES && currPart->flags.is_set(df::body_part_raw_flags::GRASP) && + ((item->getGloveHandedness() == const_GloveLeftHandedness && currPart->flags.is_set(df::body_part_raw_flags::LEFT)) || + (item->getGloveHandedness() == const_GloveRightHandedness && currPart->flags.is_set(df::body_part_raw_flags::RIGHT)))) { if (verbose) { Core::print("Hand found (%s)...", currPart->token.c_str()); } } - else if (item->getType() == df::enums::item_type::HELM && currPart->flags.is_set(df::body_part_template_flags::HEAD)) + else if (item->getType() == df::enums::item_type::HELM && currPart->flags.is_set(df::body_part_raw_flags::HEAD)) { if (verbose) { Core::print("Head found (%s)...", currPart->token.c_str()); } } - else if (item->getType() == df::enums::item_type::ARMOR && currPart->flags.is_set(df::body_part_template_flags::UPPERBODY)) + else if (item->getType() == df::enums::item_type::ARMOR && currPart->flags.is_set(df::body_part_raw_flags::UPPERBODY)) { if (verbose) { Core::print("Upper body found (%s)...", currPart->token.c_str()); } } - else if (item->getType() == df::enums::item_type::PANTS && currPart->flags.is_set(df::body_part_template_flags::LOWERBODY)) + else if (item->getType() == df::enums::item_type::PANTS && currPart->flags.is_set(df::body_part_raw_flags::LOWERBODY)) { if (verbose) { Core::print("Lower body found (%s)...", currPart->token.c_str()); } } - else if (item->getType() == df::enums::item_type::SHOES && currPart->flags.is_set(df::body_part_template_flags::STANCE)) + else if (item->getType() == df::enums::item_type::SHOES && currPart->flags.is_set(df::body_part_raw_flags::STANCE)) { if (verbose) { Core::print("Foot found (%s)...", currPart->token.c_str()); } } From 470c9f60aa2303faea68c68e946cc02de1a20c04 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 31 May 2012 13:23:00 +0200 Subject: [PATCH 13/60] remoteclient: dont use gcc deprecated auto_ptr --- library/RemoteClient.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/library/RemoteClient.cpp b/library/RemoteClient.cpp index a1ac2ec92..4d30988c6 100644 --- a/library/RemoteClient.cpp +++ b/library/RemoteClient.cpp @@ -329,17 +329,19 @@ bool sendRemoteMessage(CSimpleSocket *socket, int16_t id, const MessageLite *msg int size = size_ready ? msg->GetCachedSize() : msg->ByteSize(); int fullsz = size + sizeof(RPCMessageHeader); - std::auto_ptr data(new uint8_t[fullsz]); - RPCMessageHeader *hdr = (RPCMessageHeader*)data.get(); + uint8_t *data = new uint8_t[fullsz]; + RPCMessageHeader *hdr = (RPCMessageHeader*)data; hdr->id = id; hdr->size = size; - uint8_t *pstart = data.get() + sizeof(RPCMessageHeader); + uint8_t *pstart = data + sizeof(RPCMessageHeader); uint8_t *pend = msg->SerializeWithCachedSizesToArray(pstart); assert((pend - pstart) == size); - return (socket->Send(data.get(), fullsz) == fullsz); + int got = socket->Send(data, fullsz); + delete[] data; + return (got == fullsz); } command_result RemoteFunctionBase::execute(color_ostream &out, @@ -402,9 +404,9 @@ command_result RemoteFunctionBase::execute(color_ostream &out, return CR_LINK_FAILURE; } - std::auto_ptr buf(new uint8_t[header.size]); + uint8_t *buf = new uint8_t[header.size]; - if (!readFullBuffer(p_client->socket, buf.get(), header.size)) + if (!readFullBuffer(p_client->socket, buf, header.size)) { out.printerr("In call to %s::%s: I/O error in receive %d bytes of data.\n", this->proto.c_str(), this->name.c_str(), header.size); @@ -413,18 +415,20 @@ command_result RemoteFunctionBase::execute(color_ostream &out, switch (header.id) { case RPC_REPLY_RESULT: - if (!output->ParseFromArray(buf.get(), header.size)) + if (!output->ParseFromArray(buf, header.size)) { out.printerr("In call to %s::%s: error parsing received result.\n", this->proto.c_str(), this->name.c_str()); + delete[] buf; return CR_LINK_FAILURE; } + delete[] buf; return CR_OK; case RPC_REPLY_TEXT: text_data.Clear(); - if (text_data.ParseFromArray(buf.get(), header.size)) + if (text_data.ParseFromArray(buf, header.size)) text_decoder.decode(&text_data); else out.printerr("In call to %s::%s: received invalid text data.\n", @@ -434,5 +438,6 @@ command_result RemoteFunctionBase::execute(color_ostream &out, default: break; } + delete[] buf; } } From e143c312bf26cbbe9a65175b398546f0bc19945c Mon Sep 17 00:00:00 2001 From: Quietust Date: Thu, 31 May 2012 19:13:11 -0500 Subject: [PATCH 14/60] Sync with df-structures changes --- plugins/dwarfexport/dwarfexport.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/dwarfexport/dwarfexport.cpp b/plugins/dwarfexport/dwarfexport.cpp index 5db1bf226..64fbcd653 100644 --- a/plugins/dwarfexport/dwarfexport.cpp +++ b/plugins/dwarfexport/dwarfexport.cpp @@ -89,13 +89,13 @@ static void element(const char* name, const uint32_t content, ostream& out, cons static void printAttributes(color_ostream &con, df::unit* cre, ostream& out) { out << " " << endl; for (int i = 0; i < NUM_CREATURE_PHYSICAL_ATTRIBUTES; i++) { - element(physicals[i], cre->body.physical_attrs[i].unk1, out, " "); + element(physicals[i], cre->body.physical_attrs[i].value, out, " "); } df::unit_soul * s = cre->status.current_soul; if (s) { for (int i = 0; i < NUM_CREATURE_MENTAL_ATTRIBUTES; i++) { - element(mentals[i], s->mental_attrs[i].unk1, out, " "); + element(mentals[i], s->mental_attrs[i].value, out, " "); } } out << " " << endl; From b612532348ab18992d99f6abd507940cab877727 Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 2 Jun 2012 23:35:05 +0200 Subject: [PATCH 15/60] export openplugin/lookupplugin from plugin manager --- library/include/PluginManager.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 31633dfe2..b76df437d 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -61,11 +61,11 @@ namespace DFHack struct DFLibrary; // Open a plugin library - DFLibrary * OpenPlugin (const char * filename); + DFHACK_EXPORT DFLibrary * OpenPlugin (const char * filename); // find a symbol inside plugin - void * LookupPlugin (DFLibrary * plugin ,const char * function); + DFHACK_EXPORT void * LookupPlugin (DFLibrary * plugin ,const char * function); // Close a plugin library - void ClosePlugin (DFLibrary * plugin); + DFHACK_EXPORT void ClosePlugin (DFLibrary * plugin); struct DFHACK_EXPORT CommandReg { const char *name; From 2aace670eaa9eea168b4b4eb5e090e0047b9712d Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 2 Jun 2012 23:44:52 +0200 Subject: [PATCH 16/60] ruby: dlopen libruby -- currently segfaults with rb1.9 ... --- plugins/ruby/CMakeLists.txt | 24 +++----- plugins/ruby/ruby.cpp | 120 ++++++++++++++++++++++++++++++++++-- 2 files changed, 125 insertions(+), 19 deletions(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index 440f3a249..bcd10ac89 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -1,15 +1,9 @@ -find_package(Ruby) -if(RUBY_FOUND) - ADD_CUSTOM_COMMAND( - OUTPUT ruby-autogen.rb - COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb - DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl - ) - ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb) - include_directories("${dfhack_SOURCE_DIR}/depends/tthread" ${RUBY_INCLUDE_PATH}) - DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread) - target_link_libraries(ruby ${RUBY_LIBRARY}) - install(FILES ruby.rb ruby-autogen.rb DESTINATION ${DFHACK_LIBRARY_DESTINATION}) -else(RUBY_FOUND) - MESSAGE(STATUS "Required library (ruby) not found - ruby plugin can't be built.") -endif(RUBY_FOUND) +ADD_CUSTOM_COMMAND( + OUTPUT ruby-autogen.rb + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb + DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl +) +ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb) +include_directories("${dfhack_SOURCE_DIR}/depends/tthread") +DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread) +install(FILES ruby.rb ruby-autogen.rb DESTINATION ${DFHACK_LIBRARY_DESTINATION}) diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index c6053900e..189fc7a3b 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -11,8 +11,6 @@ #include "tinythread.h" -#include - using namespace DFHack; @@ -20,6 +18,8 @@ using namespace DFHack; // DFHack stuff +static int df_loadruby(void); +static void df_unloadruby(void); static void df_rubythread(void*); static command_result df_rubyload(color_ostream &out, std::vector & parameters); static command_result df_rubyeval(color_ostream &out, std::vector & parameters); @@ -45,6 +45,11 @@ DFHACK_PLUGIN("ruby") DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { + if (!df_loadruby()) { + Core::printerr("failed to load libruby\n"); + return CR_FAILURE; + } + m_irun = new tthread::mutex(); m_mutex = new tthread::mutex(); r_type = RB_INIT; @@ -90,6 +95,8 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) m_mutex->unlock(); delete m_mutex; + df_unloadruby(); + return CR_OK; } @@ -191,6 +198,111 @@ static command_result df_rubyeval(color_ostream &out, std::vector // ruby stuff +// ruby-dev on windows is messy +// ruby.h on linux 64 is broken +// so we dynamically load libruby instead of linking it at compile time +// lib path can be set in dfhack.ini to use the system libruby, but by +// default we'll embed our own (downloaded by cmake) + +// these ruby definitions are invalid for windows 64bit +typedef unsigned long VALUE; +typedef unsigned long ID; + +#define Qfalse ((VALUE)0) +#define Qtrue ((VALUE)2) +#define Qnil ((VALUE)4) + +#define INT2FIX(i) ((VALUE)((((long)i) << 1) | 1)) +#define FIX2INT(i) (((long)i) >> 1) +#define RUBY_METHOD_FUNC(func) ((VALUE(*)(...))func) + +VALUE *rb_eRuntimeError; + +void (*ruby_sysinit)(int *, const char ***); +void (*ruby_init)(void); +void (*ruby_init_loadpath)(void); +void (*ruby_script)(const char*); +void (*ruby_finalize)(void); +ID (*rb_intern)(const char*); +VALUE (*rb_raise)(VALUE, const char*, ...); +VALUE (*rb_funcall)(VALUE, ID, int, ...); +VALUE (*rb_define_module)(const char*); +void (*rb_define_singleton_method)(VALUE, const char*, VALUE(*)(...), int); +void (*rb_define_const)(VALUE, const char*, VALUE); +void (*rb_load_protect)(VALUE, int, int*); +VALUE (*rb_gv_get)(const char*); +VALUE (*rb_str_new)(const char*, long); +VALUE (*rb_str_new2)(const char*); +char* (*rb_string_value_ptr)(VALUE*); +VALUE (*rb_eval_string_protect)(const char*, int*); +VALUE (*rb_ary_shift)(VALUE); +VALUE (*rb_float_new)(double); +double (*rb_num2dbl)(VALUE); +VALUE (*rb_int2inum)(long); +VALUE (*rb_uint2inum)(unsigned long); +unsigned long (*rb_num2ulong)(VALUE); +// end of rip(ruby.h) + +DFHack::DFLibrary *libruby_handle; + +// load the ruby library, initialize function pointers +static int df_loadruby(void) +{ + const char *libpath = +#ifdef WIN32 + "msvcrt-ruby191.dll"; +#else + "libruby1.9.so.1.9.0"; +#endif + + libruby_handle = OpenPlugin(libpath); + if (!libruby_handle) + return 0; + + if (!(rb_eRuntimeError = (VALUE*)LookupPlugin(libruby_handle, "rb_eRuntimeError"))) + return 0; + + // XXX does msvc support decltype ? might need a #define decltype typeof + // or just assign to *(void**)(&s) = ... + // ruby_sysinit is optional (ruby1.9 only) + ruby_sysinit = (decltype(ruby_sysinit))LookupPlugin(libruby_handle, "ruby_sysinit"); +#define rbloadsym(s) if (!(s = (decltype(s))LookupPlugin(libruby_handle, #s))) return 0 + rbloadsym(ruby_init); + rbloadsym(ruby_init_loadpath); + rbloadsym(ruby_script); + rbloadsym(ruby_finalize); + rbloadsym(rb_intern); + rbloadsym(rb_raise); + rbloadsym(rb_funcall); + rbloadsym(rb_define_module); + rbloadsym(rb_define_singleton_method); + rbloadsym(rb_define_const); + rbloadsym(rb_load_protect); + rbloadsym(rb_gv_get); + rbloadsym(rb_str_new); + rbloadsym(rb_str_new2); + rbloadsym(rb_string_value_ptr); + rbloadsym(rb_eval_string_protect); + rbloadsym(rb_ary_shift); + rbloadsym(rb_float_new); + rbloadsym(rb_num2dbl); + rbloadsym(rb_int2inum); + rbloadsym(rb_uint2inum); + rbloadsym(rb_num2ulong); +#undef rbloadsym + + return 1; +} + +static void df_unloadruby(void) +{ + if (libruby_handle) { + ClosePlugin(libruby_handle); + libruby_handle = 0; + } +} + + // ruby thread code static void dump_rb_error(void) { @@ -353,7 +465,7 @@ static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) */ static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) { - rb_raise(rb_eRuntimeError, "not implemented"); + rb_raise(*rb_eRuntimeError, "not implemented"); } static VALUE rb_dfget_global_address(VALUE self, VALUE name) @@ -402,7 +514,7 @@ static VALUE rb_dfmalloc(VALUE self, VALUE len) { char *ptr = (char*)malloc(FIX2INT(len)); if (!ptr) - rb_raise(rb_eRuntimeError, "no memory"); + rb_raise(*rb_eRuntimeError, "no memory"); memset(ptr, 0, FIX2INT(len)); return rb_uint2inum((long)ptr); } From b7edbf2076e20ef5a05979e687a5cdf93ddf203e Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 5 Jun 2012 14:00:52 +0400 Subject: [PATCH 17/60] Support 7-argument vmethods. --- library/include/DataFuncs.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/library/include/DataFuncs.h b/library/include/DataFuncs.h index aff04128c..637a532f8 100644 --- a/library/include/DataFuncs.h +++ b/library/include/DataFuncs.h @@ -160,6 +160,17 @@ INSTANTIATE_WRAPPERS(6, (OSTREAM_ARG,A1,A2,A3,A4,A5,A6), (out,vA1,vA2,vA3,vA4,vA #define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7 INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7)) +INSTANTIATE_WRAPPERS(7, (A1,A2,A3,A4,A5,A6,A7), (vA1,vA2,vA3,vA4,vA5,vA6,vA7), + LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); + LOAD_ARG(A4); LOAD_ARG(A5); LOAD_ARG(A6); + LOAD_ARG(A7);) +INSTANTIATE_WRAPPERS(7, (OSTREAM_ARG,A1,A2,A3,A4,A5,A6,A7), (out,vA1,vA2,vA3,vA4,vA5,vA6,vA7), + LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); + LOAD_ARG(A4); LOAD_ARG(A5); LOAD_ARG(A6); LOAD_ARG(A7);) +#undef FW_TARGS + +#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8 +INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7,A8)) #undef FW_TARGS #undef FW_TARGSC From d35d8d3431f1c02f70c281558fa211d3bd7642ca Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 5 Jun 2012 14:06:29 +0400 Subject: [PATCH 18/60] Add the script to fix lagging fat dwarves. --- library/xml | 2 +- scripts/fix/fat-dwarves.lua | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 scripts/fix/fat-dwarves.lua diff --git a/library/xml b/library/xml index 7d5c349a4..8c0794489 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 7d5c349a40dc1f211de2d37aeece4a3d9d631ed2 +Subproject commit 8c07944891f09391cc02098074ffc8080b43b184 diff --git a/scripts/fix/fat-dwarves.lua b/scripts/fix/fat-dwarves.lua new file mode 100644 index 000000000..5394f6770 --- /dev/null +++ b/scripts/fix/fat-dwarves.lua @@ -0,0 +1,24 @@ +-- Makes fat dwarves non-fat. +-- +-- See for more info: +-- http://www.bay12games.com/dwarves/mantisbt/view.php?id=5971 + +local num_fat = 0 +local num_lagging = 0 + +for _,v in ipairs(df.global.world.units.all) do + local fat = v.counters2.stored_fat + if fat > 850000 then + v.counters2.stored_fat = 500000 + if v.race == df.global.ui.race_id then + print(fat,dfhack.TranslateName(dfhack.units.getVisibleName(v))) + num_fat = num_fat + 1 + if fat > 999990 then + num_lagging = num_lagging + 1 + end + end + end +end + +print("Fat dwarves cured: "..num_fat) +print("Lag sources: "..num_lagging) From 7aff2d6bc06c0fd8e14df08c070517bd53374dbf Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Tue, 5 Jun 2012 14:33:06 -0500 Subject: [PATCH 19/60] Sync structures for .34.11 --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 8c0794489..fc618cce6 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 8c07944891f09391cc02098074ffc8080b43b184 +Subproject commit fc618cce63f0037442ec8d650a11cbe823f0443a From 9dbcaa2733878bf089ac12514ed22d9b8ebe27a9 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 6 Jun 2012 18:54:06 +0400 Subject: [PATCH 20/60] Support adventure mode and stockpile screens in focus strings. --- library/modules/Gui.cpp | 45 ++++++++++++++++++++++++++++++++++++++++- library/xml | 2 +- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 111ea94c7..63ba811c1 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -58,12 +58,14 @@ using namespace DFHack; #include "df/viewscreen_layer_overall_healthst.h" #include "df/viewscreen_layer_assigntradest.h" #include "df/viewscreen_layer_militaryst.h" +#include "df/viewscreen_layer_stockpilest.h" #include "df/viewscreen_petst.h" #include "df/viewscreen_tradegoodsst.h" #include "df/viewscreen_storesst.h" #include "df/ui_unit_view_mode.h" #include "df/ui_sidebar_menus.h" #include "df/ui_look_list.h" +#include "df/ui_advmode.h" #include "df/job.h" #include "df/ui_build_selector.h" #include "df/building_workshopst.h" @@ -273,7 +275,9 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) break; case Burrows: - if (ui->burrows.in_add_units_mode) + if (ui->burrows.in_confirm_delete) + focus += "/ConfirmDelete"; + else if (ui->burrows.in_add_units_mode) focus += "/AddUnits"; else if (ui->burrows.in_edit_name_mode) focus += "/EditName"; @@ -288,6 +292,16 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) } } +DEFINE_GET_FOCUS_STRING_HANDLER(dungeonmode) +{ + using df::global::ui_advmode; + + if (!ui_advmode) + return; + + focus += "/" + enum_item_key(ui_advmode->menu); +} + DEFINE_GET_FOCUS_STRING_HANDLER(unitlist) { focus += "/" + enum_item_key(screen->page); @@ -407,6 +421,35 @@ DEFINE_GET_FOCUS_STRING_HANDLER(stores) focus += "/Items"; } +DEFINE_GET_FOCUS_STRING_HANDLER(layer_stockpile) +{ + auto list1 = getLayerList(screen, 0); + auto list2 = getLayerList(screen, 1); + auto list3 = getLayerList(screen, 2); + if (!list1 || !list2 || !list3 || !screen->settings) return; + + auto group = screen->cur_group; + if (group != vector_get(screen->group_ids, list1->cursor)) + return; + + focus += "/" + enum_item_key(group); + + auto bits = vector_get(screen->group_bits, list1->cursor); + if (bits.whole && !(bits.whole & screen->settings->flags.whole)) + { + focus += "/Off"; + return; + } + + focus += "/On"; + + if (list2->bright || list3->bright || screen->list_ids.empty()) { + focus += "/" + enum_item_key(screen->cur_list); + + if (list3->bright) + focus += (screen->item_names.empty() ? "/None" : "/Item"); + } +} std::string Gui::getFocusString(df::viewscreen *top) { diff --git a/library/xml b/library/xml index fc618cce6..0a0dc568d 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit fc618cce63f0037442ec8d650a11cbe823f0443a +Subproject commit 0a0dc568dff80396efd1b2c9a6ef1614df5786f7 From 36d2e6efdeb2a1d8855da18fa9bd4fba268f8ffe Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 6 Jun 2012 19:05:07 +0400 Subject: [PATCH 21/60] Add an example of focus string usage and a lua command keybinding. --- dfhack.init-example | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dfhack.init-example b/dfhack.init-example index 7bceda2d0..4af4246fc 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -13,6 +13,9 @@ keybinding add Ctrl-Shift-K autodump-destroy-here # any item: keybinding add Ctrl-K autodump-destroy-item +# quicksave, only in main dwarfmode screen and menu page +keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave + ############################## # Generic adv mode bindings # ############################## From c97e3bca0ccd84f9a0dc1d481c6a6521566c1aa3 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Thu, 7 Jun 2012 21:22:19 -0500 Subject: [PATCH 22/60] Sync structures again --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 0a0dc568d..ff87e784a 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 0a0dc568dff80396efd1b2c9a6ef1614df5786f7 +Subproject commit ff87e784a8e274fcc3d1f57e57746735eb7285c2 From bc734619b473796fad36bd84e139a7752ddf14de Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 10 Jun 2012 01:45:30 +0200 Subject: [PATCH 23/60] ruby: use ruby1.9.1 on linux --- plugins/ruby/ruby.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 189fc7a3b..863b06c0c 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -153,6 +153,7 @@ DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_ch SCASE(VIEWSCREEN_CHANGED); SCASE(CORE_INITIALIZED); SCASE(BEGIN_UNLOAD); +#undef SCASE } return plugin_eval_rb(cmd); @@ -252,7 +253,7 @@ static int df_loadruby(void) #ifdef WIN32 "msvcrt-ruby191.dll"; #else - "libruby1.9.so.1.9.0"; + "./libruby-1.9.1.so.1.9.1"; #endif libruby_handle = OpenPlugin(libpath); @@ -330,6 +331,13 @@ static void df_rubythread(void *p) { int state, running; + if (ruby_sysinit) { + // ruby1.9 specific API + static int argc; + static const char *argv[] = { "dfhack", 0 }; + ruby_sysinit(&argc, (const char ***)&argv); + } + // initialize the ruby interpreter ruby_init(); ruby_init_loadpath(); From 7ee8d79014fb6de9e05f74f010050b79f383a404 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 10 Jun 2012 02:06:22 +0200 Subject: [PATCH 24/60] ruby: download libruby with cmake --- plugins/ruby/CMakeLists.txt | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index bcd10ac89..d2d4d3167 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -1,9 +1,31 @@ +OPTION(DL_RUBY "download libruby from the internet" ON) +IF (DL_RUBY) + IF (UNIX) + FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/libruby-1.9.1.so.zip + ${dfhack_SOURCE_DIR}/libruby-1.9.1.so.zip + EXPECTED_MD5 f38c4d3effc547ccc60ac4620f40bd14) + execute_process(COMMAND unzip ${dfhack_SOURCE_DIR}/libruby-1.9.1.so.zip + WORKING_DIRECTORY ${dfhack_SOURCE_DIR}) + SET(RUBYLIB ${dfhack_SOURCE_DIR}/libruby-1.9.1.so.1.9.1) + ELSE (UNIX) + FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/msvcrt-ruby191.zip + ${dfhack_SOURCE_DIR}/msvcrt-ruby191.zip + EXPECTED_MD5 ed90e11e150b4b00588b66807a6276e7) + execute_process(COMMAND unzip ${dfhack_SOURCE_DIR}/msvcrt-ruby191.zip + WORKING_DIRECTORY ${dfhack_SOURCE_DIR}) + SET(RUBYLIB ${dfhack_SOURCE_DIR}/msvcrt-ruby191.dll) + ENDIF(UNIX) +ENDIF(DL_RUBY) + ADD_CUSTOM_COMMAND( OUTPUT ruby-autogen.rb COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl ) ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb) + include_directories("${dfhack_SOURCE_DIR}/depends/tthread") + DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread) -install(FILES ruby.rb ruby-autogen.rb DESTINATION ${DFHACK_LIBRARY_DESTINATION}) + +install(FILES ruby.rb ruby-autogen.rb ${RUBYLIB} DESTINATION ${DFHACK_LIBRARY_DESTINATION}) From c0e7295f2240852f9930ba6b6084a37d52e51de4 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 10 Jun 2012 23:42:58 +0200 Subject: [PATCH 25/60] ruby: fix cmake to rebuild ruby-autogen.rb only when necessary --- plugins/ruby/CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index d2d4d3167..8f604e746 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -18,14 +18,16 @@ IF (DL_RUBY) ENDIF(DL_RUBY) ADD_CUSTOM_COMMAND( - OUTPUT ruby-autogen.rb + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb - DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl + DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl + COMMENT ruby-autogen.rb ) -ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb) +ADD_CUSTOM_TARGET(ruby-autogen-rb DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb) include_directories("${dfhack_SOURCE_DIR}/depends/tthread") DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread) +ADD_DEPENDENCIES(ruby ruby-autogen-rb) install(FILES ruby.rb ruby-autogen.rb ${RUBYLIB} DESTINATION ${DFHACK_LIBRARY_DESTINATION}) From 31b5def131bc4cb76ab3d2cc89cb7cedaaca95fd Mon Sep 17 00:00:00 2001 From: Rumrusher Date: Mon, 11 Jun 2012 08:31:44 -0300 Subject: [PATCH 26/60] Update master --- README.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index f1e95a74e..107d26e05 100644 --- a/README.rst +++ b/README.rst @@ -633,8 +633,14 @@ produce undesirable results. There are a few good ones though. You are in fort game mode, managing your fortress and paused. You switch to the arena game mode, *assume control of a creature* and then - switch to adventure game mode(1). + switch to adventure game mode(1). You just lost a fortress and gained an adventurer. + You could also do this. + You are in fort game mode, managing your fortress and paused at the esc menu. + You switch to the adventure game mode, then use Dfusion to *assume control of a creature* and then + save or retire. + You just created a returnable mountain home and gained an adventurer. + I take no responsibility of anything that happens as a result of using this tool From 3c1cb24d9f94ca0fe678e4f91f1dee80cd73eb6e Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 12 Jun 2012 18:51:33 +0200 Subject: [PATCH 27/60] ruby: download lib from github as tgz, switch to ruby18, simply log to stderr.log if cannot load libruby --- plugins/ruby/CMakeLists.txt | 28 ++++++++++++++-------------- plugins/ruby/README | 11 ++++++++--- plugins/ruby/ruby.cpp | 25 +++++++++++++++++-------- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index 8f604e746..8c5c406bf 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -1,19 +1,19 @@ OPTION(DL_RUBY "download libruby from the internet" ON) IF (DL_RUBY) IF (UNIX) - FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/libruby-1.9.1.so.zip - ${dfhack_SOURCE_DIR}/libruby-1.9.1.so.zip - EXPECTED_MD5 f38c4d3effc547ccc60ac4620f40bd14) - execute_process(COMMAND unzip ${dfhack_SOURCE_DIR}/libruby-1.9.1.so.zip - WORKING_DIRECTORY ${dfhack_SOURCE_DIR}) - SET(RUBYLIB ${dfhack_SOURCE_DIR}/libruby-1.9.1.so.1.9.1) + FILE(DOWNLOAD http://github.com/downloads/jjyg/dfhack/libruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/libruby187.tar.gz + EXPECTED_MD5 eb2adea59911f68e6066966c1352f291) + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf libruby187.tar.gz + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + FILE(RENAME libruby1.8.so.1.8.7 libruby.so) + SET(RUBYLIB libruby.so) ELSE (UNIX) - FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/msvcrt-ruby191.zip - ${dfhack_SOURCE_DIR}/msvcrt-ruby191.zip - EXPECTED_MD5 ed90e11e150b4b00588b66807a6276e7) - execute_process(COMMAND unzip ${dfhack_SOURCE_DIR}/msvcrt-ruby191.zip - WORKING_DIRECTORY ${dfhack_SOURCE_DIR}) - SET(RUBYLIB ${dfhack_SOURCE_DIR}/msvcrt-ruby191.dll) + FILE(DOWNLOAD http://github.com/downloads/jjyg/dfhack/msvcrtruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/msvcrtruby187.tar.gz + EXPECTED_MD5 9f4a1659ac3a5308f32d3a1937bbeeae) + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf msvcrtruby187.tar.gz + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + FILE(RENAME msvcrt-ruby18.dll libruby.dll) + SET(RUBYLIB libruby.dll) ENDIF(UNIX) ENDIF(DL_RUBY) @@ -25,9 +25,9 @@ ADD_CUSTOM_COMMAND( ) ADD_CUSTOM_TARGET(ruby-autogen-rb DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb) -include_directories("${dfhack_SOURCE_DIR}/depends/tthread") +INCLUDE_DIRECTORIES("${dfhack_SOURCE_DIR}/depends/tthread") DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread) ADD_DEPENDENCIES(ruby ruby-autogen-rb) -install(FILES ruby.rb ruby-autogen.rb ${RUBYLIB} DESTINATION ${DFHACK_LIBRARY_DESTINATION}) +INSTALL(FILES ruby.rb ruby-autogen.rb ${RUBYLIB} DESTINATION ${DFHACK_LIBRARY_DESTINATION}) diff --git a/plugins/ruby/README b/plugins/ruby/README index 25b2781bd..9dc7d49f6 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -11,8 +11,7 @@ access to the raw DF data structures in memory is provided. Some library methods are stored in the ruby.rb file, with shortcuts to read a map block, find an unit or an item, etc. -Global objects are stored in the GlobalObjects class ; each object accessor is -mirrored as a DFHack module method (eg df.world). +Global objects are accessible through the 'df' accessor (eg df.world). The ruby plugin defines 2 dfhack console commands: rb_load ; load a ruby script. Ex: rb_load hack/plants.rb (no quotes) @@ -21,6 +20,12 @@ console. Ex: rb_eval df.find_unit.name.first_name You can use single-quotes for strings ; avoid double-quotes that are parsed and removed by the dfhack console. +If dfhack reports 'rb_eval is not a recognized command', check stderr.log. You +need a valid 32-bit ruby library to work, and ruby1.8 is prefered (ruby1.9 may +crash DF on startup for now). Install the library in the df root folder (or +hack/ on linux), the library should be named 'libruby.dll' (.so on linux). +You can download a tested version at http://github.com/jjyg/dfhack/downloads/ + The plugin also interfaces with dfhack 'onupdate' hook. To register ruby code to be run every graphic frame, use: handle = df.onupdate_register { puts 'i love flooding the console' } @@ -33,7 +38,7 @@ The same mechanism is available for onstatechange. Exemples -------- -For more complex exemples, check the ruby/plugins/ folder. +For more complex exemples, check the ruby/plugins/ source folder. Show info on the currently selected unit ('v' or 'k' DF menu) p df.find_unit.flags1 diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 863b06c0c..49119c9aa 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -45,10 +45,10 @@ DFHACK_PLUGIN("ruby") DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - if (!df_loadruby()) { - Core::printerr("failed to load libruby\n"); - return CR_FAILURE; - } + // fail silently instead of spamming the console with 'failed to initialize' if libruby is not present + // the error is still logged in stderr.log + if (!df_loadruby()) + return CR_OK; m_irun = new tthread::mutex(); m_mutex = new tthread::mutex(); @@ -79,10 +79,11 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector lock(); if (!r_thread) return CR_OK; + m_mutex->lock(); + r_type = RB_DIE; r_command = 0; m_irun->unlock(); @@ -135,6 +136,9 @@ static command_result plugin_eval_rb(std::string &command) DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { + if (!r_thread) + return CR_OK; + if (!onupdate_active) return CR_OK; @@ -143,6 +147,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event e) { + if (!r_thread) + return CR_OK; + std::string cmd = "DFHack.onstatechange "; switch (e) { #define SCASE(s) case SC_ ## s : cmd += ":" # s ; break @@ -251,14 +258,16 @@ static int df_loadruby(void) { const char *libpath = #ifdef WIN32 - "msvcrt-ruby191.dll"; + "./libruby.dll"; #else - "./libruby-1.9.1.so.1.9.1"; + "hack/libruby.so"; #endif libruby_handle = OpenPlugin(libpath); - if (!libruby_handle) + if (!libruby_handle) { + fprintf(stderr, "Cannot initialize ruby plugin: failed to load %s\n", libpath); return 0; + } if (!(rb_eRuntimeError = (VALUE*)LookupPlugin(libruby_handle, "rb_eRuntimeError"))) return 0; From 02854483672142df8879801932dd00b436289356 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 13 Jun 2012 00:20:52 +0200 Subject: [PATCH 28/60] ruby: fix download url, cmake doesnt handle HTTP 301 --- plugins/ruby/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index 8c5c406bf..e69632e61 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -1,14 +1,14 @@ OPTION(DL_RUBY "download libruby from the internet" ON) IF (DL_RUBY) IF (UNIX) - FILE(DOWNLOAD http://github.com/downloads/jjyg/dfhack/libruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/libruby187.tar.gz + FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/libruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/libruby187.tar.gz EXPECTED_MD5 eb2adea59911f68e6066966c1352f291) EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf libruby187.tar.gz WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) FILE(RENAME libruby1.8.so.1.8.7 libruby.so) SET(RUBYLIB libruby.so) ELSE (UNIX) - FILE(DOWNLOAD http://github.com/downloads/jjyg/dfhack/msvcrtruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/msvcrtruby187.tar.gz + FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/msvcrtruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/msvcrtruby187.tar.gz EXPECTED_MD5 9f4a1659ac3a5308f32d3a1937bbeeae) EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf msvcrtruby187.tar.gz WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) From c364b4204997af81b6b419fa245ed22c1c56f389 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 13 Jun 2012 00:21:23 +0200 Subject: [PATCH 29/60] fix minor gcc warning --- library/Console-linux.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Console-linux.cpp b/library/Console-linux.cpp index 3c0ad8938..24d13f42b 100644 --- a/library/Console-linux.cpp +++ b/library/Console-linux.cpp @@ -255,7 +255,7 @@ namespace DFHack void color(Console::color_value index) { if(!rawmode) - fprintf(dfout_C,getANSIColor(index)); + fprintf(dfout_C, "%s", getANSIColor(index)); else { const char * colstr = getANSIColor(index); @@ -761,4 +761,4 @@ void Console::msleep (unsigned int msec) { if (msec > 1000) sleep(msec/1000000); usleep((msec % 1000000) * 1000); -} \ No newline at end of file +} From 6ca5a0300840dfc710ae6ab1dfb35fd78adff1dc Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 13 Jun 2012 10:54:28 +0400 Subject: [PATCH 30/60] Add 3 more lua scripts. --- scripts/devel/nuke-items.lua | 16 ++++++++++++ scripts/fix/dead-units.lua | 29 +++++++++++++++++++++ scripts/fix/stable-temp.lua | 49 ++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 scripts/devel/nuke-items.lua create mode 100644 scripts/fix/dead-units.lua create mode 100644 scripts/fix/stable-temp.lua diff --git a/scripts/devel/nuke-items.lua b/scripts/devel/nuke-items.lua new file mode 100644 index 000000000..aa3f5b840 --- /dev/null +++ b/scripts/devel/nuke-items.lua @@ -0,0 +1,16 @@ +-- Deletes ALL items not held by units, buildings or jobs. +-- +-- Intended solely for lag investigation. + +local count = 0 + +for _,v in ipairs(df.global.world.items.all) do + if not (v.flags.in_building or v.flags.construction or v.flags.in_job + or dfhack.items.getGeneralRef(v,df.general_ref_type.UNIT_HOLDER)) then + count = count + 1 + v.flags.forbid = true + v.flags.garbage_collect = true + end +end + +print('Deletion requested: '..count) diff --git a/scripts/fix/dead-units.lua b/scripts/fix/dead-units.lua new file mode 100644 index 000000000..2d5558179 --- /dev/null +++ b/scripts/fix/dead-units.lua @@ -0,0 +1,29 @@ +-- Remove uninteresting dead units from the unit list. + +local units = df.global.world.units.active +local dwarf_race = df.global.ui.race_id +local dwarf_civ = df.global.ui.civ_id +local count = 0 + +for i=#units-1,0,-1 do + local unit = units[i] + local flags1 = unit.flags1 + local flags2 = unit.flags2 + if flags1.dead and unit.race ~= dwarf_race then + local remove = false + if flags2.slaughter then + remove = true + elseif not unit.name.has_name then + remove = true + elseif unit.civ_id ~= dwarf_civ and + not (flags1.merchant or flags1.diplomat) then + remove = true + end + if remove then + count = count + 1 + units:erase(i) + end + end +end + +print('Units removed from active: '..count) diff --git a/scripts/fix/stable-temp.lua b/scripts/fix/stable-temp.lua new file mode 100644 index 000000000..d06d0fcce --- /dev/null +++ b/scripts/fix/stable-temp.lua @@ -0,0 +1,49 @@ +-- Reset item temperature to the value of their tile. + +local count = 0 +local types = {} + +local function update_temp(item,btemp) + if item.temperature ~= btemp then + count = count + 1 + local tid = item:getType() + types[tid] = (types[tid] or 0) + 1 + end + item.temperature = btemp + item.temperature_fraction = 0 + + if item.contaminants then + for _,c in ipairs(item.contaminants) do + c.temperature = btemp + c.temperature_fraction = 0 + end + end + + for _,sub in ipairs(dfhack.items.getContainedItems(item)) do + update_temp(sub,btemp) + end + + item:checkTemperatureDamage() +end + +local last_frame = df.global.world.frame_counter-1 + +for _,item in ipairs(df.global.world.items.all) do + if item.flags.on_ground and df.item_actual:is_instance(item) and + item.temp_updated_frame == last_frame then + local pos = item.pos + local block = dfhack.maps.getTileBlock(pos) + if block then + update_temp(item, block.temperature_1[pos.x%16][pos.y%16]) + end + end +end + +print('Items updated: '..count) + +local tlist = {} +for k,_ in pairs(types) do tlist[#tlist+1] = k end +table.sort(tlist, function(a,b) return types[a] > types[b] end) +for _,k in ipairs(tlist) do + print(' '..df.item_type[k]..':', types[k]) +end From 24e2c151136fc7d565f9f9babcf24b199eb6c6f3 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 13 Jun 2012 11:00:54 +0400 Subject: [PATCH 31/60] Enhance docs on bitfield refs to cover a question raised on chat recently. --- LUA_API.rst | 11 ++++++++++- Lua API.html | 11 +++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 9fb37375d..0736b02b7 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -219,12 +219,21 @@ Bitfield references ------------------- Bitfields behave like special fixed-size containers. -The ``_enum`` property points to the bitfield type. +Consider them to be something in between structs and +fixed-size vectors. +The ``_enum`` property points to the bitfield type. Numerical indices correspond to the shift value, and if a subfield occupies multiple bits, the ``ipairs`` order would have a gap. +Since currently there is no API to allocate a bitfield +object fully in GC-managed lua heap, consider using the +lua table assignment feature outlined below in order to +pass bitfield values to dfhack API functions that need +them, e.g. ``matinfo:matches{metal=true}``. + + Named types =========== diff --git a/Lua API.html b/Lua API.html index 1576652d6..568090a76 100644 --- a/Lua API.html +++ b/Lua API.html @@ -534,10 +534,17 @@ use #ref, or just

    Bitfield references

    Bitfields behave like special fixed-size containers. -The _enum property points to the bitfield type.

    -

    Numerical indices correspond to the shift value, +Consider them to be something in between structs and +fixed-size vectors.

    +

    The _enum property points to the bitfield type. +Numerical indices correspond to the shift value, and if a subfield occupies multiple bits, the ipairs order would have a gap.

    +

    Since currently there is no API to allocate a bitfield +object fully in GC-managed lua heap, consider using the +lua table assignment feature outlined below in order to +pass bitfield values to dfhack API functions that need +them, e.g. matinfo:matches{metal=true}.

    From 8d7cf092fd6f3f1cf68e6b48b8f3dc42ef40dcfa Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 13 Jun 2012 21:12:36 +0400 Subject: [PATCH 32/60] Add Lua API for access to some contextual and low-level info. --- LUA_API.rst | 39 +++++++++++++++ Lua API.html | 52 +++++++++++++++---- library/LuaApi.cpp | 115 +++++++++++++++++++++++++++++++++++++++++++ library/LuaTools.cpp | 6 +-- 4 files changed, 199 insertions(+), 13 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 0736b02b7..54105d045 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -607,6 +607,22 @@ One notable difference is that these explicit wrappers allow argument count adjustment according to the usual lua rules, so trailing false/nil arguments can be omitted. +* ``dfhack.getOSType()`` + + Returns the OS type string from ``symbols.xml``. + +* ``dfhack.getDFVersion()`` + + Returns the DF version string from ``symbols.xml``. + +* ``dfhack.getDFPath()`` + + Returns the DF directory path. + +* ``dfhack.getHackPath()`` + + Returns the dfhack directory path, i.e. ``".../df/hack/"``. + * ``dfhack.isWorldLoaded()`` Checks if the world is loaded. @@ -1102,6 +1118,29 @@ Constructions module Returns *true, was_only_planned* if removed; or *false* if none found. +Internal API +------------ + +These functions are intended for the use by dfhack developers, +and are only documented here for completeness: + +* ``dfhack.internal.getAddress(name)`` + + Returns the global address ``name``, or *nil*. + +* ``dfhack.internal.setAddress(name, value)`` + + Sets the global address ``name``. Returns the value of ``getAddress`` before the change. + +* ``dfhack.internal.getBase()`` + + Returns the base address of the process. + +* ``dfhack.internal.getMemRanges()`` + + Returns a sequence of tables describing virtual memory ranges of the process. + + Core interpreter context ======================== diff --git a/Lua API.html b/Lua API.html index 568090a76..02e7abf3c 100644 --- a/Lua API.html +++ b/Lua API.html @@ -345,17 +345,18 @@ ul.auto-toc {
  • Burrows module
  • Buildings module
  • Constructions module
  • +
  • Internal API
  • -
  • Core interpreter context
  • -
  • Plugins @@ -862,6 +863,18 @@ One notable difference is that these explicit wrappers allow argument count adjustment according to the usual lua rules, so trailing false/nil arguments can be omitted.

      +
    • dfhack.getOSType()

      +

      Returns the OS type string from symbols.xml.

      +
    • +
    • dfhack.getDFVersion()

      +

      Returns the DF version string from symbols.xml.

      +
    • +
    • dfhack.getDFPath()

      +

      Returns the DF directory path.

      +
    • +
    • dfhack.getHackPath()

      +

      Returns the dfhack directory path, i.e. ".../df/hack/".

      +
    • dfhack.isWorldLoaded()

      Checks if the world is loaded.

    • @@ -1274,9 +1287,28 @@ Returns true, was_only_planned if removed; or false if none fo
    +
    +

    Internal API

    +

    These functions are intended for the use by dfhack developers, +and are only documented here for completeness:

    +
      +
    • dfhack.internal.getAddress(name)

      +

      Returns the global address name, or nil.

      +
    • +
    • dfhack.internal.setAddress(name, value)

      +

      Sets the global address name. Returns the value of getAddress before the change.

      +
    • +
    • dfhack.internal.getBase()

      +

      Returns the base address of the process.

      +
    • +
    • dfhack.internal.getMemRanges()

      +

      Returns a sequence of tables describing virtual memory ranges of the process.

      +
    • +
    +
    -

    Core interpreter context

    +

    Core interpreter context

    While plugins can create any number of interpreter instances, there is one special context managed by dfhack core. It is the only context that can receive events from DF and plugins.

    @@ -1307,7 +1339,7 @@ Using timeout_active(id,nil) cancels the timer
  • -

    Event type

    +

    Event type

    An event is just a lua table with a predefined metatable that contains a __call metamethod. When it is invoked, it loops through the table with next and calls all contained values. @@ -1333,14 +1365,14 @@ order using dfhack.safecall.

    -

    Plugins

    +

    Plugins

    DFHack plugins may export native functions and events to lua contexts. They are automatically imported by mkmodule('plugins.<name>'); this means that a lua module file is still necessary for require to read.

    The following plugins have lua support.

    -

    burrows

    +

    burrows

    Implements extended burrow manipulations.

    Events:

      @@ -1378,7 +1410,7 @@ set is the same as used by the command line.

      The lua module file also re-exports functions from dfhack.burrows.

    -

    sort

    +

    sort

    Does not export any native functions as of now. Instead, it calls lua code to perform the actual ordering of list items.

    diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 8e8a3c4e3..aa168db3a 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -84,6 +84,8 @@ distribution. using namespace DFHack; using namespace DFHack::LuaWrapper; +void dfhack_printerr(lua_State *S, const std::string &str); + void Lua::Push(lua_State *state, const Units::NoblePosition &pos) { lua_createtable(state, 0, 3); @@ -640,10 +642,37 @@ static void OpenModule(lua_State *state, const char *mname, /***** DFHack module *****/ +static std::string getOSType() +{ + switch (Core::getInstance().vinfo->getOS()) + { + case OS_WINDOWS: + return "windows"; + + case OS_LINUX: + return "linux"; + + case OS_APPLE: + return "darwin"; + + default: + return "unknown"; + } +} + +static std::string getDFVersion() { return Core::getInstance().vinfo->getVersion(); } + +static std::string getDFPath() { return Core::getInstance().p->getPath(); } +static std::string getHackPath() { return Core::getInstance().getHackPath(); } + static bool isWorldLoaded() { return Core::getInstance().isWorldLoaded(); } static bool isMapLoaded() { return Core::getInstance().isMapLoaded(); } static const LuaWrapper::FunctionReg dfhack_module[] = { + WRAP(getOSType), + WRAP(getDFVersion), + WRAP(getDFPath), + WRAP(getHackPath), WRAP(isWorldLoaded), WRAP(isMapLoaded), WRAPM(Translation, TranslateName), @@ -990,6 +1019,91 @@ static const luaL_Reg dfhack_constructions_funcs[] = { { NULL, NULL } }; +/***** Internal module *****/ + +static uint32_t getBase() { return Core::getInstance().p->getBase(); } + +static const LuaWrapper::FunctionReg dfhack_internal_module[] = { + WRAP(getBase), + { NULL, NULL } +}; + +static int internal_getAddress(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + uint32_t addr = Core::getInstance().vinfo->getAddress(name); + if (addr) + lua_pushnumber(L, addr); + else + lua_pushnil(L); + return 1; +} + +static int internal_setAddress(lua_State *L) +{ + std::string name = luaL_checkstring(L, 1); + uint32_t addr = luaL_checkint(L, 2); + internal_getAddress(L); + + // Set the address + Core::getInstance().vinfo->setAddress(name, addr); + + auto fields = df::global::_identity.getFields(); + + for (int i = 0; fields && fields[i].mode != struct_field_info::END; ++i) + { + if (fields[i].name != name) + continue; + + *(void**)fields[i].offset = (void*)addr; + } + + // Print via printerr, so that it is definitely logged to stderr.log. + std::string msg = stl_sprintf("", name.c_str(), addr); + dfhack_printerr(L, msg); + + return 1; +} + +static int internal_getMemRanges(lua_State *L) +{ + std::vector ranges; + Core::getInstance().p->getMemRanges(ranges); + + lua_newtable(L); + + for(size_t i = 0; i < ranges.size(); i++) + { + lua_newtable(L); + lua_pushnumber(L, (uint32_t)ranges[i].start); + lua_setfield(L, -2, "start"); + lua_pushnumber(L, (uint32_t)ranges[i].end); + lua_setfield(L, -2, "end"); + lua_pushstring(L, ranges[i].name); + lua_setfield(L, -2, "name"); + lua_pushboolean(L, ranges[i].read); + lua_setfield(L, -2, "read"); + lua_pushboolean(L, ranges[i].write); + lua_setfield(L, -2, "write"); + lua_pushboolean(L, ranges[i].execute); + lua_setfield(L, -2, "execute"); + lua_pushboolean(L, ranges[i].shared); + lua_setfield(L, -2, "shared"); + lua_pushboolean(L, ranges[i].valid); + lua_setfield(L, -2, "valid"); + lua_rawseti(L, -2, i+1); + } + + return 1; +} + +static const luaL_Reg dfhack_internal_funcs[] = { + { "getAddress", internal_getAddress }, + { "setAddress", internal_setAddress }, + { "getMemRanges", internal_getMemRanges }, + { NULL, NULL } +}; + /************************ * Main Open function * @@ -1009,4 +1123,5 @@ void OpenDFHackApi(lua_State *state) OpenModule(state, "burrows", dfhack_burrows_module, dfhack_burrows_funcs); OpenModule(state, "buildings", dfhack_buildings_module, dfhack_buildings_funcs); OpenModule(state, "constructions", dfhack_constructions_module); + OpenModule(state, "internal", dfhack_internal_module, dfhack_internal_funcs); } diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index f794f6b41..752c341b2 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -66,6 +66,8 @@ using namespace DFHack::LuaWrapper; lua_State *DFHack::Lua::Core::State = NULL; +void dfhack_printerr(lua_State *S, const std::string &str); + inline void AssertCoreSuspend(lua_State *state) { assert(!Lua::IsCoreContext(state) || DFHack::Core::getInstance().isSuspended()); @@ -96,8 +98,6 @@ static void check_valid_ptr_index(lua_State *state, int val_index) } } -static void dfhack_printerr(lua_State *S, const std::string &str); - static void signal_typeid_error(color_ostream *out, lua_State *state, type_identity *type, const char *msg, int val_index, bool perr, bool signal) @@ -233,7 +233,7 @@ static int lua_dfhack_println(lua_State *S) return 0; } -static void dfhack_printerr(lua_State *S, const std::string &str) +void dfhack_printerr(lua_State *S, const std::string &str) { if (color_ostream *out = Lua::GetOutput(S)) out->printerr("%s\n", str.c_str()); From c50b605dfc2024bf5bca7d6dfb18fc266b358978 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 13 Jun 2012 22:26:54 +0400 Subject: [PATCH 33/60] Support casting references and allocating arrays of numbers in lua wrapper. --- LUA_API.rst | 18 ++++++++ Lua API.html | 14 ++++++ library/LuaWrapper.cpp | 89 ++++++++++++++++++++++++++++++++++-- library/include/LuaWrapper.h | 1 + 4 files changed, 117 insertions(+), 5 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 54105d045..0b6ae863b 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -317,6 +317,24 @@ The ``df`` table itself contains the following functions and values: Equivalent to the method, but also allows a reference as proxy for its type. +* ``df.new(ptype[,count])`` + + Allocate a new instance, or an array of built-in types. + The ``ptype`` argument is a string from the following list: + ``string``, ``int8_t``, ``uint8_t``, ``int16_t``, ``uint16_t``, + ``int32_t``, ``uint32_t``, ``int64_t``, ``uint64_t``, ``bool``, + ``float``, ``double``. All of these except ``string`` can be + used with the count argument to allocate an array. + +* ``df.reinterpret_cast(type,ptr)`` + + Converts ptr to a ref of specified type. The type may be anything + acceptable to ``df.is_instance``. Ptr may be *nil*, a ref, + a lightuserdata, or a number. + + Returns *nil* if NULL, or a ref. + + Recursive table assignment ========================== diff --git a/Lua API.html b/Lua API.html index 02e7abf3c..2df8a7f22 100644 --- a/Lua API.html +++ b/Lua API.html @@ -615,6 +615,20 @@ lightuserdata (step is mandatory then).

  • df.is_instance(type,obj)

    Equivalent to the method, but also allows a reference as proxy for its type.

  • +
  • df.new(ptype[,count])

    +

    Allocate a new instance, or an array of built-in types. +The ptype argument is a string from the following list: +string, int8_t, uint8_t, int16_t, uint16_t, +int32_t, uint32_t, int64_t, uint64_t, bool, +float, double. All of these except string can be +used with the count argument to allocate an array.

    +
  • +
  • df.reinterpret_cast(type,ptr)

    +

    Converts ptr to a ref of specified type. The type may be anything +acceptable to df.is_instance. Ptr may be nil, a ref, +a lightuserdata, or a number.

    +

    Returns nil if NULL, or a ref.

    +
  • diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 471dba640..7c15a27aa 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -447,10 +447,21 @@ Lua::ObjectClass Lua::IsDFObject(lua_State *state, int val_index) } static const char *const primitive_types[] = { - "string", NULL + "string", + "int8_t", "uint8_t", "int16_t", "uint16_t", + "int32_t", "uint32_t", "int64_t", "uint64_t", + "bool", "float", "double", + NULL }; static type_identity *const primitive_identities[] = { - df::identity_traits::get(), NULL + df::identity_traits::get(), + df::identity_traits::get(), df::identity_traits::get(), + df::identity_traits::get(), df::identity_traits::get(), + df::identity_traits::get(), df::identity_traits::get(), + df::identity_traits::get(), df::identity_traits::get(), + df::identity_traits::get(), + df::identity_traits::get(), df::identity_traits::get(), + NULL }; /** @@ -644,12 +655,32 @@ static int meta_new(lua_State *state) { int argc = lua_gettop(state); - if (argc != 1) - luaL_error(state, "Usage: object:new() or df.new(object)"); + if (argc != 1 && argc != 2) + luaL_error(state, "Usage: object:new() or df.new(object) or df.new(ptype,count)"); type_identity *id = get_object_identity(state, 1, "df.new()", true); - void *ptr = id->allocate(); + void *ptr; + + // Support arrays of primitive types + if (argc == 2) + { + int cnt = luaL_checkint(state, 2); + if (cnt <= 0) + luaL_error(state, "Invalid array size in df.new()"); + if (id->type() != IDTYPE_PRIMITIVE) + luaL_error(state, "Cannot allocate arrays of non-primitive types."); + + size_t sz = id->byte_size() * cnt; + ptr = malloc(sz); + if (ptr) + memset(ptr, 0, sz); + } + else + { + ptr = id->allocate(); + } + if (!ptr) luaL_error(state, "Cannot allocate %s", id->getFullName().c_str()); @@ -666,6 +697,48 @@ static int meta_new(lua_State *state) return 1; } +/** + * Method: type casting of pointers. + */ +static int meta_reinterpret_cast(lua_State *state) +{ + int argc = lua_gettop(state); + + if (argc != 2) + luaL_error(state, "Usage: df.reinterpret_cast(type,ptr)"); + + type_identity *id = get_object_identity(state, 1, "df.reinterpret_cast()", true); + + // Find the raw pointer value + void *ptr; + + if (lua_isnil(state, 2)) + ptr = NULL; + else if (lua_isnumber(state, 2)) + ptr = (void*)lua_tointeger(state, 2); + else + { + ptr = get_object_internal(state, NULL, 2, false, true); + if (!ptr) + luaL_error(state, "Invalid pointer argument in df.reinterpret_cast.\n"); + } + + // Convert it to the appropriate representation + if (ptr == NULL) + { + lua_pushnil(state); + } + else if (lua_isuserdata(state, 1)) + { + lua_getmetatable(state, 1); + push_object_ref(state, ptr); + } + else + push_object_internal(state, id, ptr); + + return 1; +} + static void invoke_resize(lua_State *state, int table, lua_Integer size) { lua_getfield(state, table, "resize"); @@ -1432,6 +1505,10 @@ static int DoAttach(lua_State *state) lua_pushcclosure(state, meta_new, 1); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME); + lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN); + lua_pushcclosure(state, meta_reinterpret_cast, 1); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_CAST_NAME); + lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN); lua_pushcclosure(state, meta_assign, 1); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME); @@ -1463,6 +1540,8 @@ static int DoAttach(lua_State *state) lua_setfield(state, -2, "assign"); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME); lua_setfield(state, -2, "is_instance"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_CAST_NAME); + lua_setfield(state, -2, "reinterpret_cast"); lua_pushlightuserdata(state, NULL); lua_setfield(state, -2, "NULL"); diff --git a/library/include/LuaWrapper.h b/library/include/LuaWrapper.h index 97b2e6980..9e4490220 100644 --- a/library/include/LuaWrapper.h +++ b/library/include/LuaWrapper.h @@ -77,6 +77,7 @@ namespace DFHack { namespace LuaWrapper { #define DFHACK_ASSIGN_NAME "DFHack::Assign" #define DFHACK_IS_INSTANCE_NAME "DFHack::IsInstance" #define DFHACK_DELETE_NAME "DFHack::Delete" +#define DFHACK_CAST_NAME "DFHack::Cast" extern LuaToken DFHACK_EMPTY_TABLE_TOKEN; From 149f1759096f1fa4117c0e7ac1e6172b669a801b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 13 Jun 2012 22:40:39 +0400 Subject: [PATCH 34/60] Make primitive refs (i.e. pointers to numbers, etc) behave as arrays. --- LUA_API.rst | 6 ++++++ Lua API.html | 4 ++++ library/LuaTypes.cpp | 26 ++++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 0b6ae863b..f40b786d2 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -121,6 +121,12 @@ or as a result of calling the ``_field()`` method. They behave as structs with one field ``value`` of the right type. +To make working with numeric buffers easier, they also allow +numeric indices. Note that other than excluding negative values +no bound checking is performed, since buffer length is not available. +Index 0 is equivalent to the ``value`` field. + + Struct references ----------------- diff --git a/Lua API.html b/Lua API.html index 2df8a7f22..3edcfd274 100644 --- a/Lua API.html +++ b/Lua API.html @@ -453,6 +453,10 @@ that don't fit any of the other reference types. Such references can only appear as a value of a pointer field, or as a result of calling the _field() method.

    They behave as structs with one field value of the right type.

    +

    To make working with numeric buffers easier, they also allow +numeric indices. Note that other than excluding negative values +no bound checking is performed, since buffer length is not available. +Index 0 is equivalent to the value field.

    Struct references

    diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index c58c25d11..8548c5d0a 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -635,6 +635,28 @@ static int meta_struct_next(lua_State *state) return 2; } +/** + * Field lookup for primitive refs: behave as a quasi-array with numeric indices. + */ +static type_identity *find_primitive_field(lua_State *state, int field, const char *mode, uint8_t **ptr) +{ + if (lua_type(state, field) == LUA_TNUMBER) + { + int idx = lua_tointeger(state, field); + if (idx < 0) + field_error(state, 2, "negative index", mode); + + lua_rawgetp(state, UPVAL_METATABLE, &DFHACK_IDENTITY_FIELD_TOKEN); + auto id = (type_identity *)lua_touserdata(state, -1); + lua_pop(state, 1); + + *ptr += int(id->byte_size()) * idx; + return id; + } + + return (type_identity*)find_field(state, field, mode); +} + /** * Metamethod: __index for primitives, i.e. simple object references. * Fields point to identity, or NULL for metafields. @@ -642,7 +664,7 @@ static int meta_struct_next(lua_State *state) static int meta_primitive_index(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "read"); - auto type = (type_identity*)find_field(state, 2, "read"); + auto type = find_primitive_field(state, 2, "read", &ptr); if (!type) return 1; type->lua_read(state, 2, ptr); @@ -655,7 +677,7 @@ static int meta_primitive_index(lua_State *state) static int meta_primitive_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); - auto type = (type_identity*)find_field(state, 2, "write"); + auto type = find_primitive_field(state, 2, "write", &ptr); if (!type) field_error(state, 2, "builtin property or method", "write"); type->lua_write(state, 2, ptr, 3); From eaac32c7652be14dc45366b9d74101ae6a38fb17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Thu, 14 Jun 2012 00:29:01 +0200 Subject: [PATCH 35/60] Version bump and text updates. --- CMakeLists.txt | 2 +- Compile.html | 2 +- Lua API.html | 2 +- README.rst | 2 +- Readme.html | 7 ++++--- library/xml | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 62be3e8cb..ca8592b01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ endif() # set up versioning. set(DF_VERSION_MAJOR "0") set(DF_VERSION_MINOR "34") -set(DF_VERSION_PATCH "10") +set(DF_VERSION_PATCH "11") set(DF_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}") SET(DFHACK_RELEASE "r1" CACHE STRING "Current release revision.") diff --git a/Compile.html b/Compile.html index 53fc959b4..b0f9e9c6e 100644 --- a/Compile.html +++ b/Compile.html @@ -3,7 +3,7 @@ - + Building DFHACK