Merge branch 'master' into autolabor

Conflicts:
	plugins/autolabor.cpp
develop
Kelly Martin 2012-11-30 11:25:41 -06:00
commit 5f3328dcb2
223 changed files with 14578 additions and 6875 deletions

@ -121,7 +121,7 @@ ADD_DEFINITIONS(-DPROTOBUF_USE_DLLS)
ADD_DEFINITIONS(-DLUA_BUILD_AS_DLL) ADD_DEFINITIONS(-DLUA_BUILD_AS_DLL)
if(APPLE) if(APPLE)
add_definitions(-D_DARWIN) add_definitions(-D_DARWIN)
elseif(UNIX) elseif(UNIX)
add_definitions(-D_LINUX) add_definitions(-D_LINUX)
elseif(WIN32) elseif(WIN32)
@ -145,7 +145,7 @@ include_directories(depends/clsocket/src)
add_subdirectory(depends) add_subdirectory(depends)
find_package(Docutils) #find_package(Docutils)
#set (RST_FILES #set (RST_FILES
#"Readme" #"Readme"
@ -172,6 +172,7 @@ IF(BUILD_LIBRARY)
add_subdirectory (library) add_subdirectory (library)
## install the default documentation files ## install the default documentation files
install(FILES LICENSE "Lua API.html" Readme.html Compile.html Contributors.html DESTINATION ${DFHACK_USERDOC_DESTINATION}) install(FILES LICENSE "Lua API.html" Readme.html Compile.html Contributors.html DESTINATION ${DFHACK_USERDOC_DESTINATION})
install(DIRECTORY images DESTINATION ${DFHACK_USERDOC_DESTINATION})
endif() endif()
#build the plugins #build the plugins
@ -181,11 +182,11 @@ endif()
# Packaging with CPack! # Packaging with CPack!
IF(UNIX) IF(UNIX)
if(APPLE) if(APPLE)
SET(CPACK_GENERATOR "ZIP") SET(CPACK_GENERATOR "ZIP")
else() else()
SET(CPACK_GENERATOR "TGZ") SET(CPACK_GENERATOR "TGZ")
endif() endif()
ELSEIF(WIN32) ELSEIF(WIN32)
SET(CPACK_GENERATOR "ZIP") SET(CPACK_GENERATOR "ZIP")
ENDIF() ENDIF()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

41
NEWS

@ -10,9 +10,50 @@ DFHack future
- fastdwarf: new mode using debug flags, and some internal consistency fixes. - fastdwarf: new mode using debug flags, and some internal consistency fixes.
- added a small stand-alone utility for applying and removing binary patches. - added a small stand-alone utility for applying and removing binary patches.
- removebadthoughts: add --dry-run option - removebadthoughts: add --dry-run option
- superdwarf: work in adventure mode too
- tweak stable-cursor: carries cursor location from/to Build menu.
New tweaks:
- tweak military-training: speed up melee squad training up to 10x (normally 3-5x).
New scripts:
- binpatch: the same as the stand-alone binpatch.exe, but works at runtime.
- region-pops: displays animal populations of the region and allows tweaking them.
- lua: lua interpreter front-end converted to a script from a native command.
- dfusion: misc scripts with a text based menu.
- embark: lets you embark anywhere.
- lever: list and pull fort levers from the dfhack console.
- stripcaged: mark items inside cages for dumping, eg caged goblin weapons.
New GUI scripts: New GUI scripts:
- gui/guide-path: displays the cached path for minecart Guide orders. - gui/guide-path: displays the cached path for minecart Guide orders.
- gui/workshop-job: displays inputs of a workshop job and allows tweaking them. - gui/workshop-job: displays inputs of a workshop job and allows tweaking them.
- gui/workflow: a front-end for the workflow plugin.
- gui/assign-rack: works together with a binary patch to fix weapon racks.
- gui/gm-editor: an universal editor for lots of dfhack things.
- gui/companion-order: a adventure mode command interface for your companions.
New binary patches:
- armorstand-capacity
- custom-reagent-size
- deconstruct-heapfall
- deconstruct-teleport
- hospital-overstocking
- training-ammo
- weaponrack-unassign
Workflow plugin:
- properly considers minecarts assigned to routes busy.
- code for deducing job outputs rewritten in lua for flexibility.
- logic fix: collecting webs produces silk, and ungathered webs are not thread.
- items assigned to squads are considered busy, even if not in inventory.
- shearing and milking jobs are supported, but only with generic MILK or YARN outputs.
New Fix Armory plugin:
Together with a couple of binary patches and the gui/assign-rack script,
this plugin makes weapon racks, armor stands, chests and cabinets in
properly designated barracks be used again for storage of squad equipment.
New Search plugin by falconne:
Adds an incremental search function to the Stocks, Trading and Unit List screens.
New AutoMaterial plugin by falconne:
Makes building constructions (walls, floors, fortifications, etc) a little bit easier by
saving you from having to trawl through long lists of materials each time you place one.
Dfusion plugin:
Reworked to make use of lua modules, now all the scripts can be used from other scripts.
DFHack v0.34.11-r2 DFHack v0.34.11-r2

File diff suppressed because it is too large Load Diff

@ -58,9 +58,35 @@ The stonesense plugin might require some additional libraries on Linux.
If any of the plugins or dfhack itself refuses to load, check the stderr.log If any of the plugins or dfhack itself refuses to load, check the stderr.log
file created in your DF folder. file created in your DF folder.
Getting started
===============
If DFHack is installed correctly, it will automatically pop up a console
window once DF is started as usual on windows. Linux and Mac OS X require
running the dfhack script from the terminal, and will use that terminal for
the console.
**NOTE**: The dfhack-run executable is there for calling DFHack commands in
an already running DF+DFHack instance from external OS scripts and programs,
and is *not* the way how you use DFHack normally.
DFHack has a lot of features, which can be accessed by typing commands in the
console, or by mapping them to keyboard shortcuts. Most of the newer and more
user-friendly tools are designed to be at least partially used via the latter
way.
In order to set keybindings, you have to create a text configuration file
called ``dfhack.init``; the installation comes with an example version called
``dfhack.init-example``, which is fully functional, covers all of the recent
features and can be simply renamed to ``dfhack.init``. You are encouraged to look
through it to learn which features it makes available under which key combinations.
For more information, refer to the rest of this document.
============ ============
Using DFHack Using DFHack
============ ============
DFHack basically extends what DF can do with something similar to the drop-down DFHack basically extends what DF can do with something similar to the drop-down
console found in Quake engine games. On Windows, this is a separate command line console found in Quake engine games. On Windows, this is a separate command line
window. On linux, the terminal used to launch the dfhack script is taken over window. On linux, the terminal used to launch the dfhack script is taken over
@ -118,6 +144,16 @@ system console:
The patches are expected to be encoded in text format used by IDA. The patches are expected to be encoded in text format used by IDA.
Live patching
-------------
As an alternative, you can use the ``binpatch`` dfhack command to apply/remove
patches live in memory during a DF session.
In this case, updating symbols.xml is not necessary.
============================= =============================
Something doesn't work, help! Something doesn't work, help!
============================= =============================
@ -231,6 +267,8 @@ Controls speedydwarf and teledwarf. Speedydwarf makes dwarves move quickly and p
* 'fastdwarf 1 1' enables both * 'fastdwarf 1 1' enables both
* 'fastdwarf 0' disables both * 'fastdwarf 0' disables both
* 'fastdwarf 1' enables speedydwarf and disables teledwarf * 'fastdwarf 1' enables speedydwarf and disables teledwarf
* 'fastdwarf 2 ...' sets a native debug flag in the game memory
that implements an even more aggressive version of speedydwarf.
Game interface Game interface
============== ==============
@ -1049,6 +1087,9 @@ Subcommands that persist until disabled or DF quit:
:patrol-duty: Makes Train orders not count as patrol duty to stop unhappy thoughts. :patrol-duty: Makes Train orders not count as patrol duty to stop unhappy thoughts.
Does NOT fix the problem when soldiers go off-duty (i.e. civilian). Does NOT fix the problem when soldiers go off-duty (i.e. civilian).
:readable-build-plate: Fixes rendering of creature weight limits in pressure plate build menu. :readable-build-plate: Fixes rendering of creature weight limits in pressure plate build menu.
.. image:: images/tweak-plate.png
:stable-temp: Fixes performance bug 6012 by squashing jitter in temperature updates. :stable-temp: Fixes performance bug 6012 by squashing jitter in temperature updates.
In very item-heavy forts with big stockpiles this can improve FPS by 50-100% In very item-heavy forts with big stockpiles this can improve FPS by 50-100%
:fast-heat: Further improves temperature update performance by ensuring that 1 degree :fast-heat: Further improves temperature update performance by ensuring that 1 degree
@ -1067,9 +1108,78 @@ Subcommands that persist until disabled or DF quit:
:military-stable-assign: Preserve list order and cursor position when assigning to squad, :military-stable-assign: Preserve list order and cursor position when assigning to squad,
i.e. stop the rightmost list of the Positions page of the military i.e. stop the rightmost list of the Positions page of the military
screen from constantly resetting to the top. screen from constantly resetting to the top.
:military-color-assigned: Color squad candidates already assigned to other squads in brown/green :military-color-assigned: Color squad candidates already assigned to other squads in yellow/green
to make them stand out more in the list. to make them stand out more in the list.
.. image:: images/tweak-mil-color.png
:military-training: Speeds up melee squad training by removing an almost certainly
unintended inverse dependency of training speed on unit count
(i.e. the more units you have, the slower it becomes), and making
the units spar more.
fix-armory
----------
Enables a fix for storage of squad equipment in barracks.
Specifically, it prevents your haulers from moving squad equipment
to stockpiles, and instead queues jobs to store it on weapon racks,
armor stands, and in containers.
.. note::
In order to actually be used, weapon racks have to be patched and
manually assigned to a squad. See documentation for ``gui/assign-rack``
below.
Also, the default capacity of armor stands is way too low, so you
may want to also apply the ``armorstand-capacity`` patch. Check out
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
for more information about the bugs.
Note that the buildings in the armory are used as follows:
* Weapon racks (when patched) are used to store any assigned weapons.
Each rack belongs to a specific squad, and can store up to 5 weapons.
* Armor stands belong to specific squad members and are used for
armor and shields. By default one stand can store one item of each
type (hence one boot or gauntlet); if patched, the limit is raised to 2,
which should be sufficient.
* Cabinets are used to store assigned clothing for a specific squad member.
They are **never** used to store owned clothing.
* Chests (boxes, etc) are used for a flask, backpack or quiver assigned
to the squad member. Due to a probable bug, food is dropped out of the
backpack when it is stored.
.. warning::
Although armor stands, cabinets and chests properly belong only to one
squad member, the owner of the building used to create the barracks will
randomly use any containers inside the room. Thus, it is recommended to
always create the armory from a weapon rack.
Contrary to the common misconception, all these uses are controlled by the
*Individual Equipment* usage flag. The *Squad Equipment* flag is actually
intended for ammo, but the game does even less in that area than for armor
and weapons. This plugin implements the following rules almost from scratch:
* Combat ammo is stored in chests inside rooms with Squad Equipment enabled.
* If a chest is assigned to a squad member due to Individual Equipment also
being set, it is only used for that squad's ammo; otherwise, any squads
with Squad Equipment on the room will use all of the chests at random.
* Training ammo is stored in chests inside archery ranges designated from
archery targets, and controlled by the same Train flag as archery training
itself. This is inspired by some defunct code for weapon racks.
There are some minor traces in the game code to suggest that the first of
these rules is intended by Toady; the rest are invented by this plugin.
Mode switch and reclaim Mode switch and reclaim
======================= =======================
@ -1195,10 +1305,18 @@ Usage:
List workflow-controlled jobs (if in a workshop, filtered by it). List workflow-controlled jobs (if in a workshop, filtered by it).
``workflow list`` ``workflow list``
List active constraints, and their job counts. List active constraints, and their job counts.
``workflow count <constraint-spec> <cnt-limit> [cnt-gap], workflow amount <constraint-spec> <cnt-limit> [cnt-gap]`` ``workflow list-commands``
Set a constraint. The first form counts each stack as only 1 item. List active constraints as workflow commands that re-create them;
this list can be copied to a file, and then reloaded using the
``script`` built-in command.
``workflow count <constraint-spec> <cnt-limit> [cnt-gap]``
Set a constraint, counting every stack as 1 item.
``workflow amount <constraint-spec> <cnt-limit> [cnt-gap]``
Set a constraint, counting all items within stacks.
``workflow unlimit <constraint-spec>`` ``workflow unlimit <constraint-spec>``
Delete a constraint. Delete a constraint.
``workflow unlimit-all``
Delete all constraints.
Function Function
........ ........
@ -1213,6 +1331,37 @@ amount goes above or below the limit. The gap specifies how much below the limit
the amount has to drop before jobs are resumed; this is intended to reduce the amount has to drop before jobs are resumed; this is intended to reduce
the frequency of jobs being toggled. the frequency of jobs being toggled.
Check out the ``gui/workflow`` script below for a simple front-end integrated
in the game UI.
Constraint format
.................
The contstraint spec consists of 4 parts, separated with '/' characters::
ITEM[:SUBTYPE]/[GENERIC_MAT,...]/[SPECIFIC_MAT:...]/[LOCAL,<quality>]
The first part is mandatory and specifies the item type and subtype,
using the raw tokens for items, in the same syntax you would e.g. use
for a custom reaction input. See this list for more info: http://dwarffortresswiki.org/index.php/Item_token
The subsequent parts are optional:
- A generic material spec constrains the item material to one of
the hard-coded generic classes, which currently include::
PLANT WOOD CLOTH SILK LEATHER BONE SHELL SOAP TOOTH HORN PEARL YARN
METAL STONE SAND GLASS CLAY MILK
- A specific material spec chooses the material exactly, using the
raw syntax for reaction input materials, e.g. INORGANIC:IRON,
although for convenience it also allows just IRON, or ACACIA:WOOD etc.
See this page for more details on the unabbreviated raw syntax:
http://dwarffortresswiki.org/index.php/Material_token
- A comma-separated list of miscellaneous flags, which currently can
be used to ignore imported items or items below a certain quality.
Constraint examples Constraint examples
................... ...................
@ -1242,6 +1391,11 @@ Make sure there are always 15-20 coal and 25-30 copper bars.
workflow count BAR//COAL 20 workflow count BAR//COAL 20
workflow count BAR//COPPER 30 workflow count BAR//COPPER 30
Produce 15-20 gold crafts.
::
workflow count CRAFTS//GOLD 20
Collect 15-20 sand bags and clay boulders. Collect 15-20 sand bags and clay boulders.
:: ::
@ -1253,9 +1407,16 @@ Make sure there are always 80-100 units of dimple dye.
workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20 workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20
.. note::
In order for this to work, you have to set the material of the PLANT input In order for this to work, you have to set the material of the PLANT input
on the Mill Plants job to MUSHROOM_CUP_DIMPLE using the 'job item-material' on the Mill Plants job to MUSHROOM_CUP_DIMPLE using the 'job item-material'
command. command. Otherwise the plugin won't be able to deduce the output material.
Maintain 10-100 locally-made crafts of exceptional quality.
::
workflow count CRAFTS///LOCAL,EXCEPTIONAL 100 90
Fortress activity management Fortress activity management
@ -1545,19 +1706,17 @@ twice.
dfusion dfusion
------- -------
This is the DFusion lua plugin system by warmist/darius, running as a DFHack plugin. This is the DFusion lua plugin system by Warmist, running as a DFHack plugin. There are two parts to this plugin: an interactive script that shows a text based menu and lua modules. Some of the functionality of is intentionaly left out of the menu:
:Friendship: a binary plugin that allows multi race forts (to use make a script that imports plugins.dfusion.friendship and use Friendship:install{table} table should contain list of race names.)
See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic=69682.15 :Embark: a binary plugin that allows multi race embark (to use make a script that imports plugins.dfusion.embark and use Embark:install{table} table should contain list of race names or list of pairs (race-name, caste_id)).
Confirmed working DFusion plugins: See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic=93317.0
:simple_embark: allows changing the number of dwarves available on embark.
.. note:: .. note::
* Some of the DFusion plugins aren't completely ported yet. This can lead to crashes. * Some of the DFusion plugins aren't completely ported yet. This can lead to crashes.
* This is currently working only on Windows. * The game will be suspended while you're using dfusion. Don't panic when it doesn't respond.
* The game will be suspended while you're using dfusion. Don't panic when it doen't respond.
misery misery
------ ------
@ -1621,6 +1780,17 @@ gui/*
Scripts that implement dialogs inserted into the main game window are put in this Scripts that implement dialogs inserted into the main game window are put in this
directory. directory.
binpatch
========
Checks, applies or removes binary patches directly in memory at runtime::
binpatch check/apply/remove <patchname>
If the name of the patch has no extension or directory separators, the
script uses ``hack/patches/<df-version>/<name>.dif``, thus auto-selecting
the version appropriate for the currently loaded executable.
quicksave quicksave
========= =========
@ -1770,6 +1940,66 @@ deathcause
Focus a body part ingame, and this script will display the cause of death of Focus a body part ingame, and this script will display the cause of death of
the creature. the creature.
lua
===
There are the following ways to invoke this command:
1. ``lua`` (without any parameters)
This starts an interactive lua interpreter.
2. ``lua -f "filename"`` or ``lua --file "filename"``
This loads and runs the file indicated by filename.
3. ``lua -s ["filename"]`` or ``lua --save ["filename"]``
This loads and runs the file indicated by filename from the save
directory. If the filename is not supplied, it loads "dfhack.lua".
4. ``:lua`` *lua statement...*
Parses and executes the lua statement like the interactive interpreter would.
embark
======
Allows to embark anywhere. Currently windows only.
lever
=====
Allow manipulation of in-game levers from the dfhack console.
Can list levers, including state and links, with::
lever list
To queue a job so that a dwarf will pull the lever 42, use ``lever pull 42``.
This is the same as 'q'uerying the building and queue a 'P'ull request.
To magically toggle the lever immediately, use::
lever pull 42 --now
stripcaged
==========
For dumping items inside cages. Will mark selected items for dumping, then
a dwarf may come and actually dump it. See also ``autodump``.
With the ``items`` argument, only dumps items laying in the cage, excluding
stuff worn by caged creatures. ``weapons`` will dump worn weapons, ``armor``
will dump everything worn by caged creatures (including armor and clothing),
and ``all`` will dump everything, on a creature or not.
``stripcaged list`` will display on the dfhack console the list of all cages
and their item content.
Without further arguments, all commands work on all cages and animal traps on
the map. With the ``here`` argument, considers only the in-game selected cage
(or the cage under the game cursor). To target only specific cages, you can
alternatively pass cage IDs as arguments::
stripcaged weapons 25321 34228
======================= =======================
In-game interface tools In-game interface tools
@ -1783,6 +2013,9 @@ are mostly implemented by lua scripts.
In order to avoid user confusion, as a matter of policy all these tools In order to avoid user confusion, as a matter of policy all these tools
display the word "DFHack" on the screen somewhere while active. display the word "DFHack" on the screen somewhere while active.
When that is not appropriate because they merely add keybinding hints to
existing DF screens, they deliberately use red instead of green for the key.
As an exception, the tweak plugin described above does not follow this As an exception, the tweak plugin described above does not follow this
guideline because it arguably just fixes small usability bugs in the game UI. guideline because it arguably just fixes small usability bugs in the game UI.
@ -1793,12 +2026,18 @@ Dwarf Manipulator
Implemented by the manipulator plugin. To activate, open the unit screen and Implemented by the manipulator plugin. To activate, open the unit screen and
press 'l'. press 'l'.
.. image:: images/manipulator.png
This tool implements a Dwarf Therapist-like interface within the game UI. The This tool implements a Dwarf Therapist-like interface within the game UI. The
far left column displays the unit's Happiness (color-coded based on its far left column displays the unit's Happiness (color-coded based on its
value), and the right half of the screen displays each dwarf's labor settings value), and the right half of the screen displays each dwarf's labor settings
and skill levels (0-9 for Dabbling thru Professional, A-E for Great thru Grand and skill levels (0-9 for Dabbling thru Professional, A-E for Great thru Grand
Master, and U-Z for Legendary thru Legendary+5). Cells with red backgrounds Master, and U-Z for Legendary thru Legendary+5).
denote skills not controlled by labors.
Cells with teal backgrounds denote skills not controlled by labors, e.g.
military and social skills.
.. image:: images/manipulator2.png
Use the arrow keys or number pad to move the cursor around, holding Shift to Use the arrow keys or number pad to move the cursor around, holding Shift to
move 10 tiles at a time. move 10 tiles at a time.
@ -1832,10 +2071,75 @@ Pressing ESC normally returns to the unit screen, but Shift-ESC would exit
directly to the main dwarf mode screen. directly to the main dwarf mode screen.
Search
======
The search plugin adds search to the Stocks, Trading and Unit List screens.
.. image:: images/search.png
Searching works the same way as the search option in "Move to Depot" does.
You will see the Search option displayed on screen with a hotkey (usually 's').
Pressing it lets you start typing a query and the relevant list will start
filtering automatically.
Pressing ENTER, ESC or the arrow keys will return you to browsing the now
filtered list, which still functions as normal. You can clear the filter
by either going back into search mode and backspacing to delete it, or
pressing the "shifted" version of the search hotkey while browsing the
list (e.g. if the hotkey is 's', then hitting 'shift-s' will clear any
filter).
Leaving any screen automatically clears the filter.
In the Trade screen, the actual trade will always only act on items that
are actually visible in the list; the same effect applies to the Trade
Value numbers displayed by the screen. Because of this, pressing the 't'
key while search is active clears the search instead of executing the trade.
AutoMaterial
============
The automaterial plugin makes building constructions (walls, floors, fortifications,
etc) a little bit easier by saving you from having to trawl through long lists of
materials each time you place one.
Firstly, it moves the last used material for a given construction type to the top of
the list, if there are any left. So if you build a wall with chalk blocks, the next
time you place a wall the chalk blocks will be at the top of the list, regardless of
distance (it only does this in "grouped" mode, as individual item lists could be huge).
This should mean you can place most constructions without having to search for your
preferred material type.
.. image:: images/automaterial-mat.png
Pressing 'a' while highlighting any material will enable that material for "auto select"
for this construction type. You can enable multiple materials as autoselect. Now the next
time you place this type of construction, the plugin will automatically choose materials
for you from the kinds you enabled. If there is enough to satisfy the whole placement,
you won't be prompted with the material screen - the construction will be placed and you
will be back in the construction menu as if you did it manually.
When choosing the construction placement, you will see a couple of options:
.. image:: images/automaterial-pos.png
Use 'a' here to temporarily disable the material autoselection, e.g. if you need
to go to the material selection screen so you can toggle some materials on or off.
The other option (auto type selection, off by default) can be toggled on with 't'. If you
toggle this option on, instead of returning you to the main construction menu after selecting
materials, it returns you back to this screen. If you use this along with several autoselect
enabled materials, you should be able to place complex constructions more conveniently.
gui/liquids gui/liquids
=========== ===========
To use, bind to a key and activate in the 'k' mode. To use, bind to a key (the example config uses Alt-L) and activate in the 'k' mode.
.. image:: images/liquids.png
While active, use the suggested keys to switch the usual liquids parameters, and Enter While active, use the suggested keys to switch the usual liquids parameters, and Enter
to select the target area and apply changes. to select the target area and apply changes.
@ -1844,7 +2148,9 @@ to select the target area and apply changes.
gui/mechanisms gui/mechanisms
============== ==============
To use, bind to a key and activate in the 'q' mode. To use, bind to a key (the example config uses Ctrl-M) and activate in the 'q' mode.
.. image:: images/mechanisms.png
Lists mechanisms connected to the building, and their links. Navigating the list centers Lists mechanisms connected to the building, and their links. Navigating the list centers
the view on the relevant linked buildings. the view on the relevant linked buildings.
@ -1862,21 +2168,35 @@ via a simple dialog in the game ui.
* ``gui/rename [building]`` in 'q' mode changes the name of a building. * ``gui/rename [building]`` in 'q' mode changes the name of a building.
.. image:: images/rename-bld.png
The selected building must be one of stockpile, workshop, furnace, trap, or siege engine. The selected building must be one of stockpile, workshop, furnace, trap, or siege engine.
It is also possible to rename zones from the 'i' menu. It is also possible to rename zones from the 'i' menu.
* ``gui/rename [unit]`` with a unit selected changes the nickname. * ``gui/rename [unit]`` with a unit selected changes the nickname.
Unlike the built-in interface, this works even on enemies and animals.
* ``gui/rename unit-profession`` changes the selected unit's custom profession name. * ``gui/rename unit-profession`` changes the selected unit's custom profession name.
.. image:: images/rename-prof.png
Likewise, this can be applied to any unit, and when used on animals it overrides
their species string.
The ``building`` or ``unit`` options are automatically assumed when in relevant ui state. The ``building`` or ``unit`` options are automatically assumed when in relevant ui state.
The example config binds building/unit rename to Ctrl-Shift-N, and
unit profession change to Ctrl-Shift-T.
gui/room-list gui/room-list
============= =============
To use, bind to a key and activate in the 'q' mode, either immediately or after opening To use, bind to a key (the example config uses Alt-R) and activate in the 'q' mode,
the assign owner page. either immediately or after opening the assign owner page.
.. image:: images/room-list.png
The script lists other rooms owned by the same owner, or by the unit selected in the assign The script lists other rooms owned by the same owner, or by the unit selected in the assign
list, and allows unassigning them. list, and allows unassigning them.
@ -1885,7 +2205,8 @@ list, and allows unassigning them.
gui/choose-weapons gui/choose-weapons
================== ==================
Bind to a key, and activate in the Equip->View/Customize page of the military screen. Bind to a key (the example config uses Ctrl-W), and activate in the Equip->View/Customize
page of the military screen.
Depending on the cursor location, it rewrites all 'individual choice weapon' entries Depending on the cursor location, it rewrites all 'individual choice weapon' entries
in the selected squad or position to use a specific weapon type matching the assigned in the selected squad or position to use a specific weapon type matching the assigned
@ -1896,6 +2217,136 @@ Rationale: individual choice seems to be unreliable when there is a weapon short
and may lead to inappropriate weapons being selected. and may lead to inappropriate weapons being selected.
gui/guide-path
==============
Bind to a key (the example config uses Alt-P), and activate in the Hauling menu with
the cursor over a Guide order.
.. image:: images/guide-path.png
The script displays the cached path that will be used by the order; the game
computes it when the order is executed for the first time.
gui/workshop-job
================
Bind to a key (the example config uses Alt-A), and activate with a job selected in
a workshop in the 'q' mode.
.. image:: images/workshop-job.png
The script shows a list of the input reagents of the selected job, and allows changing
them like the ``job item-type`` and ``job item-material`` commands.
Specifically, pressing the 'i' key pops up a dialog that lets you select an item
type from a list.
.. image:: images/workshop-job-item.png
Pressing 'm', unless the item type does not allow a material,
lets you choose a material.
.. image:: images/workshop-job-material.png
Since there are a lot more materials than item types, this dialog is more complex
and uses a hierarchy of sub-menus. List choices that open a sub-menu are marked
with an arrow on the left.
.. warning::
Due to the way input reagent matching works in DF, you must select an item type
if you select a material, or the material will be matched incorrectly in some cases.
If you press 'm' without choosing an item type, the script will auto-choose it
if there is only one valid choice, or pop up an error message box instead of the
material selection dialog.
Note that both materials and item types presented in the dialogs are filtered
by the job input flags, and even the selected item type for material selection,
or material for item type selection. Many jobs would let you select only one
input item type.
For example, if you choose a *plant* input item type for your prepare meal job,
it will only let you select cookable materials.
If you choose a *barrel* item instead (meaning things stored in barrels, like
drink or milk), it will let you select any material, since in this case the
material is matched against the barrel itself. Then, if you select, say, iron,
and then try to change the input item type, now it won't let you select *plant*;
you have to unset the material first.
gui/workflow
============
Bind to a key (the example config uses Alt-W), and activate with a job selected
in a workshop in the 'q' mode.
.. image:: images/workflow.png
This script provides a simple interface to constraints managed by the workflow
plugin. When active, it displays a list of all constraints applicable to the
current job, and their current status.
A constraint specifies a certain range to be compared against either individual
*item* or whole *stack* count, an item type and optionally a material. When the
current count is below the lower bound of the range, the job is resumed; if it
is above or equal to the top bound, it will be suspended. Within the range, the
specific constraint has no effect on the job; others may still affect it.
Pressing 'c' switches the current constraint between counting stacks or items.
Pressing 'm' lets you input the range directly; 'e', 'r', 'd', 'f' adjust the
bounds by 1, 5, or 25 depending on the direction and the 'c' setting (counting
items and expanding the range each gives a 5x bonus).
Pressing 'n' produces a list of possible outputs of this job as guessed by
workflow, and lets you create a new constraint by choosing one as template. If you
don't see the choice you want in the list, it likely means you have to adjust
the job material first using ``job item-material`` or ``gui/workshop-job``,
as described in ``workflow`` documentation above. In this manner, this feature
can be used for troubleshooting jobs that don't match the right constraints.
.. image:: images/workflow-new1.png
If you select one of the outputs with Enter, the matching constraint is simply
added to the list. If you use Shift-Enter, the interface proceeds to the
next dialog, which allows you to edit the suggested constraint parameters to
suit your need, and set the item count range.
.. image:: images/workflow-new2.png
gui/assign-rack
===============
Bind to a key (the example config uses P), and activate when viewing a weapon
rack in the 'q' mode.
.. image:: images/assign-rack.png
This script is part of a group of related fixes to make the armory storage
work again. The existing issues are:
* Weapon racks have to each be assigned to a specific squad, like with
beds/boxes/armor stands and individual squad members, but nothing in
the game does this. This issue is what this script addresses.
* Even if assigned by the script, **the game will unassign the racks again without a binary patch**.
This patch is called ``weaponrack-unassign``, and can be applied via
the binpatch program, or the matching script. See this for more info
about the bug:
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
* Haulers still take equpment stored in the armory away to the stockpiles,
unless the ``fix-armory`` plugin above is used.
The script interface simply lets you designate one of the squads that
are assigned to the barracks/armory containing the selected stand as
the intended user. In order to aid in the choice, it shows the number
of currently assigned racks for every valid squad.
============= =============
Behavior Mods Behavior Mods
============= =============
@ -1931,7 +2382,10 @@ Configuration UI
---------------- ----------------
The configuration front-end to the plugin is implemented by the gui/siege-engine The configuration front-end to the plugin is implemented by the gui/siege-engine
script. Bind it to a key and activate after selecting a siege engine in 'q' mode. script. Bind it to a key (the example config uses Alt-A) and activate after selecting
a siege engine in 'q' mode.
.. image:: images/siege-engine.png
The main mode displays the current target, selected ammo item type, linked stockpiles and The main mode displays the current target, selected ammo item type, linked stockpiles and
the allowed operator skill range. The map tile color is changed to signify if it can be the allowed operator skill range. The map tile color is changed to signify if it can be
@ -1961,7 +2415,10 @@ The power-meter plugin implements a modified pressure plate that detects power b
supplied to gear boxes built in the four adjacent N/S/W/E tiles. supplied to gear boxes built in the four adjacent N/S/W/E tiles.
The configuration front-end is implemented by the gui/power-meter script. Bind it to a The configuration front-end is implemented by the gui/power-meter script. Bind it to a
key and activate after selecting Pressure Plate in the build menu. key (the example config uses Ctrl-Shift-M) and activate after selecting Pressure Plate
in the build menu.
.. image:: images/power-meter.png
The script follows the general look and feel of the regular pressure plate build The script follows the general look and feel of the regular pressure plate build
configuration page, but configures parameters relevant to the modded power meter building. configuration page, but configures parameters relevant to the modded power meter building.

@ -2,23 +2,34 @@
# Generic dwarfmode bindings # # Generic dwarfmode bindings #
############################## ##############################
# toggle the display of water level as 1-7 tiles
keybinding add Ctrl-W twaterlvl keybinding add Ctrl-W twaterlvl
# with cursor: # with cursor:
# designate the whole vein for digging
keybinding add Ctrl-V digv keybinding add Ctrl-V digv
keybinding add Ctrl-Shift-V "digv x" keybinding add Ctrl-Shift-V "digv x"
# clean the selected tile of blood etc
keybinding add Ctrl-C spotclean keybinding add Ctrl-C spotclean
# destroy items designated for dump in the selected tile
keybinding add Ctrl-Shift-K autodump-destroy-here keybinding add Ctrl-Shift-K autodump-destroy-here
# any item: # with an item selected:
# destroy the selected item
keybinding add Ctrl-K autodump-destroy-item keybinding add Ctrl-K autodump-destroy-item
# scripts:
# quicksave, only in main dwarfmode screen and menu page # quicksave, only in main dwarfmode screen and menu page
keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave
# gui/rename script # gui/rename script - rename units and buildings
keybinding add Ctrl-Shift-N gui/rename keybinding add Ctrl-Shift-N gui/rename
keybinding add Alt-Shift-P "gui/rename unit-profession" keybinding add Ctrl-Shift-T "gui/rename unit-profession"
############################## ##############################
# Generic adv mode bindings # # Generic adv mode bindings #
@ -31,10 +42,10 @@ keybinding add Ctrl-Shift-B "adv-bodyswap force"
# Context-specific bindings # # Context-specific bindings #
############################# #############################
# q->stockpile; p # q->stockpile; p - copy & paste stockpiles
keybinding add Alt-P copystock keybinding add Alt-P copystock
# q->workshop # q->workshop - duplicate the selected job
keybinding add Ctrl-D job-duplicate keybinding add Ctrl-D job-duplicate
# materials: q->workshop; b->select items # materials: q->workshop; b->select items
@ -48,7 +59,7 @@ keybinding add Shift-O "job-material OBSIDIAN"
keybinding add Shift-T "job-material ORTHOCLASE" keybinding add Shift-T "job-material ORTHOCLASE"
keybinding add Shift-G "job-material GLASS_GREEN" keybinding add Shift-G "job-material GLASS_GREEN"
# sort units and items # sort units and items in the on-screen list
keybinding add Alt-Shift-N "sort-units name" "sort-items description" keybinding add Alt-Shift-N "sort-units name" "sort-items description"
keybinding add Alt-Shift-R "sort-units arrival" keybinding add Alt-Shift-R "sort-units arrival"
keybinding add Alt-Shift-T "sort-units profession" "sort-items type material" keybinding add Alt-Shift-T "sort-units profession" "sort-items type material"
@ -60,7 +71,7 @@ keybinding add Ctrl-M@dwarfmode/QueryBuilding/Some gui/mechanisms
# browse rooms of same owner # browse rooms of same owner
keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list
# interface for the liquids plugin # interface for the liquids plugin - spawn water/magma/obsidian
keybinding add Alt-L@dwarfmode/LookAround gui/liquids keybinding add Alt-L@dwarfmode/LookAround gui/liquids
# machine power sensitive pressure plate construction # machine power sensitive pressure plate construction
@ -78,6 +89,12 @@ keybinding add Alt-P@dwarfmode/Hauling/DefineStop/Cond/Guide gui/guide-path
# workshop job details # workshop job details
keybinding add Alt-A@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workshop-job keybinding add Alt-A@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workshop-job
# workflow front-end
keybinding add Alt-W@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workflow
# assign weapon racks to squads so that they can be used
keybinding add P@dwarfmode/QueryBuilding/Some/Weaponrack gui/assign-rack
############################ ############################
# UI and game logic tweaks # # UI and game logic tweaks #
############################ ############################
@ -115,3 +132,29 @@ tweak fast-trade
tweak military-stable-assign tweak military-stable-assign
# in same list, color units already assigned to squads in brown & green # in same list, color units already assigned to squads in brown & green
tweak military-color-assigned tweak military-color-assigned
# remove inverse dependency of squad training speed on unit list size and use more sparring
tweak military-training
#######################################################
# Apply binary patches at runtime #
# #
# Commented out by default; enable the ones you want. #
#######################################################
# Bug 5994 - items teleported when removing a construction
#binpatch apply deconstruct-teleport
#binpatch apply deconstruct-heapfall
# Bug 4406 - hospital overstocking on all items
#binpatch apply hospital-overstocking
# Bug 808 - custom reactions completely using up all of their reagents
#binpatch apply custom-reagent-size
# Bug 4530 - marksdwarves not training when quiver full of combat-only ammo
#binpatch apply training-ammo
# Bug 1445 - weapon racks broken, armor stand capacity too low
#binpatch apply weaponrack-unassign
#binpatch apply armorstand-capacity

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

@ -167,7 +167,7 @@ IF(UNIX)
IF(BUILD_EGGY) IF(BUILD_EGGY)
LIST(APPEND PROJECT_SOURCES ${MAIN_SOURCES_LINUX_EGGY}) LIST(APPEND PROJECT_SOURCES ${MAIN_SOURCES_LINUX_EGGY})
ELSEIF(APPLE) ELSEIF(APPLE)
LIST(APPEND PROJECT_SOURCES ${MAIN_SOURCES_DARWIN}) LIST(APPEND PROJECT_SOURCES ${MAIN_SOURCES_DARWIN})
ELSE() ELSE()
LIST(APPEND PROJECT_SOURCES ${MAIN_SOURCES_LINUX}) LIST(APPEND PROJECT_SOURCES ${MAIN_SOURCES_LINUX})
ENDIF() ENDIF()
@ -235,7 +235,7 @@ ENDIF()
IF(UNIX) IF(UNIX)
SET(PROJECT_LIBS rt dl dfhack-md5 dfhack-tinyxml dfhack-tinythread) SET(PROJECT_LIBS rt dl dfhack-md5 dfhack-tinyxml dfhack-tinythread)
IF(APPLE) IF(APPLE)
SET(PROJECT_LIBS dl dfhack-md5 dfhack-tinyxml dfhack-tinythread) SET(PROJECT_LIBS dl dfhack-md5 dfhack-tinyxml dfhack-tinythread)
ENDIF() ENDIF()
ELSE(WIN32) ELSE(WIN32)
#FIXME: do we really need psapi? #FIXME: do we really need psapi?
@ -273,14 +273,14 @@ ENDIF()
SET_TARGET_PROPERTIES(dfhack PROPERTIES DEBUG_POSTFIX "-debug" ) SET_TARGET_PROPERTIES(dfhack PROPERTIES DEBUG_POSTFIX "-debug" )
IF(APPLE) IF(APPLE)
SET(SDL_LIBRARY ${CMAKE_INSTALL_PREFIX}/libs/SDL.framework) SET(SDL_LIBRARY ${CMAKE_INSTALL_PREFIX}/libs/SDL.framework)
SET(CXX_LIBRARY ${CMAKE_INSTALL_PREFIX}/libs/libstdc++.6.dylib) SET(CXX_LIBRARY ${CMAKE_INSTALL_PREFIX}/libs/libstdc++.6.dylib)
SET(ZIP_LIBRARY /usr/lib/libz.dylib) SET(ZIP_LIBRARY /usr/lib/libz.dylib)
TARGET_LINK_LIBRARIES(dfhack ${SDL_LIBRARY}) TARGET_LINK_LIBRARIES(dfhack ${SDL_LIBRARY})
TARGET_LINK_LIBRARIES(dfhack ${CXX_LIBRARY}) TARGET_LINK_LIBRARIES(dfhack ${CXX_LIBRARY})
TARGET_LINK_LIBRARIES(dfhack ${ZIP_LIBRARY}) TARGET_LINK_LIBRARIES(dfhack ${ZIP_LIBRARY})
SET_TARGET_PROPERTIES(dfhack PROPERTIES VERSION 1.0.0) SET_TARGET_PROPERTIES(dfhack PROPERTIES VERSION 1.0.0)
SET_TARGET_PROPERTIES(dfhack PROPERTIES SOVERSION 1.0.0) SET_TARGET_PROPERTIES(dfhack PROPERTIES SOVERSION 1.0.0)
ENDIF() ENDIF()
TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket lua ${PROJECT_LIBS}) TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket lua ${PROJECT_LIBS})
@ -290,20 +290,20 @@ TARGET_LINK_LIBRARIES(dfhack-client protobuf-lite clsocket)
TARGET_LINK_LIBRARIES(dfhack-run dfhack-client) TARGET_LINK_LIBRARIES(dfhack-run dfhack-client)
if(APPLE) if(APPLE)
add_custom_command(TARGET dfhack-run COMMAND ${dfhack_SOURCE_DIR}/package/darwin/fix-libs.sh WORKING_DIRECTORY ../ COMMENT "Fixing library dependencies...") add_custom_command(TARGET dfhack-run COMMAND ${dfhack_SOURCE_DIR}/package/darwin/fix-libs.sh WORKING_DIRECTORY ../ COMMENT "Fixing library dependencies...")
endif() endif()
IF(UNIX) IF(UNIX)
if (APPLE) if (APPLE)
install(PROGRAMS ${dfhack_SOURCE_DIR}/package/darwin/dfhack install(PROGRAMS ${dfhack_SOURCE_DIR}/package/darwin/dfhack
DESTINATION .) DESTINATION .)
install(PROGRAMS ${dfhack_SOURCE_DIR}/package/darwin/dfhack-run install(PROGRAMS ${dfhack_SOURCE_DIR}/package/darwin/dfhack-run
DESTINATION .) DESTINATION .)
else() else()
# On linux, copy our version of the df launch script which sets LD_PRELOAD # On linux, copy our version of the df launch script which sets LD_PRELOAD
install(PROGRAMS ${dfhack_SOURCE_DIR}/package/linux/dfhack install(PROGRAMS ${dfhack_SOURCE_DIR}/package/linux/dfhack
DESTINATION .) DESTINATION .)
install(PROGRAMS ${dfhack_SOURCE_DIR}/package/linux/dfhack-run install(PROGRAMS ${dfhack_SOURCE_DIR}/package/linux/dfhack-run
DESTINATION .) DESTINATION .)
endif() endif()
ELSE() ELSE()
@ -346,6 +346,10 @@ install(DIRECTORY ${dfhack_SOURCE_DIR}/scripts
PATTERN "*.rb" PATTERN "*.rb"
) )
install(DIRECTORY ${dfhack_SOURCE_DIR}/patches
DESTINATION ${DFHACK_DATA_DESTINATION}
FILES_MATCHING PATTERN "*.dif")
# Unused for so long that it's not even relevant now... # Unused for so long that it's not even relevant now...
if(BUILD_DEVEL) if(BUILD_DEVEL)
if(WIN32) if(WIN32)

@ -375,7 +375,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
" reload PLUGIN|all - Reload a plugin or all loaded plugins.\n" " reload PLUGIN|all - Reload a plugin or all loaded plugins.\n"
); );
con.print("\nDFHack version " DFHACK_VERSION ".\n"); con.print("\nDFHack version " DFHACK_VERSION ".\n");
} }
else if (parts.size() == 1) else if (parts.size() == 1)
{ {
@ -747,13 +747,13 @@ void fIOthread(void * iodata)
else if(ret) else if(ret)
{ {
// a proper, non-empty command was entered // a proper, non-empty command was entered
fprintf(stderr,"Adding command to history\n"); fprintf(stderr,"Adding command to history\n");
main_history.add(command); main_history.add(command);
fprintf(stderr,"Saving history\n"); fprintf(stderr,"Saving history\n");
main_history.save("dfhack.history"); main_history.save("dfhack.history");
} }
fprintf(stderr,"Running command\n"); fprintf(stderr,"Running command\n");
auto rv = core->runCommand(con, command); auto rv = core->runCommand(con, command);
@ -869,7 +869,6 @@ bool Core::Init()
// Init global object pointers // Init global object pointers
df::global::InitGlobals(); df::global::InitGlobals();
init_screen_module(this);
cerr << "Initializing Console.\n"; cerr << "Initializing Console.\n";
// init the console. // init the console.
@ -895,6 +894,7 @@ bool Core::Init()
*/ */
// initialize data defs // initialize data defs
virtual_identity::Init(this); virtual_identity::Init(this);
init_screen_module(this);
// initialize common lua context // initialize common lua context
Lua::Core::Init(con); Lua::Core::Init(con);
@ -1615,15 +1615,27 @@ void ClassNameCheck::getKnownClassNames(std::vector<std::string> &names)
names.push_back(*it); names.push_back(*it);
} }
bool Process::patchMemory(void *target, const void* src, size_t count) MemoryPatcher::MemoryPatcher(Process *p_) : p(p_)
{
if (!p)
p = Core::getInstance().p;
}
MemoryPatcher::~MemoryPatcher()
{
close();
}
bool MemoryPatcher::verifyAccess(void *target, size_t count, bool write)
{ {
uint8_t *sptr = (uint8_t*)target; uint8_t *sptr = (uint8_t*)target;
uint8_t *eptr = sptr + count; uint8_t *eptr = sptr + count;
// Find the valid memory ranges // Find the valid memory ranges
std::vector<t_memrange> ranges; if (ranges.empty())
getMemRanges(ranges); p->getMemRanges(ranges);
// Find the ranges that this area spans
unsigned start = 0; unsigned start = 0;
while (start < ranges.size() && ranges[start].end <= sptr) while (start < ranges.size() && ranges[start].end <= sptr)
start++; start++;
@ -1646,23 +1658,45 @@ bool Process::patchMemory(void *target, const void* src, size_t count)
return false; return false;
// Apply writable permissions & update // Apply writable permissions & update
bool ok = true; for (unsigned i = start; i < end; i++)
for (unsigned i = start; i < end && ok; i++)
{ {
t_memrange perms = ranges[i]; auto &perms = ranges[i];
if ((perms.write || !write) && perms.read)
continue;
save.push_back(perms);
perms.write = perms.read = true; perms.write = perms.read = true;
if (!setPermisions(perms, perms)) if (!p->setPermisions(perms, perms))
ok = false; return false;
} }
if (ok) return true;
memmove(target, src, count); }
bool MemoryPatcher::write(void *target, const void *src, size_t size)
{
if (!makeWritable(target, size))
return false;
memmove(target, src, size);
return true;
}
for (unsigned i = start; i < end && ok; i++) void MemoryPatcher::close()
setPermisions(ranges[i], ranges[i]); {
for (size_t i = 0; i < save.size(); i++)
p->setPermisions(save[i], save[i]);
save.clear();
ranges.clear();
};
bool Process::patchMemory(void *target, const void* src, size_t count)
{
MemoryPatcher patcher(this);
return ok; return patcher.write(target, src, count);
} }
/******************************************************************************* /*******************************************************************************

@ -271,39 +271,39 @@ DFhackCExport vPtr SDL_SetVideoMode(int width, int height, int bpp, uint32_t fla
static int (*_SDL_UpperBlit)(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) = 0; static int (*_SDL_UpperBlit)(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) = 0;
DFhackCExport int SDL_UpperBlit(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) DFhackCExport int SDL_UpperBlit(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect)
{ {
if ( dstrect != NULL && dstrect->h != 0 && dstrect->w != 0 ) if ( dstrect != NULL && dstrect->h != 0 && dstrect->w != 0 )
{ {
DFHack::Core & c = DFHack::Core::getInstance(); DFHack::Core & c = DFHack::Core::getInstance();
DFHack::Graphic* g = c.getGraphic(); DFHack::Graphic* g = c.getGraphic();
DFHack::DFTileSurface* ov = g->Call(dstrect->x/dstrect->w, dstrect->y/dstrect->h); DFHack::DFTileSurface* ov = g->Call(dstrect->x/dstrect->w, dstrect->y/dstrect->h);
if ( ov != NULL ) if ( ov != NULL )
{ {
if ( ov->paintOver ) if ( ov->paintOver )
{ {
_SDL_UpperBlit(src, srcrect, dst, dstrect); _SDL_UpperBlit(src, srcrect, dst, dstrect);
} }
DFHack::DFSDL_Rect* dstrect2 = new DFHack::DFSDL_Rect; DFHack::DFSDL_Rect* dstrect2 = new DFHack::DFSDL_Rect;
dstrect2->x = dstrect->x; dstrect2->x = dstrect->x;
dstrect2->y = dstrect->y; dstrect2->y = dstrect->y;
dstrect2->w = dstrect->w; dstrect2->w = dstrect->w;
dstrect2->h = dstrect->h; dstrect2->h = dstrect->h;
if ( ov->dstResize != NULL ) if ( ov->dstResize != NULL )
{ {
DFHack::DFSDL_Rect* r = (DFHack::DFSDL_Rect*)ov->dstResize; DFHack::DFSDL_Rect* r = (DFHack::DFSDL_Rect*)ov->dstResize;
dstrect2->x += r->x; dstrect2->x += r->x;
dstrect2->y += r->y; dstrect2->y += r->y;
dstrect2->w += r->w; dstrect2->w += r->w;
dstrect2->h += r->h; dstrect2->h += r->h;
} }
int result = _SDL_UpperBlit(ov->surface, ov->rect, dst, dstrect2); int result = _SDL_UpperBlit(ov->surface, ov->rect, dst, dstrect2);
delete dstrect2; delete dstrect2;
return result; return result;
} }
} }
return _SDL_UpperBlit(src, srcrect, dst, dstrect); return _SDL_UpperBlit(src, srcrect, dst, dstrect);
} }

@ -500,7 +500,9 @@ static void OpenPersistent(lua_State *state)
* Material info lookup * * Material info lookup *
************************/ ************************/
static void push_matinfo(lua_State *state, MaterialInfo &info) static int DFHACK_MATINFO_TOKEN = 0;
void Lua::Push(lua_State *state, MaterialInfo &info)
{ {
if (!info.isValid()) if (!info.isValid())
{ {
@ -509,7 +511,7 @@ static void push_matinfo(lua_State *state, MaterialInfo &info)
} }
lua_newtable(state); lua_newtable(state);
lua_pushvalue(state, lua_upvalueindex(1)); lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_MATINFO_TOKEN);
lua_setmetatable(state, -2); lua_setmetatable(state, -2);
lua_pushinteger(state, info.type); lua_pushinteger(state, info.type);
@ -564,7 +566,7 @@ static int dfhack_matinfo_find(lua_State *state)
info.find(tokens); info.find(tokens);
} }
push_matinfo(state, info); Lua::Push(state, info);
return 1; return 1;
} }
@ -632,7 +634,7 @@ static int dfhack_matinfo_decode(lua_State *state)
{ {
MaterialInfo info; MaterialInfo info;
decode_matinfo(state, &info, true); decode_matinfo(state, &info, true);
push_matinfo(state, info); Lua::Push(state, info);
return 1; return 1;
} }
@ -711,6 +713,9 @@ static void OpenMatinfo(lua_State *state)
{ {
luaL_getsubtable(state, lua_gettop(state), "matinfo"); luaL_getsubtable(state, lua_gettop(state), "matinfo");
lua_dup(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_MATINFO_TOKEN);
lua_dup(state); lua_dup(state);
luaL_setfuncs(state, dfhack_matinfo_funcs, 1); luaL_setfuncs(state, dfhack_matinfo_funcs, 1);
@ -720,6 +725,316 @@ static void OpenMatinfo(lua_State *state)
lua_pop(state, 1); lua_pop(state, 1);
} }
/**************
* Pen object *
**************/
static int DFHACK_PEN_TOKEN = 0;
void Lua::Push(lua_State *L, const Screen::Pen &info)
{
if (!info.valid())
{
lua_pushnil(L);
return;
}
void *pdata = lua_newuserdata(L, sizeof(Pen));
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN);
lua_setmetatable(L, -2);
new (pdata) Pen(info);
}
static Pen *check_pen_native(lua_State *L, int index)
{
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN);
if (!lua_getmetatable(L, index) || !lua_rawequal(L, -1, -2))
luaL_argerror(L, index, "not a pen object");
lua_pop(L, 2);
return (Pen*)lua_touserdata(L, index);
}
void Lua::CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil, bool allow_color)
{
index = lua_absindex(L, index);
luaL_checkany(L, index);
if (lua_isnil(L, index))
{
if (!allow_nil)
luaL_argerror(L, index, "nil pen not allowed");
*pen = Pen(0,0,0,-1);
}
else if (lua_isuserdata(L, index))
{
*pen = *check_pen_native(L, index);
}
else if (allow_color && lua_isnumber(L, index))
{
*pen = Pen(0, lua_tointeger(L, index)&15, 0);
}
else
{
luaL_checktype(L, index, LUA_TTABLE);
decode_pen(L, *pen, index);
}
}
static int adjust_pen(lua_State *L, bool no_copy)
{
lua_settop(L, 4);
Pen pen;
int iidx = 1;
Lua::CheckPen(L, &pen, 1, true, true);
if (!lua_isnil(L, 2) || !lua_isnil(L, 3) || !lua_isnil(L, 4))
{
if (lua_isnumber(L, 2) || lua_isnil(L, 2))
{
if (!pen.valid())
pen = Pen();
iidx = -1;
pen.fg = luaL_optint(L, 2, pen.fg) & 15;
pen.bg = luaL_optint(L, 3, pen.bg);
if (!lua_isnil(L, 4))
pen.bold = lua_toboolean(L, 4);
else if (!lua_isnil(L, 2))
{
pen.bold = !!(pen.fg & 8);
pen.fg &= 7;
}
}
else
{
iidx = 2;
Lua::CheckPen(L, &pen, 2, false, false);
}
}
if (no_copy && iidx > 0 && lua_isuserdata(L, iidx))
lua_pushvalue(L, iidx);
else
Lua::Push(L, pen);
return 1;
}
static int dfhack_pen_parse(lua_State *L)
{
return adjust_pen(L, true);
}
static int dfhack_pen_make(lua_State *L)
{
return adjust_pen(L, false);
}
static void make_pen_table(lua_State *L, Pen &pen)
{
if (!pen.valid())
luaL_error(L, "invalid pen state");
else
{
lua_newtable(L);
lua_pushinteger(L, (unsigned char)pen.ch); lua_setfield(L, -2, "ch");
lua_pushinteger(L, pen.fg); lua_setfield(L, -2, "fg");
lua_pushinteger(L, pen.bg); lua_setfield(L, -2, "bg");
lua_pushboolean(L, pen.bold); lua_setfield(L, -2, "bold");
if (pen.tile)
{
lua_pushinteger(L, pen.tile); lua_setfield(L, -2, "tile");
}
switch (pen.tile_mode) {
case Pen::CharColor:
lua_pushboolean(L, true); lua_setfield(L, -2, "tile_color");
break;
case Pen::TileColor:
lua_pushinteger(L, pen.tile_fg); lua_setfield(L, -2, "tile_fg");
lua_pushinteger(L, pen.tile_bg); lua_setfield(L, -2, "tile_bg");
break;
default:
lua_pushboolean(L, false); lua_setfield(L, -2, "tile_color");
break;
}
}
}
static void get_pen_mirror(lua_State *L, int idx)
{
lua_getuservalue(L, idx);
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
Pen pen;
Lua::CheckPen(L, &pen, idx, false, false);
make_pen_table(L, pen);
lua_dup(L);
lua_setuservalue(L, idx);
}
}
static int dfhack_pen_index(lua_State *L)
{
lua_settop(L, 2);
luaL_checktype(L, 1, LUA_TUSERDATA);
// check metatable
if (!lua_getmetatable(L, 1))
luaL_argerror(L, 1, "must be a pen");
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if (!lua_isnil(L, -1))
return 1;
// otherwise read from the mirror table, creating it if necessary
lua_settop(L, 2);
get_pen_mirror(L, 1);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
return 1;
}
static int pen_pnext(lua_State *L)
{
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
if (lua_next(L, lua_upvalueindex(1)))
return 2;
lua_pushnil(L);
return 1;
}
static int dfhack_pen_pairs(lua_State *L)
{
luaL_checktype(L, 1, LUA_TUSERDATA);
get_pen_mirror(L, 1);
lua_pushcclosure(L, pen_pnext, 1);
lua_pushnil(L);
lua_pushnil(L);
return 3;
}
const char *const pen_fields[] = {
"ch", "fg", "bold", "bg", "tile", "tile_color", "tile_fg", "tile_bg", NULL
};
static int dfhack_pen_newindex(lua_State *L)
{
lua_settop(L, 3);
luaL_checktype(L, 1, LUA_TUSERDATA);
int id = luaL_checkoption(L, 2, NULL, pen_fields);
int arg = 0;
Pen &pen = *check_pen_native(L, 1);
bool wipe_tile = false, wipe_tc = false;
switch (id) {
case 0:
if (lua_type(L, 3) != LUA_TNUMBER)
arg = (unsigned char)*luaL_checkstring(L, 3);
else
arg = luaL_checkint(L, 3);
pen.ch = arg;
lua_pushinteger(L, (unsigned char)pen.ch);
break;
case 1:
pen.fg = luaL_checkint(L, 3) & 15;
lua_pushinteger(L, pen.fg);
break;
case 2:
pen.bold = lua_toboolean(L, 3);
lua_pushboolean(L, pen.bold);
break;
case 3:
pen.bg = luaL_checkint(L, 3) & 15;
lua_pushinteger(L, pen.bg);
break;
case 4:
arg = lua_isnil(L, 3) ? 0 : luaL_checkint(L, 3);
if (arg < 0)
luaL_argerror(L, 3, "invalid tile index");
pen.tile = arg;
if (pen.tile)
lua_pushinteger(L, pen.tile);
else
lua_pushnil(L);
break;
case 5:
wipe_tile = (pen.tile_mode == Pen::TileColor);
pen.tile_mode = lua_toboolean(L, 3) ? Pen::CharColor : Pen::AsIs;
lua_pushboolean(L, pen.tile_mode == Pen::CharColor);
break;
case 6:
if (pen.tile_mode != Pen::TileColor) { wipe_tc = true; pen.tile_bg = 0; }
pen.tile_fg = luaL_checkint(L, 3) & 15;
pen.tile_mode = Pen::TileColor;
lua_pushinteger(L, pen.tile_fg);
break;
case 7:
if (pen.tile_mode != Pen::TileColor) { wipe_tc = true; pen.tile_fg = 7; }
pen.tile_bg = luaL_checkint(L, 3) & 15;
pen.tile_mode = Pen::TileColor;
lua_pushinteger(L, pen.tile_bg);
break;
}
lua_getuservalue(L, 1);
if (!lua_isnil(L, -1))
{
lua_remove(L, 3);
lua_insert(L, 2);
lua_rawset(L, 2);
if (wipe_tc) {
lua_pushnil(L); lua_setfield(L, 2, "tile_color");
lua_pushinteger(L, pen.tile_fg); lua_setfield(L, 2, "tile_fg");
lua_pushinteger(L, pen.tile_bg); lua_setfield(L, 2, "tile_bg");
}
if (wipe_tile) {
lua_pushnil(L); lua_setfield(L, 2, "tile_fg");
lua_pushnil(L); lua_setfield(L, 2, "tile_bg");
}
}
return 0;
}
static const luaL_Reg dfhack_pen_funcs[] = {
{ "parse", dfhack_pen_parse },
{ "make", dfhack_pen_make },
{ "__index", dfhack_pen_index },
{ "__pairs", dfhack_pen_pairs },
{ "__newindex", dfhack_pen_newindex },
{ NULL, NULL }
};
static void OpenPen(lua_State *state)
{
luaL_getsubtable(state, lua_gettop(state), "pen");
lua_dup(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN);
luaL_setfuncs(state, dfhack_pen_funcs, 0);
lua_pop(state, 1);
}
/************************ /************************
* Wrappers for C++ API * * Wrappers for C++ API *
************************/ ************************/
@ -806,6 +1121,8 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAPM(Job,cloneJobStruct), WRAPM(Job,cloneJobStruct),
WRAPM(Job,printItemDetails), WRAPM(Job,printItemDetails),
WRAPM(Job,printJobDetails), WRAPM(Job,printJobDetails),
WRAPM(Job,getGeneralRef),
WRAPM(Job,getSpecificRef),
WRAPM(Job,getHolder), WRAPM(Job,getHolder),
WRAPM(Job,getWorker), WRAPM(Job,getWorker),
WRAPM(Job,checkBuildingsNow), WRAPM(Job,checkBuildingsNow),
@ -842,6 +1159,8 @@ static const luaL_Reg dfhack_job_funcs[] = {
/***** Units module *****/ /***** Units module *****/
static const LuaWrapper::FunctionReg dfhack_units_module[] = { static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, getGeneralRef),
WRAPM(Units, getSpecificRef),
WRAPM(Units, getContainer), WRAPM(Units, getContainer),
WRAPM(Units, setNickname), WRAPM(Units, setNickname),
WRAPM(Units, getVisibleName), WRAPM(Units, getVisibleName),
@ -861,6 +1180,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, getAge), WRAPM(Units, getAge),
WRAPM(Units, getNominalSkill), WRAPM(Units, getNominalSkill),
WRAPM(Units, getEffectiveSkill), WRAPM(Units, getEffectiveSkill),
WRAPM(Units, getExperience),
WRAPM(Units, computeMovementSpeed), WRAPM(Units, computeMovementSpeed),
WRAPM(Units, getProfessionName), WRAPM(Units, getProfessionName),
WRAPM(Units, getCasteProfessionName), WRAPM(Units, getCasteProfessionName),
@ -937,6 +1257,8 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, getOwner), WRAPM(Items, getOwner),
WRAPM(Items, setOwner), WRAPM(Items, setOwner),
WRAPM(Items, getContainer), WRAPM(Items, getContainer),
WRAPM(Items, getHolderBuilding),
WRAPM(Items, getHolderUnit),
WRAPM(Items, getDescription), WRAPM(Items, getDescription),
WRAPM(Items, isCasteMaterial), WRAPM(Items, isCasteMaterial),
WRAPM(Items, getSubtypeCount), WRAPM(Items, getSubtypeCount),
@ -1110,6 +1432,8 @@ static bool buildings_containsTile(df::building *bld, int x, int y, bool room) {
} }
static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { static const LuaWrapper::FunctionReg dfhack_buildings_module[] = {
WRAPM(Buildings, getGeneralRef),
WRAPM(Buildings, getSpecificRef),
WRAPM(Buildings, setOwner), WRAPM(Buildings, setOwner),
WRAPM(Buildings, allocInstance), WRAPM(Buildings, allocInstance),
WRAPM(Buildings, checkFreeTiles), WRAPM(Buildings, checkFreeTiles),
@ -1244,7 +1568,7 @@ static int screen_getWindowSize(lua_State *L)
static int screen_paintTile(lua_State *L) static int screen_paintTile(lua_State *L)
{ {
Pen pen; Pen pen;
decode_pen(L, pen, 1); Lua::CheckPen(L, &pen, 1);
int x = luaL_checkint(L, 2); int x = luaL_checkint(L, 2);
int y = luaL_checkint(L, 3); int y = luaL_checkint(L, 3);
if (lua_gettop(L) >= 4 && !lua_isnil(L, 4)) if (lua_gettop(L) >= 4 && !lua_isnil(L, 4))
@ -1265,44 +1589,14 @@ static int screen_readTile(lua_State *L)
int x = luaL_checkint(L, 1); int x = luaL_checkint(L, 1);
int y = luaL_checkint(L, 2); int y = luaL_checkint(L, 2);
Pen pen = Screen::readTile(x, y); Pen pen = Screen::readTile(x, y);
Lua::Push(L, pen);
if (!pen.valid())
{
lua_pushnil(L);
}
else
{
lua_newtable(L);
lua_pushinteger(L, pen.ch); lua_setfield(L, -2, "ch");
lua_pushinteger(L, pen.fg); lua_setfield(L, -2, "fg");
lua_pushinteger(L, pen.bg); lua_setfield(L, -2, "bg");
lua_pushboolean(L, pen.bold); lua_setfield(L, -2, "bold");
if (pen.tile)
{
lua_pushinteger(L, pen.tile); lua_setfield(L, -2, "tile");
switch (pen.tile_mode) {
case Pen::CharColor:
lua_pushboolean(L, true); lua_setfield(L, -2, "tile_color");
break;
case Pen::TileColor:
lua_pushinteger(L, pen.tile_fg); lua_setfield(L, -2, "tile_fg");
lua_pushinteger(L, pen.tile_bg); lua_setfield(L, -2, "tile_bg");
break;
default:
break;
}
}
}
return 1; return 1;
} }
static int screen_paintString(lua_State *L) static int screen_paintString(lua_State *L)
{ {
Pen pen; Pen pen;
decode_pen(L, pen, 1); Lua::CheckPen(L, &pen, 1);
int x = luaL_checkint(L, 2); int x = luaL_checkint(L, 2);
int y = luaL_checkint(L, 3); int y = luaL_checkint(L, 3);
const char *text = luaL_checkstring(L, 4); const char *text = luaL_checkstring(L, 4);
@ -1313,7 +1607,7 @@ static int screen_paintString(lua_State *L)
static int screen_fillRect(lua_State *L) static int screen_fillRect(lua_State *L)
{ {
Pen pen; Pen pen;
decode_pen(L, pen, 1); Lua::CheckPen(L, &pen, 1);
int x1 = luaL_checkint(L, 2); int x1 = luaL_checkint(L, 2);
int y1 = luaL_checkint(L, 3); int y1 = luaL_checkint(L, 3);
int x2 = luaL_checkint(L, 4); int x2 = luaL_checkint(L, 4);
@ -1431,9 +1725,11 @@ static void *checkaddr(lua_State *L, int idx, bool allow_null = false)
return rv; return rv;
} }
static uint32_t getImageBase() { return Core::getInstance().p->getBase(); }
static int getRebaseDelta() { return Core::getInstance().vinfo->getRebaseDelta(); } static int getRebaseDelta() { return Core::getInstance().vinfo->getRebaseDelta(); }
static const LuaWrapper::FunctionReg dfhack_internal_module[] = { static const LuaWrapper::FunctionReg dfhack_internal_module[] = {
WRAP(getImageBase),
WRAP(getRebaseDelta), WRAP(getRebaseDelta),
{ NULL, NULL } { NULL, NULL }
}; };
@ -1487,6 +1783,18 @@ static int internal_getVTable(lua_State *L)
return 1; return 1;
} }
static int internal_adjustOffset(lua_State *L)
{
lua_settop(L, 2);
int off = luaL_checkint(L, 1);
int rv = Core::getInstance().p->adjustOffset(off, lua_toboolean(L, 2));
if (rv >= 0)
lua_pushinteger(L, rv);
else
lua_pushnil(L);
return 1;
}
static int internal_getMemRanges(lua_State *L) static int internal_getMemRanges(lua_State *L)
{ {
std::vector<DFHack::t_memrange> ranges; std::vector<DFHack::t_memrange> ranges;
@ -1530,6 +1838,81 @@ static int internal_patchMemory(lua_State *L)
return 1; return 1;
} }
static int internal_patchBytes(lua_State *L)
{
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 2);
MemoryPatcher patcher;
if (!lua_isnil(L, 2))
{
luaL_checktype(L, 2, LUA_TTABLE);
lua_pushnil(L);
while (lua_next(L, 2))
{
uint8_t *addr = (uint8_t*)checkaddr(L, -2, true);
int isnum;
uint8_t value = (uint8_t)lua_tounsignedx(L, -1, &isnum);
if (!isnum)
luaL_error(L, "invalid value in verify table");
lua_pop(L, 1);
if (!patcher.verifyAccess(addr, 1, false))
{
lua_pushnil(L);
lua_pushstring(L, "invalid verify address");
lua_pushvalue(L, -3);
return 3;
}
if (*addr != value)
{
lua_pushnil(L);
lua_pushstring(L, "wrong verify value");
lua_pushvalue(L, -3);
return 3;
}
}
}
lua_pushnil(L);
while (lua_next(L, 1))
{
uint8_t *addr = (uint8_t*)checkaddr(L, -2, true);
int isnum;
uint8_t value = (uint8_t)lua_tounsignedx(L, -1, &isnum);
if (!isnum)
luaL_error(L, "invalid value in write table");
lua_pop(L, 1);
if (!patcher.verifyAccess(addr, 1, true))
{
lua_pushnil(L);
lua_pushstring(L, "invalid write address");
lua_pushvalue(L, -3);
return 3;
}
}
lua_pushnil(L);
while (lua_next(L, 1))
{
uint8_t *addr = (uint8_t*)checkaddr(L, -2, true);
uint8_t value = (uint8_t)lua_tounsigned(L, -1);
lua_pop(L, 1);
*addr = value;
}
lua_pushboolean(L, true);
return 1;
}
static int internal_memmove(lua_State *L) static int internal_memmove(lua_State *L)
{ {
void *dest = checkaddr(L, 1); void *dest = checkaddr(L, 1);
@ -1619,8 +2002,10 @@ static const luaL_Reg dfhack_internal_funcs[] = {
{ "getAddress", internal_getAddress }, { "getAddress", internal_getAddress },
{ "setAddress", internal_setAddress }, { "setAddress", internal_setAddress },
{ "getVTable", internal_getVTable }, { "getVTable", internal_getVTable },
{ "adjustOffset", internal_adjustOffset },
{ "getMemRanges", internal_getMemRanges }, { "getMemRanges", internal_getMemRanges },
{ "patchMemory", internal_patchMemory }, { "patchMemory", internal_patchMemory },
{ "patchBytes", internal_patchBytes },
{ "memmove", internal_memmove }, { "memmove", internal_memmove },
{ "memcmp", internal_memcmp }, { "memcmp", internal_memcmp },
{ "memscan", internal_memscan }, { "memscan", internal_memscan },
@ -1637,6 +2022,7 @@ void OpenDFHackApi(lua_State *state)
{ {
OpenPersistent(state); OpenPersistent(state);
OpenMatinfo(state); OpenMatinfo(state);
OpenPen(state);
LuaWrapper::SetFunctionWrappers(state, dfhack_module); LuaWrapper::SetFunctionWrappers(state, dfhack_module);
OpenModule(state, "gui", dfhack_gui_module); OpenModule(state, "gui", dfhack_gui_module);

@ -107,7 +107,8 @@ static void signal_typeid_error(color_ostream *out, lua_State *state,
type_identity *type, const char *msg, type_identity *type, const char *msg,
int val_index, bool perr, bool signal) int val_index, bool perr, bool signal)
{ {
std::string error = stl_sprintf(msg, type->getFullName().c_str()); std::string typestr = type ? type->getFullName() : "any pointer";
std::string error = stl_sprintf(msg, typestr.c_str());
if (signal) if (signal)
{ {
@ -134,6 +135,8 @@ void *DFHack::Lua::CheckDFObject(lua_State *state, type_identity *type, int val_
if (lua_isnil(state, val_index)) if (lua_isnil(state, val_index))
return NULL; return NULL;
if (lua_islightuserdata(state, val_index) && !lua_touserdata(state, val_index))
return NULL;
void *rv = get_object_internal(state, type, val_index, exact_type, false); void *rv = get_object_internal(state, type, val_index, exact_type, false);

@ -9,14 +9,14 @@
NSAutoreleasePool *thePool; NSAutoreleasePool *thePool;
int create_pool() { int create_pool() {
fprintf(stderr,"Creating autorelease pool\n"); fprintf(stderr,"Creating autorelease pool\n");
thePool = [[NSAutoreleasePool alloc] init]; thePool = [[NSAutoreleasePool alloc] init];
return 1; return 1;
} }
int destroy_pool() { int destroy_pool() {
fprintf(stderr,"Draining and releasing autorelease pool\n"); fprintf(stderr,"Draining and releasing autorelease pool\n");
[thePool drain]; [thePool drain];
[thePool release]; [thePool release];
return 0; return 0;
} }

@ -148,6 +148,11 @@ bool prefix_matches(const std::string &prefix, const std::string &key, std::stri
return false; return false;
} }
int random_int(int max)
{
return int(int64_t(rand())*max/(int64_t(RAND_MAX)+1));
}
#ifdef LINUX_BUILD // Linux #ifdef LINUX_BUILD // Linux
uint64_t GetTimeMs64() uint64_t GetTimeMs64()
{ {

@ -53,10 +53,10 @@ Process::Process(VersionInfoFactory * known_versions)
char path[1024]; char path[1024];
char *real_path; char *real_path;
uint32_t size = sizeof(path); uint32_t size = sizeof(path);
if (_NSGetExecutablePath(path, &size) == 0) { if (_NSGetExecutablePath(path, &size) == 0) {
real_path = realpath(path, NULL); real_path = realpath(path, NULL);
} }
identified = false; identified = false;
my_descriptor = 0; my_descriptor = 0;
@ -166,29 +166,29 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
(vm_region_info_t)&info, &info_count, &object); (vm_region_info_t)&info, &info_count, &object);
if (kr == KERN_SUCCESS) { if (kr == KERN_SUCCESS) {
if (info.reserved==1) { if (info.reserved==1) {
address += vmsize; address += vmsize;
continue; continue;
} }
Dl_info dlinfo; Dl_info dlinfo;
int dlcheck; int dlcheck;
dlcheck = dladdr((const void*)address, &dlinfo); dlcheck = dladdr((const void*)address, &dlinfo);
if (dlcheck==0) { if (dlcheck==0) {
dlinfo.dli_fname = ""; dlinfo.dli_fname = "";
} }
t_memrange temp; t_memrange temp;
strncpy( temp.name, dlinfo.dli_fname, 1023 ); strncpy( temp.name, dlinfo.dli_fname, 1023 );
temp.name[1023] = 0; temp.name[1023] = 0;
temp.start = (void *) address; temp.start = (void *) address;
temp.end = (void *) (address+vmsize); temp.end = (void *) (address+vmsize);
temp.read = (info.protection & VM_PROT_READ); temp.read = (info.protection & VM_PROT_READ);
temp.write = (info.protection & VM_PROT_WRITE); temp.write = (info.protection & VM_PROT_WRITE);
temp.execute = (info.protection & VM_PROT_EXECUTE); temp.execute = (info.protection & VM_PROT_EXECUTE);
temp.shared = info.shared; temp.shared = info.shared;
temp.valid = true; temp.valid = true;
ranges.push_back(temp); ranges.push_back(temp);
fprintf(stderr, fprintf(stderr,
"%08x-%08x %8uK %c%c%c/%c%c%c %11s %6s %10s uwir=%hu sub=%u dlname: %s\n", "%08x-%08x %8uK %c%c%c/%c%c%c %11s %6s %10s uwir=%hu sub=%u dlname: %s\n",
address, (address + vmsize), (vmsize >> 10), address, (address + vmsize), (vmsize >> 10),
(info.protection & VM_PROT_READ) ? 'r' : '-', (info.protection & VM_PROT_READ) ? 'r' : '-',
@ -220,9 +220,14 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
}*/ }*/
} }
uint32_t Process::getBase() uintptr_t Process::getBase()
{ {
return 0; return 0x1000000;
}
int Process::adjustOffset(int offset, bool /*to_file*/)
{
return offset;
} }
static int getdir (string dir, vector<string> &files) static int getdir (string dir, vector<string> &files)
@ -272,15 +277,15 @@ uint32_t Process::getTickCount()
string Process::getPath() string Process::getPath()
{ {
char path[1024]; char path[1024];
char *real_path; char *real_path;
uint32_t size = sizeof(path); uint32_t size = sizeof(path);
if (_NSGetExecutablePath(path, &size) == 0) { if (_NSGetExecutablePath(path, &size) == 0) {
real_path = realpath(path, NULL); real_path = realpath(path, NULL);
} }
std::string path_string(real_path); std::string path_string(real_path);
int last_slash = path_string.find_last_of("/"); int last_slash = path_string.find_last_of("/");
std::string directory = path_string.substr(0,last_slash); std::string directory = path_string.substr(0,last_slash);
return directory; return directory;
} }
@ -300,3 +305,28 @@ bool Process::setPermisions(const t_memrange & range,const t_memrange &trgrange)
return result==0; return result==0;
} }
// returns -1 on error
void* Process::memAlloc(const int length)
{
return mmap(0, length, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
}
int Process::memDealloc(const void *ptr, const int length)
{
return munmap(ptr, length);
}
int Process::memProtect(const void *ptr, const int length, const int prot)
{
int prot_native = 0;
if (prot & Process::MemProt::READ)
prot_native |= PROT_READ;
if (prot & Process::MemProt::WRITE)
prot_native |= PROT_WRITE;
if (prot & Process::MemProt::EXEC)
prot_native |= PROT_EXEC;
return mprotect(ptr, length, prot_native);
}

@ -155,9 +155,14 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
fclose(mapFile); fclose(mapFile);
} }
uint32_t Process::getBase() uintptr_t Process::getBase()
{ {
return 0; return 0x8048000;
}
int Process::adjustOffset(int offset, bool /*to_file*/)
{
return offset;
} }
static int getdir (string dir, vector<string> &files) static int getdir (string dir, vector<string> &files)
@ -231,3 +236,28 @@ bool Process::setPermisions(const t_memrange & range,const t_memrange &trgrange)
return result==0; return result==0;
} }
// returns -1 on error
void* Process::memAlloc(const int length)
{
return mmap(0, length, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
}
int Process::memDealloc(void *ptr, const int length)
{
return munmap(ptr, length);
}
int Process::memProtect(void *ptr, const int length, const int prot)
{
int prot_native = 0;
if (prot & Process::MemProt::READ)
prot_native |= PROT_READ;
if (prot & Process::MemProt::WRITE)
prot_native |= PROT_WRITE;
if (prot & Process::MemProt::EXEC)
prot_native |= PROT_EXEC;
return mprotect(ptr, length, prot_native);
}

@ -160,7 +160,7 @@ Process::Process(VersionInfoFactory * factory)
identified = true; identified = true;
// give the process a data model and memory layout fixed for the base of first module // give the process a data model and memory layout fixed for the base of first module
my_descriptor = new VersionInfo(*vinfo); my_descriptor = new VersionInfo(*vinfo);
my_descriptor->rebaseTo((uint32_t)d->base); my_descriptor->rebaseTo(getBase());
for(size_t i = 0; i < threads_ids.size();i++) for(size_t i = 0; i < threads_ids.size();i++)
{ {
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads_ids[i]); HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads_ids[i]);
@ -394,13 +394,46 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
} }
} }
uint32_t Process::getBase() uintptr_t Process::getBase()
{ {
if(d) if(d)
return (uint32_t) d->base; return (uintptr_t) d->base;
return 0x400000; return 0x400000;
} }
int Process::adjustOffset(int offset, bool to_file)
{
if (!d)
return -1;
for(int i = 0; i < d->pe_header.FileHeader.NumberOfSections; i++)
{
auto &section = d->sections[i];
if (to_file)
{
unsigned delta = offset - section.VirtualAddress;
if (delta >= section.Misc.VirtualSize)
continue;
if (!section.PointerToRawData || delta >= section.SizeOfRawData)
return -1;
return (int)(section.PointerToRawData + delta);
}
else
{
unsigned delta = offset - section.PointerToRawData;
if (!section.PointerToRawData || delta >= section.SizeOfRawData)
continue;
if (delta >= section.Misc.VirtualSize)
return -1;
return (int)(section.VirtualAddress + delta);
}
}
return -1;
}
string Process::doReadClassName (void * vptr) string Process::doReadClassName (void * vptr)
{ {
char * rtti = readPtr((char *)vptr - 0x4); char * rtti = readPtr((char *)vptr - 0x4);
@ -440,3 +473,42 @@ bool Process::setPermisions(const t_memrange & range,const t_memrange &trgrange)
return result; return result;
} }
void* Process::memAlloc(const int length)
{
void *ret;
// returns 0 on error
ret = VirtualAlloc(0, length, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
if (!ret)
ret = (void*)-1;
return ret;
}
int Process::memDealloc(void *ptr, const int length)
{
// can only free the whole region at once
// vfree returns 0 on error
return !VirtualFree(ptr, 0, MEM_RELEASE);
}
int Process::memProtect(void *ptr, const int length, const int prot)
{
int prot_native = 0;
DWORD old_prot = 0;
// only support a few constant combinations
if (prot == 0)
prot_native = PAGE_NOACCESS;
else if (prot == Process::MemProt::READ)
prot_native = PAGE_READONLY;
else if (prot == (Process::MemProt::READ | Process::MemProt::WRITE))
prot_native = PAGE_READWRITE;
else if (prot == (Process::MemProt::READ | Process::MemProt::WRITE | Process::MemProt::EXEC))
prot_native = PAGE_EXECUTE_READWRITE;
else if (prot == (Process::MemProt::READ | Process::MemProt::EXEC))
prot_native = PAGE_EXECUTE_READ;
else
return -1;
return !VirtualProtect(ptr, length, prot_native, &old_prot);
}

@ -100,6 +100,24 @@ bool DFHack::removeRef(std::vector<df::general_ref*> &vec, df::general_ref_type
return false; return false;
} }
df::item *DFHack::findItemRef(std::vector<df::general_ref*> &vec, df::general_ref_type type)
{
auto ref = findRef(vec, type);
return ref ? ref->getItem() : NULL;
}
df::building *DFHack::findBuildingRef(std::vector<df::general_ref*> &vec, df::general_ref_type type)
{
auto ref = findRef(vec, type);
return ref ? ref->getBuilding() : NULL;
}
df::unit *DFHack::findUnitRef(std::vector<df::general_ref*> &vec, df::general_ref_type type)
{
auto ref = findRef(vec, type);
return ref ? ref->getUnit() : NULL;
}
df::specific_ref *DFHack::findRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type) df::specific_ref *DFHack::findRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type)
{ {
for (int i = vec.size()-1; i >= 0; i--) for (int i = vec.size()-1; i >= 0; i--)

@ -166,12 +166,12 @@ void *virtual_identity::get_vmethod_ptr(int idx)
return vtable[idx]; return vtable[idx];
} }
bool virtual_identity::set_vmethod_ptr(int idx, void *ptr) bool virtual_identity::set_vmethod_ptr(MemoryPatcher &patcher, int idx, void *ptr)
{ {
assert(idx >= 0); assert(idx >= 0);
void **vtable = (void**)vtable_ptr; void **vtable = (void**)vtable_ptr;
if (!vtable) return NULL; if (!vtable) return NULL;
return Core::getInstance().p->patchMemory(&vtable[idx], &ptr, sizeof(void*)); return patcher.write(&vtable[idx], &ptr, sizeof(void*));
} }
/* /*
@ -344,7 +344,9 @@ void VMethodInterposeLinkBase::on_host_delete(virtual_identity *from)
auto last = this; auto last = this;
while (last->prev) last = last->prev; while (last->prev) last = last->prev;
from->set_vmethod_ptr(vmethod_idx, last->saved_chain); MemoryPatcher patcher;
from->set_vmethod_ptr(patcher, vmethod_idx, last->saved_chain);
// Unlink the chains // Unlink the chains
child_hosts.erase(from); child_hosts.erase(from);
@ -379,13 +381,15 @@ bool VMethodInterposeLinkBase::apply(bool enable)
assert(old_ptr != NULL && (!old_link || old_link->interpose_method == old_ptr)); assert(old_ptr != NULL && (!old_link || old_link->interpose_method == old_ptr));
// Apply the new method ptr // Apply the new method ptr
MemoryPatcher patcher;
set_chain(old_ptr); set_chain(old_ptr);
if (next_link) if (next_link)
{ {
next_link->set_chain(interpose_method); next_link->set_chain(interpose_method);
} }
else if (!host->set_vmethod_ptr(vmethod_idx, interpose_method)) else if (!host->set_vmethod_ptr(patcher, vmethod_idx, interpose_method))
{ {
set_chain(NULL); set_chain(NULL);
return false; return false;
@ -459,7 +463,7 @@ bool VMethodInterposeLinkBase::apply(bool enable)
{ {
auto nhost = *it; auto nhost = *it;
assert(nhost->interpose_list[vmethod_idx] == old_link); assert(nhost->interpose_list[vmethod_idx] == old_link);
nhost->set_vmethod_ptr(vmethod_idx, interpose_method); nhost->set_vmethod_ptr(patcher, vmethod_idx, interpose_method);
nhost->interpose_list[vmethod_idx] = this; nhost->interpose_list[vmethod_idx] = this;
} }
@ -496,9 +500,11 @@ void VMethodInterposeLinkBase::remove()
} }
else else
{ {
MemoryPatcher patcher;
// Remove from the list in the identity and vtable // Remove from the list in the identity and vtable
host->interpose_list[vmethod_idx] = prev; host->interpose_list[vmethod_idx] = prev;
host->set_vmethod_ptr(vmethod_idx, saved_chain); host->set_vmethod_ptr(patcher, vmethod_idx, saved_chain);
for (auto it = child_next.begin(); it != child_next.end(); ++it) for (auto it = child_next.begin(); it != child_next.end(); ++it)
{ {
@ -515,7 +521,7 @@ void VMethodInterposeLinkBase::remove()
auto nhost = *it; auto nhost = *it;
assert(nhost->interpose_list[vmethod_idx] == this); assert(nhost->interpose_list[vmethod_idx] == this);
nhost->interpose_list[vmethod_idx] = prev; nhost->interpose_list[vmethod_idx] = prev;
nhost->set_vmethod_ptr(vmethod_idx, saved_chain); nhost->set_vmethod_ptr(patcher, vmethod_idx, saved_chain);
if (prev) if (prev)
prev->child_hosts.insert(nhost); prev->child_hosts.insert(nhost);
} }

@ -103,13 +103,13 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem)
{ {
mem->setOS(OS_LINUX); mem->setOS(OS_LINUX);
// this is wrong... I'm not going to do base image relocation on linux though. // this is wrong... I'm not going to do base image relocation on linux though.
mem->setBase(0x0); mem->setBase(0x8048000);
} }
else if(os == "darwin") else if(os == "darwin")
{ {
mem->setOS(OS_APPLE); mem->setOS(OS_APPLE);
// this is wrong... I'm not going to do base image relocation on linux though. // this is wrong... I'm not going to do base image relocation on linux though.
mem->setBase(0x0); mem->setBase(0x1000000);
} }
else else
{ {

@ -171,8 +171,9 @@ BinaryPatch::State BinaryPatch::checkState(const patch_byte *ptr, size_t len)
state |= Applied; state |= Applied;
else else
{ {
cerr << std::hex << bv.offset << ": " << bv.old_val << " " << bv.new_val cerr << std::hex << bv.offset << ": "
<< ", but currently " << cv << std::dec << endl; << unsigned(bv.old_val) << " " << unsigned(bv.new_val)
<< ", but currently " << unsigned(cv) << std::dec << endl;
return Conflict; return Conflict;
} }
} }

@ -65,20 +65,20 @@ namespace DFHack
bool save (const char * filename) bool save (const char * filename)
{ {
std::ofstream outfile (filename); std::ofstream outfile (filename);
//fprintf(stderr,"Save: Initialized stream\n"); //fprintf(stderr,"Save: Initialized stream\n");
if(outfile.bad()) if(outfile.bad())
return false; return false;
//fprintf(stderr,"Save: Iterating...\n"); //fprintf(stderr,"Save: Iterating...\n");
for(auto iter = history.begin();iter < history.end(); iter++) for(auto iter = history.begin();iter < history.end(); iter++)
{ {
//fprintf(stderr,"Save: Dumping %s\n",(*iter).c_str()); //fprintf(stderr,"Save: Dumping %s\n",(*iter).c_str());
outfile << *iter << std::endl; outfile << *iter << std::endl;
//fprintf(stderr,"Save: Flushing\n"); //fprintf(stderr,"Save: Flushing\n");
outfile.flush(); outfile.flush();
} }
//fprintf(stderr,"Save: Closing\n"); //fprintf(stderr,"Save: Closing\n");
outfile.close(); outfile.close();
//fprintf(stderr,"Save: Done\n"); //fprintf(stderr,"Save: Done\n");
return true; return true;
} }
/// add a command to the history /// add a command to the history

@ -35,7 +35,7 @@ distribution.
// Stop some MS stupidity // Stop some MS stupidity
#ifdef interface #ifdef interface
#undef interface #undef interface
#endif #endif
typedef struct lua_State lua_State; typedef struct lua_State lua_State;
@ -294,6 +294,7 @@ namespace DFHack
#endif #endif
class DFHACK_EXPORT VMethodInterposeLinkBase; class DFHACK_EXPORT VMethodInterposeLinkBase;
class MemoryPatcher;
class DFHACK_EXPORT virtual_identity : public struct_identity { class DFHACK_EXPORT virtual_identity : public struct_identity {
static std::map<void*, virtual_identity*> known; static std::map<void*, virtual_identity*> known;
@ -313,7 +314,7 @@ namespace DFHack
bool can_allocate() { return struct_identity::can_allocate() && (vtable_ptr != NULL); } bool can_allocate() { return struct_identity::can_allocate() && (vtable_ptr != NULL); }
void *get_vmethod_ptr(int index); void *get_vmethod_ptr(int index);
bool set_vmethod_ptr(int index, void *ptr); bool set_vmethod_ptr(MemoryPatcher &patcher, int index, void *ptr);
public: public:
virtual_identity(size_t size, TAllocateFn alloc, virtual_identity(size_t size, TAllocateFn alloc,

@ -36,10 +36,14 @@ distribution.
namespace DFHack { namespace DFHack {
class function_identity_base; class function_identity_base;
struct MaterialInfo;
namespace Units { namespace Units {
struct NoblePosition; struct NoblePosition;
} }
namespace Screen {
struct Pen;
};
} }
namespace DFHack {namespace Lua { namespace DFHack {namespace Lua {
@ -283,6 +287,8 @@ namespace DFHack {namespace Lua {
DFHACK_EXPORT void Push(lua_State *state, df::coord obj); DFHACK_EXPORT void Push(lua_State *state, df::coord obj);
DFHACK_EXPORT void Push(lua_State *state, df::coord2d obj); DFHACK_EXPORT void Push(lua_State *state, df::coord2d obj);
void Push(lua_State *state, const Units::NoblePosition &pos); void Push(lua_State *state, const Units::NoblePosition &pos);
DFHACK_EXPORT void Push(lua_State *state, MaterialInfo &info);
DFHACK_EXPORT void Push(lua_State *state, const Screen::Pen &info);
template<class T> inline void Push(lua_State *state, T *ptr) { template<class T> inline void Push(lua_State *state, T *ptr) {
PushDFObject(state, ptr); PushDFObject(state, ptr);
} }
@ -313,6 +319,8 @@ namespace DFHack {namespace Lua {
DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos); DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos);
DFHACK_EXPORT int PushPosXY(lua_State *state, df::coord2d pos); DFHACK_EXPORT int PushPosXY(lua_State *state, df::coord2d pos);
DFHACK_EXPORT void CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil = false, bool allow_color = true);
DFHACK_EXPORT bool IsCoreContext(lua_State *state); DFHACK_EXPORT bool IsCoreContext(lua_State *state);
namespace Event { namespace Event {
@ -472,3 +480,18 @@ namespace DFHack {namespace Lua {
name##_event.invoke(out, 5); \ name##_event.invoke(out, 5); \
} \ } \
} }
#define DEFINE_LUA_EVENT_6(name, handler, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5,arg_type6) \
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4,arg_type5 arg5, arg_type6 arg6) { \
handler(out, arg1, arg2, arg3, arg4, arg5, arg6); \
if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \
DFHack::Lua::Push(state, arg3); \
DFHack::Lua::Push(state, arg4); \
DFHack::Lua::Push(state, arg5); \
DFHack::Lua::Push(state, arg6); \
name##_event.invoke(out, 6); \
} \
}

@ -275,11 +275,13 @@ namespace DFHack
{ {
return my_descriptor; return my_descriptor;
}; };
uint32_t getBase(); uintptr_t getBase();
/// get the DF Process ID /// get the DF Process ID
int getPID(); int getPID();
/// get the DF Process FilePath /// get the DF Process FilePath
std::string getPath(); std::string getPath();
/// Adjust between in-memory and in-file image offset
int adjustOffset(int offset, bool to_file = false);
/// millisecond tick count, exactly as DF uses /// millisecond tick count, exactly as DF uses
uint32_t getTickCount(); uint32_t getTickCount();
@ -289,6 +291,27 @@ namespace DFHack
/// write a possibly read-only memory area /// write a possibly read-only memory area
bool patchMemory(void *target, const void* src, size_t count); bool patchMemory(void *target, const void* src, size_t count);
/// allocate new memory pages for code or stuff
/// returns -1 on error (0 is a valid address)
void* memAlloc(const int length);
/// free memory pages from memAlloc
/// should have length = alloced length for portability
/// returns 0 on success
int memDealloc(void *ptr, const int length);
/// change memory page permissions
/// prot is a bitwise OR of the MemProt enum
/// returns 0 on success
int memProtect(void *ptr, const int length, const int prot);
enum MemProt {
READ = 1,
WRITE = 2,
EXEC = 4
};
private: private:
VersionInfo * my_descriptor; VersionInfo * my_descriptor;
PlatformSpecific *d; PlatformSpecific *d;
@ -315,5 +338,22 @@ namespace DFHack
// Get list of names given to ClassNameCheck constructors. // Get list of names given to ClassNameCheck constructors.
static void getKnownClassNames(std::vector<std::string> &names); static void getKnownClassNames(std::vector<std::string> &names);
}; };
class DFHACK_EXPORT MemoryPatcher
{
Process *p;
std::vector<t_memrange> ranges, save;
public:
MemoryPatcher(Process *p = NULL);
~MemoryPatcher();
bool verifyAccess(void *target, size_t size, bool write = false);
bool makeWritable(void *target, size_t size) {
return verifyAccess(target, size, true);
}
bool write(void *target, const void *src, size_t size);
void close();
};
} }
#endif #endif

@ -331,6 +331,8 @@ inline T clip_range(T a, T1 minv, T2 maxv) {
return a; return a;
} }
DFHACK_EXPORT int random_int(int max);
/** /**
* Returns the amount of milliseconds elapsed since the UNIX epoch. * Returns the amount of milliseconds elapsed since the UNIX epoch.
* Works on both windows and linux. * Works on both windows and linux.

@ -41,7 +41,7 @@ namespace DFHack
struct struct
{ {
//Maybe should add 'up' and 'down' for Z-levels? //Maybe should add 'up' and 'down' for Z-levels?
unsigned char north,south,west,east; unsigned char north,south,west,east;
}; };
inline TileDirection() inline TileDirection()
@ -203,6 +203,12 @@ namespace DFHack
return ENUM_ATTR(tiletype_shape, passable_flow, tileShape(tiletype)); return ENUM_ATTR(tiletype_shape, passable_flow, tileShape(tiletype));
} }
inline
bool FlowPassableDown(df::tiletype tiletype)
{
return ENUM_ATTR(tiletype_shape, passable_flow_down, tileShape(tiletype));
}
inline inline
bool isWalkable(df::tiletype tiletype) bool isWalkable(df::tiletype tiletype)
{ {

@ -74,12 +74,38 @@ namespace DFHack
uint32_t xpNxtLvl; uint32_t xpNxtLvl;
}; };
typedef std::pair<df::coord2d, df::coord2d> rect2d;
inline rect2d intersect(rect2d a, rect2d b) {
df::coord2d g1 = a.first, g2 = a.second;
df::coord2d c1 = b.first, c2 = b.second;
df::coord2d rc1 = df::coord2d(std::max(g1.x, c1.x), std::max(g1.y, c1.y));
df::coord2d rc2 = df::coord2d(std::min(g2.x, c2.x), std::min(g2.y, c2.y));
return rect2d(rc1, rc2);
}
inline rect2d mkrect_xy(int x1, int y1, int x2, int y2) {
return rect2d(df::coord2d(x1, y1), df::coord2d(x2, y2));
}
inline rect2d mkrect_wh(int x, int y, int w, int h) {
return rect2d(df::coord2d(x, y), df::coord2d(x+w-1, y+h-1));
}
inline df::coord2d rect_size(const rect2d &rect) {
return rect.second - rect.first + df::coord2d(1,1);
}
DFHACK_EXPORT int getdir(std::string dir, std::vector<std::string> &files); DFHACK_EXPORT int getdir(std::string dir, std::vector<std::string> &files);
DFHACK_EXPORT bool hasEnding (std::string const &fullString, std::string const &ending); DFHACK_EXPORT bool hasEnding (std::string const &fullString, std::string const &ending);
DFHACK_EXPORT df::general_ref *findRef(std::vector<df::general_ref*> &vec, df::general_ref_type type); DFHACK_EXPORT df::general_ref *findRef(std::vector<df::general_ref*> &vec, df::general_ref_type type);
DFHACK_EXPORT bool removeRef(std::vector<df::general_ref*> &vec, df::general_ref_type type, int id); DFHACK_EXPORT bool removeRef(std::vector<df::general_ref*> &vec, df::general_ref_type type, int id);
DFHACK_EXPORT df::item *findItemRef(std::vector<df::general_ref*> &vec, df::general_ref_type type);
DFHACK_EXPORT df::building *findBuildingRef(std::vector<df::general_ref*> &vec, df::general_ref_type type);
DFHACK_EXPORT df::unit *findUnitRef(std::vector<df::general_ref*> &vec, df::general_ref_type type);
DFHACK_EXPORT df::specific_ref *findRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type); DFHACK_EXPORT df::specific_ref *findRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type);
DFHACK_EXPORT bool removeRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type, void *ptr); DFHACK_EXPORT bool removeRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type, void *ptr);
}// namespace DFHack }// namespace DFHack

@ -27,7 +27,7 @@ distribution.
#include "Pragma.h" #include "Pragma.h"
#include "Export.h" #include "Export.h"
#include "Types.h" /* #include "Types.h" */
#include <map> #include <map>
#include <sys/types.h> #include <sys/types.h>
#include <vector> #include <vector>

@ -92,6 +92,9 @@ DFHACK_EXPORT bool Read (const uint32_t index, t_building & building);
*/ */
DFHACK_EXPORT bool ReadCustomWorkshopTypes(std::map <uint32_t, std::string> & btypes); DFHACK_EXPORT bool ReadCustomWorkshopTypes(std::map <uint32_t, std::string> & btypes);
DFHACK_EXPORT df::general_ref *getGeneralRef(df::building *building, df::general_ref_type type);
DFHACK_EXPORT df::specific_ref *getSpecificRef(df::building *building, df::specific_ref_type type);
/** /**
* Sets the owner unit for the building. * Sets the owner unit for the building.
*/ */

@ -36,55 +36,55 @@ distribution.
namespace DFHack namespace DFHack
{ {
// SDL stuff // SDL stuff
typedef signed short SINT16; typedef signed short SINT16;
typedef struct typedef struct
{ {
int16_t x, y; int16_t x, y;
uint16_t w, h; uint16_t w, h;
} DFSDL_Rect; } DFSDL_Rect;
typedef struct typedef struct
{ {
uint32_t flags; uint32_t flags;
void* format; // PixelFormat* void* format; // PixelFormat*
int w, h; int w, h;
int pitch; int pitch;
void* pixels; void* pixels;
void* userdata; // as far as i could see DF doesnt use this void* userdata; // as far as i could see DF doesnt use this
int locked; int locked;
void* lock_data; void* lock_data;
DFSDL_Rect clip_rect; DFSDL_Rect clip_rect;
void* map; void* map;
int refcount; int refcount;
} DFSDL_Surface; } DFSDL_Surface;
// ========= // =========
struct DFTileSurface struct DFTileSurface
{ {
bool paintOver; // draw over original tile? bool paintOver; // draw over original tile?
DFSDL_Surface* surface; // from where it should be drawn DFSDL_Surface* surface; // from where it should be drawn
DFSDL_Rect* rect; // from which coords (NULL to draw whole surface) DFSDL_Rect* rect; // from which coords (NULL to draw whole surface)
DFSDL_Rect* dstResize; // if not NULL dst rect will be resized (x/y/w/h will be added to original dst) DFSDL_Rect* dstResize; // if not NULL dst rect will be resized (x/y/w/h will be added to original dst)
}; };
class DFHACK_EXPORT Graphic : public Module class DFHACK_EXPORT Graphic : public Module
{ {
public: public:
Graphic(); Graphic();
~Graphic(); ~Graphic();
bool Finish() bool Finish()
{ {
return true; return true;
} }
bool Register(DFTileSurface* (*func)(int,int)); bool Register(DFTileSurface* (*func)(int,int));
bool Unregister(DFTileSurface* (*func)(int,int)); bool Unregister(DFTileSurface* (*func)(int,int));
DFTileSurface* Call(int x, int y); DFTileSurface* Call(int x, int y);
private: private:
struct Private; struct Private;
Private *d; Private *d;
}; };
} }

@ -29,6 +29,8 @@ distribution.
#include "ColorText.h" #include "ColorText.h"
#include <string> #include <string>
#include "Types.h"
#include "DataDefs.h" #include "DataDefs.h"
#include "df/init.h" #include "df/init.h"
#include "df/ui.h" #include "df/ui.h"
@ -116,6 +118,9 @@ namespace DFHack
int map_x1, map_x2, menu_x1, menu_x2, area_x1, area_x2; int map_x1, map_x2, menu_x1, menu_x2, area_x1, area_x2;
int y1, y2; int y1, y2;
bool menu_on, area_on, menu_forced; bool menu_on, area_on, menu_forced;
rect2d map() { return mkrect_xy(map_x1, y1, map_x2, y2); }
rect2d menu() { return mkrect_xy(menu_x1, y1, menu_x2, y2); }
}; };
DFHACK_EXPORT DwarfmodeDims getDwarfmodeViewDims(); DFHACK_EXPORT DwarfmodeDims getDwarfmodeViewDims();

@ -151,6 +151,11 @@ DFHACK_EXPORT df::item *getContainer(df::item *item);
/// which items does it contain? /// which items does it contain?
DFHACK_EXPORT void getContainedItems(df::item *item, /*output*/ std::vector<df::item*> *items); DFHACK_EXPORT void getContainedItems(df::item *item, /*output*/ std::vector<df::item*> *items);
/// which building holds it?
DFHACK_EXPORT df::building *getHolderBuilding(df::item *item);
/// which unit holds it?
DFHACK_EXPORT df::unit *getHolderUnit(df::item *item);
/// Returns the true position of the item. /// Returns the true position of the item.
DFHACK_EXPORT df::coord getPosition(df::item *item); DFHACK_EXPORT df::coord getPosition(df::item *item);
@ -161,7 +166,7 @@ DFHACK_EXPORT bool moveToGround(MapExtras::MapCache &mc, df::item *item, df::coo
DFHACK_EXPORT bool moveToContainer(MapExtras::MapCache &mc, df::item *item, df::item *container); DFHACK_EXPORT bool moveToContainer(MapExtras::MapCache &mc, df::item *item, df::item *container);
DFHACK_EXPORT bool moveToBuilding(MapExtras::MapCache &mc, df::item *item, df::building_actual *building,int16_t use_mode); DFHACK_EXPORT bool moveToBuilding(MapExtras::MapCache &mc, df::item *item, df::building_actual *building,int16_t use_mode);
DFHACK_EXPORT bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *unit, DFHACK_EXPORT bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *unit,
df::unit_inventory_item::T_mode mode = df::unit_inventory_item::Carried, int body_part = -1); df::unit_inventory_item::T_mode mode = df::unit_inventory_item::Hauled, int body_part = -1);
/// Makes the item removed and marked for garbage collection /// Makes the item removed and marked for garbage collection
DFHACK_EXPORT bool remove(MapExtras::MapCache &mc, df::item *item, bool no_uncat = false); DFHACK_EXPORT bool remove(MapExtras::MapCache &mc, df::item *item, bool no_uncat = false);

@ -28,6 +28,8 @@ distribution.
#include "Export.h" #include "Export.h"
#include "Module.h" #include "Module.h"
#include "Types.h"
#include <ostream> #include <ostream>
#include "DataDefs.h" #include "DataDefs.h"
@ -55,6 +57,9 @@ namespace DFHack
DFHACK_EXPORT void printItemDetails(color_ostream &out, df::job_item *item, int idx); DFHACK_EXPORT void printItemDetails(color_ostream &out, df::job_item *item, int idx);
DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job); DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job);
DFHACK_EXPORT df::general_ref *getGeneralRef(df::job *job, df::general_ref_type type);
DFHACK_EXPORT df::specific_ref *getSpecificRef(df::job *job, df::specific_ref_type type);
DFHACK_EXPORT df::building *getHolder(df::job *job); DFHACK_EXPORT df::building *getHolder(df::job *job);
DFHACK_EXPORT df::unit *getWorker(df::job *job); DFHACK_EXPORT df::unit *getWorker(df::job *job);

@ -111,8 +111,8 @@ public:
{ {
if (!basemats) init_tiles(true); if (!basemats) init_tiles(true);
return t_matpair( return t_matpair(
index_tile<int16_t>(basemats->mattype,p), index_tile<int16_t>(basemats->mat_type,p),
index_tile<int16_t>(basemats->matindex,p) index_tile<int16_t>(basemats->mat_index,p)
); );
} }
bool isVeinAt(df::coord2d p) bool isVeinAt(df::coord2d p)
@ -151,8 +151,8 @@ public:
if (!basemats) init_tiles(true); if (!basemats) init_tiles(true);
if (tiles->con_info) if (tiles->con_info)
return t_matpair( return t_matpair(
index_tile<int16_t>(tiles->con_info->mattype,p), index_tile<int16_t>(tiles->con_info->mat_type,p),
index_tile<int16_t>(tiles->con_info->matindex,p) index_tile<int16_t>(tiles->con_info->mat_index,p)
); );
return baseMaterialAt(p); return baseMaterialAt(p);
} }
@ -284,8 +284,8 @@ private:
struct ConInfo { struct ConInfo {
df::tile_bitmask constructed; df::tile_bitmask constructed;
df::tiletype tiles[16][16]; df::tiletype tiles[16][16];
t_blockmaterials mattype; t_blockmaterials mat_type;
t_blockmaterials matindex; t_blockmaterials mat_index;
}; };
struct TileInfo { struct TileInfo {
df::tile_bitmask frozen; df::tile_bitmask frozen;
@ -304,8 +304,8 @@ private:
}; };
struct BasematInfo { struct BasematInfo {
df::tile_bitmask dirty; df::tile_bitmask dirty;
t_blockmaterials mattype; t_blockmaterials mat_type;
t_blockmaterials matindex; t_blockmaterials mat_index;
t_blockmaterials layermat; t_blockmaterials layermat;
BasematInfo(); BasematInfo();

@ -338,10 +338,10 @@ namespace DFHack
*/ */
struct t_material struct t_material
{ {
t_itemType itemType; t_itemType item_type;
t_itemSubtype subType; t_itemSubtype item_subtype;
t_materialType material; t_materialType mat_type;
t_materialIndex index; t_materialIndex mat_index;
uint32_t flags; uint32_t flags;
}; };
/** /**

@ -27,7 +27,10 @@ distribution.
#include "Module.h" #include "Module.h"
#include "BitArray.h" #include "BitArray.h"
#include "ColorText.h" #include "ColorText.h"
#include "Types.h"
#include <string> #include <string>
#include <set>
#include "DataDefs.h" #include "DataDefs.h"
#include "df/graphic.h" #include "df/graphic.h"
@ -50,6 +53,8 @@ namespace DFHack
{ {
class Core; class Core;
typedef std::set<df::interface_key> interface_key_set;
/** /**
* The Screen module * The Screen module
* \ingroup grp_modules * \ingroup grp_modules
@ -76,6 +81,8 @@ namespace DFHack
bool valid() const { return tile >= 0; } bool valid() const { return tile >= 0; }
bool empty() const { return ch == 0 && tile == 0; } bool empty() const { return ch == 0 && tile == 0; }
// NOTE: LuaApi.cpp assumes this struct is plain data and has empty destructor
Pen(char ch = 0, int8_t fg = 7, int8_t bg = 0, int tile = 0, bool color_tile = false) Pen(char ch = 0, int8_t fg = 7, int8_t bg = 0, int tile = 0, bool color_tile = false)
: ch(ch), fg(fg&7), bg(bg), bold(!!(fg&8)), : ch(ch), fg(fg&7), bg(bg), bold(!!(fg&8)),
tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0) tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0)
@ -92,11 +99,67 @@ namespace DFHack
: ch(ch), fg(fg), bg(bg), bold(bold), : ch(ch), fg(fg), bg(bg), bold(bold),
tile(tile), tile_mode(TileColor), tile_fg(tile_fg), tile_bg(tile_bg) tile(tile), tile_mode(TileColor), tile_fg(tile_fg), tile_bg(tile_bg)
{} {}
void adjust(int8_t nfg) { fg = nfg&7; bold = !!(nfg&8); }
void adjust(int8_t nfg, bool nbold) { fg = nfg; bold = nbold; }
void adjust(int8_t nfg, int8_t nbg) { adjust(nfg); bg = nbg; }
void adjust(int8_t nfg, bool nbold, int8_t nbg) { adjust(nfg, nbold); bg = nbg; }
Pen color(int8_t nfg) const { Pen cp(*this); cp.adjust(nfg); return cp; }
Pen color(int8_t nfg, bool nbold) const { Pen cp(*this); cp.adjust(nfg, nbold); return cp; }
Pen color(int8_t nfg, int8_t nbg) const { Pen cp(*this); cp.adjust(nfg, nbg); return cp; }
Pen color(int8_t nfg, bool nbold, int8_t nbg) const { Pen cp(*this); cp.adjust(nfg, nbold, nbg); return cp; }
Pen chtile(char ch) { Pen cp(*this); cp.ch = ch; return cp; }
Pen chtile(char ch, int tile) { Pen cp(*this); cp.ch = ch; cp.tile = tile; return cp; }
};
struct DFHACK_EXPORT ViewRect {
rect2d view, clip;
ViewRect(rect2d area) : view(area), clip(area) {}
ViewRect(rect2d area, rect2d clip) : view(area), clip(clip) {}
bool isDefunct() const {
return clip.first.x > clip.second.x || clip.first.y > clip.second.y;
}
int width() const { return view.second.x-view.first.x+1; }
int height() const { return view.second.y-view.first.y+1; }
df::coord2d local(df::coord2d pos) const {
return df::coord2d(pos.x - view.first.x, pos.y - view.first.y);
}
df::coord2d global(df::coord2d pos) const {
return df::coord2d(pos.x + view.first.x, pos.y + view.first.y);
}
df::coord2d global(int x, int y) const {
return df::coord2d(x + view.first.x, y + view.first.y);
}
bool inClipGlobal(int x, int y) const {
return x >= clip.first.x && x <= clip.second.x &&
y >= clip.first.y && y <= clip.second.y;
}
bool inClipGlobal(df::coord2d pos) const {
return inClipGlobal(pos.x, pos.y);
}
bool inClipLocal(int x, int y) const {
return inClipGlobal(x + view.first.x, y + view.first.y);
}
bool inClipLocal(df::coord2d pos) const {
return inClipLocal(pos.x, pos.y);
}
ViewRect viewport(rect2d area) const {
rect2d nview(global(area.first), global(area.second));
return ViewRect(nview, intersect(nview, clip));
}
}; };
DFHACK_EXPORT df::coord2d getMousePos(); DFHACK_EXPORT df::coord2d getMousePos();
DFHACK_EXPORT df::coord2d getWindowSize(); DFHACK_EXPORT df::coord2d getWindowSize();
inline rect2d getScreenRect() {
return rect2d(df::coord2d(0,0), getWindowSize()-df::coord2d(1,1));
}
/// Returns the state of [GRAPHICS:YES/NO] /// Returns the state of [GRAPHICS:YES/NO]
DFHACK_EXPORT bool inGraphicsMode(); DFHACK_EXPORT bool inGraphicsMode();
@ -131,6 +194,77 @@ namespace DFHack
/// Retrieve the string representation of the bound key. /// Retrieve the string representation of the bound key.
DFHACK_EXPORT std::string getKeyDisplay(df::interface_key key); DFHACK_EXPORT std::string getKeyDisplay(df::interface_key key);
/// A painter class that implements a clipping area and cursor/pen state
struct DFHACK_EXPORT Painter : ViewRect {
df::coord2d gcursor;
Pen cur_pen, cur_key_pen;
static const Pen default_pen;
static const Pen default_key_pen;
Painter(const ViewRect &area, const Pen &pen = default_pen, const Pen &kpen = default_key_pen)
: ViewRect(area), gcursor(area.view.first), cur_pen(pen), cur_key_pen(kpen)
{}
df::coord2d cursor() const { return local(gcursor); }
int cursorX() const { return gcursor.x - view.first.x; }
int cursorY() const { return gcursor.y - view.first.y; }
bool isValidPos() const { return inClipGlobal(gcursor); }
Painter viewport(rect2d area) const {
return Painter(ViewRect::viewport(area), cur_pen, cur_key_pen);
}
Painter &seek(df::coord2d pos) { gcursor = global(pos); return *this; }
Painter &seek(int x, int y) { gcursor = global(x,y); return *this; }
Painter &advance(int dx) { gcursor.x += dx; return *this; }
Painter &advance(int dx, int dy) { gcursor.x += dx; gcursor.y += dy; return *this; }
Painter &newline(int dx = 0) { gcursor.y++; gcursor.x = view.first.x + dx; return *this; }
const Pen &pen() const { return cur_pen; }
Painter &pen(const Pen &np) { cur_pen = np; return *this; }
Painter &pen(int8_t fg) { cur_pen.adjust(fg); return *this; }
const Pen &key_pen() const { return cur_key_pen; }
Painter &key_pen(const Pen &np) { cur_key_pen = np; return *this; }
Painter &key_pen(int8_t fg) { cur_key_pen.adjust(fg); return *this; }
Painter &clear() {
fillRect(Pen(' ',0,0,false), clip.first.x, clip.first.y, clip.second.x, clip.second.y);
return *this;
}
Painter &fill(const rect2d &area, const Pen &pen) {
rect2d irect = intersect(area, clip);
fillRect(pen, irect.first.x, irect.first.y, irect.second.x, irect.second.y);
return *this;
}
Painter &fill(const rect2d &area) { return fill(area, cur_pen); }
Painter &tile(const Pen &pen) {
if (isValidPos()) paintTile(pen, gcursor.x, gcursor.y);
return advance(1);
}
Painter &tile() { return tile(cur_pen); }
Painter &tile(char ch) { return tile(cur_pen.chtile(ch)); }
Painter &tile(char ch, int tileid) { return tile(cur_pen.chtile(ch, tileid)); }
Painter &string(const std::string &str, const Pen &pen) {
do_paint_string(str, pen); return advance(str.size());
}
Painter &string(const std::string &str) { return string(str, cur_pen); }
Painter &string(const std::string &str, int8_t fg) { return string(str, cur_pen.color(fg)); }
Painter &key(df::interface_key kc, const Pen &pen) {
return string(getKeyDisplay(kc), pen);
}
Painter &key(df::interface_key kc) { return key(kc, cur_key_pen); }
private:
void do_paint_string(const std::string &str, const Pen &pen);
};
} }
class DFHACK_EXPORT dfhack_viewscreen : public df::viewscreen { class DFHACK_EXPORT dfhack_viewscreen : public df::viewscreen {

@ -205,6 +205,9 @@ DFHACK_EXPORT void CopyNameTo(df::unit *creature, df::language_name * target);
/// Returns the true position of the unit (non-trivial in case of caged). /// Returns the true position of the unit (non-trivial in case of caged).
DFHACK_EXPORT df::coord getPosition(df::unit *unit); DFHACK_EXPORT df::coord getPosition(df::unit *unit);
DFHACK_EXPORT df::general_ref *getGeneralRef(df::unit *unit, df::general_ref_type type);
DFHACK_EXPORT df::specific_ref *getSpecificRef(df::unit *unit, df::specific_ref_type type);
DFHACK_EXPORT df::item *getContainer(df::unit *unit); DFHACK_EXPORT df::item *getContainer(df::unit *unit);
DFHACK_EXPORT void setNickname(df::unit *unit, std::string nick); DFHACK_EXPORT void setNickname(df::unit *unit, std::string nick);
@ -235,6 +238,8 @@ DFHACK_EXPORT double getAge(df::unit *unit, bool true_age = false);
DFHACK_EXPORT int getNominalSkill(df::unit *unit, df::job_skill skill_id, bool use_rust = false); DFHACK_EXPORT int getNominalSkill(df::unit *unit, df::job_skill skill_id, bool use_rust = false);
DFHACK_EXPORT int getEffectiveSkill(df::unit *unit, df::job_skill skill_id); DFHACK_EXPORT int getEffectiveSkill(df::unit *unit, df::job_skill skill_id);
DFHACK_EXPORT int getExperience(df::unit *unit, df::job_skill skill_id, bool total = false);
DFHACK_EXPORT int computeMovementSpeed(df::unit *unit); DFHACK_EXPORT int computeMovementSpeed(df::unit *unit);
struct NoblePosition { struct NoblePosition {

@ -81,6 +81,55 @@ namespace DFHack
int &ival(int i) { return int_values[i]; } int &ival(int i) { return int_values[i]; }
int ival(int i) const { return int_values[i]; } int ival(int i) const { return int_values[i]; }
// Pack binary data into string field.
// Since DF serialization chokes on NUL bytes,
// use bit magic to ensure none of the bytes is 0.
// Choose the lowest bit for padding so that
// sign-extend can be used normally.
size_t data_size() const { return str_value->size(); }
bool check_data(size_t off, size_t sz = 1) {
return (str_value->size() >= off+sz);
}
void ensure_data(size_t off, size_t sz = 0) {
if (str_value->size() < off+sz) str_value->resize(off+sz, '\x01');
}
uint8_t *pdata(size_t off) { return (uint8_t*)&(*str_value)[off]; }
static const size_t int7_size = 1;
uint8_t get_uint7(size_t off) {
uint8_t *p = pdata(off);
return p[0]>>1;
}
int8_t get_int7(size_t off) {
uint8_t *p = pdata(off);
return int8_t(p[0])>>1;
}
void set_uint7(size_t off, uint8_t val) {
uint8_t *p = pdata(off);
p[0] = uint8_t((val<<1) | 1);
}
void set_int7(size_t off, int8_t val) { set_uint7(off, val); }
static const size_t int28_size = 4;
uint32_t get_uint28(size_t off) {
uint8_t *p = pdata(off);
return (p[0]>>1) | ((p[1]&~1U)<<6) | ((p[2]&~1U)<<13) | ((p[3]&~1U)<<20);
}
int32_t get_int28(size_t off) {
uint8_t *p = pdata(off);
return (p[0]>>1) | ((p[1]&~1U)<<6) | ((p[2]&~1U)<<13) | ((int8_t(p[3])&~1)<<20);
}
void set_uint28(size_t off, uint32_t val) {
uint8_t *p = pdata(off);
p[0] = uint8_t((val<<1) | 1);
p[1] = uint8_t((val>>6) | 1);
p[2] = uint8_t((val>>13) | 1);
p[3] = uint8_t((val>>20) | 1);
}
void set_int28(size_t off, int32_t val) { set_uint28(off, val); }
PersistentDataItem() : id(0), str_value(0), int_values(0) {} PersistentDataItem() : id(0), str_value(0), int_values(0) {}
PersistentDataItem(int id, const std::string &key, std::string *sv, int *iv) PersistentDataItem(int id, const std::string &key, std::string *sv, int *iv)
: id(id), key_value(key), str_value(sv), int_values(iv) {} : id(id), key_value(key), str_value(sv), int_values(iv) {}

@ -0,0 +1,121 @@
-- Simple binary patch with IDA dif file support.
local function load_patch(name)
local filename = name
if not string.match(filename, '[./\\]') then
filename = dfhack.getHackPath()..'/patches/'..dfhack.getDFVersion()..'/'..name..'.dif'
end
local file, err = io.open(filename, 'r')
if not file then
if string.match(err, ': No such file or directory') then
return nil, 'patch not found'
end
end
local old_bytes = {}
local new_bytes = {}
for line in file:lines() do
if string.match(line, '^%x+:') then
local offset, oldv, newv = string.match(line, '^(%x+):%s*(%x+)%s+(%x+)%s*$')
if not offset then
file:close()
return nil, 'could not parse: '..line
end
offset, oldv, newv = tonumber(offset,16), tonumber(oldv,16), tonumber(newv,16)
if oldv > 255 or newv > 255 then
file:close()
return nil, 'invalid byte values: '..line
end
old_bytes[offset] = oldv
new_bytes[offset] = newv
end
end
return { name = name, old_bytes = old_bytes, new_bytes = new_bytes }
end
local function rebase_table(input)
local output = {}
local base = dfhack.internal.getImageBase()
for k,v in pairs(input) do
local offset = dfhack.internal.adjustOffset(k)
if not offset then
return nil, string.format('invalid offset: %x', k)
end
output[base + offset] = v
end
return output
end
local function rebase_patch(patch)
local nold, err = rebase_table(patch.old_bytes)
if not nold then return nil, err end
local nnew, err = rebase_table(patch.new_bytes)
if not nnew then return nil, err end
return { name = patch.name, old_bytes = nold, new_bytes = nnew }
end
BinaryPatch = defclass(BinaryPatch)
BinaryPatch.ATTRS {
name = DEFAULT_NIL,
old_bytes = DEFAULT_NIL,
new_bytes = DEFAULT_NIL,
}
function load_dif_file(name)
local patch, err = load_patch(name)
if not patch then return nil, err end
local rpatch, err = rebase_patch(patch)
if not rpatch then return nil, err end
return BinaryPatch(rpatch)
end
function BinaryPatch:status()
local old_ok, err, addr = dfhack.internal.patchBytes({}, self.old_bytes)
if old_ok then
return 'removed'
elseif dfhack.internal.patchBytes({}, self.new_bytes) then
return 'applied'
else
return 'conflict', addr
end
end
function BinaryPatch:isApplied()
return dfhack.internal.patchBytes({}, self.new_bytes)
end
function BinaryPatch:apply()
local ok, err, addr = dfhack.internal.patchBytes(self.new_bytes, self.old_bytes)
if ok then
return true, 'applied the patch'
elseif dfhack.internal.patchBytes({}, self.new_bytes) then
return true, 'patch is already applied'
else
return false, string.format('conflict at address %x', addr)
end
end
function BinaryPatch:isRemoved()
return dfhack.internal.patchBytes({}, self.old_bytes)
end
function BinaryPatch:remove()
local ok, err, addr = dfhack.internal.patchBytes(self.old_bytes, self.new_bytes)
if ok then
return true, 'removed the patch'
elseif dfhack.internal.patchBytes({}, self.old_bytes) then
return true, 'patch is already removed'
else
return false, string.format('conflict at address %x', addr)
end
end
return _ENV

@ -65,10 +65,14 @@ end
local function apply_attrs(obj, attrs, init_table) local function apply_attrs(obj, attrs, init_table)
for k,v in pairs(attrs) do for k,v in pairs(attrs) do
if v == DEFAULT_NIL then local init_v = init_table[k]
v = nil if init_v ~= nil then
obj[k] = init_v
elseif v == DEFAULT_NIL then
obj[k] = nil
else
obj[k] = v
end end
obj[k] = init_table[k] or v
end end
end end

@ -6,7 +6,15 @@ local dscreen = dfhack.screen
USE_GRAPHICS = dscreen.inGraphicsMode() USE_GRAPHICS = dscreen.inGraphicsMode()
CLEAR_PEN = {ch=32,fg=0,bg=0} local to_pen = dfhack.pen.parse
CLEAR_PEN = to_pen{ch=32,fg=0,bg=0}
local FAKE_INPUT_KEYS = {
_MOUSE_L = true,
_MOUSE_R = true,
_STRING = true,
}
function simulateInput(screen,...) function simulateInput(screen,...)
local keys = {} local keys = {}
@ -14,7 +22,7 @@ function simulateInput(screen,...)
local kv = arg local kv = arg
if type(arg) == 'string' then if type(arg) == 'string' then
kv = df.interface_key[arg] kv = df.interface_key[arg]
if kv == nil then if kv == nil and not FAKE_INPUT_KEYS[arg] then
error('Invalid keycode: '..arg) error('Invalid keycode: '..arg)
end end
end end
@ -104,10 +112,14 @@ function inset_frame(rect, inset, gap)
return mkdims_xy(rect.x1+l+gap, rect.y1+t+gap, rect.x2-r-gap, rect.y2-b-gap) return mkdims_xy(rect.x1+l+gap, rect.y1+t+gap, rect.x2-r-gap, rect.y2-b-gap)
end end
function compute_frame_body(wavail, havail, spec, inset, gap) function compute_frame_body(wavail, havail, spec, inset, gap, inner_frame)
gap = gap or 0 gap = gap or 0
local l,t,r,b = parse_inset(inset) local l,t,r,b = parse_inset(inset)
local rect = compute_frame_rect(wavail, havail, spec, gap*2+l+r, gap*2+t+b) local xgap,ygap = 0,0
if inner_frame then
xgap,ygap = gap*2+l+r, gap*2+t+b
end
local rect = compute_frame_rect(wavail, havail, spec, xgap, ygap)
local body = mkdims_xy(rect.x1+l+gap, rect.y1+t+gap, rect.x2-r-gap, rect.y2-b-gap) local body = mkdims_xy(rect.x1+l+gap, rect.y1+t+gap, rect.x2-r-gap, rect.y2-b-gap)
return rect, body return rect, body
end end
@ -116,16 +128,6 @@ function blink_visible(delay)
return math.floor(dfhack.getTickCount()/delay) % 2 == 0 return math.floor(dfhack.getTickCount()/delay) % 2 == 0
end end
function to_pen(default, pen, bg, bold)
if pen == nil then
return default or {}
elseif type(pen) ~= 'table' then
return {fg=pen,bg=bg,bold=bold}
else
return pen
end
end
function getKeyDisplay(code) function getKeyDisplay(code)
if type(code) == 'string' then if type(code) == 'string' then
code = df.interface_key[code] code = df.interface_key[code]
@ -215,7 +217,8 @@ Painter = defclass(Painter, ViewRect)
function Painter:init(args) function Painter:init(args)
self.x = self.x1 self.x = self.x1
self.y = self.y1 self.y = self.y1
self.cur_pen = to_pen(nil, args.pen or COLOR_GREY) self.cur_pen = to_pen(args.pen or COLOR_GREY)
self.cur_key_pen = to_pen(args.key_pen or COLOR_LIGHTGREEN)
end end
function Painter.new(rect, pen) function Painter.new(rect, pen)
@ -241,6 +244,7 @@ end
function Painter:viewport(x,y,w,h) function Painter:viewport(x,y,w,h)
local vp = ViewRect.viewport(x,y,w,h) local vp = ViewRect.viewport(x,y,w,h)
vp.cur_pen = self.cur_pen vp.cur_pen = self.cur_pen
vp.cur_key_pen = self.cur_key_pen
return mkinstance(Painter, vp):seek(0,0) return mkinstance(Painter, vp):seek(0,0)
end end
@ -280,10 +284,12 @@ function Painter:pen(pen,...)
end end
function Painter:color(fg,bold,bg) function Painter:color(fg,bold,bg)
self.cur_pen = copyall(self.cur_pen) self.cur_pen = to_pen(self.cur_pen, fg, bg, bold)
self.cur_pen.fg = fg return self
self.cur_pen.bold = bold end
if bg then self.cur_pen.bg = bg end
function Painter:key_pen(pen,...)
self.cur_key_pen = to_pen(self.cur_key_pen, pen, ...)
return self return self
end end
@ -339,10 +345,10 @@ function Painter:string(text,pen,...)
return self:advance(#text, nil) return self:advance(#text, nil)
end end
function Painter:key(code,pen,bg,...) function Painter:key(code,pen,...)
return self:string( return self:string(
getKeyDisplay(code), getKeyDisplay(code),
pen or COLOR_LIGHTGREEN, bg or self.cur_pen.bg, ... to_pen(self.cur_key_pen, pen, ...)
) )
end end
@ -513,17 +519,18 @@ function Screen:sendInputToParent(...)
end end
end end
function Screen:show(below) function Screen:show(parent)
if self._native then if self._native then
error("This screen is already on display") error("This screen is already on display")
end end
self:onAboutToShow(below) parent = parent or dfhack.gui.getCurViewscreen(true)
if not dscreen.show(self, below) then self:onAboutToShow(parent)
if not dscreen.show(self, parent.child) then
error('Could not show screen') error('Could not show screen')
end end
end end
function Screen:onAboutToShow() function Screen:onAboutToShow(parent)
end end
function Screen:onShow() function Screen:onShow()
@ -556,28 +563,28 @@ end
-- Plain grey-colored frame. -- Plain grey-colored frame.
GREY_FRAME = { GREY_FRAME = {
frame_pen = { ch = ' ', fg = COLOR_BLACK, bg = COLOR_GREY }, frame_pen = to_pen{ ch = ' ', fg = COLOR_BLACK, bg = COLOR_GREY },
title_pen = { fg = COLOR_BLACK, bg = COLOR_WHITE }, title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_WHITE },
signature_pen = { fg = COLOR_BLACK, bg = COLOR_GREY }, signature_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY },
} }
-- The usual boundary used by the DF screens. Often has fancy pattern in tilesets. -- The usual boundary used by the DF screens. Often has fancy pattern in tilesets.
BOUNDARY_FRAME = { BOUNDARY_FRAME = {
frame_pen = { ch = 0xDB, fg = COLOR_DARKGREY, bg = COLOR_BLACK }, frame_pen = to_pen{ ch = 0xDB, fg = COLOR_DARKGREY, bg = COLOR_BLACK },
title_pen = { fg = COLOR_BLACK, bg = COLOR_GREY }, title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY },
signature_pen = { fg = COLOR_BLACK, bg = COLOR_DARKGREY }, signature_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_DARKGREY },
} }
GREY_LINE_FRAME = { GREY_LINE_FRAME = {
frame_pen = { ch = 206, fg = COLOR_GREY, bg = COLOR_BLACK }, frame_pen = to_pen{ ch = 206, fg = COLOR_GREY, bg = COLOR_BLACK },
h_frame_pen = { ch = 205, fg = COLOR_GREY, bg = COLOR_BLACK }, h_frame_pen = to_pen{ ch = 205, fg = COLOR_GREY, bg = COLOR_BLACK },
v_frame_pen = { ch = 186, fg = COLOR_GREY, bg = COLOR_BLACK }, v_frame_pen = to_pen{ ch = 186, fg = COLOR_GREY, bg = COLOR_BLACK },
lt_frame_pen = { ch = 201, fg = COLOR_GREY, bg = COLOR_BLACK }, lt_frame_pen = to_pen{ ch = 201, fg = COLOR_GREY, bg = COLOR_BLACK },
lb_frame_pen = { ch = 200, fg = COLOR_GREY, bg = COLOR_BLACK }, lb_frame_pen = to_pen{ ch = 200, fg = COLOR_GREY, bg = COLOR_BLACK },
rt_frame_pen = { ch = 187, fg = COLOR_GREY, bg = COLOR_BLACK }, rt_frame_pen = to_pen{ ch = 187, fg = COLOR_GREY, bg = COLOR_BLACK },
rb_frame_pen = { ch = 188, fg = COLOR_GREY, bg = COLOR_BLACK }, rb_frame_pen = to_pen{ ch = 188, fg = COLOR_GREY, bg = COLOR_BLACK },
title_pen = { fg = COLOR_BLACK, bg = COLOR_GREY }, title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY },
signature_pen = { fg = COLOR_DARKGREY, bg = COLOR_BLACK }, signature_pen = to_pen{ fg = COLOR_DARKGREY, bg = COLOR_BLACK },
} }
function paint_frame(x1,y1,x2,y2,style,title) function paint_frame(x1,y1,x2,y2,style,title)
@ -620,7 +627,7 @@ end
function FramedScreen:computeFrame(parent_rect) function FramedScreen:computeFrame(parent_rect)
local sw, sh = parent_rect.width, parent_rect.height local sw, sh = parent_rect.width, parent_rect.height
local fw, fh = self:getWantedFrameSize(parent_rect) local fw, fh = self:getWantedFrameSize(parent_rect)
return compute_frame_body(sw, sh, { w = fw, h = fh }, self.frame_inset, 1) return compute_frame_body(sw, sh, { w = fw, h = fh }, self.frame_inset, 1, true)
end end
function FramedScreen:onRenderFrame(dc, rect) function FramedScreen:onRenderFrame(dc, rect)

@ -152,7 +152,9 @@ ListBox.ATTRS{
with_filter = false, with_filter = false,
cursor_pen = DEFAULT_NIL, cursor_pen = DEFAULT_NIL,
select_pen = DEFAULT_NIL, select_pen = DEFAULT_NIL,
on_select = DEFAULT_NIL on_select = DEFAULT_NIL,
on_select2 = DEFAULT_NIL,
select2_hint = DEFAULT_NIL,
} }
function ListBox:preinit(info) function ListBox:preinit(info)
@ -160,14 +162,24 @@ function ListBox:preinit(info)
end end
function ListBox:init(info) function ListBox:init(info)
local spen = gui.to_pen(COLOR_CYAN, self.select_pen, nil, false) local spen = dfhack.pen.parse(COLOR_CYAN, self.select_pen, nil, false)
local cpen = gui.to_pen(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true) local cpen = dfhack.pen.parse(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true)
local list_widget = widgets.List local list_widget = widgets.List
if self.with_filter then if self.with_filter then
list_widget = widgets.FilteredList list_widget = widgets.FilteredList
end end
local on_submit2
if self.select2_hint or self.on_select2 then
on_submit2 = function(sel, obj)
self:dismiss()
if self.on_select2 then self.on_select2(sel, obj) end
local cb = obj.on_select2
if cb then cb(obj, sel) end
end
end
self:addviews{ self:addviews{
list_widget{ list_widget{
view_id = 'list', view_id = 'list',
@ -182,11 +194,19 @@ function ListBox:init(info)
local cb = obj.on_select or obj[2] local cb = obj.on_select or obj[2]
if cb then cb(obj, sel) end if cb then cb(obj, sel) end
end, end,
on_submit2 = on_submit2,
frame = { l = 0, r = 0 }, frame = { l = 0, r = 0 },
} }
} }
end end
function ListBox:onRenderFrame(dc,rect)
ListBox.super.onRenderFrame(self,dc,rect)
if self.select2_hint then
dc:seek(rect.x1+2,rect.y2):key('SEC_SELECT'):string(': '..self.select2_hint,COLOR_DARKGREY)
end
end
function ListBox:getWantedFrameSize() function ListBox:getWantedFrameSize()
local mw, mh = InputBox.super.getWantedFrameSize(self) local mw, mh = InputBox.super.getWantedFrameSize(self)
local list = self.subviews.list local list = self.subviews.list

@ -373,10 +373,8 @@ function DwarfOverlay:simulateCursorMovement(keys, anchor)
end end
end end
function DwarfOverlay:onAboutToShow(below) function DwarfOverlay:onAboutToShow(parent)
local screen = dfhack.gui.getCurViewscreen() if not df.viewscreen_dwarfmodest:is_instance(parent) then
if below then screen = below.parent end
if not df.viewscreen_dwarfmodest:is_instance(screen) then
error("This screen requires the main dwarfmode view") error("This screen requires the main dwarfmode view")
end end
end end

@ -23,6 +23,7 @@ MaterialDialog.ATTRS{
frame_title = 'Select Material', frame_title = 'Select Material',
-- new attrs -- new attrs
none_caption = 'none', none_caption = 'none',
hide_none = false,
use_inorganic = true, use_inorganic = true,
use_creature = true, use_creature = true,
use_plant = true, use_plant = true,
@ -68,7 +69,7 @@ function MaterialDialog:init(info)
end end
function MaterialDialog:getWantedFrameSize(rect) function MaterialDialog:getWantedFrameSize(rect)
return math.max(40, #self.prompt), math.min(28, rect.height-8) return math.max(self.frame_width or 40, #self.prompt), math.min(28, rect.height-8)
end end
function MaterialDialog:onDestroy() function MaterialDialog:onDestroy()
@ -78,9 +79,10 @@ function MaterialDialog:onDestroy()
end end
function MaterialDialog:initBuiltinMode() function MaterialDialog:initBuiltinMode()
local choices = { local choices = {}
{ text = self.none_caption, mat_type = -1, mat_index = -1 }, if not self.hide_none then
} table.insert(choices, { text = self.none_caption, mat_type = -1, mat_index = -1 })
end
if self.use_inorganic then if self.use_inorganic then
table.insert(choices, { table.insert(choices, {
@ -281,9 +283,15 @@ function ItemTypeDialog(args)
args.with_filter = true args.with_filter = true
args.icon_width = 2 args.icon_width = 2
local choices = { { local choices = {}
icon = '?', text = args.none_caption or 'none', item_type = -1, item_subtype = -1
} } if not args.hide_none then
table.insert(choices, {
icon = '?', text = args.none_caption or 'none',
item_type = -1, item_subtype = -1
})
end
local filter = args.item_filter local filter = args.item_filter
for itype = 0,df.item_type._last_item do for itype = 0,df.item_type._last_item do

@ -60,6 +60,7 @@ Panel = defclass(Panel, Widget)
Panel.ATTRS { Panel.ATTRS {
on_render = DEFAULT_NIL, on_render = DEFAULT_NIL,
on_layout = DEFAULT_NIL,
} }
function Panel:init(args) function Panel:init(args)
@ -70,6 +71,10 @@ function Panel:onRenderBody(dc)
if self.on_render then self.on_render(dc) end if self.on_render then self.on_render(dc) end
end end
function Panel:postComputeFrame(body)
if self.on_layout then self.on_layout(body) end
end
----------- -----------
-- Pages -- -- Pages --
----------- -----------
@ -216,7 +221,7 @@ local function is_disabled(token)
(token.enabled ~= nil and not getval(token.enabled)) (token.enabled ~= nil and not getval(token.enabled))
end end
function render_text(obj,dc,x0,y0,pen,dpen) function render_text(obj,dc,x0,y0,pen,dpen,disabled)
local width = 0 local width = 0
for iline,line in ipairs(obj.text_lines) do for iline,line in ipairs(obj.text_lines) do
local x = 0 local x = 0
@ -242,20 +247,37 @@ function render_text(obj,dc,x0,y0,pen,dpen)
end end
if token.text or token.key then if token.text or token.key then
local text = getval(token.text) or '' local text = ''..(getval(token.text) or '')
local keypen local keypen
if dc then if dc then
if is_disabled(token) then local tpen = getval(token.pen)
dc:pen(getval(token.dpen) or dpen) if disabled or is_disabled(token) then
dc:pen(getval(token.dpen) or tpen or dpen)
keypen = COLOR_GREEN keypen = COLOR_GREEN
else else
dc:pen(getval(token.pen) or pen) dc:pen(tpen or pen)
keypen = COLOR_LIGHTGREEN keypen = COLOR_LIGHTGREEN
end end
end end
x = x + #text local width = getval(token.width)
local padstr
if width then
x = x + width
if #text > width then
text = string.sub(text,1,width)
else
if token.pad_char then
padstr = string.rep(token.pad_char,width-#text)
end
if dc and token.rjustify then
if padstr then dc:string(padstr) else dc:advance(width-#text) end
end
end
else
x = x + #text
end
if token.key then if token.key then
local keystr = gui.getKeyDisplay(token.key) local keystr = gui.getKeyDisplay(token.key)
@ -280,6 +302,10 @@ function render_text(obj,dc,x0,y0,pen,dpen)
dc:string(text) dc:string(text)
end end
end end
if width and dc and not token.rjustify then
if padstr then dc:string(padstr) else dc:advance(width-#text) end
end
end end
token.x2 = x token.x2 = x
@ -305,6 +331,8 @@ Label = defclass(Label, Widget)
Label.ATTRS{ Label.ATTRS{
text_pen = COLOR_WHITE, text_pen = COLOR_WHITE,
text_dpen = COLOR_DARKGREY, text_dpen = COLOR_DARKGREY,
disabled = DEFAULT_NIL,
enabled = DEFAULT_NIL,
auto_height = true, auto_height = true,
auto_width = false, auto_width = false,
} }
@ -346,11 +374,13 @@ function Label:getTextWidth()
end end
function Label:onRenderBody(dc) function Label:onRenderBody(dc)
render_text(self,dc,0,0,self.text_pen,self.text_dpen) render_text(self,dc,0,0,self.text_pen,self.text_dpen,is_disabled(self))
end end
function Label:onInput(keys) function Label:onInput(keys)
return check_text_keys(self, keys) if not is_disabled(self) then
return check_text_keys(self, keys)
end
end end
---------- ----------
@ -376,10 +406,10 @@ SECONDSCROLL = {
List.ATTRS{ List.ATTRS{
text_pen = COLOR_CYAN, text_pen = COLOR_CYAN,
cursor_pen = COLOR_LIGHTCYAN, cursor_pen = COLOR_LIGHTCYAN,
cursor_dpen = DEFAULT_NIL,
inactive_pen = DEFAULT_NIL, inactive_pen = DEFAULT_NIL,
on_select = DEFAULT_NIL, on_select = DEFAULT_NIL,
on_submit = DEFAULT_NIL, on_submit = DEFAULT_NIL,
on_submit2 = DEFAULT_NIL,
row_height = 1, row_height = 1,
scroll_keys = STANDARDSCROLL, scroll_keys = STANDARDSCROLL,
icon_width = DEFAULT_NIL, icon_width = DEFAULT_NIL,
@ -417,7 +447,9 @@ function List:getChoices()
end end
function List:getSelected() function List:getSelected()
return self.selected, self.choices[self.selected] if #self.choices > 0 then
return self.selected, self.choices[self.selected]
end
end end
function List:getContentWidth() function List:getContentWidth()
@ -482,38 +514,51 @@ function List:onRenderBody(dc)
local iend = math.min(#choices, top+self.page_size-1) local iend = math.min(#choices, top+self.page_size-1)
local iw = self.icon_width local iw = self.icon_width
local function paint_icon(icon, obj)
if type(icon) ~= 'string' then
dc:char(nil,icon)
else
if current then
dc:string(icon, obj.icon_pen or self.icon_pen or cur_pen)
else
dc:string(icon, obj.icon_pen or self.icon_pen or cur_dpen)
end
end
end
for i = top,iend do for i = top,iend do
local obj = choices[i] local obj = choices[i]
local current = (i == self.selected) local current = (i == self.selected)
local cur_pen = self.text_pen local cur_pen = self.cursor_pen
local cur_dpen = cur_pen local cur_dpen = self.text_pen
if current and active then if not self.active then
cur_pen = self.cursor_pen
cur_dpen = self.cursor_dpen or self.text_pen
elseif current then
cur_pen = self.inactive_pen or self.cursor_pen cur_pen = self.inactive_pen or self.cursor_pen
cur_dpen = self.inactive_pen or self.text_pen
end end
local y = (i - top)*self.row_height local y = (i - top)*self.row_height
local icon = getval(obj.icon)
if iw and obj.icon then if iw and icon then
dc:seek(0, y) dc:seek(0, y)
if type(obj.icon) == 'table' then paint_icon(icon, obj)
dc:char(nil,obj.icon)
else
dc:string(obj.icon, obj.icon_pen or cur_pen)
end
end end
render_text(obj, dc, iw or 0, y, cur_pen, cur_dpen) render_text(obj, dc, iw or 0, y, cur_pen, cur_dpen, not current)
local ip = dc.width
if obj.key then if obj.key then
local keystr = gui.getKeyDisplay(obj.key) local keystr = gui.getKeyDisplay(obj.key)
dc:seek(dc.width-2-#keystr,y):pen(self.text_pen) ip = ip-2-#keystr
dc:seek(ip,y):pen(self.text_pen)
dc:string('('):string(keystr,COLOR_LIGHTGREEN):string(')') dc:string('('):string(keystr,COLOR_LIGHTGREEN):string(')')
end end
if icon and not iw then
dc:seek(ip-1,y)
paint_icon(icon, obj)
end
end end
end end
@ -523,10 +568,19 @@ function List:submit()
end end
end end
function List:submit2()
if self.on_submit2 and #self.choices > 0 then
self.on_submit2(self:getSelected())
end
end
function List:onInput(keys) function List:onInput(keys)
if self.on_submit and keys.SELECT then if self.on_submit and keys.SELECT then
self:submit() self:submit()
return true return true
elseif self.on_submit2 and keys.SEC_SELECT then
self:submit2()
return true
else else
for k,v in pairs(self.scroll_keys) do for k,v in pairs(self.scroll_keys) do
if keys[k] then if keys[k] then
@ -562,10 +616,14 @@ end
FilteredList = defclass(FilteredList, Widget) FilteredList = defclass(FilteredList, Widget)
FilteredList.ATTRS {
edit_below = false,
}
function FilteredList:init(info) function FilteredList:init(info)
self.edit = EditField{ self.edit = EditField{
text_pen = info.cursor_pen, text_pen = info.edit_pen or info.cursor_pen,
frame = { l = info.icon_width, t = 0 }, frame = { l = info.icon_width, t = 0, h = 1 },
on_change = self:callback('onFilterChange'), on_change = self:callback('onFilterChange'),
on_char = self:callback('onFilterChar'), on_char = self:callback('onFilterChar'),
} }
@ -574,10 +632,15 @@ function FilteredList:init(info)
text_pen = info.text_pen, text_pen = info.text_pen,
cursor_pen = info.cursor_pen, cursor_pen = info.cursor_pen,
inactive_pen = info.inactive_pen, inactive_pen = info.inactive_pen,
icon_pen = info.icon_pen,
row_height = info.row_height, row_height = info.row_height,
scroll_keys = info.scroll_keys, scroll_keys = info.scroll_keys,
icon_width = info.icon_width, icon_width = info.icon_width,
} }
if self.edit_below then
self.edit.frame = { l = info.icon_width, b = 0, h = 1 }
self.list.frame = { t = 0, b = 2 }
end
if info.on_select then if info.on_select then
self.list.on_select = function() self.list.on_select = function()
return info.on_select(self:getSelected()) return info.on_select(self:getSelected())
@ -588,11 +651,16 @@ function FilteredList:init(info)
return info.on_submit(self:getSelected()) return info.on_submit(self:getSelected())
end end
end end
if info.on_submit2 then
self.list.on_submit2 = function()
return info.on_submit2(self:getSelected())
end
end
self.not_found = Label{ self.not_found = Label{
visible = false, visible = false,
text = info.not_found_label or 'No matches', text = info.not_found_label or 'No matches',
text_pen = COLOR_LIGHTRED, text_pen = COLOR_LIGHTRED,
frame = { l = info.icon_width, t = 2 }, frame = { l = info.icon_width, t = self.list.frame.t },
} }
self:addviews{ self.edit, self.list, self.not_found } self:addviews{ self.edit, self.list, self.not_found }
self:setChoices(info.choices, info.selected) self:setChoices(info.choices, info.selected)
@ -614,13 +682,19 @@ function FilteredList:submit()
return self.list:submit() return self.list:submit()
end end
function FilteredList:submit2()
return self.list:submit2()
end
function FilteredList:canSubmit() function FilteredList:canSubmit()
return not self.not_found.visible return not self.not_found.visible
end end
function FilteredList:getSelected() function FilteredList:getSelected()
local i,v = self.list:getSelected() local i,v = self.list:getSelected()
return map_opttab(self.choice_index, i), v if i then
return map_opttab(self.choice_index, i), v
end
end end
function FilteredList:getContentWidth() function FilteredList:getContentWidth()

@ -24,7 +24,7 @@ function CheckedArray:__len()
return self.count return self.count
end end
function CheckedArray:__index(idx) function CheckedArray:__index(idx)
if type(idx) == number then if type(idx) == "number" then
if idx >= self.count then if idx >= self.count then
error('Index out of bounds: '..tostring(idx)) error('Index out of bounds: '..tostring(idx))
end end
@ -195,6 +195,26 @@ function MemoryArea:delete()
for k,v in pairs(self) do self[k] = nil end for k,v in pairs(self) do self[k] = nil end
end end
-- Static code segment search
function get_code_segment()
local cstart, cend
for i,mem in ipairs(dfhack.internal.getMemRanges()) do
if mem.read and mem.execute
and (string.match(mem.name,'/dwarfort%.exe$')
or string.match(mem.name,'/Dwarf_Fortress$')
or string.match(mem.name,'Dwarf Fortress%.exe'))
then
cstart = mem.start_addr
cend = mem.end_addr
end
end
if cstart and cend then
return MemoryArea.new(cstart, cend)
end
end
-- Static data segment search -- Static data segment search
local function find_data_segment() local function find_data_segment()

@ -283,6 +283,33 @@ function clone_with_default(obj,default,force)
return rv return rv
end end
-- Parse an integer value into a bitfield table
function parse_bitfield_int(value, type_ref)
if value == 0 then
return nil
end
local res = {}
for i,v in ipairs(type_ref) do
if bit32.extract(value, i) ~= 0 then
res[v] = true
end
end
return res
end
-- List the enabled flag names in the bitfield table
function list_bitfield_flags(bitfield, list)
list = list or {}
if bitfield then
for name,val in pairs(bitfield) do
if val then
table.insert(list, name)
end
end
end
return list
end
-- Sort a vector or lua table -- Sort a vector or lua table
function sort_vector(vector,field,cmp) function sort_vector(vector,field,cmp)
local fcmp = compare_field(field,cmp) local fcmp = compare_field(field,cmp)
@ -304,16 +331,26 @@ end
-- Linear search -- Linear search
function linear_index(vector,obj) function linear_index(vector,key,field)
local min,max local min,max
if df.isvalid(vector) then if df.isvalid(vector) then
min,max = 0,#vector-1 min,max = 0,#vector-1
else else
min,max = 1,#vector min,max = 1,#vector
end end
for i=min,max do if field then
if vector[i] == obj then for i=min,max do
return i local obj = vector[i]
if obj[field] == key then
return i, obj
end
end
else
for i=min,max do
local obj = vector[i]
if obj == key then
return i, obj
end
end end
end end
return nil return nil
@ -420,6 +457,7 @@ function getBuildingCenter(building)
return xyz2pos(building.centerx, building.centery, building.z) return xyz2pos(building.centerx, building.centery, building.z)
end end
-- Split the string by the given delimiter
function split_string(self, delimiter) function split_string(self, delimiter)
local result = { } local result = { }
local from = 1 local from = 1

@ -178,6 +178,20 @@ bool Buildings::ReadCustomWorkshopTypes(map <uint32_t, string> & btypes)
return true; return true;
} }
df::general_ref *Buildings::getGeneralRef(df::building *building, df::general_ref_type type)
{
CHECK_NULL_POINTER(building);
return findRef(building->general_refs, type);
}
df::specific_ref *Buildings::getSpecificRef(df::building *building, df::specific_ref_type type)
{
CHECK_NULL_POINTER(building);
return findRef(building->specific_refs, type);
}
bool Buildings::setOwner(df::building *bld, df::unit *unit) bool Buildings::setOwner(df::building *bld, df::unit *unit)
{ {
CHECK_NULL_POINTER(bld); CHECK_NULL_POINTER(bld);
@ -762,7 +776,7 @@ static void markBuildingTiles(df::building *bld, bool remove)
static void linkRooms(df::building *bld) static void linkRooms(df::building *bld)
{ {
auto &vec = world->buildings.other[buildings_other_id::ANY_FREE]; auto &vec = world->buildings.other[buildings_other_id::IN_PLAY];
bool changed = false; bool changed = false;
@ -895,7 +909,7 @@ static bool linkForConstruct(df::job* &job, df::building *bld)
job = new df::job(); job = new df::job();
job->job_type = df::job_type::ConstructBuilding; job->job_type = df::job_type::ConstructBuilding;
job->pos = df::coord(bld->centerx, bld->centery, bld->z); job->pos = df::coord(bld->centerx, bld->centery, bld->z);
job->references.push_back(ref); job->general_refs.push_back(ref);
bld->jobs.push_back(job); bld->jobs.push_back(job);

@ -48,63 +48,63 @@ Module* DFHack::createGraphic()
struct Graphic::Private struct Graphic::Private
{ {
bool Started; bool Started;
vector<DFTileSurface* (*)(int,int)> funcs; vector<DFTileSurface* (*)(int,int)> funcs;
}; };
Graphic::Graphic() Graphic::Graphic()
{ {
d = new Private; d = new Private;
d->Started = true; d->Started = true;
} }
Graphic::~Graphic() Graphic::~Graphic()
{ {
delete d; delete d;
} }
bool Graphic::Register(DFTileSurface* (*func)(int,int)) bool Graphic::Register(DFTileSurface* (*func)(int,int))
{ {
d->funcs.push_back(func); d->funcs.push_back(func);
return true; return true;
} }
bool Graphic::Unregister(DFTileSurface* (*func)(int,int)) bool Graphic::Unregister(DFTileSurface* (*func)(int,int))
{ {
if ( d->funcs.empty() ) return false; if ( d->funcs.empty() ) return false;
vector<DFTileSurface* (*)(int,int)>::iterator it = d->funcs.begin(); vector<DFTileSurface* (*)(int,int)>::iterator it = d->funcs.begin();
while ( it != d->funcs.end() ) while ( it != d->funcs.end() )
{ {
if ( *it == func ) if ( *it == func )
{ {
d->funcs.erase(it); d->funcs.erase(it);
return true; return true;
} }
it++; it++;
} }
return false; return false;
} }
// This will return first DFTileSurface it can get (or NULL if theres none) // This will return first DFTileSurface it can get (or NULL if theres none)
DFTileSurface* Graphic::Call(int x, int y) DFTileSurface* Graphic::Call(int x, int y)
{ {
if ( d->funcs.empty() ) return NULL; if ( d->funcs.empty() ) return NULL;
DFTileSurface* temp = NULL; DFTileSurface* temp = NULL;
vector<DFTileSurface* (*)(int,int)>::iterator it = d->funcs.begin(); vector<DFTileSurface* (*)(int,int)>::iterator it = d->funcs.begin();
while ( it != d->funcs.end() ) while ( it != d->funcs.end() )
{ {
temp = (*it)(x,y); temp = (*it)(x,y);
if ( temp != NULL ) if ( temp != NULL )
{ {
return temp; return temp;
} }
it++; it++;
} }
return NULL; return NULL;
} }

@ -495,10 +495,10 @@ bool Items::copyItem(df::item * itembase, DFHack::dfh_item &item)
item.id = itreal->id; item.id = itreal->id;
item.age = itreal->age; item.age = itreal->age;
item.flags = itreal->flags; item.flags = itreal->flags;
item.matdesc.itemType = itreal->getType(); item.matdesc.item_type = itreal->getType();
item.matdesc.subType = itreal->getSubtype(); item.matdesc.item_subtype = itreal->getSubtype();
item.matdesc.material = itreal->getMaterial(); item.matdesc.mat_type = itreal->getMaterial();
item.matdesc.index = itreal->getMaterialIndex(); item.matdesc.mat_index = itreal->getMaterialIndex();
item.wear_level = itreal->getWear(); item.wear_level = itreal->getWear();
item.quality = itreal->getQuality(); item.quality = itreal->getQuality();
item.quantity = itreal->getStackSize(); item.quantity = itreal->getStackSize();
@ -509,7 +509,7 @@ df::general_ref *Items::getGeneralRef(df::item *item, df::general_ref_type type)
{ {
CHECK_NULL_POINTER(item); CHECK_NULL_POINTER(item);
return findRef(item->itemrefs, type); return findRef(item->general_refs, type);
} }
df::specific_ref *Items::getSpecificRef(df::item *item, df::specific_ref_type type) df::specific_ref *Items::getSpecificRef(df::item *item, df::specific_ref_type type)
@ -530,9 +530,9 @@ bool Items::setOwner(df::item *item, df::unit *unit)
{ {
CHECK_NULL_POINTER(item); CHECK_NULL_POINTER(item);
for (int i = item->itemrefs.size()-1; i >= 0; i--) for (int i = item->general_refs.size()-1; i >= 0; i--)
{ {
df::general_ref *ref = item->itemrefs[i]; df::general_ref *ref = item->general_refs[i];
if (!strict_virtual_cast<df::general_ref_unit_itemownerst>(ref)) if (!strict_virtual_cast<df::general_ref_unit_itemownerst>(ref))
continue; continue;
@ -546,7 +546,7 @@ bool Items::setOwner(df::item *item, df::unit *unit)
} }
delete ref; delete ref;
vector_erase_at(item->itemrefs, i); vector_erase_at(item->general_refs, i);
} }
item->flags.bits.owned = false; item->flags.bits.owned = false;
@ -561,7 +561,7 @@ bool Items::setOwner(df::item *item, df::unit *unit)
ref->unit_id = unit->id; ref->unit_id = unit->id;
insert_into_vector(unit->owned_items, item->id); insert_into_vector(unit->owned_items, item->id);
item->itemrefs.push_back(ref); item->general_refs.push_back(ref);
} }
return true; return true;
@ -580,9 +580,9 @@ void Items::getContainedItems(df::item *item, std::vector<df::item*> *items)
items->clear(); items->clear();
for (size_t i = 0; i < item->itemrefs.size(); i++) for (size_t i = 0; i < item->general_refs.size(); i++)
{ {
df::general_ref *ref = item->itemrefs[i]; df::general_ref *ref = item->general_refs[i];
if (ref->getType() != general_ref_type::CONTAINS_ITEM) if (ref->getType() != general_ref_type::CONTAINS_ITEM)
continue; continue;
@ -592,6 +592,20 @@ void Items::getContainedItems(df::item *item, std::vector<df::item*> *items)
} }
} }
df::building *Items::getHolderBuilding(df::item * item)
{
auto ref = getGeneralRef(item, general_ref_type::BUILDING_HOLDER);
return ref ? ref->getBuilding() : NULL;
}
df::unit *Items::getHolderUnit(df::item * item)
{
auto ref = getGeneralRef(item, general_ref_type::UNIT_HOLDER);
return ref ? ref->getUnit() : NULL;
}
df::coord Items::getPosition(df::item *item) df::coord Items::getPosition(df::item *item)
{ {
CHECK_NULL_POINTER(item); CHECK_NULL_POINTER(item);
@ -603,9 +617,9 @@ df::coord Items::getPosition(df::item *item)
if (item->flags.bits.in_inventory) if (item->flags.bits.in_inventory)
{ {
for (size_t i = 0; i < item->itemrefs.size(); i++) for (size_t i = 0; i < item->general_refs.size(); i++)
{ {
df::general_ref *ref = item->itemrefs[i]; df::general_ref *ref = item->general_refs[i];
switch (ref->getType()) switch (ref->getType())
{ {
@ -702,9 +716,9 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item)
if (item->world_data_id != -1) if (item->world_data_id != -1)
return false; return false;
for (size_t i = 0; i < item->itemrefs.size(); i++) for (size_t i = 0; i < item->general_refs.size(); i++)
{ {
df::general_ref *ref = item->itemrefs[i]; df::general_ref *ref = item->general_refs[i];
switch (ref->getType()) switch (ref->getType())
{ {
@ -734,9 +748,9 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item)
{ {
bool found = false; bool found = false;
for (int i = item->itemrefs.size()-1; i >= 0; i--) for (int i = item->general_refs.size()-1; i >= 0; i--)
{ {
df::general_ref *ref = item->itemrefs[i]; df::general_ref *ref = item->general_refs[i];
switch (ref->getType()) switch (ref->getType())
{ {
@ -753,7 +767,7 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item)
item2->flags.bits.weight_computed = false; item2->flags.bits.weight_computed = false;
removeRef(item2->itemrefs, general_ref_type::CONTAINS_ITEM, item->id); removeRef(item2->general_refs, general_ref_type::CONTAINS_ITEM, item->id);
} }
break; break;
@ -785,7 +799,7 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item)
} }
found = true; found = true;
vector_erase_at(item->itemrefs, i); vector_erase_at(item->general_refs, i);
delete ref; delete ref;
} }
@ -864,10 +878,10 @@ bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df:
container->flags.bits.weight_computed = false; container->flags.bits.weight_computed = false;
ref1->item_id = item->id; ref1->item_id = item->id;
container->itemrefs.push_back(ref1); container->general_refs.push_back(ref1);
ref2->item_id = container->id; ref2->item_id = container->id;
item->itemrefs.push_back(ref2); item->general_refs.push_back(ref2);
return true; return true;
} }
@ -898,7 +912,7 @@ bool DFHack::Items::moveToBuilding(MapExtras::MapCache &mc, df::item *item, df::
item->flags.bits.in_building=true; item->flags.bits.in_building=true;
ref->building_id=building->id; ref->building_id=building->id;
item->itemrefs.push_back(ref); item->general_refs.push_back(ref);
auto con=new df::building_actual::T_contained_items; auto con=new df::building_actual::T_contained_items;
con->item=item; con->item=item;
@ -941,7 +955,7 @@ bool DFHack::Items::moveToInventory(
unit->inventory.push_back(newInventoryItem); unit->inventory.push_back(newInventoryItem);
holderReference->unit_id = unit->id; holderReference->unit_id = unit->id;
item->itemrefs.push_back(holderReference); item->general_refs.push_back(holderReference);
resetUnitInvFlags(unit, newInventoryItem); resetUnitInvFlags(unit, newInventoryItem);
@ -1002,7 +1016,7 @@ df::proj_itemst *Items::makeProjectile(MapExtras::MapCache &mc, df::item *item)
proj->item = item; proj->item = item;
ref->projectile_id = proj->id; ref->projectile_id = proj->id;
item->itemrefs.push_back(ref); item->general_refs.push_back(ref);
linked_list_append(&world->proj_list, proj->link); linked_list_append(&world->proj_list, proj->link);

@ -35,6 +35,7 @@ using namespace std;
#include "Error.h" #include "Error.h"
#include "PluginManager.h" #include "PluginManager.h"
#include "MiscUtils.h" #include "MiscUtils.h"
#include "Types.h"
#include "modules/Job.h" #include "modules/Job.h"
#include "modules/Materials.h" #include "modules/Materials.h"
@ -71,14 +72,14 @@ df::job *DFHack::Job::cloneJobStruct(df::job *job)
pnew->specific_refs.clear(); pnew->specific_refs.clear();
// Clone refs // Clone refs
for (int i = pnew->references.size()-1; i >= 0; i--) for (int i = pnew->general_refs.size()-1; i >= 0; i--)
{ {
df::general_ref *ref = pnew->references[i]; df::general_ref *ref = pnew->general_refs[i];
if (virtual_cast<df::general_ref_unit_workerst>(ref)) if (virtual_cast<df::general_ref_unit>(ref))
vector_erase_at(pnew->references, i); vector_erase_at(pnew->general_refs, i);
else else
pnew->references[i] = ref->clone(); pnew->general_refs[i] = ref->clone();
} }
// Clone items // Clone items
@ -96,8 +97,8 @@ void DFHack::Job::deleteJobStruct(df::job *job)
// Only allow free-floating job structs // Only allow free-floating job structs
assert(!job->list_link && job->items.empty() && job->specific_refs.empty()); assert(!job->list_link && job->items.empty() && job->specific_refs.empty());
for (int i = job->references.size()-1; i >= 0; i--) for (int i = job->general_refs.size()-1; i >= 0; i--)
delete job->references[i]; delete job->general_refs[i];
for (int i = job->job_items.size()-1; i >= 0; i--) for (int i = job->job_items.size()-1; i >= 0; i--)
delete job->job_items[i]; delete job->job_items[i];
@ -228,13 +229,27 @@ void DFHack::Job::printJobDetails(color_ostream &out, df::job *job)
printItemDetails(out, job->job_items[i], i); printItemDetails(out, job->job_items[i], i);
} }
df::general_ref *Job::getGeneralRef(df::job *job, df::general_ref_type type)
{
CHECK_NULL_POINTER(job);
return findRef(job->general_refs, type);
}
df::specific_ref *Job::getSpecificRef(df::job *job, df::specific_ref_type type)
{
CHECK_NULL_POINTER(job);
return findRef(job->specific_refs, type);
}
df::building *DFHack::Job::getHolder(df::job *job) df::building *DFHack::Job::getHolder(df::job *job)
{ {
CHECK_NULL_POINTER(job); CHECK_NULL_POINTER(job);
for (size_t i = 0; i < job->references.size(); i++) for (size_t i = 0; i < job->general_refs.size(); i++)
{ {
VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->references[i]); VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->general_refs[i]);
if (ref) if (ref)
return ref->getBuilding(); return ref->getBuilding();
} }
@ -246,9 +261,9 @@ df::unit *DFHack::Job::getWorker(df::job *job)
{ {
CHECK_NULL_POINTER(job); CHECK_NULL_POINTER(job);
for (size_t i = 0; i < job->references.size(); i++) for (size_t i = 0; i < job->general_refs.size(); i++)
{ {
VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->references[i]); VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->general_refs[i]);
if (ref) if (ref)
return ref->getUnit(); return ref->getUnit();
} }

@ -650,15 +650,15 @@ void MapExtras::Block::TileInfo::init_coninfo()
con_info = new ConInfo(); con_info = new ConInfo();
con_info->constructed.clear(); con_info->constructed.clear();
COPY(con_info->tiles, base_tiles); COPY(con_info->tiles, base_tiles);
memset(con_info->mattype, -1, sizeof(con_info->mattype)); memset(con_info->mat_type, -1, sizeof(con_info->mat_type));
memset(con_info->matindex, -1, sizeof(con_info->matindex)); memset(con_info->mat_index, -1, sizeof(con_info->mat_index));
} }
MapExtras::Block::BasematInfo::BasematInfo() MapExtras::Block::BasematInfo::BasematInfo()
{ {
dirty.clear(); dirty.clear();
memset(mattype,0,sizeof(mattype)); memset(mat_type,0,sizeof(mat_type));
memset(matindex,-1,sizeof(matindex)); memset(mat_index,-1,sizeof(mat_index));
memset(layermat,-1,sizeof(layermat)); memset(layermat,-1,sizeof(layermat));
} }
@ -719,8 +719,8 @@ void MapExtras::Block::ParseTiles(TileInfo *tiles)
is_con = true; is_con = true;
tiles->con_info->constructed.setassignment(x,y,true); tiles->con_info->constructed.setassignment(x,y,true);
tiles->con_info->tiles[x][y] = tt; tiles->con_info->tiles[x][y] = tt;
tiles->con_info->mattype[x][y] = con->mat_type; tiles->con_info->mat_type[x][y] = con->mat_type;
tiles->con_info->matindex[x][y] = con->mat_index; tiles->con_info->mat_index[x][y] = con->mat_index;
tt = con->original_tile; tt = con->original_tile;
} }
@ -753,14 +753,14 @@ void MapExtras::Block::ParseBasemats(TileInfo *tiles, BasematInfo *bmats)
auto tt = tiles->base_tiles[x][y]; auto tt = tiles->base_tiles[x][y];
auto mat = info.getBaseMaterial(tt, df::coord2d(x,y)); auto mat = info.getBaseMaterial(tt, df::coord2d(x,y));
bmats->mattype[x][y] = mat.mat_type; bmats->mat_type[x][y] = mat.mat_type;
bmats->matindex[x][y] = mat.mat_index; bmats->mat_index[x][y] = mat.mat_index;
// Copy base info back to construction layer // Copy base info back to construction layer
if (tiles->con_info && !tiles->con_info->constructed.getassignment(x,y)) if (tiles->con_info && !tiles->con_info->constructed.getassignment(x,y))
{ {
tiles->con_info->mattype[x][y] = mat.mat_type; tiles->con_info->mat_type[x][y] = mat.mat_type;
tiles->con_info->matindex[x][y] = mat.mat_index; tiles->con_info->mat_index[x][y] = mat.mat_index;
} }
} }
} }

@ -190,6 +190,8 @@ bool MaterialInfo::find(const std::vector<std::string> &items)
} }
else if (items.size() == 2) else if (items.size() == 2)
{ {
if (items[1] == "NONE" && findBuiltin(items[0]))
return true;
if (findPlant(items[0], items[1])) if (findPlant(items[0], items[1]))
return true; return true;
if (findCreature(items[0], items[1])) if (findCreature(items[0], items[1]))
@ -210,7 +212,7 @@ bool MaterialInfo::findBuiltin(const std::string &token)
} }
df::world_raws &raws = world->raws; df::world_raws &raws = world->raws;
for (int i = 1; i < NUM_BUILTIN; i++) for (int i = 0; i < NUM_BUILTIN; i++)
{ {
auto obj = raws.mat_table.builtin[i]; auto obj = raws.mat_table.builtin[i];
if (obj && obj->id == token) if (obj && obj->id == token)
@ -423,6 +425,8 @@ bool MaterialInfo::matches(const df::dfhack_material_category &cat)
TEST(glass, IS_GLASS); TEST(glass, IS_GLASS);
if (cat.bits.clay && linear_index(material->reaction_product.id, std::string("FIRED_MAT")) >= 0) if (cat.bits.clay && linear_index(material->reaction_product.id, std::string("FIRED_MAT")) >= 0)
return true; return true;
if (cat.bits.milk && linear_index(material->reaction_product.id, std::string("CHEESE_MAT")) >= 0)
return true;
return false; return false;
} }
@ -836,7 +840,7 @@ bool Materials::ReadAllMaterials(void)
std::string Materials::getDescription(const t_material & mat) std::string Materials::getDescription(const t_material & mat)
{ {
MaterialInfo mi(mat.material, mat.index); MaterialInfo mi(mat.mat_type, mat.mat_index);
if (mi.creature) if (mi.creature)
return mi.creature->creature_id + " " + mi.material->id; return mi.creature->creature_id + " " + mi.material->id;
else if (mi.plant) else if (mi.plant)
@ -849,7 +853,7 @@ std::string Materials::getDescription(const t_material & mat)
// This is completely worthless now // This is completely worthless now
std::string Materials::getType(const t_material & mat) std::string Materials::getType(const t_material & mat)
{ {
MaterialInfo mi(mat.material, mat.index); MaterialInfo mi(mat.mat_type, mat.mat_index);
switch (mi.mode) switch (mi.mode)
{ {
case MaterialInfo::Builtin: case MaterialInfo::Builtin:

@ -55,6 +55,7 @@ using namespace DFHack;
#include "df/item.h" #include "df/item.h"
#include "df/job.h" #include "df/job.h"
#include "df/building.h" #include "df/building.h"
#include "df/renderer.h"
using namespace df::enums; using namespace df::enums;
using df::global::init; using df::global::init;
@ -109,10 +110,10 @@ bool Screen::paintTile(const Pen &pen, int x, int y)
{ {
if (!gps || !pen.valid()) return false; if (!gps || !pen.valid()) return false;
int dimx = gps->dimx, dimy = gps->dimy; auto dim = getWindowSize();
if (x < 0 || x >= dimx || y < 0 || y >= dimy) return false; if (x < 0 || x >= dim.x || y < 0 || y >= dim.y) return false;
doSetTile(pen, x*dimy + y); doSetTile(pen, x*dim.y + y);
return true; return true;
} }
@ -120,11 +121,11 @@ Pen Screen::readTile(int x, int y)
{ {
if (!gps) return Pen(0,0,0,-1); if (!gps) return Pen(0,0,0,-1);
int dimx = gps->dimx, dimy = gps->dimy; auto dim = getWindowSize();
if (x < 0 || x >= dimx || y < 0 || y >= dimy) if (x < 0 || x >= dim.x || y < 0 || y >= dim.y)
return Pen(0,0,0,-1); return Pen(0,0,0,-1);
int index = x*dimy + y; int index = x*dim.y + y;
auto screen = gps->screen + index*4; auto screen = gps->screen + index*4;
if (screen[3] & 0x80) if (screen[3] & 0x80)
return Pen(0,0,0,-1); return Pen(0,0,0,-1);
@ -153,14 +154,15 @@ Pen Screen::readTile(int x, int y)
bool Screen::paintString(const Pen &pen, int x, int y, const std::string &text) bool Screen::paintString(const Pen &pen, int x, int y, const std::string &text)
{ {
if (!gps || y < 0 || y >= gps->dimy) return false; auto dim = getWindowSize();
if (!gps || y < 0 || y >= dim.y) return false;
Pen tmp(pen); Pen tmp(pen);
bool ok = false; bool ok = false;
for (size_t i = -std::min(0,x); i < text.size(); i++) for (size_t i = -std::min(0,x); i < text.size(); i++)
{ {
if (x + i >= size_t(gps->dimx)) if (x + i >= size_t(dim.x))
break; break;
tmp.ch = text[i]; tmp.ch = text[i];
@ -174,17 +176,18 @@ bool Screen::paintString(const Pen &pen, int x, int y, const std::string &text)
bool Screen::fillRect(const Pen &pen, int x1, int y1, int x2, int y2) bool Screen::fillRect(const Pen &pen, int x1, int y1, int x2, int y2)
{ {
auto dim = getWindowSize();
if (!gps || !pen.valid()) return false; if (!gps || !pen.valid()) return false;
if (x1 < 0) x1 = 0; if (x1 < 0) x1 = 0;
if (y1 < 0) y1 = 0; if (y1 < 0) y1 = 0;
if (x2 >= gps->dimx) x2 = gps->dimx-1; if (x2 >= dim.x) x2 = dim.x-1;
if (y2 >= gps->dimy) y2 = gps->dimy-1; if (y2 >= dim.y) y2 = dim.y-1;
if (x1 > x2 || y1 > y2) return false; if (x1 > x2 || y1 > y2) return false;
for (int x = x1; x <= x2; x++) for (int x = x1; x <= x2; x++)
{ {
int index = x*gps->dimy; int index = x*dim.y;
for (int y = y1; y <= y2; y++) for (int y = y1; y <= y2; y++)
doSetTile(pen, index+y); doSetTile(pen, index+y);
@ -197,32 +200,33 @@ bool Screen::drawBorder(const std::string &title)
{ {
if (!gps) return false; if (!gps) return false;
int dimx = gps->dimx, dimy = gps->dimy; auto dim = getWindowSize();
Pen border('\xDB', 8); Pen border('\xDB', 8);
Pen text(0, 0, 7); Pen text(0, 0, 7);
Pen signature(0, 0, 8); Pen signature(0, 0, 8);
for (int x = 0; x < dimx; x++) for (int x = 0; x < dim.x; x++)
{ {
doSetTile(border, x * dimy + 0); doSetTile(border, x * dim.y + 0);
doSetTile(border, x * dimy + dimy - 1); doSetTile(border, x * dim.y + dim.y - 1);
} }
for (int y = 0; y < dimy; y++) for (int y = 0; y < dim.y; y++)
{ {
doSetTile(border, 0 * dimy + y); doSetTile(border, 0 * dim.y + y);
doSetTile(border, (dimx - 1) * dimy + y); doSetTile(border, (dim.x - 1) * dim.y + y);
} }
paintString(signature, dimx-8, dimy-1, "DFHack"); paintString(signature, dim.x-8, dim.y-1, "DFHack");
return paintString(text, (dimx - title.length()) / 2, 0, title); return paintString(text, (dim.x - title.length()) / 2, 0, title);
} }
bool Screen::clear() bool Screen::clear()
{ {
if (!gps) return false; if (!gps) return false;
return fillRect(Pen(' ',0,0,false), 0, 0, gps->dimx-1, gps->dimy-1); auto dim = getWindowSize();
return fillRect(Pen(' ',0,0,false), 0, 0, dim.x-1, dim.y-1);
} }
bool Screen::invalidate() bool Screen::invalidate()
@ -233,6 +237,21 @@ bool Screen::invalidate()
return true; return true;
} }
const Pen Screen::Painter::default_pen(0,COLOR_GREY,0);
const Pen Screen::Painter::default_key_pen(0,COLOR_LIGHTGREEN,0);
void Screen::Painter::do_paint_string(const std::string &str, const Pen &pen)
{
if (gcursor.y < clip.first.y || gcursor.y > clip.second.y)
return;
int dx = std::max(0, int(clip.first.x - gcursor.x));
int len = std::min((int)str.size(), int(clip.second.x - gcursor.x + 1));
if (len > dx)
paintString(pen, gcursor.x + dx, gcursor.y, str.substr(dx, len-dx));
}
bool Screen::findGraphicsTile(const std::string &pagename, int x, int y, int *ptile, int *pgs) bool Screen::findGraphicsTile(const std::string &pagename, int x, int y, int *ptile, int *pgs)
{ {
if (!gps || !texture || x < 0 || y < 0) return false; if (!gps || !texture || x < 0 || y < 0) return false;
@ -312,6 +331,47 @@ class DFHACK_EXPORT enabler_inputst {
public: public:
std::string GetKeyDisplay(int binding); std::string GetKeyDisplay(int binding);
}; };
class DFHACK_EXPORT renderer {
unsigned char *screen;
long *screentexpos;
char *screentexpos_addcolor;
unsigned char *screentexpos_grayscale;
unsigned char *screentexpos_cf;
unsigned char *screentexpos_cbr;
// For partial printing:
unsigned char *screen_old;
long *screentexpos_old;
char *screentexpos_addcolor_old;
unsigned char *screentexpos_grayscale_old;
unsigned char *screentexpos_cf_old;
unsigned char *screentexpos_cbr_old;
public:
virtual void update_tile(int x, int y) {};
virtual void update_all() {};
virtual void render() {};
virtual void set_fullscreen();
virtual void zoom(df::zoom_commands cmd);
virtual void resize(int w, int h) {};
virtual void grid_resize(int w, int h) {};
renderer() {
screen = NULL;
screentexpos = NULL;
screentexpos_addcolor = NULL;
screentexpos_grayscale = NULL;
screentexpos_cf = NULL;
screentexpos_cbr = NULL;
screen_old = NULL;
screentexpos_old = NULL;
screentexpos_addcolor_old = NULL;
screentexpos_grayscale_old = NULL;
screentexpos_cf_old = NULL;
screentexpos_cbr_old = NULL;
}
virtual ~renderer();
virtual bool get_mouse_coords(int &x, int &y) { return false; }
virtual bool uses_opengl();
};
#else #else
struct less_sz { struct less_sz {
bool operator() (const string &a, const string &b) const { bool operator() (const string &a, const string &b) const {
@ -326,7 +386,9 @@ static std::map<df::interface_key,std::set<string,less_sz> > *keydisplay = NULL;
void init_screen_module(Core *core) void init_screen_module(Core *core)
{ {
#ifdef _LINUX #ifdef _LINUX
core = core; renderer tmp;
if (!strict_virtual_cast<df::renderer>((virtual_ptr)&tmp))
cerr << "Could not fetch the renderer vtable." << std::endl;
#else #else
if (!core->vinfo->getAddress("keydisplay", keydisplay)) if (!core->vinfo->getAddress("keydisplay", keydisplay))
keydisplay = NULL; keydisplay = NULL;
@ -639,7 +701,12 @@ dfhack_lua_viewscreen::~dfhack_lua_viewscreen()
void dfhack_lua_viewscreen::render() void dfhack_lua_viewscreen::render()
{ {
if (Screen::isDismissed(this)) return; if (Screen::isDismissed(this))
{
if (parent)
parent->render();
return;
}
dfhack_viewscreen::render(); dfhack_viewscreen::render();

@ -405,7 +405,7 @@ bool Creatures::WriteJob(const t_creature * furball, std::vector<t_material> con
for(i=0;i<cmats.size();i++) for(i=0;i<cmats.size();i++)
{ {
p->writeWord(cmats[i] + off.job_material_itemtype_o, mat[i].itemType); p->writeWord(cmats[i] + off.job_material_itemtype_o, mat[i].itemType);
p->writeWord(cmats[i] + off.job_material_subtype_o, mat[i].subType); p->writeWord(cmats[i] + off.job_material_subtype_o, mat[i].itemSubtype);
p->writeWord(cmats[i] + off.job_material_subindex_o, mat[i].subIndex); p->writeWord(cmats[i] + off.job_material_subindex_o, mat[i].subIndex);
p->writeDWord(cmats[i] + off.job_material_index_o, mat[i].index); p->writeDWord(cmats[i] + off.job_material_index_o, mat[i].index);
p->writeDWord(cmats[i] + off.job_material_flags_o, mat[i].flags); p->writeDWord(cmats[i] + off.job_material_flags_o, mat[i].flags);
@ -475,7 +475,7 @@ bool Creatures::ReadJob(const t_creature * furball, vector<t_material> & mat)
for(i=0;i<cmats.size();i++) for(i=0;i<cmats.size();i++)
{ {
mat[i].itemType = p->readWord(cmats[i] + off.job_material_itemtype_o); mat[i].itemType = p->readWord(cmats[i] + off.job_material_itemtype_o);
mat[i].subType = p->readWord(cmats[i] + off.job_material_subtype_o); mat[i].itemSubtype = p->readWord(cmats[i] + off.job_material_subtype_o);
mat[i].subIndex = p->readWord(cmats[i] + off.job_material_subindex_o); mat[i].subIndex = p->readWord(cmats[i] + off.job_material_subindex_o);
mat[i].index = p->readDWord(cmats[i] + off.job_material_index_o); mat[i].index = p->readDWord(cmats[i] + off.job_material_index_o);
mat[i].flags = p->readDWord(cmats[i] + off.job_material_flags_o); mat[i].flags = p->readDWord(cmats[i] + off.job_material_flags_o);
@ -519,18 +519,25 @@ df::coord Units::getPosition(df::unit *unit)
return unit->pos; return unit->pos;
} }
df::item *Units::getContainer(df::unit *unit) df::general_ref *Units::getGeneralRef(df::unit *unit, df::general_ref_type type)
{ {
CHECK_NULL_POINTER(unit); CHECK_NULL_POINTER(unit);
for (size_t i = 0; i < unit->refs.size(); i++) return findRef(unit->general_refs, type);
{ }
df::general_ref *ref = unit->refs[i];
if (ref->getType() == general_ref_type::CONTAINED_IN_ITEM)
return ref->getItem();
}
return NULL; df::specific_ref *Units::getSpecificRef(df::unit *unit, df::specific_ref_type type)
{
CHECK_NULL_POINTER(unit);
return findRef(unit->specific_refs, type);
}
df::item *Units::getContainer(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
return findItemRef(unit->general_refs, general_ref_type::CONTAINED_IN_ITEM);
} }
static df::assumed_identity *getFigureIdentity(df::historical_figure *figure) static df::assumed_identity *getFigureIdentity(df::historical_figure *figure)
@ -607,9 +614,9 @@ df::nemesis_record *Units::getNemesis(df::unit *unit)
if (!unit) if (!unit)
return NULL; return NULL;
for (unsigned i = 0; i < unit->refs.size(); i++) for (unsigned i = 0; i < unit->general_refs.size(); i++)
{ {
df::nemesis_record *rv = unit->refs[i]->getNemesis(); df::nemesis_record *rv = unit->general_refs[i]->getNemesis();
if (rv && rv->unit == unit) if (rv && rv->unit == unit)
return rv; return rv;
} }
@ -889,8 +896,7 @@ int Units::getNominalSkill(df::unit *unit, df::job_skill skill_id, bool use_rust
// Retrieve skill from unit soul: // Retrieve skill from unit soul:
df::enum_field<df::job_skill,int16_t> key(skill_id); auto skill = binsearch_in_vector(unit->status.current_soul->skills, &df::unit_skill::id, skill_id);
auto skill = binsearch_in_vector(unit->status.current_soul->skills, &df::unit_skill::id, key);
if (skill) if (skill)
{ {
@ -903,6 +909,24 @@ int Units::getNominalSkill(df::unit *unit, df::job_skill skill_id, bool use_rust
return 0; return 0;
} }
int Units::getExperience(df::unit *unit, df::job_skill skill_id, bool total)
{
CHECK_NULL_POINTER(unit);
if (!unit->status.current_soul)
return 0;
auto skill = binsearch_in_vector(unit->status.current_soul->skills, &df::unit_skill::id, skill_id);
if (!skill)
return 0;
int xp = skill->experience;
// exact formula used by the game:
if (total && skill->rating > 0)
xp += 500*skill->rating + 100*skill->rating*(skill->rating - 1)/2;
return xp;
}
int Units::getEffectiveSkill(df::unit *unit, df::job_skill skill_id) int Units::getEffectiveSkill(df::unit *unit, df::job_skill skill_id)
{ {
/* /*

@ -71,15 +71,15 @@ bool Vegetation::copyPlant(const int32_t index, t_plant &out)
out.material = out.origin->material; out.material = out.origin->material;
out.pos = out.origin->pos; out.pos = out.origin->pos;
out.grow_counter = out.origin->grow_counter; out.grow_counter = out.origin->grow_counter;
out.temperature_1 = out.origin->temperature_1; out.temperature_1 = out.origin->temperature.whole;
out.temperature_2 = out.origin->temperature_2; out.temperature_2 = out.origin->temperature.fraction;
out.is_burning = out.origin->is_burning; out.is_burning = out.origin->is_burning;
out.hitpoints = out.origin->hitpoints; out.hitpoints = out.origin->hitpoints;
out.update_order = out.origin->update_order; out.update_order = out.origin->update_order;
//out.unk1 = out.origin->anon_1; //out.unk1 = out.origin->anon_1;
//out.unk2 = out.origin->anon_2; //out.unk2 = out.origin->anon_2;
//out.temperature_3 = out.origin->temperature_3; //out.temperature_3 = out.origin->temperature_unk;
//out.temperature_4 = out.origin->temperature_4; //out.temperature_4 = out.origin->min_safe_temp;
//out.temperature_5 = out.origin->temperature_5; //out.temperature_5 = out.origin->max_safe_temp;
return true; return true;
} }

@ -197,11 +197,15 @@ PersistentDataItem World::AddPersistentData(const std::string &key)
std::vector<df::historical_figure*> &hfvec = df::historical_figure::get_vector(); std::vector<df::historical_figure*> &hfvec = df::historical_figure::get_vector();
df::historical_figure *hfig = new df::historical_figure(); df::historical_figure *hfig = new df::historical_figure();
hfig->id = next_persistent_id--; hfig->id = next_persistent_id;
hfig->name.has_name = true; hfig->name.has_name = true;
hfig->name.first_name = key; hfig->name.first_name = key;
memset(hfig->name.words, 0xFF, sizeof(hfig->name.words)); memset(hfig->name.words, 0xFF, sizeof(hfig->name.words));
if (!hfvec.empty())
hfig->id = std::min(hfig->id, hfvec[0]->id-1);
next_persistent_id = hfig->id-1;
hfvec.insert(hfvec.begin(), hfig); hfvec.insert(hfvec.begin(), hfig);
persistent_index.insert(T_persistent_item(key, -hfig->id)); persistent_index.insert(T_persistent_item(key, -hfig->id));

@ -0,0 +1,142 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
0x2ac6b
CC CC CC CC CC
66 39 E8 EB 53
.text:0042B86B loc_42B86B:
.text:0042B86B cmp ax, bp
.text:0042B86E jmp short loc_42B8C3
0x2ac7b
CC CC CC CC CC
E9 96 A2 00 00
.text:0042B87B loc_42B87B:
.text:0042B87B jmp loc_435B16
0x2acc3
CC CC CC CC CC CC CC CC CC CC CC CC CC
75 0A 66 FF 4C 24 16 79 03 58 EB AC C3
.text:0042B8C3 loc_42B8C3:
.text:0042B8C3 jnz short locret_42B8CF
.text:0042B8C5 dec word ptr [esp+16h] ; 4+8+8+2
.text:0042B8CA jns short locret_42B8CF
.text:0042B8CC pop eax
.text:0042B8CD jmp short loc_42B87B
.text:0042B8CF locret_42B8CF:
.text:0042B8CF retn
0x2b2a1
CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
66 C7 44 24 0E 01 00 8B 90 44 01 00 00 C3 CC
.text:0042BEA1 loc_42BEA1:
.text:0042BEA1 mov word ptr [esp+0Eh], 1 ; 4+8+2
.text:0042BEA8 mov edx, [eax+144h]
.text:0042BEAE retn
0x34d91
8B 90 44 01 00 00
E8 0B 65 FF FF 90
<<<<
.text:00435991 mov edx, [eax+144h]
====
.text:00435991 call loc_42BEA1
.text:00435996 nop
>>>>
0x34e53
0F 84 BD 00 00 00
E8 6B 5E FF FF 90
<<<<
.text:00435A53 jz loc_435B16
====
.text:00435A53 call loc_42B8C3
.text:00435A58 nop
>>>>
0x34ef3
66 3B C5 74 1E
E8 73 5D FF FF
<<<<
.text:00435AF3 cmp ax, bp
.text:00435AF6 jz short loc_435B16
====
.text:00435AF3 call loc_42B86B
>>>>
basically:
+ int allowed_count = 1; // to mean 2
...
- if (type(item) == new_type)
+ if (type(item) == new_type && --allowed_count < 0)
return false;
to allow up to two items of the same type at the same time
---8<---
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
0002AC6B: CC 66
0002AC6C: CC 39
0002AC6D: CC E8
0002AC6E: CC EB
0002AC6F: CC 53
0002AC7B: CC E9
0002AC7C: CC 96
0002AC7D: CC A2
0002AC7E: CC 00
0002AC7F: CC 00
0002ACC3: CC 75
0002ACC4: CC 0A
0002ACC5: CC 66
0002ACC6: CC FF
0002ACC7: CC 4C
0002ACC8: CC 24
0002ACC9: CC 16
0002ACCA: CC 79
0002ACCB: CC 03
0002ACCC: CC 58
0002ACCD: CC EB
0002ACCE: CC AC
0002ACCF: CC C3
0002B2A1: CC 66
0002B2A2: CC C7
0002B2A3: CC 44
0002B2A4: CC 24
0002B2A5: CC 0E
0002B2A6: CC 01
0002B2A7: CC 00
0002B2A8: CC 8B
0002B2A9: CC 90
0002B2AA: CC 44
0002B2AB: CC 01
0002B2AC: CC 00
0002B2AD: CC 00
0002B2AE: CC C3
00034D91: 8B E8
00034D92: 90 0B
00034D93: 44 65
00034D94: 01 FF
00034D95: 00 FF
00034D96: 00 90
00034E53: 0F E8
00034E54: 84 6B
00034E55: BD 5E
00034E56: 00 FF
00034E57: 00 FF
00034E58: 00 90
00034EF3: 66 E8
00034EF4: 3B 73
00034EF5: C5 5D
00034EF6: 74 FF
00034EF7: 1E FF

@ -0,0 +1,91 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=808
Original code:
.text:00916BCE mov edi, ebp
.text:00916BD0 call eax
.text:00916BD2 test eax, eax
.text:00916BD4 jnz short loc_916C1C
.text:00916C0A mov edi, ebp
.text:00916C14 mov edi, ebp
Patch:
0x2ac34:
CC CC CC CC CC CC CC CC CC CC CC CC
8B 7C 24 78 8B 3C B7 FF D0 EB 25 CC
.text:0042B834 loc_42B834:
.text:0042B834 mov edi, [esp+78h]
.text:0042B838 mov edi, [edi+esi*4]
.text:0042B83B call eax
.text:0042B83D jmp short unk_42B864
0x2ac64
CC CC CC CC CC CC CC CC CC CC CC CC
85 C0 E9 69 B3 4E 00 CC CC CC CC CC
.text:0042B864 loc_42B864:
.text:0042B864 test eax, eax
.text:0042B866 jmp loc_916BD4
0x515fce
8B FD FF D0 85 C0
E9 61 4C B1 FF 90
.text:00916BCE jmp loc_42B834
.text:00916BD3 nop
.text:00916BD4 loc_916BD4:
0x51600a
8B FD
90 90
.text:00916C0A nop
.text:00916C0B nop
0x516014
8B FD
90 90
.text:00916C14 nop
.text:00916C15 nop
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
0002AC34: CC 8B
0002AC35: CC 7C
0002AC36: CC 24
0002AC37: CC 78
0002AC38: CC 8B
0002AC39: CC 3C
0002AC3A: CC B7
0002AC3B: CC FF
0002AC3C: CC D0
0002AC3D: CC EB
0002AC3E: CC 25
0002AC64: CC 85
0002AC65: CC C0
0002AC66: CC E9
0002AC67: CC 69
0002AC68: CC B3
0002AC69: CC 4E
0002AC6A: CC 00
00515FCE: 8B E9
00515FCF: FD 61
00515FD0: FF 4C
00515FD1: D0 B1
00515FD2: 85 FF
00515FD3: C0 90
0051600A: 8B 90
0051600B: FD 90
00516014: 8B 90
00516015: FD 90

@ -0,0 +1,61 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994
Original code:
.text:008629BD mov edi, [eax+38h]
.text:008629C0 mov eax, [eax+3Ch]
.text:008629C3 mov [esp+1Ch], eax
.text:008629C7 cmp edi, eax
.text:008629C9 jnb short loc_862A22
.text:008629CB jmp short loc_8629D0
.text:008629CD lea ecx, [ecx+0]
...
.text:00862A19 add edi, 4
.text:00862A1C cmp edi, [esp+1Ch]
.text:00862A20 jb short loc_8629D0
Patch:
0x461dbd
8B 78 38 8B 40 3C 89 44 24 1C 3B F8
8B 78 3C 8B 40 38 89 44 24 1C 39 F8
.text:008629BD mov edi, [eax+3Ch]
.text:008629C0 mov eax, [eax+38h]
.text:008629C3 mov [esp+1Ch], eax
.text:008629C7 cmp eax, edi
0x461dcb
EB 03 8D 49 00
83 EF 04 90 90
.text:008629CB sub edi, 4
.text:008629CE nop
.text:008629CF nop
0x461e19
83 C7 04 3B 7C 24 1C 72 AE
83 EF 04 3B 7C 24 1C 73 AE
.text:00862A19 sub edi, 4
.text:00862A1C cmp edi, [esp+1Ch]
.text:00862A20 jnb short loc_8629D0
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
00461DBF: 38 3C
00461DC2: 3C 38
00461DC7: 3B 39
00461DCB: EB 83
00461DCC: 03 EF
00461DCD: 8D 04
00461DCE: 49 90
00461DCF: 00 90
00461E1A: C7 EF
00461E20: 72 73

@ -0,0 +1,104 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994
0x461de2
F6 46 0C 01 74 31
E9 0A 8E BC FF 90
.text:008629E2 jmp near ptr loc_42B7F1 ; << CAVE
.text:008629E7 nop
.text:008629E8 loc_8629E8:
0x2abf1
CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
8B 4C 24 2C F6 46 0C 01 75 08 E9 19 72 43 00
.text:0042B7F1 loc_42B7F1:
.text:0042B7F1 mov ecx, [esp+2Ch] ; job
.text:0042B7F5 test byte ptr [esi+0Ch], 1
.text:0042B7F9 jnz short near ptr loc_42B803
.text:0042B7FB coord_test_jfail:
.text:0042B7FB jmp loc_862A19
0x2ac03
CC CC CC CC CC CC CC CC CC CC CC CC CC
8B 41 10 3B 46 04 75 F0 EB 06 CC CC CC
.text:0042B803 loc_42B803:
.text:0042B803 mov eax, [ecx+10h] ; job->pos.(x,y)
.text:0042B806 cmp eax, [esi+4] ; item->pos.(x,y)
.text:0042B809 jnz short coord_test_jfail
.text:0042B80B jmp short near ptr loc_42B813
0x2ac13
CC CC CC CC CC CC CC CC CC CC CC CC CC
66 8B 41 14 66 3B 46 08 75 DE EB 06 CC
text:0042B813 loc_42B813:
.text:0042B813 mov ax, [ecx+14h] ; job->pos.z
.text:0042B817 cmp ax, [esi+8] ; item->pos.z
.text:0042B81B jnz short coord_test_jfail
.text:0042B81D jmp short near ptr loc_42B825
0x2ac25
CC CC CC CC CC CC CC CC CC CC CC
E9 BE 71 43 00 CC CC CC CC CC CC
.text:0042B825 loc_42B825:
.text:0042B825 jmp loc_8629E8
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
0002ABF1: CC 8B
0002ABF2: CC 4C
0002ABF3: CC 24
0002ABF4: CC 2C
0002ABF5: CC F6
0002ABF6: CC 46
0002ABF7: CC 0C
0002ABF8: CC 01
0002ABF9: CC 75
0002ABFA: CC 08
0002ABFB: CC E9
0002ABFC: CC 19
0002ABFD: CC 72
0002ABFE: CC 43
0002ABFF: CC 00
0002AC03: CC 8B
0002AC04: CC 41
0002AC05: CC 10
0002AC06: CC 3B
0002AC07: CC 46
0002AC08: CC 04
0002AC09: CC 75
0002AC0A: CC F0
0002AC0B: CC EB
0002AC0C: CC 06
0002AC13: CC 66
0002AC14: CC 8B
0002AC15: CC 41
0002AC16: CC 14
0002AC17: CC 66
0002AC18: CC 3B
0002AC19: CC 46
0002AC1A: CC 08
0002AC1B: CC 75
0002AC1C: CC DE
0002AC1D: CC EB
0002AC1E: CC 06
0002AC25: CC E9
0002AC26: CC BE
0002AC27: CC 71
0002AC28: CC 43
0002AC29: CC 00
00461DE2: F6 E9
00461DE3: 46 0A
00461DE4: 0C 8E
00461DE5: 01 BC
00461DE6: 74 FF
00461DE7: 31 90

@ -0,0 +1,62 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=4406
1. Include store in hospital jobs when recomputing counters
0x68a63
0F 85 58 01 00 00
90 90 90 90 90 90
<<<<
.text:00469663 jnz loc_4697C1
====
.text:00469663 nop
.text:00469664 nop
.text:00469665 nop
.text:00469666 nop
.text:00469667 nop
.text:00469668 nop
>>>>
- if (job->getBuildingRef(BUILDING_DESTINATION) != this) continue;
+ // NOP
This reference points to the containers, not the hospital civzone.
Since fixing this properly is too hard for a patch, just remove the
check. Most people have only one hospital anyway, and it is better
to err on the side of caution here.
2. Make the stockpiling code increment the right stock counters
0x3dcbf9
8B 0C 90 8B 81 80 00 00 00
8B 3C 90 8B 87 80 00 00 00
<<<<
.text:007DD7F9 mov ecx, [eax+edx*4]
.text:007DD7FC mov eax, [ecx+80h]
====
.text:007DD7F9 mov edi, [eax+edx*4]
.text:007DD7FC mov eax, [edi+80h]
>>>>
- id = civzones[i]->children[child_idx[i]]->id
+ cur_civzone = civzones[i] // existing var from previous loop
+ id = cur_civzone->children[child_idx[i]]->id
The reason being, later code uses that var (at this point containing
useless data) to increment counters and amounts in the hospital.
---8<---
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
00068A63: 0F 90
00068A64: 85 90
00068A65: 58 90
00068A66: 01 90
00068A67: 00 90
00068A68: 00 90
003DCBFA: 0C 3C
003DCBFD: 81 87

@ -0,0 +1,83 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=4530
0x2ac85
CC CC CC CC CC CC CC CC CC CC CC
29 44 24 24 8B 9D 28 01 00 00 C3
.text:0042B885 loc_42B885:
.text:0042B885 sub [esp+24h], eax
.text:0042B889 mov ebx, [ebp+128h]
.text:0042B88F retn
0x2ac94
CC CC CC CC CC CC CC CC CC CC CC CC
89 C1 8B 00 FF 90 34 02 00 00 EB E5
.text:0042B894 loc_42B894:
.text:0042B894 mov ecx, eax
.text:0042B896 mov eax, [eax]
.text:0042B898 call dword ptr [eax+234h]
.text:0042B89E jmp short loc_42B885
0x6e28ff
29 44 24 20
90 90 90 90
<<<<
.text:00AE34FF sub [esp+20h], eax
====
.text:00AE34FF nop
.text:00AE3500 nop
.text:00AE3501 nop
.text:00AE3502 nop
>>>>
0x6e2999
8B 9D 28 01 00 00
E8 F6 82 94 FF 90
<<<<
.text:00AE3599 mov ebx, [ebp+128h]
====
.text:00AE3599 call loc_42B894
.text:00AE359E nop
>>>>
---8<---
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
0002AC85: CC 29
0002AC86: CC 44
0002AC87: CC 24
0002AC88: CC 24
0002AC89: CC 8B
0002AC8A: CC 9D
0002AC8B: CC 28
0002AC8C: CC 01
0002AC8D: CC 00
0002AC8E: CC 00
0002AC8F: CC C3
0002AC94: CC 89
0002AC95: CC C1
0002AC96: CC 8B
0002AC97: CC 00
0002AC98: CC FF
0002AC99: CC 90
0002AC9A: CC 34
0002AC9B: CC 02
0002AC9C: CC 00
0002AC9D: CC 00
0002AC9E: CC EB
0002AC9F: CC E5
006E28FF: 29 90
006E2900: 44 90
006E2901: 24 90
006E2902: 20 90
006E2999: 8B E8
006E299A: 9D F6
006E299B: 28 82
006E299C: 01 94
006E299D: 00 FF
006E299E: 00 90

@ -0,0 +1,61 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
0x4c05c4
8B 8C 24 80 00 00 00
89 C1 90 90 90 90 90
<<<<
.text:008C11C4 mov ecx, [esp+98h+var_18]
====
.text:008C11C4 mov ecx, eax
.text:008C11C6 nop
.text:008C11C7 nop
.text:008C11C8 nop
.text:008C11C9 nop
.text:008C11CA nop
>>>>
0x4c06a1
8B 8C 24 80 00 00 00
89 C1 90 90 90 90 90
<<<<
.text:008C12A1 mov ecx, [esp+98h+var_18]
====
.text:008C12A1 mov ecx, eax
.text:008C12A3 nop
.text:008C12A4 nop
.text:008C12A5 nop
.text:008C12A6 nop
.text:008C12A7 nop
>>>>
basically:
b_squad_id = building->getSpecificSquad();
- if (b_squad_id != squad->id || !building->canUse(some_squad_id, 4))
+ if (b_squad_id != squad->id || !building->canUse(b_squad_id, 4))
unassign(building);
the reason being, some_other_squad_id contains irrelevant garbage at this point
---8<---
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
004C05C4: 8B 89
004C05C5: 8C C1
004C05C6: 24 90
004C05C7: 80 90
004C05C8: 00 90
004C05C9: 00 90
004C05CA: 00 90
004C06A1: 8B 89
004C06A2: 8C C1
004C06A3: 24 90
004C06A4: 80 90
004C06A5: 00 90
004C06A6: 00 90
004C06A7: 00 90

@ -0,0 +1,147 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
0x9461
90 90 90 90 90 90 90 90 90 90 90 90 90 90
C7 44 24 18 01 00 00 00 FF A0 44 01 00 00
.text:08051461 sub_8051461 proc near
.text:08051461 mov dword ptr [esp+18h], 1
.text:08051469 jmp dword ptr [eax+144h]
.text:08051469 sub_8051461 endp
0x9548
90 90 90 90 90 90 90 90
FF 4C 24 14 78 08 EB 0B
.text:08051548 loc_8051548:
.text:08051548 dec dword ptr [esp+14h]
.text:0805154C js short loc_8051556
.text:0805154E jmp short loc_805155B
0x9556
90 90 90 90 90 90 90 90 90 90
E9 F6 8C 05 00 E9 D0 8D 05 00
.text:08051556 loc_8051556:
.text:08051556 jmp loc_80AA251
.text:0805155B loc_805155B:
.text:0805155B jmp loc_80AA330
0x9568
90 90 90 90 90 90 90 90
FF 4C 24 14 78 E8 EB 06
.text:08051568 loc_8051568:
.text:08051568 dec [esp+14h]
.text:0805156C js short loc_8051556
.text:0805156E jmp short loc_8051576
0x9576
90 90 90 90 90
E9 4D 8E 05 00
.text:08051576 loc_8051576:
.text:08051576 jmp loc_80AA3C8
0x62243
FF 90 44 01 00 00
E8 19 72 FA FF 90
<<<<
.text:080AA243 call dword ptr [eax+144h]
====
.text:080AA243 call sub_8051461
.text:080AA248 nop
>>>>
0x62369
E9 E3 FE FF FF
E9 DA 71 FA FF
<<<<
.text:080AA369 jmp loc_80AA251
====
.text:080AA369 jmp loc_8051548
>>>>
0x623f6
E9 56 FE FF FF
E9 6D 71 FA FF
<<<<
.text:080AA3F6 jmp loc_80AA251
====
.text:080AA3F6 jmp loc_8051568
>>>>
basically:
+ int allowed_count = 1; // to mean 2
...
- if (type(item) == new_type)
+ if (type(item) == new_type && --allowed_count < 0)
return false;
to allow up to two items of the same type at the same time
---8<---
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
00009461: 90 C7
00009462: 90 44
00009463: 90 24
00009464: 90 18
00009465: 90 01
00009466: 90 00
00009467: 90 00
00009468: 90 00
00009469: 90 FF
0000946A: 90 A0
0000946B: 90 44
0000946C: 90 01
0000946D: 90 00
0000946E: 90 00
00009548: 90 FF
00009549: 90 4C
0000954A: 90 24
0000954B: 90 14
0000954C: 90 78
0000954D: 90 08
0000954E: 90 EB
0000954F: 90 0B
00009556: 90 E9
00009557: 90 F6
00009558: 90 8C
00009559: 90 05
0000955A: 90 00
0000955B: 90 E9
0000955C: 90 D0
0000955D: 90 8D
0000955E: 90 05
0000955F: 90 00
00009568: 90 FF
00009569: 90 4C
0000956A: 90 24
0000956B: 90 14
0000956C: 90 78
0000956D: 90 E8
0000956E: 90 EB
0000956F: 90 06
00009576: 90 E9
00009577: 90 4D
00009578: 90 8E
00009579: 90 05
0000957A: 90 00
00062243: FF E8
00062244: 90 19
00062245: 44 72
00062246: 01 FA
00062247: 00 FF
00062248: 00 90
0006236A: E3 DA
0006236B: FE 71
0006236C: FF FA
000623F7: 56 6D
000623F8: FE 71
000623F9: FF FA

@ -0,0 +1,40 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=808
for (i = 0; i < num_items; i++)
{
ridx = reagent_idx[i];
sz = reagent_quantity[ridx]; // used quantity
if (sz <= 0) continue;
reag = reagent[ridx];
if (reag->flags.PRESERVE_REAGENT) continue;
rsz = items[i]->getTotalDimension();
<<<<<<<<
if (reag->flags3.ANY_RAW_MATERIAL)
rsz *= BASE_SIZE(items[i]->getType());
if (items[i]->subtractDimension(rsz))
========
/* Not in patch, but necessary for full correctness:
if (reag->flags3.ANY_RAW_MATERIAL)
rsz /= BASE_SIZE(items[i]->getType());
*/
if (reag->flags3.ANY_RAW_MATERIAL)
sz *= BASE_SIZE(items[i]->getType());
if (items[i]->subtractDimension(sz))
>>>>>>>>
destroy_item(items[i]);
reagent_quantity[ridx] -= rsz
if (reagent_quantity[ridx] < 0)
reagent_quantity[ridx] = 0;
}
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
0087F7EF: F8 D8
0087F86F: F8 D8
0087F9AD: C7 C3
0087F9ED: C7 C3

@ -0,0 +1,83 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994
Original code:
.text:087AC378 cmp edx, eax
.text:087AC37A mov [esp+4Ch], eax
.text:087AC37E jnb loc_87A7034
.text:087AC384 mov [esp+48h], edx
.text:087AC388 mov [esp+54h], ebx
...
.text:087AC440 add dword ptr [esp+48h], 4
.text:087AC445 mov ebp, [esp+48h]
.text:087AC449 cmp [esp+4Ch], ebp
.text:087AC44D ja loc_87AC38C
Patch:
0x76437a
89 44 24 4C
89 54 24 4C
.text:087AC37A mov [esp+4Ch], edx
0x764384
89 54 24 48 89 5C 24 54
E8 8A 51 8A FF 90 90 90
.text:087AC384 call sub_8051513
.text:087AC389 nop
.text:087AC38A nop
.text:087AC38B nop
0x764440
83 44 24 48 04 8B 6C 24 48 39 6C 24 4C 0F 87 39 FF FF FF
83 6C 24 48 04 8B 6C 24 48 39 6C 24 4C 0F 86 39 FF FF FF
.text:087AC440 sub dword ptr [esp+48h], 4
.text:087AC445 mov ebp, [esp+48h]
.text:087AC449 cmp [esp+4Ch], ebp
.text:087AC44D jbe loc_87AC38C
0x9513
90 90 90 90 90 90 90 90 90 90 90 90 90
83 E8 04 89 44 24 4C 89 5C 24 58 C3 90
.text:08051513 sub_8051513 proc near
.text:08051513 sub eax, 4
.text:08051516 mov [esp+4Ch], eax ; 48h
.text:0805151A mov [esp+58h], ebx ; 54h
.text:0805151E retn
.text:0805151E sub_8051513 endp
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
00009513: 90 83
00009514: 90 E8
00009515: 90 04
00009516: 90 89
00009517: 90 44
00009518: 90 24
00009519: 90 4C
0000951A: 90 89
0000951B: 90 5C
0000951C: 90 24
0000951D: 90 58
0000951E: 90 C3
0076437B: 44 54
00764384: 89 E8
00764385: 54 8A
00764386: 24 51
00764387: 48 8A
00764388: 89 FF
00764389: 5C 90
0076438A: 24 90
0076438B: 54 90
00764441: 44 6C
0076444E: 87 86

@ -0,0 +1,139 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994
0x7643f8
F6 46 0C 01 74 42
E9 B6 50 8A FF 90
.text:087AC3F8 jmp loc_80514B3 ; << CAVE
.text:087AC3FD nop
.text:087AC3FE loc_87AC3FE:
0x94b3
90 90 90 90 90 90 90 90 90 90 90 90 90
F6 46 0C 01 75 0A E9 82 AF 75 00 90 90
.text:080514B3 loc_80514B3:
.text:080514B3 test byte ptr [esi+0Ch], 1
.text:080514B7 jnz short loc_80514C3
.text:080514B9 coord_test_jfail:
.text:080514B9 jmp loc_87AC440
0x94c3
90 90 90 90 90 90 90 90 90 90 90 90 90
8D 9C 24 60 03 00 00 0F BF 03 EB 07 90
.text:080514C3 loc_80514C3:
.text:080514C3 lea ebx, [esp+360h]
.text:080514CA movsx eax, word ptr [ebx] ; job_z
.text:080514CD jmp short loc_80514D6
0x94d6
90 90 90 90 90 90 90 90 90 90
66 3B 46 08 75 DD EB 05 90 90
.text:080514D6 loc_80514D6:
.text:080514D6 cmp ax, [esi+8] ; item->pos.z
.text:080514DA jnz short coord_test_jfail
.text:080514DC jmp short loc_80514E3
0x94e3
90 90 90 90 90 90 90 90 90 90 90 90 90
0F BF 43 10 66 3B 46 04 75 CC EB 04 90
.text:080514E3 loc_80514E3:
.text:080514E3 movsx eax, word ptr [ebx+10h] ; job_x
.text:080514E7 cmp ax, [esi+4] ; item->pos.x
.text:080514EB jnz short coord_test_jfail
.text:080514ED jmp short loc_80514F3
0x94f3
90 90 90 90 90 90 90 90 90 90 90 90 90
0F BF 43 20 66 3B 46 06 75 BC EB 04 90
.text:080514F3 loc_80514F3:
.text:080514F3 movsx eax, word ptr [ebx+20h] ; job_y
.text:080514F7 cmp ax, [esi+6] ; item->pos.y
.text:080514FB jnz short coord_test_jfail
.text:080514FD jmp short loc_8051503
0x9503
90 90 90 90 90 90 90 90 90 90 90 90 90
E9 F6 AE 75 00 90 90 90 90 90 90 90 90
.text:08051503 loc_8051503:
.text:08051503 jmp loc_87AC3FE
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
000094B3: 90 F6
000094B4: 90 46
000094B5: 90 0C
000094B6: 90 01
000094B7: 90 75
000094B8: 90 0A
000094B9: 90 E9
000094BA: 90 82
000094BB: 90 AF
000094BC: 90 75
000094BD: 90 00
000094C3: 90 8D
000094C4: 90 9C
000094C5: 90 24
000094C6: 90 60
000094C7: 90 03
000094C8: 90 00
000094C9: 90 00
000094CA: 90 0F
000094CB: 90 BF
000094CC: 90 03
000094CD: 90 EB
000094CE: 90 07
000094D6: 90 66
000094D7: 90 3B
000094D8: 90 46
000094D9: 90 08
000094DA: 90 75
000094DB: 90 DD
000094DC: 90 EB
000094DD: 90 05
000094E3: 90 0F
000094E4: 90 BF
000094E5: 90 43
000094E6: 90 10
000094E7: 90 66
000094E8: 90 3B
000094E9: 90 46
000094EA: 90 04
000094EB: 90 75
000094EC: 90 CC
000094ED: 90 EB
000094EE: 90 04
000094F3: 90 0F
000094F4: 90 BF
000094F5: 90 43
000094F6: 90 20
000094F7: 90 66
000094F8: 90 3B
000094F9: 90 46
000094FA: 90 06
000094FB: 90 75
000094FC: 90 BC
000094FD: 90 EB
000094FE: 90 04
00009503: 90 E9
00009504: 90 F6
00009505: 90 AE
00009506: 90 75
00009507: 90 00
007643F8: F6 E9
007643F9: 46 B6
007643FA: 0C 50
007643FB: 01 8A
007643FC: 74 FF
007643FD: 42 90

@ -0,0 +1,60 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=4406
1. Include store in hospital jobs when recomputing counters
0x746d7
75 D7
90 90
<<<<
.text:080BC6D7 jnz short loc_80BC6B0
====
.text:080BC6D7 nop
.text:080BC6D8 nop
>>>>
- if (job->getBuildingRef(BUILDING_DESTINATION) != this) continue;
+ // NOP
This reference points to the containers, not the hospital civzone.
Since fixing this properly is too hard for a patch, just remove the
check. Most people have only one hospital anyway, and it is better
to err on the side of caution here.
2. Make the stockpiling code increment the right stock counters
0x67cb0e
0B 04 90
8B 1C 90
0x67cb18
8B 40 74
8B 43 74
<<<<
.text:086C4B0E mov eax, [eax+edx*4]
.text:086C4B11 mov edx, [esp+ecx*4+39Ch+var_2B4]
.text:086C4B18 mov eax, [eax+74h]
====
.text:086C4B0E mov ebx, [eax+edx*4]
.text:086C4B11 mov edx, [esp+ecx*4+39Ch+var_2B4]
.text:086C4B18 mov eax, [ebx+74h]
>>>>
- id = civzones[i]->children[child_idx[i]]->id
+ cur_civzone = civzones[i] // existing var from previous loop
+ id = cur_civzone->children[child_idx[i]]->id
The reason being, later code uses that var (at this point containing
useless data) to increment counters and amounts in the hospital.
---8<---
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
000746D7: 75 90
000746D8: D7 90
0067CB0F: 04 1C
0067CB19: 40 43

@ -0,0 +1,85 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=4530
0x9508
90 90 90 90 90 90 90 90
E9 13 76 9C 00 90 90 90
.text:08051508 loc_8051508:
.text:08051508 jmp sub_8A18B20
0x9523
90 90 90 90 90 90 90 90 90 90 90 90 90
50 8B 03 53 FF 90 34 02 00 00 EB 09 90
.text:08051523 sub_8051523:
.text:08051523 push eax
.text:08051524 mov eax, [ebx]
.text:08051526 push ebx
.text:08051527 call dword ptr [eax+234h]
.text:0805152D jmp short loc_8051538
0x9538
90 90 90 90 90 90 90 90
29 44 24 64 5B 58 EB C8
.text:08051538 loc_8051538:
.text:08051538 sub [esp+64h], eax
.text:0805153C pop ebx
.text:0805153D pop eax
.text:0805153E jmp short loc_8051508
0xa5cdd0
29 44 24 58
90 90 90 90
.text:08AA4DD0 nop
.text:08AA4DD1 nop
.text:08AA4DD2 nop
.text:08AA4DD3 nop
0xa5e2c3
E8 58 28 F7 FF
E8 5B B2 5A FF
.text:08AA62C3 call sub_8051523
---8<---
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
00009508: 90 E9
00009509: 90 13
0000950A: 90 76
0000950B: 90 9C
0000950C: 90 00
00009523: 90 50
00009524: 90 8B
00009525: 90 03
00009526: 90 53
00009527: 90 FF
00009529: 90 34
0000952A: 90 02
0000952B: 90 00
0000952C: 90 00
0000952D: 90 EB
0000952E: 90 09
00009538: 90 29
00009539: 90 44
0000953A: 90 24
0000953B: 90 64
0000953C: 90 5B
0000953D: 90 58
0000953E: 90 EB
0000953F: 90 C8
00A5CDD0: 29 90
00A5CDD1: 44 90
00A5CDD2: 24 90
00A5CDD3: 58 90
00A5E2C4: 58 5B
00A5E2C5: 28 B2
00A5E2C6: F7 5A

@ -0,0 +1,45 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
Fix use of uninitialized variables to stop auto-unassigning racks:
0x7ee948
8B 7C 24 3C
89 C7 90 90
.text:08836948 mov edi, eax
.text:0883694A nop
.text:0883694B nop
0x7eea2f
8B 7C 24 3C
89 C7 90 90
.text:08836A2F mov edi, eax
.text:08836A31 nop
.text:08836A32 nop
basically:
b_squad_id = building->getSpecificSquad();
- if (b_squad_id != squad->id || !building->canUse(some_squad_id, 4))
+ if (b_squad_id != squad->id || !building->canUse(b_squad_id, 4))
unassign(building);
the reason being, some_other_squad_id contains irrelevant garbage at this point
---8<---
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
007EE948: 8B 89
007EE949: 7C C7
007EE94A: 24 90
007EE94B: 3C 90
007EEA2F: 8B 89
007EEA30: 7C C7
007EEA31: 24 90
007EEA32: 3C 90

@ -100,7 +100,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(stockpiles stockpiles.cpp) DFHACK_PLUGIN(stockpiles stockpiles.cpp)
DFHACK_PLUGIN(rename rename.cpp LINK_LIBRARIES lua PROTOBUFS rename) DFHACK_PLUGIN(rename rename.cpp LINK_LIBRARIES lua PROTOBUFS rename)
DFHACK_PLUGIN(jobutils jobutils.cpp) DFHACK_PLUGIN(jobutils jobutils.cpp)
DFHACK_PLUGIN(workflow workflow.cpp) DFHACK_PLUGIN(workflow workflow.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(showmood showmood.cpp) DFHACK_PLUGIN(showmood showmood.cpp)
DFHACK_PLUGIN(fixveins fixveins.cpp) DFHACK_PLUGIN(fixveins fixveins.cpp)
DFHACK_PLUGIN(fixpositions fixpositions.cpp) DFHACK_PLUGIN(fixpositions fixpositions.cpp)
@ -117,16 +117,21 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(regrass regrass.cpp) DFHACK_PLUGIN(regrass regrass.cpp)
DFHACK_PLUGIN(forceequip forceequip.cpp) DFHACK_PLUGIN(forceequip forceequip.cpp)
DFHACK_PLUGIN(manipulator manipulator.cpp) DFHACK_PLUGIN(manipulator manipulator.cpp)
DFHACK_PLUGIN(search search.cpp)
DFHACK_PLUGIN(automaterial automaterial.cpp)
# this one exports functions to lua # this one exports functions to lua
DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(steam-engine steam-engine.cpp) DFHACK_PLUGIN(steam-engine steam-engine.cpp)
DFHACK_PLUGIN(power-meter power-meter.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(power-meter power-meter.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(siege-engine siege-engine.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(siege-engine siege-engine.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(reactionhooks reactionhooks.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(add-spatter add-spatter.cpp) DFHACK_PLUGIN(add-spatter add-spatter.cpp)
DFHACK_PLUGIN(fix-armory fix-armory.cpp)
# not yet. busy with other crud again... # not yet. busy with other crud again...
#DFHACK_PLUGIN(versionosd versionosd.cpp) #DFHACK_PLUGIN(versionosd versionosd.cpp)
DFHACK_PLUGIN(misery misery.cpp) DFHACK_PLUGIN(misery misery.cpp)
#DFHACK_PLUGIN(dfstream dfstream.cpp LINK_LIBRARIES clsocket dfhack-tinythread)
endif() endif()

@ -1,5 +1,5 @@
include_directories(include) include_directories(include)
include_directories("${dfhack_SOURCE_DIR}/library/depends/tthread")
FILE(GLOB DFUSION_CPPS src/*.c*) FILE(GLOB DFUSION_CPPS src/*.c*)
set( set(
DFUSION_CPPS_ALL DFUSION_CPPS_ALL
@ -9,6 +9,3 @@ set(
FILE(GLOB DFUSION_HS include/*) FILE(GLOB DFUSION_HS include/*)
SET_SOURCE_FILES_PROPERTIES( ${DFUSION_HS} PROPERTIES HEADER_FILE_ONLY TRUE ) SET_SOURCE_FILES_PROPERTIES( ${DFUSION_HS} PROPERTIES HEADER_FILE_ONLY TRUE )
DFHACK_PLUGIN(dfusion ${DFUSION_CPPS_ALL} ${DFUSION_HS} LINK_LIBRARIES lua dfhack-tinythread) DFHACK_PLUGIN(dfusion ${DFUSION_CPPS_ALL} ${DFUSION_HS} LINK_LIBRARIES lua dfhack-tinythread)
# installs into DF root
install(DIRECTORY luafiles/ DESTINATION dfusion)

@ -6,18 +6,11 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include "tinythread.h"
#include "luamain.h" #include "luamain.h"
#include "lua_Process.h" #include "lua_Process.h"
#include "lua_Hexsearch.h" #include "lua_Hexsearch.h"
#include "lua_Misc.h" #include "lua_Misc.h"
#include "lua_VersionInfo.h"
#include "functioncall.h"
#include "lua_FunctionCall.h"
#include "lua_Offsets.h"
#include "DataDefs.h" #include "DataDefs.h"
#include "LuaTools.h" #include "LuaTools.h"
@ -25,177 +18,71 @@ using std::vector;
using std::string; using std::string;
using namespace DFHack; using namespace DFHack;
static tthread::mutex* mymutex=0;
static tthread::thread* thread_dfusion=0;
uint64_t timeLast=0;
DFHACK_PLUGIN("dfusion")
command_result dfusion (color_ostream &out, std::vector <std::string> &parameters);
command_result dfuse (color_ostream &out, std::vector <std::string> &parameters);
command_result lua_run (color_ostream &out, std::vector <std::string> &parameters);
command_result lua_run_file (color_ostream &out, std::vector <std::string> &parameters);
DFhackCExport const char * plugin_name ( void )
{
return "dfusion";
}
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands) DFHACK_PLUGIN("dfusion")
{
lua::state st=lua::glua::Get();
//maybe remake it to run automaticaly
Lua::Open(out, st);
lua::RegisterProcess(st);
lua::RegisterHexsearch(st);
lua::RegisterMisc(st);
lua::RegisterVersionInfo(st);
lua::RegisterFunctionCall(st);
lua::RegisterEngine(st);
#ifdef LINUX_BUILD
st.push(1);
st.setglobal("LINUX");
#else
st.push(1);
st.setglobal("WINDOWS");
#endif
commands.push_back(PluginCommand("dfusion","Run dfusion system (interactive i.e. can input further commands).",dfusion,true));
commands.push_back(PluginCommand("dfuse","Init dfusion system (not interactive).",dfuse,false));
commands.push_back(PluginCommand("lua", "Run interactive interpreter. Use 'lua <filename>' to run <filename> instead.",lua_run,true));
commands.push_back(PluginCommand("runlua", "Run non-interactive interpreter. Use 'runlua <filename>' to run <filename>.",lua_run_file,false));
mymutex=new tthread::mutex;
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( Core * c ) static int loadObjectFile(lua_State* L)
{ {
std::string path;
path=luaL_checkstring(L,1);
OutFile::File f(path);
lua_newtable(L);
int table_pos=lua_gettop(L);
size_t size=f.GetTextSize();
Lua::Push(L,size);
lua_setfield(L,table_pos,"data_size");
char* buf=new char[size];
f.GetText(buf);
//Lua::PushDFObject(L,DFHack::,buf);
//Lua::Push(L,buf);
lua_pushlightuserdata(L,buf);
lua_setfield(L,table_pos,"data");
const OutFile::vSymbol &symbols=f.GetSymbols();
lua_newtable(L);
for(size_t i=0;i<symbols.size();i++)
{
Lua::Push(L,i);
lua_newtable(L);
Lua::Push(L,symbols[i].name);
lua_setfield(L,-2,"name");
Lua::Push(L,symbols[i].pos);
lua_setfield(L,-2,"pos");
// shutdown stuff
if(thread_dfusion)
delete thread_dfusion;
delete mymutex;
return CR_OK;
}
DFhackCExport command_result plugin_onupdate_DISABLED ( Core * c ) lua_settable(L,-3);
{ }
uint64_t time2 = GetTimeMs64(); lua_setfield(L,table_pos,"symbols");
uint64_t delta = time2-timeLast; return 1;
if(delta<100)
return CR_OK;
timeLast = time2;
mymutex->lock();
lua::state s=lua::glua::Get();
s.getglobal("OnTick");
if(s.is<lua::function>())
{
try{
s.pcall();
}
catch(lua::exception &e)
{
c->getConsole().printerr("Error OnTick:%s\n",e.what());
c->getConsole().printerr("%s\n",lua::DebugDump(lua::glua::Get()).c_str());
c->getConsole().msleep(1000);
}
}
s.settop(0);
mymutex->unlock();
return CR_OK;
}
command_result lua_run_file (color_ostream &out, std::vector <std::string> &parameters)
{
if(parameters.size()==0)
{
out.printerr("runlua without file to run!");
return CR_FAILURE;
}
return lua_run(out,parameters);
} }
command_result lua_run (color_ostream &out, std::vector <std::string> &parameters) static int markAsExecutable(lua_State* L)
{ {
if (!parameters.empty()) unsigned addr=luaL_checkunsigned(L,1);
std::vector<DFHack::t_memrange> ranges;
DFHack::Core::getInstance().p->getMemRanges(ranges);
for(size_t i=0;i<ranges.size();i++)
{ {
if (parameters[0] == "--core-context") if(ranges[i].isInRange((void*)addr))
{ {
Lua::InterpreterLoop(out, Lua::Core::State, "core lua"); DFHack::t_memrange newperm=ranges[i];
return CR_OK; newperm.execute=true;
} DFHack::Core::getInstance().p->setPermisions(ranges[i],newperm);
else if (parameters[0] == "--core-reload") return 0;
{
CoreSuspender suspend;
for (size_t i = 1; i < parameters.size(); i++)
{
lua_getglobal(Lua::Core::State, "reload");
lua_pushstring(Lua::Core::State, parameters[i].c_str());
Lua::SafeCall(out, Lua::Core::State, 1, 0);
}
return CR_OK;
} }
} }
lua_pushlstring(L,"Memory range not found",23);
mymutex->lock(); lua_error(L);
lua::state s=lua::glua::Get(); return 0;
if(parameters.size()>0)
{
try{
s.loadfile(parameters[0]); //load file
for(size_t i=1;i<parameters.size();i++)
s.push(parameters[i]);
Lua::SafeCall(out, s, parameters.size()-1,0);// run it
}
catch(lua::exception &e)
{
out.printerr("Error:%s\n",e.what());
out.printerr("%s\n",lua::DebugDump(lua::glua::Get()).c_str());
}
}
else
{
Lua::InterpreterLoop(out, s);
}
s.settop(0);// clean up
mymutex->unlock();
return CR_OK;
}
void RunDfusion(color_ostream &out, std::vector <std::string> &parameters)
{
mymutex->lock();
lua::state s=lua::glua::Get();
try{
s.loadfile("dfusion/init.lua"); //load script
for(size_t i=0;i<parameters.size();i++)
s.push(parameters[i]);
Lua::SafeCall(out, s, parameters.size(),0);
}
catch(lua::exception &e)
{
out.printerr("Error:%s\n",e.what());
out.printerr("%s\n",lua::DebugDump(lua::glua::Get()).c_str());
}
s.settop(0);// clean up
mymutex->unlock();
}
command_result dfuse(color_ostream &out, std::vector <std::string> &parameters)
{
lua::state s=lua::glua::Get();
s.push(1);
s.setglobal("INIT");
RunDfusion(out,parameters);
return CR_OK;
} }
command_result dfusion (color_ostream &out, std::vector <std::string> &parameters) DFHACK_PLUGIN_LUA_COMMANDS {
DFHACK_LUA_COMMAND(loadObjectFile),
DFHACK_LUA_COMMAND(markAsExecutable),
DFHACK_LUA_END
};
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{ {
lua::state s=lua::glua::Get(); return CR_OK;
s.push();
s.setglobal("INIT");
RunDfusion(out,parameters);
return CR_OK;
} }

@ -105,7 +105,7 @@ public:
void GetText(char *ptr); void GetText(char *ptr);
size_t GetTextSize(); size_t GetTextSize();
void LoadSymbols(); void LoadSymbols();
vSymbol GetSymbols(){LoadSymbols();return symbols;}; const vSymbol& GetSymbols(){LoadSymbols();return symbols;};
void PrintSymbols(); void PrintSymbols();
void PrintRelocations(); void PrintRelocations();
protected: protected:

@ -1,26 +0,0 @@
#ifndef FUNCTIONCALL__H
#define FUNCTIONCALL__H
#include <vector>
using std::vector;
using std::size_t;
class FunctionCaller
{
public:
enum callconv
{
STD_CALL, //__stdcall - all in stack
FAST_CALL, //__fastcall - as much in registers as fits
THIS_CALL, //__thiscall - eax ptr to this, rest in stack
CDECL_CALL //__cdecl - same as stdcall but no stack realign
};
FunctionCaller(size_t base):base_(base){};
int CallFunction(size_t func_ptr,callconv conv,const vector<int> &arguments);
private:
int CallF(size_t count,callconv conv,void* f,const vector<int> &arguments);
size_t base_;
};
#endif //FUNCTIONCALL__H

@ -1,14 +0,0 @@
#ifndef LUA_FUNCTIONCALL__H
#define LUA_FUNCTIONCALL__H
#include "luamain.h"
#include "functioncall.h"
namespace lua
{
void RegisterFunctionCall(lua::state &st);
}
#endif

Some files were not shown because too many files have changed in this diff Show More