Merge branch 'master' into diggingInvaders

develop
expwnent 2012-12-16 16:34:45 -05:00
commit 8a242b3c0d
225 changed files with 30378 additions and 4236 deletions

3
.gitignore vendored

@ -36,6 +36,9 @@ build/tools
build/plugins build/plugins
build/depends build/depends
build/install_manifest.txt build/install_manifest.txt
build/README.html
build/LUA_API.html
build/COMPILE.html
#ignore Kdevelop stuff #ignore Kdevelop stuff
.kdev4 .kdev4

@ -0,0 +1,3 @@
FIND_PROGRAM(RST2HTML_EXECUTABLE NAMES rst2html rst2html.py)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Docutils DEFAULT_MSG RST2HTML_EXECUTABLE)

@ -63,7 +63,7 @@ set(DF_VERSION_MINOR "34")
set(DF_VERSION_PATCH "11") set(DF_VERSION_PATCH "11")
set(DF_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}") set(DF_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}")
SET(DFHACK_RELEASE "r1" CACHE STRING "Current release revision.") SET(DFHACK_RELEASE "r2" CACHE STRING "Current release revision.")
set(DFHACK_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}-${DFHACK_RELEASE}") set(DFHACK_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}-${DFHACK_RELEASE}")
add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}") add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}")
@ -111,6 +111,9 @@ IF(UNIX)
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g -Wall -Wno-unused-variable") SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g -Wall -Wno-unused-variable")
SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic -std=c++0x") SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic -std=c++0x")
SET(CMAKE_C_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic") SET(CMAKE_C_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic")
ELSEIF(MSVC)
# for msvc, tell it to always use 8-byte pointers to member functions to avoid confusion
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /vmg /vmm")
ENDIF() ENDIF()
# use shared libraries for protobuf # use shared libraries for protobuf
@ -142,12 +145,33 @@ include_directories(depends/clsocket/src)
add_subdirectory(depends) add_subdirectory(depends)
#find_package(Docutils)
#set (RST_FILES
#"Readme"
#"Compile"
#"LUA Api"
#"Contributors"
#)
#set (RST_PROCESSED_FILES "")
#IF(RST2HTML_EXECUTABLE)
# foreach(F ${RST_FILES})
# add_custom_command(
# OUTPUT "${dfhack_BINARY_DIR}/${F}.html"
# COMMAND ${RST2HTML_EXECUTABLE} "${dfhack_SOURCE_DIR}/${F}.rst" "${dfhack_BINARY_DIR}/${F}.html"
# COMMENT "Translating ${F} to html"
# DEPENDS "${dfhack_SOURCE_DIR}/${F}.rst")
# list (APPEND RST_PROCESSED_FILES "${dfhack_BINARY_DIR}/${F}.html")
# endforeach()
# add_custom_target(HTML_DOCS ALL DEPENDS ${RST_PROCESSED_FILES})
#ENDIF()
# build the lib itself # build the lib itself
IF(BUILD_LIBRARY) IF(BUILD_LIBRARY)
add_subdirectory (library) add_subdirectory (library)
## install the default documentation files ## install the default documentation files
install(FILES LICENSE Readme.html Compile.html Lua\ API.html DESTINATION ${DFHACK_USERDOC_DESTINATION}) install(FILES LICENSE "Lua API.html" Readme.html Compile.html Contributors.html DESTINATION ${DFHACK_USERDOC_DESTINATION})
endif() endif()
#build the plugins #build the plugins

@ -3,13 +3,13 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.9: http://docutils.sourceforge.net/" /> <meta name="generator" content="Docutils 0.9.1: http://docutils.sourceforge.net/" />
<title>Building DFHACK</title> <title>Building DFHACK</title>
<style type="text/css"> <style type="text/css">
/* /*
:Author: David Goodger (goodger@python.org) :Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7056 2011-06-17 10:50:48Z milde $ :Id: $Id: html4css1.css 7434 2012-05-11 21:06:27Z milde $
:Copyright: This stylesheet has been placed in the public domain. :Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils. Default cascading style sheet for the HTML output of Docutils.
@ -249,10 +249,18 @@ pre.address {
margin-top: 0 ; margin-top: 0 ;
font: inherit } font: inherit }
pre.literal-block, pre.doctest-block, pre.math { pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ; margin-left: 2em ;
margin-right: 2em } margin-right: 2em }
pre.code .ln { /* line numbers */
color: grey;
}
.code {
background-color: #eeeeee
}
span.classifier { span.classifier {
font-family: sans-serif ; font-family: sans-serif ;
font-style: oblique } font-style: oblique }
@ -326,20 +334,21 @@ ul.auto-toc {
<li><a class="reference internal" href="#build" id="id7">Build</a></li> <li><a class="reference internal" href="#build" id="id7">Build</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#windows" id="id8">Windows</a><ul> <li><a class="reference internal" href="#mac-os-x" id="id8">Mac OS X</a></li>
<li><a class="reference internal" href="#id1" id="id9">How to get the code</a></li> <li><a class="reference internal" href="#windows" id="id9">Windows</a><ul>
<li><a class="reference internal" href="#id2" id="id10">Dependencies</a></li> <li><a class="reference internal" href="#id1" id="id10">How to get the code</a></li>
<li><a class="reference internal" href="#id3" id="id11">Build</a></li> <li><a class="reference internal" href="#id2" id="id11">Dependencies</a></li>
<li><a class="reference internal" href="#id3" id="id12">Build</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#build-types" id="id12">Build types</a></li> <li><a class="reference internal" href="#build-types" id="id13">Build types</a></li>
<li><a class="reference internal" href="#using-the-library-as-a-developer" id="id13">Using the library as a developer</a><ul> <li><a class="reference internal" href="#using-the-library-as-a-developer" id="id14">Using the library as a developer</a><ul>
<li><a class="reference internal" href="#df-data-structure-definitions" id="id14">DF data structure definitions</a></li> <li><a class="reference internal" href="#df-data-structure-definitions" id="id15">DF data structure definitions</a></li>
<li><a class="reference internal" href="#remote-access-interface" id="id15">Remote access interface</a></li> <li><a class="reference internal" href="#remote-access-interface" id="id16">Remote access interface</a></li>
<li><a class="reference internal" href="#contributing-to-dfhack" id="id16">Contributing to DFHack</a><ul> <li><a class="reference internal" href="#contributing-to-dfhack" id="id17">Contributing to DFHack</a><ul>
<li><a class="reference internal" href="#coding-style" id="id17">Coding style</a></li> <li><a class="reference internal" href="#coding-style" id="id18">Coding style</a></li>
<li><a class="reference internal" href="#how-to-get-new-code-into-dfhack" id="id18">How to get new code into DFHack</a></li> <li><a class="reference internal" href="#how-to-get-new-code-into-dfhack" id="id19">How to get new code into DFHack</a></li>
<li><a class="reference internal" href="#memory-research" id="id19">Memory research</a></li> <li><a class="reference internal" href="#memory-research" id="id20">Memory research</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -395,11 +404,66 @@ extra options.</p>
program.</p> program.</p>
</div> </div>
</div> </div>
<div class="section" id="mac-os-x">
<h1><a class="toc-backref" href="#id8">Mac OS X</a></h1>
<ol class="arabic">
<li><p class="first">Download and unpack a copy of the latest DF</p>
</li>
<li><p class="first">Install Xcode from Mac App Store</p>
</li>
<li><p class="first">Open Xcode, go to Preferences &gt; Downloads, and install the Command Line Tools.</p>
</li>
<li><p class="first">Install MacPorts.</p>
</li>
<li><p class="first">Install dependencies from MacPorts:</p>
<ul>
<li><p class="first"><tt class="docutils literal">sudo port install gcc45 +universal cmake +universal <span class="pre">git-core</span> +universal</tt></p>
<p>This will take some time—maybe hours, depending on your machine.</p>
</li>
<li><p class="first">At some point during this process, it may ask you to install a Java environment; let it do so.</p>
</li>
</ul>
</li>
<li><p class="first">Install perl dependencies</p>
<blockquote>
<ol class="arabic">
<li><p class="first"><tt class="docutils literal">sudo cpan</tt></p>
<p>If this is the first time you've run cpan, you will need to go through the setup
process. Just stick with the defaults for everything and you'll be fine.</p>
</li>
<li><p class="first"><tt class="docutils literal">install <span class="pre">XML::LibXML</span></tt></p>
</li>
<li><p class="first"><tt class="docutils literal">install <span class="pre">XML::LibXSLT</span></tt></p>
</li>
</ol>
</blockquote>
</li>
<li><p class="first">Get the dfhack source:</p>
<pre class="literal-block">
git clone https://github.com/danaris/dfhack.git
cd dfhack
git submodule init
git submodule update
</pre>
</li>
<li><p class="first">Build dfhack:</p>
<pre class="literal-block">
mkdir build-osx
cd build-osx
export CC=/opt/local/bin/gcc-mp-4.5
export CXX=/opt/local/bin/g++-mp-4.5
cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/path/to/DF/directory
make
make install
</pre>
</li>
</ol>
</div>
<div class="section" id="windows"> <div class="section" id="windows">
<h1><a class="toc-backref" href="#id8">Windows</a></h1> <h1><a class="toc-backref" href="#id9">Windows</a></h1>
<p>On Windows, DFHack replaces the SDL library distributed with DF.</p> <p>On Windows, DFHack replaces the SDL library distributed with DF.</p>
<div class="section" id="id1"> <div class="section" id="id1">
<h2><a class="toc-backref" href="#id9">How to get the code</a></h2> <h2><a class="toc-backref" href="#id10">How to get the code</a></h2>
<p>DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git. <p>DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git.
You will need some sort of Windows port of git, or a GUI. Some examples:</p> You will need some sort of Windows port of git, or a GUI. Some examples:</p>
<blockquote> <blockquote>
@ -420,7 +484,7 @@ git submodule update
<p>If you want to get really involved with the development, create an account on github, make a clone there and then use that as your remote repository instead. Detailed instructions are beyond the scope of this document. If you need help, join us on IRC (#dfhack channel on freenode).</p> <p>If you want to get really involved with the development, create an account on github, make a clone there and then use that as your remote repository instead. Detailed instructions are beyond the scope of this document. If you need help, join us on IRC (#dfhack channel on freenode).</p>
</div> </div>
<div class="section" id="id2"> <div class="section" id="id2">
<h2><a class="toc-backref" href="#id10">Dependencies</a></h2> <h2><a class="toc-backref" href="#id11">Dependencies</a></h2>
<p>First, you need <tt class="docutils literal">cmake</tt>. Get the win32 installer version from the official <p>First, you need <tt class="docutils literal">cmake</tt>. Get the win32 installer version from the official
site: <a class="reference external" href="http://www.cmake.org/cmake/resources/software.html">http://www.cmake.org/cmake/resources/software.html</a></p> site: <a class="reference external" href="http://www.cmake.org/cmake/resources/software.html">http://www.cmake.org/cmake/resources/software.html</a></p>
<p>It has the usual installer wizard. Make sure you let it add its binary folder <p>It has the usual installer wizard. Make sure you let it add its binary folder
@ -437,7 +501,7 @@ Grab it from Microsoft's site.</p>
<p>If you already have a different version of perl (for example the one from cygwin), you can run into some trouble. Either remove the other perl install from PATH, or install libxml and libxslt for it instead. Strawberry perl works though and has all the required packages.</p> <p>If you already have a different version of perl (for example the one from cygwin), you can run into some trouble. Either remove the other perl install from PATH, or install libxml and libxslt for it instead. Strawberry perl works though and has all the required packages.</p>
</div> </div>
<div class="section" id="id3"> <div class="section" id="id3">
<h2><a class="toc-backref" href="#id11">Build</a></h2> <h2><a class="toc-backref" href="#id12">Build</a></h2>
<p>There are several different batch files in the <tt class="docutils literal">build</tt> folder along with a script that's used for picking the DF path.</p> <p>There are several different batch files in the <tt class="docutils literal">build</tt> folder along with a script that's used for picking the DF path.</p>
<p>First, run set_df_path.vbs and point the dialog that pops up at your DF folder that you want to use for development. <p>First, run set_df_path.vbs and point the dialog that pops up at your DF folder that you want to use for development.
Next, run one of the scripts with <tt class="docutils literal">generate</tt> prefix. These create the MSVC solution file(s):</p> Next, run one of the scripts with <tt class="docutils literal">generate</tt> prefix. These create the MSVC solution file(s):</p>
@ -459,7 +523,7 @@ So pick either Release or RelWithDebInfo build and build the INSTALL target.</p>
</div> </div>
</div> </div>
<div class="section" id="build-types"> <div class="section" id="build-types">
<h1><a class="toc-backref" href="#id12">Build types</a></h1> <h1><a class="toc-backref" href="#id13">Build types</a></h1>
<p><tt class="docutils literal">cmake</tt> allows you to pick a build type by changing this <p><tt class="docutils literal">cmake</tt> allows you to pick a build type by changing this
variable: <tt class="docutils literal">CMAKE_BUILD_TYPE</tt></p> variable: <tt class="docutils literal">CMAKE_BUILD_TYPE</tt></p>
<pre class="literal-block"> <pre class="literal-block">
@ -471,7 +535,7 @@ cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE
'RelWithDebInfo'. 'Debug' is not available on Windows.</p> 'RelWithDebInfo'. 'Debug' is not available on Windows.</p>
</div> </div>
<div class="section" id="using-the-library-as-a-developer"> <div class="section" id="using-the-library-as-a-developer">
<h1><a class="toc-backref" href="#id13">Using the library as a developer</a></h1> <h1><a class="toc-backref" href="#id14">Using the library as a developer</a></h1>
<p>Currently, the most direct way to use the library is to write a plugin that can be loaded by it. <p>Currently, the most direct way to use the library is to write a plugin that can be loaded by it.
All the plugins can be found in the 'plugins' folder. There's no in-depth documentation All the plugins can be found in the 'plugins' folder. There's no in-depth documentation
on how to write one yet, but it should be easy enough to copy one and just follow the pattern.</p> on how to write one yet, but it should be easy enough to copy one and just follow the pattern.</p>
@ -489,29 +553,29 @@ The main license is zlib/libpng, some bits are MIT licensed, and some are BSD li
<p>Feel free to add your own extensions and plugins. Contributing back to <p>Feel free to add your own extensions and plugins. Contributing back to
the dfhack repository is welcome and the right thing to do :)</p> the dfhack repository is welcome and the right thing to do :)</p>
<div class="section" id="df-data-structure-definitions"> <div class="section" id="df-data-structure-definitions">
<h2><a class="toc-backref" href="#id14">DF data structure definitions</a></h2> <h2><a class="toc-backref" href="#id15">DF data structure definitions</a></h2>
<p>DFHack uses information about the game data structures, represented via xml files in the library/xml/ submodule.</p> <p>DFHack uses information about the game data structures, represented via xml files in the library/xml/ submodule.</p>
<p>Data structure layouts are described in files following the df.*.xml name pattern. This information is transformed by a perl script into C++ headers describing the structures, and associated metadata for the Lua wrapper. These headers and data are then compiled into the DFHack libraries, thus necessitating a compatibility break every time layouts change; in return it significantly boosts the efficiency and capabilities of DFHack code.</p> <p>Data structure layouts are described in files following the df.*.xml name pattern. This information is transformed by a perl script into C++ headers describing the structures, and associated metadata for the Lua wrapper. These headers and data are then compiled into the DFHack libraries, thus necessitating a compatibility break every time layouts change; in return it significantly boosts the efficiency and capabilities of DFHack code.</p>
<p>Global object addresses are stored in symbols.xml, which is copied to the dfhack release package and loaded as data at runtime.</p> <p>Global object addresses are stored in symbols.xml, which is copied to the dfhack release package and loaded as data at runtime.</p>
</div> </div>
<div class="section" id="remote-access-interface"> <div class="section" id="remote-access-interface">
<h2><a class="toc-backref" href="#id15">Remote access interface</a></h2> <h2><a class="toc-backref" href="#id16">Remote access interface</a></h2>
<p>DFHack supports remote access by exchanging Google protobuf messages via a TCP socket. Both the core and plugins can define remotely accessible methods. The <tt class="docutils literal"><span class="pre">dfhack-run</span></tt> command uses this interface to invoke ordinary console commands.</p> <p>DFHack supports remote access by exchanging Google protobuf messages via a TCP socket. Both the core and plugins can define remotely accessible methods. The <tt class="docutils literal"><span class="pre">dfhack-run</span></tt> command uses this interface to invoke ordinary console commands.</p>
<p>Currently the supported set of requests is limited, because the developers don't know what exactly is most useful.</p> <p>Currently the supported set of requests is limited, because the developers don't know what exactly is most useful.</p>
<p>Protocol client implementations exist for Java and C#.</p> <p>Protocol client implementations exist for Java and C#.</p>
</div> </div>
<div class="section" id="contributing-to-dfhack"> <div class="section" id="contributing-to-dfhack">
<h2><a class="toc-backref" href="#id16">Contributing to DFHack</a></h2> <h2><a class="toc-backref" href="#id17">Contributing to DFHack</a></h2>
<p>Several things should be kept in mind when contributing to DFHack.</p> <p>Several things should be kept in mind when contributing to DFHack.</p>
<div class="section" id="coding-style"> <div class="section" id="coding-style">
<h3><a class="toc-backref" href="#id17">Coding style</a></h3> <h3><a class="toc-backref" href="#id18">Coding style</a></h3>
<p>DFhack uses ANSI formatting and four spaces as indentation. Line <p>DFhack uses ANSI formatting and four spaces as indentation. Line
endings are UNIX. The files use UTF-8 encoding. Code not following this endings are UNIX. The files use UTF-8 encoding. Code not following this
won't make me happy, because I'll have to fix it. There's a good chance won't make me happy, because I'll have to fix it. There's a good chance
I'll make <em>you</em> fix it ;)</p> I'll make <em>you</em> fix it ;)</p>
</div> </div>
<div class="section" id="how-to-get-new-code-into-dfhack"> <div class="section" id="how-to-get-new-code-into-dfhack">
<h3><a class="toc-backref" href="#id18">How to get new code into DFHack</a></h3> <h3><a class="toc-backref" href="#id19">How to get new code into DFHack</a></h3>
<p>You can send patches or make a clone of the github repo and ask me on <p>You can send patches or make a clone of the github repo and ask me on
the IRC channel to pull your code in. I'll review it and see if there the IRC channel to pull your code in. I'll review it and see if there
are any problems. I'll fix them if they are minor.</p> are any problems. I'll fix them if they are minor.</p>
@ -521,7 +585,7 @@ this is also a good place to dump new ideas and/or bugs that need
fixing.</p> fixing.</p>
</div> </div>
<div class="section" id="memory-research"> <div class="section" id="memory-research">
<h3><a class="toc-backref" href="#id19">Memory research</a></h3> <h3><a class="toc-backref" href="#id20">Memory research</a></h3>
<p>If you want to do memory research, you'll need some tools and some knowledge. <p>If you want to do memory research, you'll need some tools and some knowledge.
In general, you'll need a good memory viewer and optionally something In general, you'll need a good memory viewer and optionally something
to look at machine code without getting crazy :)</p> to look at machine code without getting crazy :)</p>

@ -63,6 +63,49 @@ extra options.
You can also use a cmake-friendly IDE like KDevelop 4 or the cmake-gui You can also use a cmake-friendly IDE like KDevelop 4 or the cmake-gui
program. program.
========
Mac OS X
========
1. Download and unpack a copy of the latest DF
2. Install Xcode from Mac App Store
3. Open Xcode, go to Preferences > Downloads, and install the Command Line Tools.
4. Install MacPorts.
5. Install dependencies from MacPorts:
* ``sudo port install gcc45 +universal cmake +universal git-core +universal``
This will take some time—maybe hours, depending on your machine.
* At some point during this process, it may ask you to install a Java environment; let it do so.
6. Install perl dependencies
1. ``sudo cpan``
If this is the first time you've run cpan, you will need to go through the setup
process. Just stick with the defaults for everything and you'll be fine.
2. ``install XML::LibXML``
3. ``install XML::LibXSLT``
7. Get the dfhack source::
git clone https://github.com/danaris/dfhack.git
cd dfhack
git submodule init
git submodule update
8. Build dfhack::
mkdir build-osx
cd build-osx
export CC=/opt/local/bin/gcc-mp-4.5
export CXX=/opt/local/bin/g++-mp-4.5
cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/path/to/DF/directory
make
make install
======= =======
Windows Windows
======= =======

@ -0,0 +1,402 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.9.1: http://docutils.sourceforge.net/" />
<title>Contributors</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7434 2012-05-11 21:06:27Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { /* line numbers */
color: grey;
}
.code {
background-color: #eeeeee
}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="contributors">
<h1 class="title">Contributors</h1>
<p>If you belong here and are missing, please add yourself and send me (peterix) a pull request :-)</p>
<p>The following is a list of people who have contributed to <strong>DFHack</strong>.</p>
<ul class="simple">
<li>Petr Mrázek &lt;<a class="reference external" href="mailto:peterix&#64;gmail.com">peterix&#64;gmail.com</a>&gt;</li>
<li>Alexander Gavrilov &lt;<a class="reference external" href="mailto:angavrilov&#64;gmail.com">angavrilov&#64;gmail.com</a>&gt;</li>
<li>doomchild &lt;<a class="reference external" href="mailto:lee.crabtree&#64;gmail.com">lee.crabtree&#64;gmail.com</a>&gt;</li>
<li>Quietust &lt;<a class="reference external" href="mailto:quietust&#64;gmail.com">quietust&#64;gmail.com</a>&gt;</li>
<li>jj &lt;<a class="reference external" href="mailto:john-git&#64;ofjj.net">john-git&#64;ofjj.net</a>&gt;</li>
<li>Warmist &lt;<a class="reference external" href="mailto:warmist&#64;gmail.com">warmist&#64;gmail.com</a>&gt;</li>
<li>Robert Heinrich &lt;<a class="reference external" href="mailto:robertheinrich73&#64;googlemail.com">robertheinrich73&#64;googlemail.com</a>&gt;</li>
<li>simon &lt;<a class="reference external" href="mailto:simon&#64;banquise.net">simon&#64;banquise.net</a>&gt;</li>
<li>Kelly Martin &lt;<a class="reference external" href="mailto:kelly.lynn.martin&#64;gmail.com">kelly.lynn.martin&#64;gmail.com</a>&gt;</li>
<li>mizipzor &lt;<a class="reference external" href="mailto:mizipzor&#64;gmail.com">mizipzor&#64;gmail.com</a>&gt;</li>
<li>Simon Jackson &lt;<a class="reference external" href="mailto:sizeak&#64;hotmail.com">sizeak&#64;hotmail.com</a>&gt;</li>
<li>belal &lt;<a class="reference external" href="mailto:jimbelal&#64;gmail.com">jimbelal&#64;gmail.com</a>&gt;</li>
<li>RusAnon &lt;<a class="reference external" href="mailto:rusanon&#64;dollchan.ru">rusanon&#64;dollchan.ru</a>&gt;</li>
<li>Raoul XQ &lt;<a class="reference external" href="mailto:raoulxq&#64;gmail.com">raoulxq&#64;gmail.com</a>&gt;</li>
<li>Matthew Cline &lt;<a class="reference external" href="mailto:zelgadis&#64;sourceforge.net">zelgadis&#64;sourceforge.net</a>&gt;</li>
<li>Mike Stewart &lt;<a class="reference external" href="mailto:thewonderidiot&#64;gmail.com">thewonderidiot&#64;gmail.com</a>&gt;</li>
<li>Timothy Collett &lt;<a class="reference external" href="mailto:tcollett+github&#64;topazgryphon.org">tcollett+github&#64;topazgryphon.org</a>&gt;</li>
<li>RossM &lt;<a class="reference external" href="mailto:Ross&#64;Gnome">Ross&#64;Gnome</a>&gt;</li>
<li>Tom Prince &lt;<a class="reference external" href="mailto:tom.prince&#64;ualberta.net">tom.prince&#64;ualberta.net</a>&gt;</li>
<li>Jared Adams &lt;<a class="reference external" href="mailto:jaxad0127&#64;gmail.com">jaxad0127&#64;gmail.com</a>&gt;</li>
<li>expwnent &lt;<a class="reference external" href="mailto:q309185&#64;gmail.com">q309185&#64;gmail.com</a>&gt;</li>
<li>Erik Youngren &lt;<a class="reference external" href="mailto:artanis.00&#64;gmail.com">artanis.00&#64;gmail.com</a>&gt;</li>
<li>Espen Wiborg &lt;<a class="reference external" href="mailto:espen.wiborg&#64;telio.no">espen.wiborg&#64;telio.no</a>&gt;</li>
<li>Tim Walberg &lt;<a class="reference external" href="mailto:twalberg&#64;comcast.net">twalberg&#64;comcast.net</a>&gt;</li>
<li>Mikko Juola &lt;<a class="reference external" href="mailto:mikko.juola&#64;kolumbus.fi">mikko.juola&#64;kolumbus.fi</a>&gt;</li>
<li>rampaging-poet &lt;<a class="reference external" href="mailto:yrudoingthis&#64;hotmail.com">yrudoingthis&#64;hotmail.com</a>&gt;</li>
<li>U-glouglou\simon</li>
<li>Clayton Hughes &lt;<a class="reference external" href="mailto:clayton.hughes&#64;gmail.com">clayton.hughes&#64;gmail.com</a>&gt;</li>
<li>zilpin &lt;<a class="reference external" href="mailto:ziLpin&#64;gmail.com">ziLpin&#64;gmail.com</a>&gt;</li>
<li>Will Rogers &lt;<a class="reference external" href="mailto:wjrogers&#64;gmail.com">wjrogers&#64;gmail.com</a>&gt;</li>
<li>NMLittle &lt;<a class="reference external" href="mailto:nmlittle&#64;gmail.com">nmlittle&#64;gmail.com</a>&gt;</li>
<li>root</li>
<li>reverb</li>
<li>Zhentar &lt;<a class="reference external" href="mailto:Zhentar&#64;gmail.com">Zhentar&#64;gmail.com</a>&gt;</li>
<li>Valentin Ochs &lt;<a class="reference external" href="mailto:a&#64;0au.de">a&#64;0au.de</a>&gt;</li>
<li>Priit Laes &lt;<a class="reference external" href="mailto:plaes&#64;plaes.org">plaes&#64;plaes.org</a>&gt;</li>
<li>kmartin</li>
<li>Neil Little</li>
<li>rout &lt;<a class="reference external" href="mailto:rout.mail+github&#64;gmail.com">rout.mail+github&#64;gmail.com</a>&gt;</li>
<li>rofl0r &lt;<a class="reference external" href="mailto:retnyg&#64;gmx.net">retnyg&#64;gmx.net</a>&gt;</li>
<li>harlanplayford &lt;<a class="reference external" href="mailto:harlanplayford&#64;gmail.com">harlanplayford&#64;gmail.com</a>&gt;</li>
<li>gsvslto &lt;<a class="reference external" href="mailto:gsvslto&#64;gmail.com">gsvslto&#64;gmail.com</a>&gt;</li>
<li>sami</li>
<li>potato</li>
<li>playfordh &lt;<a class="reference external" href="mailto:harlanplayford&#64;gmail.com">harlanplayford&#64;gmail.com</a>&gt;</li>
<li>feng1st &lt;<a class="reference external" href="mailto:nf_xp&#64;hotmail.com">nf_xp&#64;hotmail.com</a>&gt;</li>
<li>comestible &lt;<a class="reference external" href="mailto:nickolas.g.russell&#64;gmail.com">nickolas.g.russell&#64;gmail.com</a>&gt;</li>
<li>Rumrusher &lt;<a class="reference external" href="mailto:Anuleakage&#64;yahoo.com">Anuleakage&#64;yahoo.com</a>&gt;</li>
<li>Rinin &lt;<a class="reference external" href="mailto:RininS&#64;Gmail.com">RininS&#64;Gmail.com</a>&gt;</li>
<li>Raoul van Putten</li>
<li>John Shade &lt;<a class="reference external" href="mailto:gsvslto&#64;gmail.com">gsvslto&#64;gmail.com</a>&gt;</li>
<li>John Beisley &lt;<a class="reference external" href="mailto:greatred&#64;gmail.com">greatred&#64;gmail.com</a>&gt;</li>
<li>Feng &lt;<a class="reference external" href="mailto:nf_xp&#64;hotmail.com">nf_xp&#64;hotmail.com</a>&gt;</li>
<li>Donald Ruegsegger &lt;<a class="reference external" href="mailto:druegsegger&#64;gmail.com">druegsegger&#64;gmail.com</a>&gt;</li>
<li>Caldfir &lt;<a class="reference external" href="mailto:caldfir&#64;hotmail.com">caldfir&#64;hotmail.com</a>&gt;</li>
<li>Antalia &lt;<a class="reference external" href="mailto:tamarakorr&#64;gmail.com">tamarakorr&#64;gmail.com</a>&gt;</li>
<li>Angus Mezick &lt;<a class="reference external" href="mailto:amezick&#64;gmail.com">amezick&#64;gmail.com</a>&gt;</li>
</ul>
<p>And those are the cool people who made <strong>stonesense</strong>.</p>
<ul class="simple">
<li>Kris Parker &lt;kaypy&gt;</li>
<li>Japa &lt;<a class="reference external" href="mailto:japa.mala.illo&#64;gmail.com">japa.mala.illo&#64;gmail.com</a>&gt;</li>
<li>Jonas Ask &lt;<a class="reference external" href="mailto:jonask84&#64;gmail.com">jonask84&#64;gmail.com</a>&gt;</li>
<li>Petr Mrázek &lt;<a class="reference external" href="mailto:peterix&#64;gmail.com">peterix&#64;gmail.com</a>&gt;</li>
<li>Caldfir &lt;<a class="reference external" href="mailto:aitken.tim&#64;gmail.com">aitken.tim&#64;gmail.com</a>&gt;</li>
<li>8Z &lt;<a class="reference external" href="mailto:git8z&#64;ya.ru">git8z&#64;ya.ru</a>&gt;</li>
<li>Alexander Gavrilov &lt;<a class="reference external" href="mailto:angavrilov&#64;gmail.com">angavrilov&#64;gmail.com</a>&gt;</li>
<li>Timothy Collett &lt;<a class="reference external" href="mailto:tcollett+github&#64;topazgryphon.org">tcollett+github&#64;topazgryphon.org</a>&gt;</li>
</ul>
</div>
</body>
</html>

@ -0,0 +1,74 @@
Contributors
============
If you belong here and are missing, please add yourself and send me (peterix) a pull request :-)
The following is a list of people who have contributed to **DFHack**.
- Petr Mrázek <peterix@gmail.com>
- Alexander Gavrilov <angavrilov@gmail.com>
- doomchild <lee.crabtree@gmail.com>
- Quietust <quietust@gmail.com>
- jj <john-git@ofjj.net>
- Warmist <warmist@gmail.com>
- Robert Heinrich <robertheinrich73@googlemail.com>
- simon <simon@banquise.net>
- Kelly Martin <kelly.lynn.martin@gmail.com>
- mizipzor <mizipzor@gmail.com>
- Simon Jackson <sizeak@hotmail.com>
- belal <jimbelal@gmail.com>
- RusAnon <rusanon@dollchan.ru>
- Raoul XQ <raoulxq@gmail.com>
- Matthew Cline <zelgadis@sourceforge.net>
- Mike Stewart <thewonderidiot@gmail.com>
- Timothy Collett <tcollett+github@topazgryphon.org>
- RossM <Ross@Gnome>
- Tom Prince <tom.prince@ualberta.net>
- Jared Adams <jaxad0127@gmail.com>
- expwnent <q309185@gmail.com>
- Erik Youngren <artanis.00@gmail.com>
- Espen Wiborg <espen.wiborg@telio.no>
- Tim Walberg <twalberg@comcast.net>
- Mikko Juola <mikko.juola@kolumbus.fi>
- rampaging-poet <yrudoingthis@hotmail.com>
- U-glouglou\\simon
- Clayton Hughes <clayton.hughes@gmail.com>
- zilpin <ziLpin@gmail.com>
- Will Rogers <wjrogers@gmail.com>
- NMLittle <nmlittle@gmail.com>
- root
- reverb
- Zhentar <Zhentar@gmail.com>
- Valentin Ochs <a@0au.de>
- Priit Laes <plaes@plaes.org>
- kmartin
- Neil Little
- rout <rout.mail+github@gmail.com>
- rofl0r <retnyg@gmx.net>
- harlanplayford <harlanplayford@gmail.com>
- gsvslto <gsvslto@gmail.com>
- sami
- potato
- playfordh <harlanplayford@gmail.com>
- feng1st <nf_xp@hotmail.com>
- comestible <nickolas.g.russell@gmail.com>
- Rumrusher <Anuleakage@yahoo.com>
- Rinin <RininS@Gmail.com>
- Raoul van Putten
- John Shade <gsvslto@gmail.com>
- John Beisley <greatred@gmail.com>
- Feng <nf_xp@hotmail.com>
- Donald Ruegsegger <druegsegger@gmail.com>
- Caldfir <caldfir@hotmail.com>
- Antalia <tamarakorr@gmail.com>
- Angus Mezick <amezick@gmail.com>
And those are the cool people who made **stonesense**.
- Kris Parker <kaypy>
- Japa <japa.mala.illo@gmail.com>
- Jonas Ask <jonask84@gmail.com>
- Petr Mrázek <peterix@gmail.com>
- Caldfir <aitken.tim@gmail.com>
- 8Z <git8z@ya.ru>
- Alexander Gavrilov <angavrilov@gmail.com>
- Timothy Collett <tcollett+github@topazgryphon.org>

@ -2,7 +2,7 @@
License of dfhack License of dfhack
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

124
NEWS

@ -0,0 +1,124 @@
DFHack future
Internals:
- support for displaying active keybindings properly.
- support for reusable widgets in lua screen library.
Notable bugfixes:
- autobutcher can be re-enabled again after being stopped.
- stopped Dwarf Manipulator from unmasking vampires.
Misc improvements:
- fastdwarf: new mode using debug flags, and some internal consistency fixes.
- added a small stand-alone utility for applying and removing binary patches.
- removebadthoughts: add --dry-run option
New scripts:
- region-pops: displays animal populations of the region and allows tweaking them.
New GUI scripts:
- 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/workflow: a front-end for the workflow plugin.
- gui/assign-rack: works together with a binary patch to fix weapon racks.
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.
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.
DFHack v0.34.11-r2
Internals:
- full support for Mac OS X.
- a plugin that adds scripting in ruby.
- support for interposing virtual methods in DF from C++ plugins.
- support for creating new interface screens from C++ and lua.
- added various other API functions.
Notable bugfixes:
- better terminal reset after exit on linux.
- seedwatch now works on reclaim.
- the sort plugin won't crash on cages anymore.
Misc improvements:
- autodump: can move items to any walkable tile, not just floors.
- stripcaged: by default keep armor, new dumparmor option.
- zone: allow non-domesticated birds in nestboxes.
- workflow: quality range in constraints.
- cleanplants: new command to remove rain water from plants.
- liquids: can paint permaflow, i.e. what makes rivers power water wheels.
- prospect: pre-embark prospector accounts for caves & magma sea in its estimate.
- rename: supports renaming stockpiles, workshops, traps, siege engines.
- fastdwarf: now has an additional option to make dwarves teleport to their destination.
New commands:
- misery: multiplies every negative thought gained (2x by default).
- digtype: designates every tile of the same type of vein on the map for 'digging' (any dig designation).
New tweaks:
- tweak stable-cursor: keeps exact cursor position between d/k/t/q/v etc menus.
- tweak patrol-duty: makes Train orders reduce patrol timer, like the binary patch does.
- tweak readable-build-plate: fix unreadable truncation in unit pressure plate build ui.
- tweak stable-temp: fixes bug 6012; may improve FPS by 50-100% on a slow item-heavy fort.
- tweak fast-heat: speeds up item heating & cooling, thus making stable-temp act faster.
- tweak fix-dimensions: fixes subtracting small amounts from stacked liquids etc.
- tweak advmode-contained: fixes UI bug in custom reactions with container inputs in advmode.
- tweak fast-trade: Shift-Enter for selecting items quckly in Trade and Move to Depot screens.
- tweak military-stable-assign: Stop rightmost list of military->Positions from jumping to top.
- tweak military-color-assigned: In same list, color already assigned units in brown & green.
New scripts:
- fixnaked: removes thoughts about nakedness.
- setfps: set FPS cap at runtime, in case you want slow motion or speed-up.
- siren: wakes up units, stops breaks and parties - but causes bad thoughts.
- fix/population-cap: run after every migrant wave to prevent exceeding the cap.
- fix/stable-temp: counts items with temperature updates; does instant one-shot stable-temp.
- fix/loyaltycascade: fix units allegiance, eg after ordering a dwarf merchant kill.
- deathcause: shows the circumstances of death for a given body.
- digfort: designate areas to dig from a csv file.
- drainaquifer: remove aquifers from the map.
- growcrops: cheat to make farm crops instantly grow.
- magmasource: continuously spawn magma from any map tile.
- removebadthoughts: delete all negative thoughts from your dwarves.
- slayrace: instakill all units of a given race, optionally with magma.
- superdwarf: per-creature fastdwarf.
New GUI scripts:
- gui/mechanisms: browse mechanism links of the current building.
- gui/room-list: browse other rooms owned by the unit when assigning one.
- gui/liquids: a GUI front-end for the liquids plugin.
- gui/rename: renaming stockpiles, workshops and units via an in-game dialog.
- gui/power-meter: front-end for the Power Meter plugin.
- gui/siege-engine: front-end for the Siege Engine plugin.
- gui/choose-weapons: auto-choose matching weapons in the military equip screen.
Autolabor plugin:
- can set nonidle hauler percentage.
- broker excluded from all labors when needed at depot.
- likewise, anybody with a scheduled diplomat meeting.
New Dwarf Manipulator plugin:
Open the unit list, and press 'l' to access a Dwarf Therapist like UI in the game.
New Steam Engine plugin:
Dwarven Water Reactors don't make any sense whatsoever and cause lag, so this may be
a replacement for those concerned by it. The plugin detects if a workshop with a
certain name is in the raws used by the current world, and provides the necessary
behavior. See hack/raw/*_steam_engine.txt for the necessary raw definitions.
Note: Stuff like animal treadmills might be more period, but absolutely can't be
done with tools dfhack has access to.
New Power Meter plugin:
When activated, implements a pressure plate modification that detects power in gear
boxes built on the four adjacent N/S/W/E tiles. The gui/power-meter script implements
the necessary build configuration UI.
New Siege Engine plugin:
When enabled and configured via gui/siege-engine, allows aiming siege engines
at a designated rectangular area with 360 degree fire range and across Z levels;
this works by rewriting the projectile trajectory immediately after it appears.
Also supports loading catapults with non-boulder projectiles, taking from a stockpile,
and restricting operator skill range like with ordinary workshops.
Disclaimer: not in any way to undermine the future siege update from Toady, but
the aiming logic of existing engines hasn't been updated since 2D, and is almost
useless above ground :(. Again, things like making siegers bring their own engines
is totally out of the scope of dfhack and can only be done by Toady.
New Add Spatter plugin:
Detects reactions with certain names in the raws, and changes them from adding
improvements to adding item contaminants. This allows directly covering items
with poisons. The added spatters are immune both to water and 'clean items'.
Intended to give some use to all those giant cave spider poison barrels brought
by the caravans.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1 +1 @@
Subproject commit d0b2d0750dc2d529a152eba4f3f519f69ff7eab0 Subproject commit 178a4916da838e46e46e769e566e47fff6eff8f8

@ -36,16 +36,14 @@
* internal hash function, calling * internal hash function, calling
* the basic methods from md5.h * the basic methods from md5.h
*/ */
std::string md5wrapper::hashit(std::string text) std::string md5wrapper::hashit(unsigned char *data, size_t length)
{ {
MD5Context ctx; MD5Context ctx;
//init md5 //init md5
MD5Init(&ctx); MD5Init(&ctx);
//update with our string //update with our string
MD5Update(&ctx, MD5Update(&ctx, data, length);
(unsigned char*)text.c_str(),
text.length());
//create the hash //create the hash
unsigned char buff[16] = ""; unsigned char buff[16] = "";
@ -95,10 +93,9 @@ md5wrapper::~md5wrapper()
*/ */
std::string md5wrapper::getHashFromString(std::string text) std::string md5wrapper::getHashFromString(std::string text)
{ {
return this->hashit(text); return this->hashit((unsigned char*)text.data(), text.length());
} }
/* /*
* creates a MD5 hash from * creates a MD5 hash from
* a file specified in "filename" and * a file specified in "filename" and

@ -31,7 +31,7 @@ class md5wrapper
* internal hash function, calling * internal hash function, calling
* the basic methods from md5.h * the basic methods from md5.h
*/ */
std::string hashit(std::string text); std::string hashit(unsigned char *data, size_t length);
/* /*
* converts the numeric giets to * converts the numeric giets to
@ -52,6 +52,10 @@ class md5wrapper
*/ */
std::string getHashFromString(std::string text); std::string getHashFromString(std::string text);
std::string getHashFromBytes(const unsigned char *data, size_t size) {
return hashit(const_cast<unsigned char*>(data),size);
}
/* /*
* creates a MD5 hash from * creates a MD5 hash from
* a file specified in "filename" and * a file specified in "filename" and

@ -7,10 +7,10 @@ IF(CMAKE_COMPILER_IS_GNUCC)
STRING(REGEX MATCHALL "[0-9]+" GCC_VERSION_COMPONENTS ${GCC_VERSION}) STRING(REGEX MATCHALL "[0-9]+" GCC_VERSION_COMPONENTS ${GCC_VERSION})
LIST(GET GCC_VERSION_COMPONENTS 0 GCC_MAJOR) LIST(GET GCC_VERSION_COMPONENTS 0 GCC_MAJOR)
LIST(GET GCC_VERSION_COMPONENTS 1 GCC_MINOR) LIST(GET GCC_VERSION_COMPONENTS 1 GCC_MINOR)
IF(GCC_MAJOR LESS 4 OR (GCC_MAJOR EQUAL 4 AND GCC_MINOR LESS 2)) #IF(GCC_MAJOR LESS 4 OR (GCC_MAJOR EQUAL 4 AND GCC_MINOR LESS 2))
#GCC is too old #GCC is too old
SET(STL_HASH_OLD_GCC 1) # SET(STL_HASH_OLD_GCC 1)
ENDIF() #ENDIF()
#SET(CMAKE_CXX_FLAGS "-std=c++0x") #SET(CMAKE_CXX_FLAGS "-std=c++0x")
SET(HAVE_HASH_MAP 0) SET(HAVE_HASH_MAP 0)

@ -16,6 +16,10 @@ keybinding add Ctrl-K autodump-destroy-item
# 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
keybinding add Ctrl-Shift-N gui/rename
keybinding add Ctrl-Shift-T "gui/rename unit-profession"
############################## ##############################
# Generic adv mode bindings # # Generic adv mode bindings #
############################## ##############################
@ -41,4 +45,79 @@ keybinding add Shift-R "job-material RHYOLITE"
keybinding add Shift-I "job-material CINNABAR" keybinding add Shift-I "job-material CINNABAR"
keybinding add Shift-B "job-material COBALTITE" keybinding add Shift-B "job-material COBALTITE"
keybinding add Shift-O "job-material OBSIDIAN" keybinding add Shift-O "job-material OBSIDIAN"
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
keybinding add Alt-Shift-N "sort-units name" "sort-items description"
keybinding add Alt-Shift-R "sort-units arrival"
keybinding add Alt-Shift-T "sort-units profession" "sort-items type material"
keybinding add Alt-Shift-Q "sort-units squad_position" "sort-items quality"
# browse linked mechanisms
keybinding add Ctrl-M@dwarfmode/QueryBuilding/Some gui/mechanisms
# browse rooms of same owner
keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list
# interface for the liquids plugin
keybinding add Alt-L@dwarfmode/LookAround gui/liquids
# machine power sensitive pressure plate construction
keybinding add Ctrl-Shift-M@dwarfmode/Build/Position/Trap gui/power-meter
# siege engine control
keybinding add Alt-A@dwarfmode/QueryBuilding/Some/SiegeEngine gui/siege-engine
# military weapon auto-select
keybinding add Ctrl-W@layer_military/Equip/Customize/View gui/choose-weapons
# minecart Guide path
keybinding add Alt-P@dwarfmode/Hauling/DefineStop/Cond/Guide gui/guide-path
# workshop job details
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 #
############################
# stabilize the cursor of dwarfmode when switching menus
tweak stable-cursor
# stop military from considering training as 'patrol duty'
tweak patrol-duty
# display creature weight in build plate menu as ??K, instead of (???df: Max
tweak readable-build-plate
# improve FPS by squashing endless item temperature update loops
tweak stable-temp
# speed up items reaching temp equilibrium with environment by
# capping the rate to no less than 1 degree change per 500 frames
# Note: will also cause stuff to melt faster in magma etc
tweak fast-heat 500
# stop stacked liquid/bar/thread/cloth items from lasting forever
# if used in reactions that use only a fraction of the dimension.
tweak fix-dimensions
# make reactions requiring containers usable in advmode - the issue is
# that the screen asks for those reagents to be selected directly
tweak advmode-contained
# support Shift-Enter in Trade and Move Goods to Depot screens for faster
# selection; it selects the current item or stack and scrolls down one line
tweak fast-trade
# stop the right list in military->positions from resetting to top all the time
tweak military-stable-assign
# in same list, color units already assigned to squads in brown & green
tweak military-color-assigned

@ -1,5 +1,18 @@
#!/bin/bash #!/bin/bash
rst2html README.rst > Readme.html # regenerate documentation after editing the .rst files. Requires python and docutils.
rst2html COMPILE.rst > Compile.html
rst2html DEVEL.rst > Devel.html cd `dirname $0`
rst2html LUA_API.rst > Lua\ API.html
function process() {
if [ "$1" -nt "$2" ]; then
rst2html --no-generator --no-datestamp "$1" "$2"
else
echo "$2 - up to date."
fi
}
process Readme.rst Readme.html
process Compile.rst Compile.html
process Lua\ API.rst Lua\ API.html
process Contributors.rst Contributors.html

@ -27,6 +27,7 @@ include/Core.h
include/ColorText.h include/ColorText.h
include/DataDefs.h include/DataDefs.h
include/DataIdentity.h include/DataIdentity.h
include/VTableInterpose.h
include/LuaWrapper.h include/LuaWrapper.h
include/LuaTools.h include/LuaTools.h
include/Error.h include/Error.h
@ -53,6 +54,7 @@ SET(MAIN_SOURCES
Core.cpp Core.cpp
ColorText.cpp ColorText.cpp
DataDefs.cpp DataDefs.cpp
VTableInterpose.cpp
LuaWrapper.cpp LuaWrapper.cpp
LuaTypes.cpp LuaTypes.cpp
LuaTools.cpp LuaTools.cpp
@ -117,6 +119,7 @@ include/modules/Maps.h
include/modules/MapCache.h include/modules/MapCache.h
include/modules/Materials.h include/modules/Materials.h
include/modules/Notes.h include/modules/Notes.h
include/modules/Screen.h
include/modules/Translation.h include/modules/Translation.h
include/modules/Vegetation.h include/modules/Vegetation.h
include/modules/Vermin.h include/modules/Vermin.h
@ -137,6 +140,7 @@ modules/kitchen.cpp
modules/Maps.cpp modules/Maps.cpp
modules/Materials.cpp modules/Materials.cpp
modules/Notes.cpp modules/Notes.cpp
modules/Screen.cpp
modules/Translation.cpp modules/Translation.cpp
modules/Vegetation.cpp modules/Vegetation.cpp
modules/Vermin.cpp modules/Vermin.cpp
@ -246,6 +250,9 @@ ADD_DEPENDENCIES(dfhack-client dfhack)
ADD_EXECUTABLE(dfhack-run dfhack-run.cpp) ADD_EXECUTABLE(dfhack-run dfhack-run.cpp)
ADD_EXECUTABLE(binpatch binpatch.cpp)
TARGET_LINK_LIBRARIES(binpatch dfhack-md5)
IF(BUILD_EGGY) IF(BUILD_EGGY)
SET_TARGET_PROPERTIES(dfhack PROPERTIES OUTPUT_NAME "egg" ) SET_TARGET_PROPERTIES(dfhack PROPERTIES OUTPUT_NAME "egg" )
else() else()
@ -282,6 +289,10 @@ SET_TARGET_PROPERTIES(dfhack PROPERTIES LINK_INTERFACE_LIBRARIES "")
TARGET_LINK_LIBRARIES(dfhack-client protobuf-lite clsocket) TARGET_LINK_LIBRARIES(dfhack-client protobuf-lite clsocket)
TARGET_LINK_LIBRARIES(dfhack-run dfhack-client) TARGET_LINK_LIBRARIES(dfhack-run dfhack-client)
if(APPLE)
add_custom_command(TARGET dfhack-run COMMAND ${dfhack_SOURCE_DIR}/package/darwin/fix-libs.sh WORKING_DIRECTORY ../ COMMENT "Fixing library dependencies...")
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
@ -321,7 +332,7 @@ install(FILES xml/symbols.xml
install(FILES ../dfhack.init-example install(FILES ../dfhack.init-example
DESTINATION ${DFHACK_BINARY_DESTINATION}) DESTINATION ${DFHACK_BINARY_DESTINATION})
install(TARGETS dfhack-run dfhack-client install(TARGETS dfhack-run dfhack-client binpatch
LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION} LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION}
RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION}) RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION})

@ -275,7 +275,7 @@ namespace DFHack
/// Reset color to default /// Reset color to default
void reset_color(void) void reset_color(void)
{ {
color(Console::COLOR_RESET); color(COLOR_RESET);
if(!rawmode) if(!rawmode)
fflush(dfout_C); fflush(dfout_C);
} }

@ -277,7 +277,7 @@ namespace DFHack
/// Reset color to default /// Reset color to default
void reset_color(void) void reset_color(void)
{ {
color(Console::COLOR_RESET); color(COLOR_RESET);
if(!rawmode) if(!rawmode)
fflush(dfout_C); fflush(dfout_C);
} }

@ -179,7 +179,7 @@ namespace DFHack
void color(int index) void color(int index)
{ {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, index == color_ostream::COLOR_RESET ? default_attributes : index); SetConsoleTextAttribute(hConsole, index == COLOR_RESET ? default_attributes : index);
} }
void reset_color( void ) void reset_color( void )

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -126,8 +126,34 @@ struct Core::Private
void Core::cheap_tokenise(string const& input, vector<string> &output) void Core::cheap_tokenise(string const& input, vector<string> &output)
{ {
string *cur = NULL; string *cur = NULL;
size_t i = 0;
for (size_t i = 0; i < input.size(); i++) { // Check the first non-space character
while (i < input.size() && isspace(input[i])) i++;
// Special verbatim argument mode?
if (i < input.size() && input[i] == ':')
{
// Read the command
std::string cmd;
i++;
while (i < input.size() && !isspace(input[i]))
cmd.push_back(input[i++]);
if (!cmd.empty())
output.push_back(cmd);
// Find the argument
while (i < input.size() && isspace(input[i])) i++;
if (i < input.size())
output.push_back(input.substr(i));
return;
}
// Otherwise, parse in the regular quoted mode
for (; i < input.size(); i++)
{
unsigned char c = input[i]; unsigned char c = input[i];
if (isspace(c)) { if (isspace(c)) {
cur = NULL; cur = NULL;
@ -219,28 +245,30 @@ static std::string getScriptHelp(std::string path, std::string helpprefix)
return "No help available."; return "No help available.";
} }
static std::map<string,string> listScripts(PluginManager *plug_mgr, std::string path) static void listScripts(PluginManager *plug_mgr, std::map<string,string> &pset, std::string path, bool all, std::string prefix = "")
{ {
std::vector<string> files; std::vector<string> files;
getdir(path, files); getdir(path, files);
std::map<string,string> pset;
for (size_t i = 0; i < files.size(); i++) for (size_t i = 0; i < files.size(); i++)
{ {
if (hasEnding(files[i], ".lua")) if (hasEnding(files[i], ".lua"))
{ {
std::string help = getScriptHelp(path + files[i], "-- "); std::string help = getScriptHelp(path + files[i], "-- ");
pset[files[i].substr(0, files[i].size()-4)] = help; pset[prefix + files[i].substr(0, files[i].size()-4)] = help;
} }
else if (plug_mgr->eval_ruby && hasEnding(files[i], ".rb")) else if (plug_mgr->eval_ruby && hasEnding(files[i], ".rb"))
{ {
std::string help = getScriptHelp(path + files[i], "# "); std::string help = getScriptHelp(path + files[i], "# ");
pset[files[i].substr(0, files[i].size()-3)] = help; pset[prefix + files[i].substr(0, files[i].size()-3)] = help;
}
else if (all && !files[i].empty() && files[i][0] != '.')
{
listScripts(plug_mgr, pset, path+files[i]+"/", all, prefix+files[i]+"/");
} }
} }
return pset;
} }
static bool fileExists(std::string path) static bool fileExists(std::string path)
@ -335,7 +363,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
con.print("Basic commands:\n" con.print("Basic commands:\n"
" help|?|man - This text.\n" " help|?|man - This text.\n"
" help COMMAND - Usage help for the given command.\n" " help COMMAND - Usage help for the given command.\n"
" ls|dir [PLUGIN] - List available commands. Optionally for single plugin.\n" " ls|dir [-a] [PLUGIN] - List available commands. Optionally for single plugin.\n"
" cls - Clear the console.\n" " cls - Clear the console.\n"
" fpause - Force DF to pause.\n" " fpause - Force DF to pause.\n"
" die - Force DF to close immediately\n" " die - Force DF to close immediately\n"
@ -346,6 +374,8 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
" unload PLUGIN|all - Unload a plugin or all loaded plugins.\n" " unload PLUGIN|all - Unload a plugin or all loaded plugins.\n"
" 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");
} }
else if (parts.size() == 1) else if (parts.size() == 1)
{ {
@ -358,7 +388,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
continue; continue;
if (pcmd.isHotkeyCommand()) if (pcmd.isHotkeyCommand())
con.color(Console::COLOR_CYAN); con.color(COLOR_CYAN);
con.print("%s: %s\n",pcmd.name.c_str(), pcmd.description.c_str()); con.print("%s: %s\n",pcmd.name.c_str(), pcmd.description.c_str());
con.reset_color(); con.reset_color();
if (!pcmd.usage.empty()) if (!pcmd.usage.empty())
@ -469,6 +499,12 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
} }
else if(first == "ls" || first == "dir") else if(first == "ls" || first == "dir")
{ {
bool all = false;
if (parts.size() && parts[0] == "-a")
{
all = true;
vector_erase_at(parts, 0);
}
if(parts.size()) if(parts.size())
{ {
string & plugname = parts[0]; string & plugname = parts[0];
@ -481,7 +517,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
{ {
const PluginCommand & pcmd = (plug->operator[](j)); const PluginCommand & pcmd = (plug->operator[](j));
if (pcmd.isHotkeyCommand()) if (pcmd.isHotkeyCommand())
con.color(Console::COLOR_CYAN); con.color(COLOR_CYAN);
con.print(" %-22s - %s\n",pcmd.name.c_str(), pcmd.description.c_str()); con.print(" %-22s - %s\n",pcmd.name.c_str(), pcmd.description.c_str());
con.reset_color(); con.reset_color();
} }
@ -491,7 +527,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
con.print( con.print(
"builtin:\n" "builtin:\n"
" help|?|man - This text or help specific to a plugin.\n" " help|?|man - This text or help specific to a plugin.\n"
" ls [PLUGIN] - List available commands. Optionally for single plugin.\n" " ls [-a] [PLUGIN] - List available commands. Optionally for single plugin.\n"
" cls - Clear the console.\n" " cls - Clear the console.\n"
" fpause - Force DF to pause.\n" " fpause - Force DF to pause.\n"
" die - Force DF to close immediately\n" " die - Force DF to close immediately\n"
@ -519,11 +555,12 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
for(auto iter = out.begin();iter != out.end();iter++) for(auto iter = out.begin();iter != out.end();iter++)
{ {
if ((*iter).recolor) if ((*iter).recolor)
con.color(Console::COLOR_CYAN); con.color(COLOR_CYAN);
con.print(" %-22s- %s\n",(*iter).name.c_str(), (*iter).description.c_str()); con.print(" %-22s- %s\n",(*iter).name.c_str(), (*iter).description.c_str());
con.reset_color(); con.reset_color();
} }
auto scripts = listScripts(plug_mgr, getHackPath() + "scripts/"); std::map<string, string> scripts;
listScripts(plug_mgr, scripts, getHackPath() + "scripts/", all);
if (!scripts.empty()) if (!scripts.empty())
{ {
con.print("\nscripts:\n"); con.print("\nscripts:\n");
@ -592,8 +629,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
} }
else if(first == "fpause") else if(first == "fpause")
{ {
World * w = getWorld(); World::SetPauseState(true);
w->SetPauseState(true);
con.print("The game was forced to pause!\n"); con.print("The game was forced to pause!\n");
} }
else if(first == "cls") else if(first == "cls")
@ -786,6 +822,8 @@ std::string Core::getHackPath()
#endif #endif
} }
void init_screen_module(Core *);
bool Core::Init() bool Core::Init()
{ {
if(started) if(started)
@ -856,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);
@ -1027,35 +1066,41 @@ int Core::TileUpdate()
return true; return true;
} }
// should always be from simulation thread! int Core::ClaimSuspend(bool force_base)
int Core::Update()
{
if(errorstate)
return -1;
// Pretend this thread has suspended the core in the usual way
{ {
auto tid = this_thread::get_id();
lock_guard<mutex> lock(d->AccessMutex); lock_guard<mutex> lock(d->AccessMutex);
if (force_base || d->df_suspend_depth <= 0)
{
assert(d->df_suspend_depth == 0); assert(d->df_suspend_depth == 0);
d->df_suspend_thread = this_thread::get_id();
d->df_suspend_depth = 1000;
}
// Initialize the core d->df_suspend_thread = tid;
bool first_update = false; d->df_suspend_depth = 1000000;
return 1000000;
if(!started) }
else
{ {
first_update = true; assert(d->df_suspend_thread == tid);
Init(); return ++d->df_suspend_depth;
if(errorstate) }
return -1;
Lua::Core::Reset(con, "core init");
} }
color_ostream_proxy out(con); void Core::DisclaimSuspend(int level)
{
auto tid = this_thread::get_id();
lock_guard<mutex> lock(d->AccessMutex);
assert(d->df_suspend_depth == level && d->df_suspend_thread == tid);
if (level == 1000000)
d->df_suspend_depth = 0;
else
--d->df_suspend_depth;
}
void Core::doUpdate(color_ostream &out, bool first_update)
{
Lua::Core::Reset(out, "DF code execution"); Lua::Core::Reset(out, "DF code execution");
if (first_update) if (first_update)
@ -1081,7 +1126,7 @@ int Core::Update()
last_world_data_ptr = new_wdata; last_world_data_ptr = new_wdata;
last_local_map_ptr = new_mapdata; last_local_map_ptr = new_mapdata;
getWorld()->ClearPersistentCache(); World::ClearPersistentCache();
// and if the world is going away, we report the map change first // and if the world is going away, we report the map change first
if(had_map) if(had_map)
@ -1099,7 +1144,7 @@ int Core::Update()
if (isMapLoaded() != had_map) if (isMapLoaded() != had_map)
{ {
getWorld()->ClearPersistentCache(); World::ClearPersistentCache();
onStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED); onStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED);
} }
} }
@ -1129,15 +1174,36 @@ int Core::Update()
// Execute per-frame handlers // Execute per-frame handlers
onUpdate(out); onUpdate(out);
// Release the fake suspend lock out << std::flush;
}
// should always be from simulation thread!
int Core::Update()
{ {
lock_guard<mutex> lock(d->AccessMutex); if(errorstate)
return -1;
assert(d->df_suspend_depth == 1000); color_ostream_proxy out(con);
d->df_suspend_depth = 0;
// Pretend this thread has suspended the core in the usual way,
// and run various processing hooks.
{
CoreSuspendClaimer suspend(true);
// Initialize the core
bool first_update = false;
if(!started)
{
first_update = true;
Init();
if(errorstate)
return -1;
Lua::Core::Reset(con, "core init");
} }
out << std::flush; doUpdate(out, first_update);
}
// wake waiting tools // wake waiting tools
// do not allow more tools to join in while we process stuff here // do not allow more tools to join in while we process stuff here
@ -1158,7 +1224,7 @@ int Core::Update()
// destroy condition // destroy condition
delete nc; delete nc;
// check lua stack depth // check lua stack depth
Lua::Core::Reset(con, "suspend"); Lua::Core::Reset(out, "suspend");
} }
return 0; return 0;
@ -1198,6 +1264,7 @@ int Core::Shutdown ( void )
if(errorstate) if(errorstate)
return true; return true;
errorstate = 1; errorstate = 1;
CoreSuspendClaimer suspend;
if(plug_mgr) if(plug_mgr)
{ {
delete plug_mgr; delete plug_mgr;
@ -1232,7 +1299,7 @@ bool Core::ncurses_wgetch(int in, int & out)
// FIXME: copypasta, push into a method! // FIXME: copypasta, push into a method!
if(df::global::ui && df::global::gview) if(df::global::ui && df::global::gview)
{ {
df::viewscreen * ws = Gui::GetCurrentScreen(); df::viewscreen * ws = Gui::getCurViewscreen();
if (strict_virtual_cast<df::viewscreen_dwarfmodest>(ws) && if (strict_virtual_cast<df::viewscreen_dwarfmodest>(ws) &&
df::global::ui->main.mode != ui_sidebar_mode::Hotkeys && df::global::ui->main.mode != ui_sidebar_mode::Hotkeys &&
df::global::ui->main.hotkeys[idx].cmd == df::ui_hotkey::T_cmd::None) df::global::ui->main.hotkeys[idx].cmd == df::ui_hotkey::T_cmd::None)
@ -1548,6 +1615,90 @@ void ClassNameCheck::getKnownClassNames(std::vector<std::string> &names)
names.push_back(*it); names.push_back(*it);
} }
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 *eptr = sptr + count;
// Find the valid memory ranges
if (ranges.empty())
p->getMemRanges(ranges);
// Find the ranges that this area spans
unsigned start = 0;
while (start < ranges.size() && ranges[start].end <= sptr)
start++;
if (start >= ranges.size() || ranges[start].start > sptr)
return false;
unsigned end = start+1;
while (end < ranges.size() && ranges[end].start < eptr)
{
if (ranges[end].start != ranges[end-1].end)
return false;
end++;
}
if (ranges[end-1].end < eptr)
return false;
// Verify current permissions
for (unsigned i = start; i < end; i++)
if (!ranges[i].valid || !(ranges[i].read || ranges[i].execute) || ranges[i].shared)
return false;
// Apply writable permissions & update
for (unsigned i = start; i < end; i++)
{
auto &perms = ranges[i];
if ((perms.write || !write) && perms.read)
continue;
save.push_back(perms);
perms.write = perms.read = true;
if (!p->setPermisions(perms, perms))
return false;
}
return true;
}
bool MemoryPatcher::write(void *target, const void *src, size_t size)
{
if (!makeWritable(target, size))
return false;
memmove(target, src, size);
return true;
}
void MemoryPatcher::close()
{
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 patcher.write(target, src, count);
}
/******************************************************************************* /*******************************************************************************
M O D U L E S M O D U L E S
*******************************************************************************/ *******************************************************************************/
@ -1565,7 +1716,6 @@ TYPE * Core::get##TYPE() \
return s_mods.p##TYPE;\ return s_mods.p##TYPE;\
} }
MODULE_GETTER(World);
MODULE_GETTER(Materials); MODULE_GETTER(Materials);
MODULE_GETTER(Notes); MODULE_GETTER(Notes);
MODULE_GETTER(Graphic); MODULE_GETTER(Graphic);

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -35,6 +35,7 @@ distribution.
// must be last due to MS stupidity // must be last due to MS stupidity
#include "DataDefs.h" #include "DataDefs.h"
#include "DataIdentity.h" #include "DataIdentity.h"
#include "VTableInterpose.h"
#include "MiscUtils.h" #include "MiscUtils.h"
@ -214,6 +215,15 @@ virtual_identity::virtual_identity(size_t size, TAllocateFn alloc,
{ {
} }
virtual_identity::~virtual_identity()
{
// Remove interpose entries, so that they don't try accessing this object later
for (auto it = interpose_list.begin(); it != interpose_list.end(); ++it)
if (it->second)
it->second->on_host_delete(this);
interpose_list.clear();
}
/* Vtable name to identity lookup. */ /* Vtable name to identity lookup. */
static std::map<std::string, virtual_identity*> name_lookup; static std::map<std::string, virtual_identity*> name_lookup;
@ -364,7 +374,7 @@ void DFHack::bitfieldToString(std::vector<std::string> *pvec, const void *p,
unsigned size, const bitfield_item_info *items) unsigned size, const bitfield_item_info *items)
{ {
for (unsigned i = 0; i < size; i++) { for (unsigned i = 0; i < size; i++) {
int value = getBitfieldField(p, i, std::min(1,items[i].size)); int value = getBitfieldField(p, i, std::max(1,items[i].size));
if (value) { if (value) {
std::string name = format_key(items[i].name, i); std::string name = format_key(items[i].name, i);

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -68,6 +68,11 @@ lua_State *DFHack::Lua::Core::State = NULL;
void dfhack_printerr(lua_State *S, const std::string &str); void dfhack_printerr(lua_State *S, const std::string &str);
inline bool is_null_userdata(lua_State *L, int idx)
{
return lua_islightuserdata(L, idx) && !lua_touserdata(L, idx);
}
inline void AssertCoreSuspend(lua_State *state) inline void AssertCoreSuspend(lua_State *state)
{ {
assert(!Lua::IsCoreContext(state) || DFHack::Core::getInstance().isSuspended()); assert(!Lua::IsCoreContext(state) || DFHack::Core::getInstance().isSuspended());
@ -102,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)
{ {
@ -129,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);
@ -252,7 +260,7 @@ static int lua_dfhack_color(lua_State *S)
{ {
int cv = luaL_optint(S, 1, -1); int cv = luaL_optint(S, 1, -1);
if (cv < -1 || cv > color_ostream::COLOR_MAX) if (cv < -1 || cv > COLOR_MAX)
luaL_argerror(S, 1, "invalid color value"); luaL_argerror(S, 1, "invalid color value");
color_ostream *out = Lua::GetOutput(S); color_ostream *out = Lua::GetOutput(S);
@ -1206,6 +1214,39 @@ static int dfhack_open_plugin(lua_State *L)
return 0; return 0;
} }
static int dfhack_curry_wrap(lua_State *L)
{
int nargs = lua_gettop(L);
int ncurry = lua_tointeger(L, lua_upvalueindex(1));
int scount = nargs + ncurry;
luaL_checkstack(L, ncurry, "stack overflow in curry");
// Insert values in O(N+M) by first shifting the existing data
lua_settop(L, scount);
for (int i = 0; i < nargs; i++)
lua_copy(L, nargs-i, scount-i);
for (int i = 1; i <= ncurry; i++)
lua_copy(L, lua_upvalueindex(i+1), i);
lua_callk(L, scount-1, LUA_MULTRET, 0, lua_gettop);
return lua_gettop(L);
}
static int dfhack_curry(lua_State *L)
{
luaL_checkany(L, 1);
if (lua_isnil(L, 1))
luaL_argerror(L, 1, "nil function in curry");
if (lua_gettop(L) == 1)
return 1;
lua_pushinteger(L, lua_gettop(L));
lua_insert(L, 1);
lua_pushcclosure(L, dfhack_curry_wrap, lua_gettop(L));
return 1;
}
bool Lua::IsCoreContext(lua_State *state) bool Lua::IsCoreContext(lua_State *state)
{ {
// This uses a private field of the lua state to // This uses a private field of the lua state to
@ -1229,6 +1270,7 @@ static const luaL_Reg dfhack_funcs[] = {
{ "call_with_finalizer", dfhack_call_with_finalizer }, { "call_with_finalizer", dfhack_call_with_finalizer },
{ "with_suspend", lua_dfhack_with_suspend }, { "with_suspend", lua_dfhack_with_suspend },
{ "open_plugin", dfhack_open_plugin }, { "open_plugin", dfhack_open_plugin },
{ "curry", dfhack_curry },
{ NULL, NULL } { NULL, NULL }
}; };
@ -1244,14 +1286,123 @@ static const luaL_Reg dfhack_coro_funcs[] = {
static int DFHACK_EVENT_META_TOKEN = 0; static int DFHACK_EVENT_META_TOKEN = 0;
int DFHack::Lua::NewEvent(lua_State *state) namespace {
struct EventObject {
int item_count;
Lua::Event::Owner *owner;
};
}
void DFHack::Lua::Event::New(lua_State *state, Owner *owner)
{ {
lua_newtable(state); auto obj = (EventObject *)lua_newuserdata(state, sizeof(EventObject));
obj->item_count = 0;
obj->owner = owner;
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_EVENT_META_TOKEN); lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_EVENT_META_TOKEN);
lua_setmetatable(state, -2); lua_setmetatable(state, -2);
lua_newtable(state);
lua_setuservalue(state, -2);
}
void DFHack::Lua::Event::SetPrivateCallback(lua_State *L, int event)
{
lua_getuservalue(L, event);
lua_swap(L);
lua_rawsetp(L, -2, NULL);
lua_pop(L, 1);
}
static int dfhack_event_new(lua_State *L)
{
Lua::Event::New(L);
return 1; return 1;
} }
static int dfhack_event_len(lua_State *L)
{
luaL_checktype(L, 1, LUA_TUSERDATA);
auto obj = (EventObject *)lua_touserdata(L, 1);
lua_pushinteger(L, obj->item_count);
return 1;
}
static int dfhack_event_tostring(lua_State *L)
{
luaL_checktype(L, 1, LUA_TUSERDATA);
auto obj = (EventObject *)lua_touserdata(L, 1);
lua_pushfstring(L, "<event: %d listeners>", obj->item_count);
return 1;
}
static int dfhack_event_index(lua_State *L)
{
luaL_checktype(L, 1, LUA_TUSERDATA);
lua_getuservalue(L, 1);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
return 1;
}
static int dfhack_event_next(lua_State *L)
{
luaL_checktype(L, 1, LUA_TUSERDATA);
lua_getuservalue(L, 1);
lua_pushvalue(L, 2);
while (lua_next(L, -2))
{
if (is_null_userdata(L, -2))
lua_pop(L, 1);
else
return 2;
}
lua_pushnil(L);
return 1;
}
static int dfhack_event_pairs(lua_State *L)
{
luaL_checktype(L, 1, LUA_TUSERDATA);
lua_pushcfunction(L, dfhack_event_next);
lua_pushvalue(L, 1);
lua_pushnil(L);
return 3;
}
static int dfhack_event_newindex(lua_State *L)
{
luaL_checktype(L, 1, LUA_TUSERDATA);
if (is_null_userdata(L, 2))
luaL_argerror(L, 2, "Key NULL is reserved in events.");
lua_settop(L, 3);
lua_getuservalue(L, 1);
bool new_nil = lua_isnil(L, 3);
lua_pushvalue(L, 2);
lua_rawget(L, 4);
bool old_nil = lua_isnil(L, -1);
lua_settop(L, 4);
lua_pushvalue(L, 2);
lua_pushvalue(L, 3);
lua_rawset(L, 4);
int delta = 0;
if (old_nil && !new_nil) delta = 1;
else if (new_nil && !old_nil) delta = -1;
if (delta != 0)
{
auto obj = (EventObject *)lua_touserdata(L, 1);
obj->item_count += delta;
if (obj->owner)
obj->owner->on_count_changed(obj->item_count, delta);
}
return 0;
}
static void do_invoke_event(lua_State *L, int argbase, int num_args, int errorfun) static void do_invoke_event(lua_State *L, int argbase, int num_args, int errorfun)
{ {
for (int i = 0; i < num_args; i++) for (int i = 0; i < num_args; i++)
@ -1292,7 +1443,7 @@ static void dfhack_event_invoke(lua_State *L, int base, bool from_c)
while (lua_next(L, event)) while (lua_next(L, event))
{ {
// Skip the NULL key in the main loop // Skip the NULL key in the main loop
if (lua_islightuserdata(L, -2) && !lua_touserdata(L, -2)) if (is_null_userdata(L, -2))
lua_pop(L, 1); lua_pop(L, 1);
else else
do_invoke_event(L, argbase, num_args, errorfun); do_invoke_event(L, argbase, num_args, errorfun);
@ -1303,14 +1454,20 @@ static void dfhack_event_invoke(lua_State *L, int base, bool from_c)
static int dfhack_event_call(lua_State *state) static int dfhack_event_call(lua_State *state)
{ {
luaL_checktype(state, 1, LUA_TTABLE); luaL_checktype(state, 1, LUA_TUSERDATA);
luaL_checkstack(state, lua_gettop(state)+2, "stack overflow in event dispatch"); luaL_checkstack(state, lua_gettop(state)+2, "stack overflow in event dispatch");
auto obj = (EventObject *)lua_touserdata(state, 1);
if (obj->owner)
obj->owner->on_invoked(state, lua_gettop(state)-1, false);
lua_getuservalue(state, 1);
lua_replace(state, 1);
dfhack_event_invoke(state, 0, false); dfhack_event_invoke(state, 0, false);
return 0; return 0;
} }
void DFHack::Lua::InvokeEvent(color_ostream &out, lua_State *state, void *key, int num_args) void DFHack::Lua::Event::Invoke(color_ostream &out, lua_State *state, void *key, int num_args)
{ {
AssertCoreSuspend(state); AssertCoreSuspend(state);
@ -1325,7 +1482,7 @@ void DFHack::Lua::InvokeEvent(color_ostream &out, lua_State *state, void *key, i
lua_rawgetp(state, LUA_REGISTRYINDEX, key); lua_rawgetp(state, LUA_REGISTRYINDEX, key);
if (!lua_istable(state, -1)) if (!lua_isuserdata(state, -1))
{ {
if (!lua_isnil(state, -1)) if (!lua_isnil(state, -1))
out.printerr("Invalid event object in Lua::InvokeEvent"); out.printerr("Invalid event object in Lua::InvokeEvent");
@ -1333,22 +1490,29 @@ void DFHack::Lua::InvokeEvent(color_ostream &out, lua_State *state, void *key, i
return; return;
} }
auto obj = (EventObject *)lua_touserdata(state, -1);
lua_insert(state, base+1); lua_insert(state, base+1);
if (obj->owner)
obj->owner->on_invoked(state, num_args, true);
lua_getuservalue(state, base+1);
lua_replace(state, base+1);
color_ostream *cur_out = Lua::GetOutput(state); color_ostream *cur_out = Lua::GetOutput(state);
set_dfhack_output(state, &out); set_dfhack_output(state, &out);
dfhack_event_invoke(state, base, true); dfhack_event_invoke(state, base, true);
set_dfhack_output(state, cur_out); set_dfhack_output(state, cur_out);
} }
void DFHack::Lua::MakeEvent(lua_State *state, void *key) void DFHack::Lua::Event::Make(lua_State *state, void *key, Owner *owner)
{ {
lua_rawgetp(state, LUA_REGISTRYINDEX, key); lua_rawgetp(state, LUA_REGISTRYINDEX, key);
if (lua_isnil(state, -1)) if (lua_isnil(state, -1))
{ {
lua_pop(state, 1); lua_pop(state, 1);
NewEvent(state); New(state, owner);
} }
lua_dup(state); lua_dup(state);
@ -1358,7 +1522,7 @@ void DFHack::Lua::MakeEvent(lua_State *state, void *key)
void DFHack::Lua::Notification::invoke(color_ostream &out, int nargs) void DFHack::Lua::Notification::invoke(color_ostream &out, int nargs)
{ {
assert(state); assert(state);
InvokeEvent(out, state, key, nargs); Event::Invoke(out, state, key, nargs);
} }
void DFHack::Lua::Notification::bind(lua_State *state, void *key) void DFHack::Lua::Notification::bind(lua_State *state, void *key)
@ -1369,12 +1533,12 @@ void DFHack::Lua::Notification::bind(lua_State *state, void *key)
void DFHack::Lua::Notification::bind(lua_State *state, const char *name) void DFHack::Lua::Notification::bind(lua_State *state, const char *name)
{ {
MakeEvent(state, this); Event::Make(state, this);
if (handler) if (handler)
{ {
PushFunctionWrapper(state, 0, name, handler); PushFunctionWrapper(state, 0, name, handler);
lua_rawsetp(state, -2, NULL); Event::SetPrivateCallback(state, -2);
} }
this->state = state; this->state = state;
@ -1419,6 +1583,9 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_BASE_G_TOKEN); lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_BASE_G_TOKEN);
lua_setfield(state, -2, "BASE_G"); lua_setfield(state, -2, "BASE_G");
lua_pushstring(state, DFHACK_VERSION);
lua_setfield(state, -2, "VERSION");
lua_pushboolean(state, IsCoreContext(state)); lua_pushboolean(state, IsCoreContext(state));
lua_setfield(state, -2, "is_core_context"); lua_setfield(state, -2, "is_core_context");
@ -1435,11 +1602,26 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
lua_newtable(state); lua_newtable(state);
lua_pushcfunction(state, dfhack_event_call); lua_pushcfunction(state, dfhack_event_call);
lua_setfield(state, -2, "__call"); lua_setfield(state, -2, "__call");
lua_pushcfunction(state, Lua::NewEvent); lua_pushcfunction(state, dfhack_event_len);
lua_setfield(state, -2, "new"); lua_setfield(state, -2, "__len");
lua_pushcfunction(state, dfhack_event_tostring);
lua_setfield(state, -2, "__tostring");
lua_pushcfunction(state, dfhack_event_index);
lua_setfield(state, -2, "__index");
lua_pushcfunction(state, dfhack_event_newindex);
lua_setfield(state, -2, "__newindex");
lua_pushcfunction(state, dfhack_event_pairs);
lua_setfield(state, -2, "__pairs");
lua_dup(state); lua_dup(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_EVENT_META_TOKEN); lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_EVENT_META_TOKEN);
lua_setfield(state, -2, "event");
lua_newtable(state);
lua_pushcfunction(state, dfhack_event_new);
lua_setfield(state, -2, "new");
lua_dup(state);
lua_setfield(state, -3, "__metatable");
lua_setfield(state, -3, "event");
lua_pop(state, 1);
// Initialize the dfhack global // Initialize the dfhack global
luaL_setfuncs(state, dfhack_funcs, 0); luaL_setfuncs(state, dfhack_funcs, 0);
@ -1599,7 +1781,7 @@ void DFHack::Lua::Core::onStateChange(color_ostream &out, int code) {
} }
Lua::Push(State, code); Lua::Push(State, code);
Lua::InvokeEvent(out, State, (void*)onStateChange, 1); Lua::Event::Invoke(out, State, (void*)onStateChange, 1);
} }
static void run_timers(color_ostream &out, lua_State *L, static void run_timers(color_ostream &out, lua_State *L,
@ -1635,6 +1817,8 @@ void DFHack::Lua::Core::onUpdate(color_ostream &out)
lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN);
run_timers(out, State, frame_timers, frame[1], ++frame_idx); run_timers(out, State, frame_timers, frame[1], ++frame_idx);
if (world)
run_timers(out, State, tick_timers, frame[1], world->frame_counter); run_timers(out, State, tick_timers, frame[1], world->frame_counter);
} }
@ -1653,7 +1837,7 @@ void DFHack::Lua::Core::Init(color_ostream &out)
// Register events // Register events
lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_DFHACK_TOKEN); lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_DFHACK_TOKEN);
MakeEvent(State, (void*)onStateChange); Event::Make(State, (void*)onStateChange);
lua_setfield(State, -2, "onStateChange"); lua_setfield(State, -2, "onStateChange");
lua_pushcfunction(State, dfhack_timeout); lua_pushcfunction(State, dfhack_timeout);

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -37,6 +37,7 @@ distribution.
#include "DataDefs.h" #include "DataDefs.h"
#include "DataIdentity.h" #include "DataIdentity.h"
#include "LuaWrapper.h" #include "LuaWrapper.h"
#include "LuaTools.h"
#include "DataFuncs.h" #include "DataFuncs.h"
#include "MiscUtils.h" #include "MiscUtils.h"
@ -285,6 +286,9 @@ void container_identity::lua_item_read(lua_State *state, int fname_idx, void *pt
void container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) void container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index)
{ {
if (is_readonly())
field_error(state, fname_idx, "container is read-only", "write");
auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
void *pitem = item_pointer(id, ptr, idx); void *pitem = item_pointer(id, ptr, idx);
id->lua_write(state, fname_idx, pitem, val_index); id->lua_write(state, fname_idx, pitem, val_index);
@ -890,7 +894,7 @@ static int meta_bitfield_len(lua_State *state)
static void read_bitfield(lua_State *state, uint8_t *ptr, bitfield_identity *id, int idx) static void read_bitfield(lua_State *state, uint8_t *ptr, bitfield_identity *id, int idx)
{ {
int size = id->getBits()[idx].size; int size = std::max(1, id->getBits()[idx].size);
int value = getBitfieldField(ptr, idx, size); int value = getBitfieldField(ptr, idx, size);
if (size <= 1) if (size <= 1)
@ -947,7 +951,7 @@ static int meta_bitfield_newindex(lua_State *state)
} }
int idx = check_container_index(state, id->getNumBits(), 2, iidx, "write"); int idx = check_container_index(state, id->getNumBits(), 2, iidx, "write");
int size = id->getBits()[idx].size; int size = std::max(1, id->getBits()[idx].size);
if (lua_isboolean(state, 3) || lua_isnil(state, 3)) if (lua_isboolean(state, 3) || lua_isnil(state, 3))
setBitfieldField(ptr, idx, size, lua_toboolean(state, 3)); setBitfieldField(ptr, idx, size, lua_toboolean(state, 3));
@ -1064,6 +1068,27 @@ int LuaWrapper::method_wrapper_core(lua_State *state, function_identity_base *id
return 1; return 1;
} }
int Lua::CallWithCatch(lua_State *state, int (*fn)(lua_State*), const char *context)
{
if (!context)
context = "native code";
try {
return fn(state);
}
catch (Error::NullPointer &e) {
const char *vn = e.varname();
return luaL_error(state, "%s: NULL pointer: %s", context, vn ? vn : "?");
}
catch (Error::InvalidArgument &e) {
const char *vn = e.expr();
return luaL_error(state, "%s: Invalid argument; expected: %s", context, vn ? vn : "?");
}
catch (std::exception &e) {
return luaL_error(state, "%s: C++ exception: %s", context, e.what());
}
}
/** /**
* Push a closure invoking the given function. * Push a closure invoking the given function.
*/ */

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -1265,6 +1265,51 @@ static void BuildTypeMetatable(lua_State *state, type_identity *type)
* Recursive walk of scopes to construct the df... tree. * Recursive walk of scopes to construct the df... tree.
*/ */
static int wtype_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 wtype_pairs(lua_State *state)
{
lua_pushvalue(state, lua_upvalueindex(1));
lua_pushcclosure(state, wtype_pnext, 1);
lua_pushnil(state);
lua_pushnil(state);
return 3;
}
static int wtype_inext(lua_State *L)
{
int i = luaL_checkint(L, 2);
i++; /* next value */
if (i <= lua_tointeger(L, lua_upvalueindex(2)))
{
lua_pushinteger(L, i);
lua_rawgeti(L, lua_upvalueindex(1), i);
return 2;
}
else
{
lua_pushnil(L);
return 1;
}
}
static int wtype_ipairs(lua_State *state)
{
lua_pushvalue(state, lua_upvalueindex(1));
lua_pushvalue(state, lua_upvalueindex(3));
lua_pushcclosure(state, wtype_inext, 2);
lua_pushnil(state);
lua_pushvalue(state, lua_upvalueindex(2));
return 3;
}
static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children); static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children);
void LuaWrapper::AssociateId(lua_State *state, int table, int val, const char *name) void LuaWrapper::AssociateId(lua_State *state, int table, int val, const char *name)
@ -1278,7 +1323,7 @@ void LuaWrapper::AssociateId(lua_State *state, int table, int val, const char *n
lua_rawset(state, table); lua_rawset(state, table);
} }
static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid) static void FillEnumKeys(lua_State *state, int ix_meta, int ftable, enum_identity *eid)
{ {
const char *const *keys = eid->getKeys(); const char *const *keys = eid->getKeys();
@ -1296,11 +1341,17 @@ static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid)
if (eid->getFirstItem() <= eid->getLastItem()) if (eid->getFirstItem() <= eid->getLastItem())
{ {
lua_pushvalue(state, base+1);
lua_pushinteger(state, eid->getFirstItem()-1);
lua_pushinteger(state, eid->getLastItem());
lua_pushcclosure(state, wtype_ipairs, 3);
lua_setfield(state, ix_meta, "__ipairs");
lua_pushinteger(state, eid->getFirstItem()); lua_pushinteger(state, eid->getFirstItem());
lua_setfield(state, base+1, "_first_item"); lua_setfield(state, ftable, "_first_item");
lua_pushinteger(state, eid->getLastItem()); lua_pushinteger(state, eid->getLastItem());
lua_setfield(state, base+1, "_last_item"); lua_setfield(state, ftable, "_last_item");
} }
SaveInTable(state, eid, &DFHACK_ENUM_TABLE_TOKEN); SaveInTable(state, eid, &DFHACK_ENUM_TABLE_TOKEN);
@ -1321,7 +1372,7 @@ static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid)
lua_setmetatable(state, ftable); lua_setmetatable(state, ftable);
} }
static void FillBitfieldKeys(lua_State *state, int ftable, bitfield_identity *eid) static void FillBitfieldKeys(lua_State *state, int ix_meta, int ftable, bitfield_identity *eid)
{ {
// Create a new table attached to ftable as __index // Create a new table attached to ftable as __index
lua_newtable(state); lua_newtable(state);
@ -1338,11 +1389,17 @@ static void FillBitfieldKeys(lua_State *state, int ftable, bitfield_identity *ei
i += bits[i].size-1; i += bits[i].size-1;
} }
lua_pushvalue(state, base+1);
lua_pushinteger(state, -1);
lua_pushinteger(state, eid->getNumBits()-1);
lua_pushcclosure(state, wtype_ipairs, 3);
lua_setfield(state, ix_meta, "__ipairs");
lua_pushinteger(state, 0); lua_pushinteger(state, 0);
lua_setfield(state, base+1, "_first_item"); lua_setfield(state, ftable, "_first_item");
lua_pushinteger(state, eid->getNumBits()-1); lua_pushinteger(state, eid->getNumBits()-1);
lua_setfield(state, base+1, "_last_item"); lua_setfield(state, ftable, "_last_item");
SaveInTable(state, eid, &DFHACK_ENUM_TABLE_TOKEN); SaveInTable(state, eid, &DFHACK_ENUM_TABLE_TOKEN);
@ -1355,7 +1412,12 @@ static void RenderType(lua_State *state, compound_identity *node)
assert(node->getName()); assert(node->getName());
std::string name = node->getFullName(); std::string name = node->getFullName();
int base = lua_gettop(state); // Frame:
// base+1 - outer table
// base+2 - metatable of outer table
// base+3 - inner table
// base+4 - pairs table
Lua::StackUnwinder base(state);
lua_newtable(state); lua_newtable(state);
if (!lua_checkstack(state, 20)) if (!lua_checkstack(state, 20))
@ -1365,51 +1427,59 @@ static void RenderType(lua_State *state, compound_identity *node)
// metatable // metatable
lua_newtable(state); lua_newtable(state);
int ix_meta = base+2;
lua_dup(state); lua_dup(state);
lua_setmetatable(state, base+1); lua_setmetatable(state, base+1);
lua_pushstring(state, name.c_str()); lua_pushstring(state, name.c_str());
lua_setfield(state, base+2, "__metatable"); lua_setfield(state, ix_meta, "__metatable");
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME);
lua_setfield(state, base+2, "__tostring"); lua_setfield(state, ix_meta, "__tostring");
lua_pushlightuserdata(state, node); lua_pushlightuserdata(state, node);
lua_rawsetp(state, base+2, &DFHACK_IDENTITY_FIELD_TOKEN); lua_rawsetp(state, ix_meta, &DFHACK_IDENTITY_FIELD_TOKEN);
// inner table // inner table
lua_newtable(state); lua_newtable(state);
int ftable = base+3;
lua_dup(state); lua_dup(state);
lua_setfield(state, base+2, "__index"); lua_setfield(state, ix_meta, "__index");
int ftable = base+3; // pairs table
lua_newtable(state);
int ptable = base+4;
lua_pushvalue(state, ptable);
lua_pushcclosure(state, wtype_pairs, 1);
lua_setfield(state, ix_meta, "__pairs");
switch (node->type()) switch (node->type())
{ {
case IDTYPE_STRUCT: case IDTYPE_STRUCT:
lua_pushstring(state, "struct-type"); lua_pushstring(state, "struct-type");
lua_setfield(state, ftable, "_kind"); lua_setfield(state, ftable, "_kind");
IndexStatics(state, base+2, base+3, (struct_identity*)node); IndexStatics(state, ix_meta, ftable, (struct_identity*)node);
break; break;
case IDTYPE_CLASS: case IDTYPE_CLASS:
lua_pushstring(state, "class-type"); lua_pushstring(state, "class-type");
lua_setfield(state, ftable, "_kind"); lua_setfield(state, ftable, "_kind");
IndexStatics(state, base+2, base+3, (struct_identity*)node); IndexStatics(state, ix_meta, ftable, (struct_identity*)node);
break; break;
case IDTYPE_ENUM: case IDTYPE_ENUM:
lua_pushstring(state, "enum-type"); lua_pushstring(state, "enum-type");
lua_setfield(state, ftable, "_kind"); lua_setfield(state, ftable, "_kind");
FillEnumKeys(state, ftable, (enum_identity*)node); FillEnumKeys(state, ix_meta, ftable, (enum_identity*)node);
break; break;
case IDTYPE_BITFIELD: case IDTYPE_BITFIELD:
lua_pushstring(state, "bitfield-type"); lua_pushstring(state, "bitfield-type");
lua_setfield(state, ftable, "_kind"); lua_setfield(state, ftable, "_kind");
FillBitfieldKeys(state, ftable, (bitfield_identity*)node); FillBitfieldKeys(state, ix_meta, ftable, (bitfield_identity*)node);
break; break;
case IDTYPE_GLOBAL: case IDTYPE_GLOBAL:
@ -1425,14 +1495,14 @@ static void RenderType(lua_State *state, compound_identity *node)
BuildTypeMetatable(state, node); BuildTypeMetatable(state, node);
lua_dup(state); lua_dup(state);
lua_setmetatable(state, base+3); lua_setmetatable(state, ftable);
lua_getfield(state, -1, "__newindex"); lua_getfield(state, -1, "__newindex");
lua_setfield(state, base+2, "__newindex"); lua_setfield(state, ix_meta, "__newindex");
lua_getfield(state, -1, "__pairs"); lua_getfield(state, -1, "__pairs");
lua_setfield(state, base+2, "__pairs"); lua_setfield(state, ix_meta, "__pairs");
lua_pop(state, 3); base += 1;
return; return;
} }
@ -1454,17 +1524,25 @@ static void RenderType(lua_State *state, compound_identity *node)
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME);
lua_setfield(state, ftable, "is_instance"); lua_setfield(state, ftable, "is_instance");
lua_pop(state, 2); base += 1;
} }
static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children) static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children)
{ {
// fieldtable pairstable |
int base = lua_gettop(state);
for (size_t i = 0; i < children.size(); i++) for (size_t i = 0; i < children.size(); i++)
{ {
RenderType(state, children[i]); RenderType(state, children[i]);
lua_pushstring(state, children[i]->getName()); lua_pushstring(state, children[i]->getName());
lua_swap(state); lua_swap(state);
lua_rawset(state, -3);
// save in both tables
lua_pushvalue(state, -2);
lua_pushvalue(state, -2);
lua_rawset(state, base);
lua_rawset(state, base-1);
} }
} }
@ -1524,10 +1602,13 @@ static int DoAttach(lua_State *state)
{ {
// Assign df a metatable with read-only contents // Assign df a metatable with read-only contents
lua_newtable(state); lua_newtable(state);
lua_newtable(state);
// Render the type structure // Render the type structure
RenderTypeChildren(state, compound_identity::getTopScope()); RenderTypeChildren(state, compound_identity::getTopScope());
lua_swap(state); // -> pairstable fieldtable
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME);
lua_setfield(state, -2, "sizeof"); lua_setfield(state, -2, "sizeof");
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME);
@ -1558,7 +1639,15 @@ static int DoAttach(lua_State *state)
lua_pushcfunction(state, meta_isnull); lua_pushcfunction(state, meta_isnull);
lua_setfield(state, -2, "isnull"); lua_setfield(state, -2, "isnull");
freeze_table(state, false, "df"); freeze_table(state, true, "df");
// pairstable dftable dfmeta
lua_pushvalue(state, -3);
lua_pushcclosure(state, wtype_pairs, 1);
lua_setfield(state, -2, "__pairs");
lua_pop(state, 1);
lua_remove(state, -2);
} }
return 1; return 1;

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -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()
{ {

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -108,6 +108,50 @@ struct Plugin::RefAutoinc
~RefAutoinc(){ lock->lock_sub(); }; ~RefAutoinc(){ lock->lock_sub(); };
}; };
struct Plugin::LuaCommand {
Plugin *owner;
std::string name;
int (*command)(lua_State *state);
LuaCommand(Plugin *owner, std::string name)
: owner(owner), name(name), command(NULL) {}
};
struct Plugin::LuaFunction {
Plugin *owner;
std::string name;
function_identity_base *identity;
bool silent;
LuaFunction(Plugin *owner, std::string name)
: owner(owner), name(name), identity(NULL), silent(false) {}
};
struct Plugin::LuaEvent : public Lua::Event::Owner {
LuaFunction handler;
Lua::Notification *event;
bool active;
int count;
LuaEvent(Plugin *owner, std::string name)
: handler(owner,name), event(NULL), active(false), count(0)
{
handler.silent = true;
}
void on_count_changed(int new_cnt, int delta) {
RefAutoinc lock(handler.owner->access);
count = new_cnt;
if (event)
event->on_count_changed(new_cnt, delta);
}
void on_invoked(lua_State *state, int nargs, bool from_c) {
RefAutoinc lock(handler.owner->access);
if (event)
event->on_invoked(state, nargs, from_c);
}
};
Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _filename, PluginManager * pm) Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _filename, PluginManager * pm)
{ {
filename = filepath; filename = filepath;
@ -141,20 +185,27 @@ Plugin::~Plugin()
} }
bool Plugin::load(color_ostream &con) bool Plugin::load(color_ostream &con)
{
{ {
RefAutolock lock(access); RefAutolock lock(access);
if(state == PS_BROKEN) if(state == PS_LOADED)
{ {
return false; return true;
} }
else if(state == PS_LOADED) else if(state != PS_UNLOADED)
{ {
return true; return false;
}
state = PS_LOADING;
} }
// enter suspend
CoreSuspender suspend;
// open the library, etc
DFLibrary * plug = OpenPlugin(filename.c_str()); DFLibrary * plug = OpenPlugin(filename.c_str());
if(!plug) if(!plug)
{ {
con.printerr("Can't load plugin %s\n", filename.c_str()); con.printerr("Can't load plugin %s\n", filename.c_str());
RefAutolock lock(access);
state = PS_BROKEN; state = PS_BROKEN;
return false; return false;
} }
@ -164,6 +215,7 @@ bool Plugin::load(color_ostream &con)
{ {
con.printerr("Plugin %s has no name or version.\n", filename.c_str()); con.printerr("Plugin %s has no name or version.\n", filename.c_str());
ClosePlugin(plug); ClosePlugin(plug);
RefAutolock lock(access);
state = PS_BROKEN; state = PS_BROKEN;
return false; return false;
} }
@ -172,9 +224,11 @@ bool Plugin::load(color_ostream &con)
con.printerr("Plugin %s was not built for this version of DFHack.\n" con.printerr("Plugin %s was not built for this version of DFHack.\n"
"Plugin: %s, DFHack: %s\n", *plug_name, *plug_version, DFHACK_VERSION); "Plugin: %s, DFHack: %s\n", *plug_name, *plug_version, DFHACK_VERSION);
ClosePlugin(plug); ClosePlugin(plug);
RefAutolock lock(access);
state = PS_BROKEN; state = PS_BROKEN;
return false; return false;
} }
RefAutolock lock(access);
plugin_init = (command_result (*)(color_ostream &, std::vector <PluginCommand> &)) LookupPlugin(plug, "plugin_init"); plugin_init = (command_result (*)(color_ostream &, std::vector <PluginCommand> &)) LookupPlugin(plug, "plugin_init");
if(!plugin_init) if(!plugin_init)
{ {
@ -226,6 +280,11 @@ bool Plugin::unload(color_ostream &con)
} }
// wait for all calls to finish // wait for all calls to finish
access->wait(); access->wait();
state = PS_UNLOADING;
access->unlock();
// enter suspend
CoreSuspender suspend;
access->lock();
// notify plugin about shutdown, if it has a shutdown function // notify plugin about shutdown, if it has a shutdown function
command_result cr = CR_OK; command_result cr = CR_OK;
if(plugin_shutdown) if(plugin_shutdown)
@ -439,7 +498,11 @@ void Plugin::index_lua(DFLibrary *lib)
cmd->handler.identity = evlist->event->get_handler(); cmd->handler.identity = evlist->event->get_handler();
cmd->event = evlist->event; cmd->event = evlist->event;
if (cmd->active) if (cmd->active)
{
cmd->event->bind(Lua::Core::State, cmd); cmd->event->bind(Lua::Core::State, cmd);
if (cmd->count > 0)
cmd->event->on_count_changed(cmd->count, 0);
}
} }
} }
} }
@ -467,7 +530,7 @@ int Plugin::lua_cmd_wrapper(lua_State *state)
luaL_error(state, "plugin command %s() has been unloaded", luaL_error(state, "plugin command %s() has been unloaded",
(cmd->owner->name+"."+cmd->name).c_str()); (cmd->owner->name+"."+cmd->name).c_str());
return cmd->command(state); return Lua::CallWithCatch(state, cmd->command, cmd->name.c_str());
} }
int Plugin::lua_fun_wrapper(lua_State *state) int Plugin::lua_fun_wrapper(lua_State *state)
@ -477,8 +540,13 @@ int Plugin::lua_fun_wrapper(lua_State *state)
RefAutoinc lock(cmd->owner->access); RefAutoinc lock(cmd->owner->access);
if (!cmd->identity) if (!cmd->identity)
{
if (cmd->silent)
return 0;
luaL_error(state, "plugin function %s() has been unloaded", luaL_error(state, "plugin function %s() has been unloaded",
(cmd->owner->name+"."+cmd->name).c_str()); (cmd->owner->name+"."+cmd->name).c_str());
}
return LuaWrapper::method_wrapper_core(state, cmd->identity); return LuaWrapper::method_wrapper_core(state, cmd->identity);
} }
@ -506,14 +574,14 @@ void Plugin::open_lua(lua_State *state, int table)
{ {
for (auto it = lua_events.begin(); it != lua_events.end(); ++it) for (auto it = lua_events.begin(); it != lua_events.end(); ++it)
{ {
Lua::MakeEvent(state, it->second); Lua::Event::Make(state, it->second, it->second);
push_function(state, &it->second->handler); push_function(state, &it->second->handler);
lua_rawsetp(state, -2, NULL); Lua::Event::SetPrivateCallback(state, -2);
it->second->active = true; it->second->active = true;
if (it->second->event) if (it->second->event)
it->second->event->bind(state, it->second); it->second->event->bind(Lua::Core::State, it->second);
lua_setfield(state, table, it->first.c_str()); lua_setfield(state, table, it->first.c_str());
} }

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -27,6 +27,7 @@ distribution.
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/time.h>
#include <mach-o/dyld.h> #include <mach-o/dyld.h>
@ -262,6 +263,13 @@ bool Process::getThreadIDs(vector<uint32_t> & threads )
return true; return true;
} }
uint32_t Process::getTickCount()
{
struct timeval tp;
gettimeofday(&tp, NULL);
return (tp.tv_sec * 1000) + (tp.tv_usec / 1000);
}
string Process::getPath() string Process::getPath()
{ {
char path[1024]; char path[1024];

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -27,6 +27,7 @@ distribution.
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/time.h>
#include <string> #include <string>
#include <vector> #include <vector>
@ -126,6 +127,9 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
char permissions[5]; // r/-, w/-, x/-, p/s, 0 char permissions[5]; // r/-, w/-, x/-, p/s, 0
FILE *mapFile = ::fopen("/proc/self/maps", "r"); FILE *mapFile = ::fopen("/proc/self/maps", "r");
if (!mapFile)
return;
size_t start, end, offset, device1, device2, node; size_t start, end, offset, device1, device2, node;
while (fgets(buffer, 1024, mapFile)) while (fgets(buffer, 1024, mapFile))
@ -147,6 +151,8 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
temp.valid = true; temp.valid = true;
ranges.push_back(temp); ranges.push_back(temp);
} }
fclose(mapFile);
} }
uint32_t Process::getBase() uint32_t Process::getBase()
@ -192,6 +198,13 @@ bool Process::getThreadIDs(vector<uint32_t> & threads )
return true; return true;
} }
uint32_t Process::getTickCount()
{
struct timeval tp;
gettimeofday(&tp, NULL);
return (tp.tv_sec * 1000) + (tp.tv_usec / 1000);
}
string Process::getPath() string Process::getPath()
{ {
const char * cwd_name = "/proc/self/cwd"; const char * cwd_name = "/proc/self/cwd";

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -410,6 +410,11 @@ string Process::doReadClassName (void * vptr)
return raw; return raw;
} }
uint32_t Process::getTickCount()
{
return GetTickCount();
}
string Process::getPath() string Process::getPath()
{ {
HMODULE hmod; HMODULE hmod;

@ -394,7 +394,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
//out.print("Received %d:%d\n", header.id, header.size); //out.print("Received %d:%d\n", header.id, header.size);
if (header.id == RPC_REPLY_FAIL) if ((DFHack::DFHackReplyCode)header.id == RPC_REPLY_FAIL)
return header.size == CR_OK ? CR_FAILURE : command_result(header.size); return header.size == CR_OK ? CR_FAILURE : command_result(header.size);
if (header.size < 0 || header.size > RPCMessageHeader::MAX_MESSAGE_SIZE) if (header.size < 0 || header.size > RPCMessageHeader::MAX_MESSAGE_SIZE)

@ -250,7 +250,7 @@ void ServerConnection::threadFn()
break; break;
} }
if (header.id == RPC_REQUEST_QUIT) if ((DFHack::DFHackReplyCode)header.id == RPC_REQUEST_QUIT)
break; break;
if (header.size < 0 || header.size > RPCMessageHeader::MAX_MESSAGE_SIZE) if (header.size < 0 || header.size > RPCMessageHeader::MAX_MESSAGE_SIZE)

@ -287,14 +287,14 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
if (mask && mask->profession()) if (mask && mask->profession())
{ {
if (unit->profession >= 0) if (unit->profession >= (df::profession)0)
info->set_profession(unit->profession); info->set_profession(unit->profession);
if (!unit->custom_profession.empty()) if (!unit->custom_profession.empty())
info->set_custom_profession(unit->custom_profession); info->set_custom_profession(unit->custom_profession);
if (unit->military.squad_index >= 0) if (unit->military.squad_id >= 0)
{ {
info->set_squad_id(unit->military.squad_index); info->set_squad_id(unit->military.squad_id);
info->set_squad_position(unit->military.squad_position); info->set_squad_position(unit->military.squad_position);
} }
} }

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -0,0 +1,535 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "Internal.h"
#include <string>
#include <vector>
#include <map>
#include "MemAccess.h"
#include "Core.h"
#include "VersionInfo.h"
#include "VTableInterpose.h"
#include "MiscUtils.h"
using namespace DFHack;
/*
* Code for accessing method pointers directly. Very compiler-specific.
*/
#if defined(_MSC_VER)
struct MSVC_MPTR {
void *method;
intptr_t this_shift;
};
static uint32_t *follow_jmp(void *ptr)
{
uint8_t *p = (uint8_t*)ptr;
for (;;)
{
switch (*p)
{
case 0xE9:
p += 5 + *(int32_t*)(p+1);
break;
case 0xEB:
p += 2 + *(int8_t*)(p+1);
break;
default:
return (uint32_t*)p;
}
}
}
bool DFHack::is_vmethod_pointer_(void *pptr)
{
auto pobj = (MSVC_MPTR*)pptr;
if (!pobj->method) return false;
// MSVC implements pointers to vmethods via thunks.
// This expects that they all follow a very specific pattern.
auto pval = follow_jmp(pobj->method);
switch (pval[0]) {
case 0x20FF018BU: // mov eax, [ecx]; jmp [eax]
case 0x60FF018BU: // mov eax, [ecx]; jmp [eax+0x??]
case 0xA0FF018BU: // mov eax, [ecx]; jmp [eax+0x????????]
return true;
default:
return false;
}
}
int DFHack::vmethod_pointer_to_idx_(void *pptr)
{
auto pobj = (MSVC_MPTR*)pptr;
if (!pobj->method || pobj->this_shift != 0) return -1;
auto pval = follow_jmp(pobj->method);
switch (pval[0]) {
case 0x20FF018BU: // mov eax, [ecx]; jmp [eax]
return 0;
case 0x60FF018BU: // mov eax, [ecx]; jmp [eax+0x??]
return ((int8_t)pval[1])/sizeof(void*);
case 0xA0FF018BU: // mov eax, [ecx]; jmp [eax+0x????????]
return ((int32_t)pval[1])/sizeof(void*);
default:
return -1;
}
}
void* DFHack::method_pointer_to_addr_(void *pptr)
{
if (is_vmethod_pointer_(pptr)) return NULL;
auto pobj = (MSVC_MPTR*)pptr;
return pobj->method;
}
void DFHack::addr_to_method_pointer_(void *pptr, void *addr)
{
auto pobj = (MSVC_MPTR*)pptr;
pobj->method = addr;
pobj->this_shift = 0;
}
#elif defined(__GXX_ABI_VERSION)
struct GCC_MPTR {
intptr_t method;
intptr_t this_shift;
};
bool DFHack::is_vmethod_pointer_(void *pptr)
{
auto pobj = (GCC_MPTR*)pptr;
return (pobj->method & 1) != 0;
}
int DFHack::vmethod_pointer_to_idx_(void *pptr)
{
auto pobj = (GCC_MPTR*)pptr;
if ((pobj->method & 1) == 0 || pobj->this_shift != 0)
return -1;
return (pobj->method-1)/sizeof(void*);
}
void* DFHack::method_pointer_to_addr_(void *pptr)
{
auto pobj = (GCC_MPTR*)pptr;
if ((pobj->method & 1) != 0 || pobj->this_shift != 0)
return NULL;
return (void*)pobj->method;
}
void DFHack::addr_to_method_pointer_(void *pptr, void *addr)
{
auto pobj = (GCC_MPTR*)pptr;
pobj->method = (intptr_t)addr;
pobj->this_shift = 0;
}
#else
#error Unknown compiler type
#endif
void *virtual_identity::get_vmethod_ptr(int idx)
{
assert(idx >= 0);
void **vtable = (void**)vtable_ptr;
if (!vtable) return NULL;
return vtable[idx];
}
bool virtual_identity::set_vmethod_ptr(MemoryPatcher &patcher, int idx, void *ptr)
{
assert(idx >= 0);
void **vtable = (void**)vtable_ptr;
if (!vtable) return NULL;
return patcher.write(&vtable[idx], &ptr, sizeof(void*));
}
/*
VMethod interposing data structures.
In order to properly support adding and removing hooks,
it is necessary to track them. This is what this class
is for. The task is further complicated by propagating
hooks to child classes that use exactly the same original
vmethod implementation.
Every applied link contains in the saved_chain field a
pointer to the next vmethod body that should be called
by the hook the link represents. This is the actual
control flow structure that needs to be maintained.
There also are connections between link objects themselves,
which constitute the bookkeeping for doing that. Finally,
every link is associated with a fixed virtual_identity host,
which represents the point in the class hierarchy where
the hook is applied.
When there are no subclasses (i.e. only one host), the
structures look like this:
+--------------+ +------------+
| link1 |-next------->| link2 |-next=NULL
|s_c: original |<-------prev-|s_c: $link1 |<--+
+--------------+ +------------+ |
|
host->interpose_list[vmethod_idx] ------+
vtable: $link2
The original vtable entry is stored in the saved_chain of the
first link. The interpose_list map points to the last one.
The hooks are called in order: link2 -> link1 -> original.
When there are subclasses that use the same vmethod, but don't
hook it, the topmost link gets a set of the child_hosts, and
the hosts have the link added to their interpose_list:
+--------------+ +----------------+
| link0 @host0 |<--+-interpose_list-| host1 |
| |-child_hosts-+----->| vtable: $link |
+--------------+ | | +----------------+
| |
| | +----------------+
+-interpose_list-| host2 |
+----->| vtable: $link |
+----------------+
When a child defines its own hook, the child_hosts link is
severed and replaced with a child_next pointer to the new
hook. The hook still points back the chain with prev.
All child links to subclasses of host2 are migrated from
link1 to link2.
+--------------+-next=NULL +--------------+-next=NULL
| link1 @host1 |-child_next------->| link2 @host2 |-child_*--->subclasses
| |<-------------prev-|s_c: $link1 |
+--------------+<-------+ +--------------+<-------+
| |
+--------------+ | +--------------+ |
| host1 |-i_list-+ | host2 |-i_list-+
|vtable: $link1| |vtable: $link2|
+--------------+ +--------------+
*/
void VMethodInterposeLinkBase::set_chain(void *chain)
{
saved_chain = chain;
addr_to_method_pointer_(chain_mptr, chain);
}
VMethodInterposeLinkBase::VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority)
: host(host), vmethod_idx(vmethod_idx), interpose_method(interpose_method),
chain_mptr(chain_mptr), priority(priority),
applied(false), saved_chain(NULL), next(NULL), prev(NULL)
{
if (vmethod_idx < 0 || interpose_method == NULL)
{
fprintf(stderr, "Bad VMethodInterposeLinkBase arguments: %d %08x\n",
vmethod_idx, unsigned(interpose_method));
fflush(stderr);
abort();
}
}
VMethodInterposeLinkBase::~VMethodInterposeLinkBase()
{
if (is_applied())
remove();
}
VMethodInterposeLinkBase *VMethodInterposeLinkBase::get_first_interpose(virtual_identity *id)
{
auto item = id->interpose_list[vmethod_idx];
if (!item)
return NULL;
if (item->host != id)
return NULL;
while (item->prev && item->prev->host == id)
item = item->prev;
return item;
}
bool VMethodInterposeLinkBase::find_child_hosts(virtual_identity *cur, void *vmptr)
{
auto &children = cur->getChildren();
bool found = false;
for (size_t i = 0; i < children.size(); i++)
{
auto child = static_cast<virtual_identity*>(children[i]);
auto base = get_first_interpose(child);
if (base)
{
assert(base->prev == NULL);
if (base->saved_chain != vmptr)
continue;
child_next.insert(base);
found = true;
}
else if (child->vtable_ptr)
{
void *cptr = child->get_vmethod_ptr(vmethod_idx);
if (cptr != vmptr)
continue;
child_hosts.insert(child);
found = true;
find_child_hosts(child, vmptr);
}
else
{
// If this vtable is not known, but any of the children
// have the same vmethod, this one definitely does too
if (find_child_hosts(child, vmptr))
{
child_hosts.insert(child);
found = true;
}
}
}
return found;
}
void VMethodInterposeLinkBase::on_host_delete(virtual_identity *from)
{
if (from == host)
{
// When in own host, fully delete
remove();
}
else
{
// Otherwise, drop the link to that child:
assert(child_hosts.count(from) != 0 &&
from->interpose_list[vmethod_idx] == this);
// Find and restore the original vmethod ptr
auto last = this;
while (last->prev) last = last->prev;
MemoryPatcher patcher;
from->set_vmethod_ptr(patcher, vmethod_idx, last->saved_chain);
// Unlink the chains
child_hosts.erase(from);
from->interpose_list[vmethod_idx] = NULL;
}
}
bool VMethodInterposeLinkBase::apply(bool enable)
{
if (!enable)
{
remove();
return true;
}
if (is_applied())
return true;
if (!host->vtable_ptr)
return false;
// Retrieve the current vtable entry
VMethodInterposeLinkBase *old_link = host->interpose_list[vmethod_idx];
VMethodInterposeLinkBase *next_link = NULL;
while (old_link && old_link->host == host && old_link->priority > priority)
{
next_link = old_link;
old_link = old_link->prev;
}
void *old_ptr = next_link ? next_link->saved_chain : host->get_vmethod_ptr(vmethod_idx);
assert(old_ptr != NULL && (!old_link || old_link->interpose_method == old_ptr));
// Apply the new method ptr
MemoryPatcher patcher;
set_chain(old_ptr);
if (next_link)
{
next_link->set_chain(interpose_method);
}
else if (!host->set_vmethod_ptr(patcher, vmethod_idx, interpose_method))
{
set_chain(NULL);
return false;
}
// Push the current link into the home host
applied = true;
prev = old_link;
next = next_link;
if (next_link)
next_link->prev = this;
else
host->interpose_list[vmethod_idx] = this;
child_hosts.clear();
child_next.clear();
if (old_link && old_link->host == host)
{
// If the old link is home, just push into the plain chain
assert(old_link->next == next_link);
old_link->next = this;
// Child links belong to the topmost local entry
child_hosts.swap(old_link->child_hosts);
child_next.swap(old_link->child_next);
}
else if (next_link)
{
if (old_link)
{
assert(old_link->child_next.count(next_link));
old_link->child_next.erase(next_link);
old_link->child_next.insert(this);
}
}
else
{
// If creating a new local chain, find children with same vmethod
find_child_hosts(host, old_ptr);
if (old_link)
{
// Enter the child chain set
assert(old_link->child_hosts.count(host));
old_link->child_hosts.erase(host);
old_link->child_next.insert(this);
// Subtract our own children from the parent's sets
for (auto it = child_next.begin(); it != child_next.end(); ++it)
old_link->child_next.erase(*it);
for (auto it = child_hosts.begin(); it != child_hosts.end(); ++it)
old_link->child_hosts.erase(*it);
}
}
assert (!next_link || (child_next.empty() && child_hosts.empty()));
// Chain subclass hooks
for (auto it = child_next.begin(); it != child_next.end(); ++it)
{
auto nlink = *it;
assert(nlink->saved_chain == old_ptr && nlink->prev == old_link);
nlink->set_chain(interpose_method);
nlink->prev = this;
}
// Chain passive subclass hosts
for (auto it = child_hosts.begin(); it != child_hosts.end(); ++it)
{
auto nhost = *it;
assert(nhost->interpose_list[vmethod_idx] == old_link);
nhost->set_vmethod_ptr(patcher, vmethod_idx, interpose_method);
nhost->interpose_list[vmethod_idx] = this;
}
return true;
}
void VMethodInterposeLinkBase::remove()
{
if (!is_applied())
return;
// Remove the link from prev to this
if (prev)
{
if (prev->host == host)
prev->next = next;
else
{
prev->child_next.erase(this);
if (next)
prev->child_next.insert(next);
else
prev->child_hosts.insert(host);
}
}
if (next)
{
next->set_chain(saved_chain);
next->prev = prev;
assert(child_next.empty() && child_hosts.empty());
}
else
{
MemoryPatcher patcher;
// Remove from the list in the identity and vtable
host->interpose_list[vmethod_idx] = prev;
host->set_vmethod_ptr(patcher, vmethod_idx, saved_chain);
for (auto it = child_next.begin(); it != child_next.end(); ++it)
{
auto nlink = *it;
assert(nlink->saved_chain == interpose_method && nlink->prev == this);
nlink->set_chain(saved_chain);
nlink->prev = prev;
if (prev)
prev->child_next.insert(nlink);
}
for (auto it = child_hosts.begin(); it != child_hosts.end(); ++it)
{
auto nhost = *it;
assert(nhost->interpose_list[vmethod_idx] == this);
nhost->interpose_list[vmethod_idx] = prev;
nhost->set_vmethod_ptr(patcher, vmethod_idx, saved_chain);
if (prev)
prev->child_hosts.insert(nhost);
}
}
applied = false;
prev = next = NULL;
child_next.clear();
child_hosts.clear();
set_chain(NULL);
}

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -0,0 +1,315 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2011 Petr Mrázek <peterix@gmail.com>
A thread-safe logging console with a line editor for windows.
Based on linenoise win32 port,
copyright 2010, Jon Griffiths <jon_p_griffiths at yahoo dot com>.
All rights reserved.
Based on linenoise, copyright 2010, Salvatore Sanfilippo <antirez at gmail dot com>.
The original linenoise can be found at: http://github.com/antirez/linenoise
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Redis nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <iostream>
#include <fstream>
#include <istream>
#include <string>
#include <stdint.h>
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <vector>
#include <memory>
#include <md5wrapper.h>
using std::cout;
using std::cerr;
using std::endl;
typedef unsigned char patch_byte;
struct BinaryPatch {
struct Byte {
unsigned offset;
patch_byte old_val, new_val;
};
enum State {
Conflict = 0,
Unapplied = 1,
Applied = 2,
Partial = 3
};
std::vector<Byte> entries;
bool loadDIF(std::string name);
State checkState(const patch_byte *ptr, size_t len);
void apply(patch_byte *ptr, size_t len, bool newv);
};
inline bool is_hex(char c)
{
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
bool BinaryPatch::loadDIF(std::string name)
{
entries.clear();
std::ifstream infile(name);
if(infile.bad())
{
cerr << "Cannot open file: " << name << endl;
return false;
}
std::string s;
while(std::getline(infile, s))
{
// Parse lines that begin with "[0-9a-f]+:"
size_t idx = s.find(':');
if (idx == std::string::npos || idx == 0 || idx > 8)
continue;
bool ok = true;
for (size_t i = 0; i < idx; i++)
if (!is_hex(s[i]))
ok = false;
if (!ok)
continue;
unsigned off, oval, nval;
int nchar = 0;
int cnt = sscanf(s.c_str(), "%x: %x %x%n", &off, &oval, &nval, &nchar);
if (cnt < 3)
{
cerr << "Could not parse: " << s << endl;
return false;
}
for (size_t i = nchar; i < s.size(); i++)
{
if (!isspace(s[i]))
{
cerr << "Garbage at end of line: " << s << endl;
return false;
}
}
if (oval >= 256 || nval >= 256)
{
cerr << "Invalid byte values: " << s << endl;
return false;
}
Byte bv = { off, patch_byte(oval), patch_byte(nval) };
entries.push_back(bv);
}
if (entries.empty())
{
cerr << "No lines recognized." << endl;
return false;
}
return true;
}
BinaryPatch::State BinaryPatch::checkState(const patch_byte *ptr, size_t len)
{
int state = 0;
for (size_t i = 0; i < entries.size(); i++)
{
Byte &bv = entries[i];
if (bv.offset >= len)
{
cerr << "Offset out of range: 0x" << std::hex << bv.offset << std::dec << endl;
return Conflict;
}
patch_byte cv = ptr[bv.offset];
if (bv.old_val == cv)
state |= Unapplied;
else if (bv.new_val == cv)
state |= Applied;
else
{
cerr << std::hex << bv.offset << ": "
<< unsigned(bv.old_val) << " " << unsigned(bv.new_val)
<< ", but currently " << unsigned(cv) << std::dec << endl;
return Conflict;
}
}
return State(state);
}
void BinaryPatch::apply(patch_byte *ptr, size_t len, bool newv)
{
for (size_t i = 0; i < entries.size(); i++)
{
Byte &bv = entries[i];
assert (bv.offset < len);
ptr[bv.offset] = (newv ? bv.new_val : bv.old_val);
}
}
bool load_file(std::vector<patch_byte> *pvec, std::string fname)
{
FILE *f = fopen(fname.c_str(), "rb");
if (!f)
{
cerr << "Cannot open file: " << fname << endl;
return false;
}
fseek(f, 0, SEEK_END);
pvec->resize(ftell(f));
fseek(f, 0, SEEK_SET);
size_t cnt = fread(pvec->data(), 1, pvec->size(), f);
fclose(f);
return cnt == pvec->size();
}
bool save_file(const std::vector<patch_byte> &pvec, std::string fname)
{
FILE *f = fopen(fname.c_str(), "wb");
if (!f)
{
cerr << "Cannot open file: " << fname << endl;
return false;
}
size_t cnt = fwrite(pvec.data(), 1, pvec.size(), f);
fclose(f);
return cnt == pvec.size();
}
std::string compute_hash(const std::vector<patch_byte> &pvec)
{
md5wrapper md5;
return md5.getHashFromBytes(pvec.data(), pvec.size());
}
int main (int argc, char *argv[])
{
if (argc <= 3)
{
cerr << "Usage: binpatch check|apply|remove <exe> <patch>" << endl;
return 2;
}
std::string cmd = argv[1];
if (cmd != "check" && cmd != "apply" && cmd != "remove")
{
cerr << "Invalid command: " << cmd << endl;
return 2;
}
std::string exe_file = argv[2];
std::vector<patch_byte> bindata;
if (!load_file(&bindata, exe_file))
return 2;
BinaryPatch patch;
if (!patch.loadDIF(argv[3]))
return 2;
BinaryPatch::State state = patch.checkState(bindata.data(), bindata.size());
if (state == BinaryPatch::Conflict)
return 1;
if (cmd == "check")
{
switch (state)
{
case BinaryPatch::Unapplied:
cout << "Currently not applied." << endl;
break;
case BinaryPatch::Applied:
cout << "Currently applied." << endl;
break;
case BinaryPatch::Partial:
cout << "Currently partially applied." << endl;
break;
default:
break;
}
return 0;
}
else if (cmd == "apply")
{
if (state == BinaryPatch::Applied)
{
cout << "Already applied." << endl;
return 0;
}
patch.apply(bindata.data(), bindata.size(), true);
}
else if (cmd == "remove")
{
if (state == BinaryPatch::Unapplied)
{
cout << "Already removed." << endl;
return 0;
}
patch.apply(bindata.data(), bindata.size(), false);
}
if (!save_file(bindata, exe_file + ".bak"))
{
cerr << "Could not create backup." << endl;
return 1;
}
if (!save_file(bindata, exe_file))
return 1;
cout << "Patched " << patch.entries.size()
<< " bytes, new hash: " << compute_hash(bindata) << endl;
return 0;
}

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -64,7 +64,7 @@ namespace DFHack
if (newsize == size) if (newsize == size)
return; return;
uint8_t* mem = (uint8_t *) realloc(bits, newsize); uint8_t* mem = (uint8_t *) realloc(bits, newsize);
if(!mem) if(!mem && newsize != 0)
throw std::bad_alloc(); throw std::bad_alloc();
bits = mem; bits = mem;
if (newsize > size) if (newsize > size)
@ -207,7 +207,7 @@ namespace DFHack
else else
{ {
T* mem = (T*) realloc(m_data, sizeof(T)*new_size); T* mem = (T*) realloc(m_data, sizeof(T)*new_size);
if(!mem) if(!mem && new_size != 0)
throw std::bad_alloc(); throw std::bad_alloc();
m_data = mem; m_data = mem;
} }

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -41,9 +41,6 @@ namespace dfproto
namespace DFHack namespace DFHack
{ {
class DFHACK_EXPORT color_ostream : public std::ostream
{
public:
enum color_value enum color_value
{ {
COLOR_RESET = -1, COLOR_RESET = -1,
@ -66,6 +63,11 @@ namespace DFHack
COLOR_MAX = COLOR_WHITE COLOR_MAX = COLOR_WHITE
}; };
class DFHACK_EXPORT color_ostream : public std::ostream
{
public:
typedef DFHack::color_value color_value;
private: private:
color_value cur_color; color_value cur_color;

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -54,7 +54,6 @@ namespace DFHack
{ {
class Process; class Process;
class Module; class Module;
class World;
class Materials; class Materials;
class Notes; class Notes;
struct VersionInfo; struct VersionInfo;
@ -120,8 +119,6 @@ namespace DFHack
/// Is everything OK? /// Is everything OK?
bool isValid(void) { return !errorstate; } bool isValid(void) { return !errorstate; }
/// get the world module
World * getWorld();
/// get the materials module /// get the materials module
Materials * getMaterials(); Materials * getMaterials();
/// get the notes module /// get the notes module
@ -174,6 +171,10 @@ namespace DFHack
struct Private; struct Private;
Private *d; Private *d;
friend class CoreSuspendClaimer;
int ClaimSuspend(bool force_base);
void DisclaimSuspend(int level);
bool Init(); bool Init();
int Update (void); int Update (void);
int TileUpdate (void); int TileUpdate (void);
@ -181,6 +182,7 @@ namespace DFHack
int DFH_SDL_Event(SDL::Event* event); int DFH_SDL_Event(SDL::Event* event);
bool ncurses_wgetch(int in, int & out); bool ncurses_wgetch(int in, int & out);
void doUpdate(color_ostream &out, bool first_update);
void onUpdate(color_ostream &out); void onUpdate(color_ostream &out);
void onStateChange(color_ostream &out, state_change_event event); void onStateChange(color_ostream &out, state_change_event event);
@ -200,7 +202,6 @@ namespace DFHack
// Module storage // Module storage
struct struct
{ {
World * pWorld;
Materials * pMaterials; Materials * pMaterials;
Notes * pNotes; Notes * pNotes;
Graphic * pGraphic; Graphic * pGraphic;
@ -249,4 +250,20 @@ namespace DFHack
CoreSuspender(Core *core) : core(core) { core->Suspend(); } CoreSuspender(Core *core) : core(core) { core->Suspend(); }
~CoreSuspender() { core->Resume(); } ~CoreSuspender() { core->Resume(); }
}; };
/** Claims the current thread already has the suspend lock.
* Strictly for use in callbacks from DF.
*/
class CoreSuspendClaimer {
Core *core;
int level;
public:
CoreSuspendClaimer(bool base = false) : core(&Core::getInstance()) {
level = core->ClaimSuspend(base);
}
CoreSuspendClaimer(Core *core, bool base = false) : core(core) {
level = core->ClaimSuspend(base);
}
~CoreSuspendClaimer() { core->DisclaimSuspend(level); }
};
} }

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -28,6 +28,7 @@ distribution.
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include <map> #include <map>
#include <set>
#include "Core.h" #include "Core.h"
#include "BitArray.h" #include "BitArray.h"
@ -292,6 +293,9 @@ namespace DFHack
typedef virtual_class *virtual_ptr; typedef virtual_class *virtual_ptr;
#endif #endif
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;
@ -299,6 +303,9 @@ namespace DFHack
void *vtable_ptr; void *vtable_ptr;
friend class VMethodInterposeLinkBase;
std::map<int,VMethodInterposeLinkBase*> interpose_list;
protected: protected:
virtual void doInit(Core *core); virtual void doInit(Core *core);
@ -306,10 +313,14 @@ 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);
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,
const char *dfhack_name, const char *original_name, const char *dfhack_name, const char *original_name,
virtual_identity *parent, const struct_field_info *fields); virtual_identity *parent, const struct_field_info *fields);
~virtual_identity();
virtual identity_type type() { return IDTYPE_CLASS; } virtual identity_type type() { return IDTYPE_CLASS; }
@ -337,6 +348,8 @@ namespace DFHack
: (this == get(instance_ptr)); : (this == get(instance_ptr));
} }
template<class P> static P get_vmethod_ptr(P selector);
public: public:
bool can_instantiate() { return can_allocate(); } bool can_instantiate() { return can_allocate(); }
virtual_ptr instantiate() { return can_instantiate() ? (virtual_ptr)do_allocate() : NULL; } virtual_ptr instantiate() { return can_instantiate() ? (virtual_ptr)do_allocate() : NULL; }
@ -506,7 +519,7 @@ namespace DFHack {
template<class T> template<class T>
inline const char *enum_item_raw_key(T val) { inline const char *enum_item_raw_key(T val) {
typedef df::enum_traits<T> traits; typedef df::enum_traits<T> traits;
return traits::is_valid(val) ? traits::key_table[val - traits::first_item_value] : NULL; return traits::is_valid(val) ? traits::key_table[(short)val - traits::first_item_value] : NULL;
} }
/** /**
@ -695,6 +708,9 @@ namespace DFHack {
// Global object pointers // Global object pointers
#include "df/global_objects.h" #include "df/global_objects.h"
#define DF_GLOBAL_VALUE(name,defval) (df::global::name ? *df::global::name : defval)
#define DF_GLOBAL_FIELD(name,fname,defval) (df::global::name ? df::global::name->fname : defval)
// A couple of headers that have to be included at once // A couple of headers that have to be included at once
#include "df/coord2d.h" #include "df/coord2d.h"
#include "df/coord.h" #include "df/coord.h"

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -75,106 +75,133 @@ namespace df {
cur_lua_ostream_argument name(state); cur_lua_ostream_argument name(state);
#define INSTANTIATE_RETURN_TYPE(FArgs) \ #define INSTANTIATE_RETURN_TYPE(FArgs) \
template<FW_TARGSC class RT> struct return_type<RT (*) FArgs> { typedef RT type; }; \ template<FW_TARGSC class RT> struct return_type<RT (*) FArgs> { \
template<FW_TARGSC class RT, class CT> struct return_type<RT (CT::*) FArgs> { typedef RT type; }; typedef RT type; \
static const bool is_method = false; \
}; \
template<FW_TARGSC class RT, class CT> struct return_type<RT (CT::*) FArgs> { \
typedef RT type; \
typedef CT class_type; \
static const bool is_method = true; \
};
#define INSTANTIATE_WRAPPERS(Count, FArgs, Args, Loads) \ #define INSTANTIATE_WRAPPERS2(Count, FArgs, Args, Loads) \
template<FW_TARGS> struct function_wrapper<void (*) FArgs, true> { \ template<FW_TARGS> struct function_wrapper<void (*) FArgs, true> { \
static const bool is_method = false; \
static const int num_args = Count; \ static const int num_args = Count; \
static void execute(lua_State *state, int base, void (*cb) FArgs) { Loads; INVOKE_VOID(cb Args); } \ static void execute(lua_State *state, int base, void (*cb) FArgs) { Loads; INVOKE_VOID(cb Args); } \
}; \ }; \
template<FW_TARGSC class RT> struct function_wrapper<RT (*) FArgs, false> { \ template<FW_TARGSC class RT> struct function_wrapper<RT (*) FArgs, false> { \
static const bool is_method = false; \
static const int num_args = Count; \ static const int num_args = Count; \
static void execute(lua_State *state, int base, RT (*cb) FArgs) { Loads; INVOKE_RV(cb Args); } \ static void execute(lua_State *state, int base, RT (*cb) FArgs) { Loads; INVOKE_RV(cb Args); } \
}; \ }; \
template<FW_TARGSC class CT> struct function_wrapper<void (CT::*) FArgs, true> { \ template<FW_TARGSC class CT> struct function_wrapper<void (CT::*) FArgs, true> { \
static const bool is_method = true; \
static const int num_args = Count+1; \ static const int num_args = Count+1; \
static void execute(lua_State *state, int base, void (CT::*cb) FArgs) { \ static void execute(lua_State *state, int base, void (CT::*cb) FArgs) { \
LOAD_CLASS() Loads; INVOKE_VOID((self->*cb) Args); } \ LOAD_CLASS() Loads; INVOKE_VOID((self->*cb) Args); } \
}; \ }; \
template<FW_TARGSC class RT, class CT> struct function_wrapper<RT (CT::*) FArgs, false> { \ template<FW_TARGSC class RT, class CT> struct function_wrapper<RT (CT::*) FArgs, false> { \
static const bool is_method = true; \
static const int num_args = Count+1; \ static const int num_args = Count+1; \
static void execute(lua_State *state, int base, RT (CT::*cb) FArgs) { \ static void execute(lua_State *state, int base, RT (CT::*cb) FArgs) { \
LOAD_CLASS(); Loads; INVOKE_RV((self->*cb) Args); } \ LOAD_CLASS(); Loads; INVOKE_RV((self->*cb) Args); } \
}; };
#define INSTANTIATE_WRAPPERS(Count, FArgs, OFArgs, Args, OArgs, Loads) \
INSTANTIATE_WRAPPERS2(Count, FArgs, Args, Loads) \
INSTANTIATE_WRAPPERS2(Count, OFArgs, OArgs, LOAD_OSTREAM(out); Loads)
#define FW_TARGSC #define FW_TARGSC
#define FW_TARGS #define FW_TARGS
INSTANTIATE_RETURN_TYPE(()) INSTANTIATE_RETURN_TYPE(())
INSTANTIATE_WRAPPERS(0, (), (), ;) INSTANTIATE_WRAPPERS(0, (), (OSTREAM_ARG), (), (out), ;)
INSTANTIATE_WRAPPERS(0, (OSTREAM_ARG), (out), LOAD_OSTREAM(out);)
#undef FW_TARGS #undef FW_TARGS
#undef FW_TARGSC #undef FW_TARGSC
#define FW_TARGSC FW_TARGS, #define FW_TARGSC FW_TARGS,
#define FW_TARGS class A1 #define FW_TARGS class A1
INSTANTIATE_RETURN_TYPE((A1)) INSTANTIATE_RETURN_TYPE((A1))
INSTANTIATE_WRAPPERS(1, (A1), (vA1), LOAD_ARG(A1);) INSTANTIATE_WRAPPERS(1, (A1), (OSTREAM_ARG,A1), (vA1), (out,vA1), LOAD_ARG(A1);)
INSTANTIATE_WRAPPERS(1, (OSTREAM_ARG,A1), (out,vA1), LOAD_OSTREAM(out); LOAD_ARG(A1);)
#undef FW_TARGS #undef FW_TARGS
#define FW_TARGS class A1, class A2 #define FW_TARGS class A1, class A2
INSTANTIATE_RETURN_TYPE((A1,A2)) INSTANTIATE_RETURN_TYPE((A1,A2))
INSTANTIATE_WRAPPERS(2, (A1,A2), (vA1,vA2), LOAD_ARG(A1); LOAD_ARG(A2);) INSTANTIATE_WRAPPERS(2, (A1,A2), (OSTREAM_ARG,A1,A2), (vA1,vA2), (out,vA1,vA2),
INSTANTIATE_WRAPPERS(2, (OSTREAM_ARG,A1,A2), (out,vA1,vA2), LOAD_ARG(A1); LOAD_ARG(A2);)
LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2);)
#undef FW_TARGS #undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3 #define FW_TARGS class A1, class A2, class A3
INSTANTIATE_RETURN_TYPE((A1,A2,A3)) INSTANTIATE_RETURN_TYPE((A1,A2,A3))
INSTANTIATE_WRAPPERS(3, (A1,A2,A3), (vA1,vA2,vA3), LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);) INSTANTIATE_WRAPPERS(3, (A1,A2,A3), (OSTREAM_ARG,A1,A2,A3), (vA1,vA2,vA3), (out,vA1,vA2,vA3),
INSTANTIATE_WRAPPERS(3, (OSTREAM_ARG,A1,A2,A3), (out,vA1,vA2,vA3), LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);)
LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);)
#undef FW_TARGS #undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3, class A4 #define FW_TARGS class A1, class A2, class A3, class A4
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4)) INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4))
INSTANTIATE_WRAPPERS(4, (A1,A2,A3,A4), (vA1,vA2,vA3,vA4), INSTANTIATE_WRAPPERS(4, (A1,A2,A3,A4), (OSTREAM_ARG,A1,A2,A3,A4),
(vA1,vA2,vA3,vA4), (out,vA1,vA2,vA3,vA4),
LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);) LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);)
INSTANTIATE_WRAPPERS(4, (OSTREAM_ARG,A1,A2,A3,A4), (out,vA1,vA2,vA3,vA4),
LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);)
#undef FW_TARGS #undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3, class A4, class A5 #define FW_TARGS class A1, class A2, class A3, class A4, class A5
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5)) INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5))
INSTANTIATE_WRAPPERS(5, (A1,A2,A3,A4,A5), (vA1,vA2,vA3,vA4,vA5), INSTANTIATE_WRAPPERS(5, (A1,A2,A3,A4,A5), (OSTREAM_ARG,A1,A2,A3,A4,A5),
LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4); LOAD_ARG(A5);) (vA1,vA2,vA3,vA4,vA5), (out,vA1,vA2,vA3,vA4,vA5),
INSTANTIATE_WRAPPERS(5, (OSTREAM_ARG,A1,A2,A3,A4,A5), (out,vA1,vA2,vA3,vA4,vA5), LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);
LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A5);)
LOAD_ARG(A3); LOAD_ARG(A4); LOAD_ARG(A5);)
#undef FW_TARGS #undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6 #define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6)) INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6))
INSTANTIATE_WRAPPERS(6, (A1,A2,A3,A4,A5,A6), (vA1,vA2,vA3,vA4,vA5,vA6), INSTANTIATE_WRAPPERS(6, (A1,A2,A3,A4,A5,A6), (OSTREAM_ARG,A1,A2,A3,A4,A5,A6),
LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); (vA1,vA2,vA3,vA4,vA5,vA6), (out,vA1,vA2,vA3,vA4,vA5,vA6),
LOAD_ARG(A4); LOAD_ARG(A5); LOAD_ARG(A6);) LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);
INSTANTIATE_WRAPPERS(6, (OSTREAM_ARG,A1,A2,A3,A4,A5,A6), (out,vA1,vA2,vA3,vA4,vA5,vA6), LOAD_ARG(A5); LOAD_ARG(A6);)
LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);
LOAD_ARG(A4); LOAD_ARG(A5); LOAD_ARG(A6);)
#undef FW_TARGS #undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7 #define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7)) INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7))
INSTANTIATE_WRAPPERS(7, (A1,A2,A3,A4,A5,A6,A7), (vA1,vA2,vA3,vA4,vA5,vA6,vA7), INSTANTIATE_WRAPPERS(7, (A1,A2,A3,A4,A5,A6,A7), (OSTREAM_ARG,A1,A2,A3,A4,A5,A6,A7),
LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); (vA1,vA2,vA3,vA4,vA5,vA6,vA7), (out,vA1,vA2,vA3,vA4,vA5,vA6,vA7),
LOAD_ARG(A4); LOAD_ARG(A5); LOAD_ARG(A6); LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);
LOAD_ARG(A7);) LOAD_ARG(A5); LOAD_ARG(A6); LOAD_ARG(A7);)
INSTANTIATE_WRAPPERS(7, (OSTREAM_ARG,A1,A2,A3,A4,A5,A6,A7), (out,vA1,vA2,vA3,vA4,vA5,vA6,vA7),
LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);
LOAD_ARG(A4); LOAD_ARG(A5); LOAD_ARG(A6); LOAD_ARG(A7);)
#undef FW_TARGS #undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8 #define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7,A8)) INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7,A8))
INSTANTIATE_WRAPPERS(8, (A1,A2,A3,A4,A5,A6,A7,A8), (OSTREAM_ARG,A1,A2,A3,A4,A5,A6,A7,A8),
(vA1,vA2,vA3,vA4,vA5,vA6,vA7,vA8), (out,vA1,vA2,vA3,vA4,vA5,vA6,vA7,vA8),
LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);
LOAD_ARG(A5); LOAD_ARG(A6); LOAD_ARG(A7); LOAD_ARG(A8);)
#undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8, class A9
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7,A8,A9))
INSTANTIATE_WRAPPERS(9, (A1,A2,A3,A4,A5,A6,A7,A8,A9),
(OSTREAM_ARG,A1,A2,A3,A4,A5,A6,A7,A8,A9),
(vA1,vA2,vA3,vA4,vA5,vA6,vA7,vA8,vA9),
(out,vA1,vA2,vA3,vA4,vA5,vA6,vA7,vA8,vA9),
LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);
LOAD_ARG(A5); LOAD_ARG(A6); LOAD_ARG(A7); LOAD_ARG(A8);
LOAD_ARG(A9);)
#undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8, class A9, class A10
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7,A8,A9,A10))
INSTANTIATE_WRAPPERS(10, (A1,A2,A3,A4,A5,A6,A7,A8,A9,A10),
(OSTREAM_ARG,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10),
(vA1,vA2,vA3,vA4,vA5,vA6,vA7,vA8,vA9,vA10),
(out,vA1,vA2,vA3,vA4,vA5,vA6,vA7,vA8,vA9,vA10),
LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);
LOAD_ARG(A5); LOAD_ARG(A6); LOAD_ARG(A7); LOAD_ARG(A8);
LOAD_ARG(A9); LOAD_ARG(A10);)
#undef FW_TARGS
#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8, class A9, class A10, class A11
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11))
#undef FW_TARGS #undef FW_TARGS
#undef FW_TARGSC #undef FW_TARGSC
#undef INSTANTIATE_WRAPPERS #undef INSTANTIATE_WRAPPERS
#undef INSTANTIATE_WRAPPERS2
#undef INVOKE_VOID #undef INVOKE_VOID
#undef INVOKE_RV #undef INVOKE_RV
#undef LOAD_CLASS #undef LOAD_CLASS

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -115,6 +115,8 @@ namespace DFHack
virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx);
virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index);
virtual bool is_readonly() { return false; }
virtual bool resize(void *ptr, int size) { return false; } virtual bool resize(void *ptr, int size) { return false; }
virtual bool erase(void *ptr, int index) { return false; } virtual bool erase(void *ptr, int index) { return false; }
virtual bool insert(void *ptr, int index, void *pitem) { return false; } virtual bool insert(void *ptr, int index, void *pitem) { return false; }
@ -343,6 +345,33 @@ namespace df
} }
}; };
template<class T>
class ro_stl_container_identity : public container_identity {
const char *name;
public:
ro_stl_container_identity(const char *name, type_identity *item, enum_identity *ienum = NULL)
: container_identity(sizeof(T), &allocator_fn<T>, item, ienum), name(name)
{}
std::string getFullName(type_identity *item) {
return name + container_identity::getFullName(item);
}
virtual bool is_readonly() { return true; }
virtual bool resize(void *ptr, int size) { return false; }
virtual bool erase(void *ptr, int size) { return false; }
virtual bool insert(void *ptr, int idx, void *item) { return false; }
protected:
virtual int item_count(void *ptr, CountMode) { return ((T*)ptr)->size(); }
virtual void *item_pointer(type_identity *item, void *ptr, int idx) {
auto iter = (*(T*)ptr).begin();
for (; idx > 0; idx--) ++iter;
return (void*)&*iter;
}
};
class bit_array_identity : public bit_container_identity { class bit_array_identity : public bit_container_identity {
public: public:
/* /*
@ -361,7 +390,7 @@ namespace df
} }
virtual bool resize(void *ptr, int size) { virtual bool resize(void *ptr, int size) {
((container*)ptr)->resize(size); ((container*)ptr)->resize(size*8);
return true; return true;
} }
@ -517,6 +546,10 @@ namespace df
static container_identity *get(); static container_identity *get();
}; };
template<class T> struct identity_traits<std::set<T> > {
static container_identity *get();
};
template<> struct identity_traits<BitArray<int> > { template<> struct identity_traits<BitArray<int> > {
static bit_array_identity identity; static bit_array_identity identity;
static bit_container_identity *get() { return &identity; } static bit_container_identity *get() { return &identity; }
@ -579,6 +612,13 @@ namespace df
return &identity; return &identity;
} }
template<class T>
inline container_identity *identity_traits<std::set<T> >::get() {
typedef std::set<T> container;
static ro_stl_container_identity<container> identity("set", identity_traits<T>::get());
return &identity;
}
template<class T> template<class T>
inline bit_container_identity *identity_traits<BitArray<T> >::get() { inline bit_container_identity *identity_traits<BitArray<T> >::get() {
static bit_array_identity identity(identity_traits<T>::get()); static bit_array_identity identity(identity_traits<T>::get());

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -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 {
@ -191,6 +195,16 @@ namespace DFHack {namespace Lua {
return cb(state, rv, ctx); return cb(state, rv, ctx);
} }
/**
* Call through to the function with try/catch for C++ exceptions.
*/
DFHACK_EXPORT int CallWithCatch(lua_State *, int (*fn)(lua_State*), const char *context = NULL);
template<int (*cb)(lua_State*)>
int CallWithCatchWrapper(lua_State *state) {
return CallWithCatch(state, cb);
}
/** /**
* Invoke lua function via pcall. Returns true if success. * Invoke lua function via pcall. Returns true if success.
* If an error is signalled, and perr is true, it is printed and popped from the stack. * If an error is signalled, and perr is true, it is printed and popped from the stack.
@ -273,10 +287,17 @@ 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);
} }
template<class T> inline void SetField(lua_State *L, T val, int idx, const char *name) {
if (idx < 0) idx = lua_absindex(L, idx);
Push(L, val); lua_setfield(L, idx, name);
}
template<class T> template<class T>
void PushVector(lua_State *state, const T &pvec, bool addn = false) void PushVector(lua_State *state, const T &pvec, bool addn = false)
{ {
@ -298,11 +319,22 @@ 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);
DFHACK_EXPORT int NewEvent(lua_State *state); namespace Event {
DFHACK_EXPORT void MakeEvent(lua_State *state, void *key); struct DFHACK_EXPORT Owner {
DFHACK_EXPORT void InvokeEvent(color_ostream &out, lua_State *state, void *key, int num_args); virtual ~Owner() {}
virtual void on_count_changed(int new_cnt, int delta) {}
virtual void on_invoked(lua_State *state, int nargs, bool from_c) {}
};
DFHACK_EXPORT void New(lua_State *state, Owner *owner = NULL);
DFHACK_EXPORT void Make(lua_State *state, void *key, Owner *owner = NULL);
DFHACK_EXPORT void SetPrivateCallback(lua_State *state, int ev_idx);
DFHACK_EXPORT void Invoke(color_ostream &out, lua_State *state, void *key, int num_args);
}
class StackUnwinder { class StackUnwinder {
lua_State *state; lua_State *state;
@ -355,18 +387,24 @@ namespace DFHack {namespace Lua {
} }
} }
class DFHACK_EXPORT Notification { class DFHACK_EXPORT Notification : public Event::Owner {
lua_State *state; lua_State *state;
void *key; void *key;
function_identity_base *handler; function_identity_base *handler;
int count;
public: public:
Notification(function_identity_base *handler = NULL) Notification(function_identity_base *handler = NULL)
: state(NULL), key(NULL), handler(handler) {} : state(NULL), key(NULL), handler(handler), count(0) {}
int get_listener_count() { return count; }
lua_State *get_state() { return state; } lua_State *get_state() { return state; }
function_identity_base *get_handler() { return handler; } function_identity_base *get_handler() { return handler; }
lua_State *state_if_count() { return (count > 0) ? state : NULL; }
void on_count_changed(int new_cnt, int) { count = new_cnt; }
void invoke(color_ostream &out, int nargs); void invoke(color_ostream &out, int nargs);
void bind(lua_State *state, const char *name); void bind(lua_State *state, const char *name);
@ -378,7 +416,7 @@ namespace DFHack {namespace Lua {
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \ static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
void name(color_ostream &out) { \ void name(color_ostream &out) { \
handler(out); \ handler(out); \
if (name##_event.get_state()) { \ if (name##_event.state_if_count()) { \
name##_event.invoke(out, 0); \ name##_event.invoke(out, 0); \
} \ } \
} }
@ -387,7 +425,7 @@ namespace DFHack {namespace Lua {
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \ static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
void name(color_ostream &out, arg_type1 arg1) { \ void name(color_ostream &out, arg_type1 arg1) { \
handler(out, arg1); \ handler(out, arg1); \
if (auto state = name##_event.get_state()) { \ if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \ DFHack::Lua::Push(state, arg1); \
name##_event.invoke(out, 1); \ name##_event.invoke(out, 1); \
} \ } \
@ -397,7 +435,7 @@ namespace DFHack {namespace Lua {
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \ static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2) { \ void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2) { \
handler(out, arg1, arg2); \ handler(out, arg1, arg2); \
if (auto state = name##_event.get_state()) { \ if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \ DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \ DFHack::Lua::Push(state, arg2); \
name##_event.invoke(out, 2); \ name##_event.invoke(out, 2); \
@ -408,7 +446,7 @@ namespace DFHack {namespace Lua {
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \ 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) { \ void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3) { \
handler(out, arg1, arg2, arg3); \ handler(out, arg1, arg2, arg3); \
if (auto state = name##_event.get_state()) { \ if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \ DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \ DFHack::Lua::Push(state, arg2); \
DFHack::Lua::Push(state, arg3); \ DFHack::Lua::Push(state, arg3); \
@ -420,7 +458,7 @@ namespace DFHack {namespace Lua {
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \ 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) { \ void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4) { \
handler(out, arg1, arg2, arg3, arg4); \ handler(out, arg1, arg2, arg3, arg4); \
if (auto state = name##_event.get_state()) { \ if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \ DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \ DFHack::Lua::Push(state, arg2); \
DFHack::Lua::Push(state, arg3); \ DFHack::Lua::Push(state, arg3); \
@ -433,7 +471,7 @@ namespace DFHack {namespace Lua {
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \ 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) { \ void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4, arg_type5 arg5) { \
handler(out, arg1, arg2, arg3, arg4, arg5); \ handler(out, arg1, arg2, arg3, arg4, arg5); \
if (auto state = name##_event.get_state()) { \ if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \ DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \ DFHack::Lua::Push(state, arg2); \
DFHack::Lua::Push(state, arg3); \ DFHack::Lua::Push(state, arg3); \

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -281,8 +281,14 @@ namespace DFHack
/// get the DF Process FilePath /// get the DF Process FilePath
std::string getPath(); std::string getPath();
/// millisecond tick count, exactly as DF uses
uint32_t getTickCount();
/// modify permisions of memory range /// modify permisions of memory range
bool setPermisions(const t_memrange & range,const t_memrange &trgrange); bool setPermisions(const t_memrange & range,const t_memrange &trgrange);
/// write a possibly read-only memory area
bool patchMemory(void *target, const void* src, size_t count);
private: private:
VersionInfo * my_descriptor; VersionInfo * my_descriptor;
PlatformSpecific *d; PlatformSpecific *d;
@ -309,5 +315,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

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -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.

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -128,7 +128,9 @@ namespace DFHack
{ {
PS_UNLOADED, PS_UNLOADED,
PS_LOADED, PS_LOADED,
PS_BROKEN PS_BROKEN,
PS_LOADING,
PS_UNLOADING
}; };
friend class PluginManager; friend class PluginManager;
friend class RPCService; friend class RPCService;
@ -173,31 +175,16 @@ namespace DFHack
PluginManager * parent; PluginManager * parent;
plugin_state state; plugin_state state;
struct LuaCommand { struct LuaCommand;
Plugin *owner;
std::string name;
int (*command)(lua_State *state);
LuaCommand(Plugin *owner, std::string name) : owner(owner), name(name) {}
};
std::map<std::string, LuaCommand*> lua_commands; std::map<std::string, LuaCommand*> lua_commands;
static int lua_cmd_wrapper(lua_State *state); static int lua_cmd_wrapper(lua_State *state);
struct LuaFunction { struct LuaFunction;
Plugin *owner;
std::string name;
function_identity_base *identity;
LuaFunction(Plugin *owner, std::string name) : owner(owner), name(name) {}
};
std::map<std::string, LuaFunction*> lua_functions; std::map<std::string, LuaFunction*> lua_functions;
static int lua_fun_wrapper(lua_State *state); static int lua_fun_wrapper(lua_State *state);
void push_function(lua_State *state, LuaFunction *fn); void push_function(lua_State *state, LuaFunction *fn);
struct LuaEvent { struct LuaEvent;
LuaFunction handler;
Lua::Notification *event;
bool active;
LuaEvent(Plugin *owner, std::string name) : handler(owner,name), active(false) {}
};
std::map<std::string, LuaEvent*> lua_events; std::map<std::string, LuaEvent*> lua_events;
void index_lua(DFLibrary *lib); void index_lua(DFLibrary *lib);

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -65,6 +65,7 @@ namespace DFHack
RPCService *owner, const char *name, int flags) RPCService *owner, const char *name, int flags)
: RPCFunctionBase(in, out), name(name), flags(flags), owner(owner), id(-1) : RPCFunctionBase(in, out), name(name), flags(flags), owner(owner), id(-1)
{} {}
virtual ~ServerFunctionBase() {}
RPCService *owner; RPCService *owner;
int16_t id; int16_t id;

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -88,7 +88,7 @@ namespace DFHack
{ {
typedef df::enum_traits<T> traits; typedef df::enum_traits<T> traits;
int base = traits::first_item; int base = traits::first_item;
int size = traits::last_item - base + 1; int size = (int)traits::last_item - base + 1;
describeEnum(pf, base, size, traits::key_table); describeEnum(pf, base, size, traits::key_table);
} }

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -0,0 +1,190 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#pragma once
#include "DataFuncs.h"
namespace DFHack
{
template<bool> struct StaticAssert;
template<> struct StaticAssert<true> {};
#define STATIC_ASSERT(condition) { StaticAssert<(condition)>(); }
/* Wrapping around compiler-specific representation of pointers to methods. */
#if defined(_MSC_VER)
#define METHOD_POINTER_SIZE (sizeof(void*)*2)
#elif defined(__GXX_ABI_VERSION)
#define METHOD_POINTER_SIZE (sizeof(void*)*2)
#else
#error Unknown compiler type
#endif
#define ASSERT_METHOD_POINTER(type) \
STATIC_ASSERT(df::return_type<type>::is_method && sizeof(type)==METHOD_POINTER_SIZE);
DFHACK_EXPORT bool is_vmethod_pointer_(void*);
DFHACK_EXPORT int vmethod_pointer_to_idx_(void*);
DFHACK_EXPORT void* method_pointer_to_addr_(void*);
DFHACK_EXPORT void addr_to_method_pointer_(void*,void*);
template<class T> bool is_vmethod_pointer(T ptr) {
ASSERT_METHOD_POINTER(T);
return is_vmethod_pointer_(&ptr);
}
template<class T> int vmethod_pointer_to_idx(T ptr) {
ASSERT_METHOD_POINTER(T);
return vmethod_pointer_to_idx_(&ptr);
}
template<class T> void *method_pointer_to_addr(T ptr) {
ASSERT_METHOD_POINTER(T);
return method_pointer_to_addr_(&ptr);
}
template<class T> T addr_to_method_pointer(void *addr) {
ASSERT_METHOD_POINTER(T);
T rv;
addr_to_method_pointer_(&rv, addr);
return rv;
}
/* Access to vmethod pointers from the vtable. */
template<class P>
P virtual_identity::get_vmethod_ptr(P selector)
{
typedef typename df::return_type<P>::class_type host_class;
virtual_identity &identity = host_class::_identity;
int idx = vmethod_pointer_to_idx(selector);
return addr_to_method_pointer<P>(identity.get_vmethod_ptr(idx));
}
/* VMethod interpose API.
This API allows replacing an entry in the original vtable
with code defined by DFHack, while retaining ability to
call the original code. The API can be safely used from
plugins, and multiple hooks for the same vmethod are
automatically chained (subclass before superclass; at same
level highest priority called first; undefined order otherwise).
Usage:
struct my_hack : df::someclass {
typedef df::someclass interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, foo, (int arg)) {
// If needed by the code, claim the suspend lock.
// DO NOT USE THE USUAL CoreSuspender, OR IT WILL DEADLOCK!
// CoreSuspendClaimer suspend;
...
INTERPOSE_NEXT(foo)(arg) // call the original
...
}
};
IMPLEMENT_VMETHOD_INTERPOSE(my_hack, foo);
or
IMPLEMENT_VMETHOD_INTERPOSE_PRIO(my_hack, foo, priority);
void init() {
if (!INTERPOSE_HOOK(my_hack, foo).apply())
error();
}
void shutdown() {
INTERPOSE_HOOK(my_hack, foo).remove();
}
*/
#define DEFINE_VMETHOD_INTERPOSE(rtype, name, args) \
typedef rtype (interpose_base::*interpose_ptr_##name)args; \
static DFHack::VMethodInterposeLink<interpose_base,interpose_ptr_##name> interpose_##name; \
rtype interpose_fn_##name args
#define IMPLEMENT_VMETHOD_INTERPOSE_PRIO(class,name,priority) \
DFHack::VMethodInterposeLink<class::interpose_base,class::interpose_ptr_##name> \
class::interpose_##name(&class::interpose_base::name, &class::interpose_fn_##name, priority);
#define IMPLEMENT_VMETHOD_INTERPOSE(class,name) IMPLEMENT_VMETHOD_INTERPOSE_PRIO(class,name,0)
#define INTERPOSE_NEXT(name) (this->*interpose_##name.chain)
#define INTERPOSE_HOOK(class, name) (class::interpose_##name)
class DFHACK_EXPORT VMethodInterposeLinkBase {
/*
These link objects try to:
1) Allow multiple hooks into the same vmethod
2) Auto-remove hooks when a plugin is unloaded.
*/
friend class virtual_identity;
virtual_identity *host; // Class with the vtable
int vmethod_idx;
void *interpose_method; // Pointer to the code of the interposing method
void *chain_mptr; // Pointer to the chain field below
int priority;
bool applied;
void *saved_chain; // Previous pointer to the code
VMethodInterposeLinkBase *next, *prev; // Other hooks for the same method
// inherited vtable members
std::set<virtual_identity*> child_hosts;
std::set<VMethodInterposeLinkBase*> child_next;
void set_chain(void *chain);
void on_host_delete(virtual_identity *host);
VMethodInterposeLinkBase *get_first_interpose(virtual_identity *id);
bool find_child_hosts(virtual_identity *cur, void *vmptr);
public:
VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority);
~VMethodInterposeLinkBase();
bool is_applied() { return applied; }
bool apply(bool enable = true);
void remove();
};
template<class Base, class Ptr>
class VMethodInterposeLink : public VMethodInterposeLinkBase {
public:
Ptr chain;
operator Ptr () { return chain; }
template<class Ptr2>
VMethodInterposeLink(Ptr target, Ptr2 src, int priority)
: VMethodInterposeLinkBase(
&Base::_identity,
vmethod_pointer_to_idx(target),
method_pointer_to_addr(src),
&chain,
priority
)
{ src = target; /* check compatibility */ }
};
}

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,5 +1,12 @@
bool empty() const { return x.empty(); }
unsigned size() const { return x.size(); } unsigned size() const { return x.size(); }
void clear() {
x.clear();
y.clear();
z.clear();
}
coord operator[] (unsigned idx) const { coord operator[] (unsigned idx) const {
if (idx >= x.size()) if (idx >= x.size())
return coord(); return coord();

@ -16,7 +16,7 @@ inline bool getassignment( const df::coord2d &xy )
} }
inline bool getassignment( int x, int y ) inline bool getassignment( int x, int y )
{ {
return (bits[y] & (1 << x)); return (bits[(y&15)] & (1 << (x&15)));
} }
inline void setassignment( const df::coord2d &xy, bool bit ) inline void setassignment( const df::coord2d &xy, bool bit )
{ {
@ -25,9 +25,9 @@ inline void setassignment( const df::coord2d &xy, bool bit )
inline void setassignment( int x, int y, bool bit ) inline void setassignment( int x, int y, bool bit )
{ {
if(bit) if(bit)
bits[y] |= (1 << x); bits[(y&15)] |= (1 << (x&15));
else else
bits[y] &= ~(1 << x); bits[(y&15)] &= ~(1 << (x&15));
} }
bool has_assignments() bool has_assignments()
{ {

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -92,6 +92,11 @@ 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);
/**
* Sets the owner unit for the building.
*/
DFHACK_EXPORT bool setOwner(df::building *building, df::unit *owner);
/** /**
* Find the building located at the specified tile. * Find the building located at the specified tile.
* Does not work on civzones. * Does not work on civzones.

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mr<EFBFBD>zek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mr<EFBFBD>zek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -32,6 +32,7 @@ distribution.
#include "DataDefs.h" #include "DataDefs.h"
#include "df/init.h" #include "df/init.h"
#include "df/ui.h" #include "df/ui.h"
#include "df/announcement_type.h"
namespace df { namespace df {
struct viewscreen; struct viewscreen;
@ -55,8 +56,6 @@ namespace DFHack
*/ */
namespace Gui namespace Gui
{ {
inline df::viewscreen *getCurViewscreen() { return Core::getTopViewscreen(); }
DFHACK_EXPORT std::string getFocusString(df::viewscreen *top); DFHACK_EXPORT std::string getFocusString(df::viewscreen *top);
// Full-screen item details view // Full-screen item details view
@ -92,13 +91,38 @@ namespace DFHack
DFHACK_EXPORT bool any_item_hotkey(df::viewscreen *top); DFHACK_EXPORT bool any_item_hotkey(df::viewscreen *top);
DFHACK_EXPORT df::item *getSelectedItem(color_ostream &out, bool quiet = false); DFHACK_EXPORT df::item *getSelectedItem(color_ostream &out, bool quiet = false);
// A building is selected via 'q', 't' or 'i' (civzone)
DFHACK_EXPORT bool any_building_hotkey(df::viewscreen *top);
DFHACK_EXPORT df::building *getSelectedBuilding(color_ostream &out, bool quiet = false);
// Show a plain announcement, or a titan-style popup message // Show a plain announcement, or a titan-style popup message
DFHACK_EXPORT void showAnnouncement(std::string message, int color = 7, bool bright = true); DFHACK_EXPORT void showAnnouncement(std::string message, int color = 7, bool bright = true);
DFHACK_EXPORT void showZoomAnnouncement(df::announcement_type type, df::coord pos, std::string message, int color = 7, bool bright = true);
DFHACK_EXPORT void showPopupAnnouncement(std::string message, int color = 7, bool bright = true); DFHACK_EXPORT void showPopupAnnouncement(std::string message, int color = 7, bool bright = true);
// Show an announcement with effects determined by announcements.txt
DFHACK_EXPORT void showAutoAnnouncement(df::announcement_type type, df::coord pos, std::string message, int color = 7, bool bright = true);
/* /*
* Cursor and window coords * Cursor and window coords
*/ */
DFHACK_EXPORT df::coord getViewportPos();
DFHACK_EXPORT df::coord getCursorPos();
static const int AREA_MAP_WIDTH = 23;
static const int MENU_WIDTH = 30;
struct DwarfmodeDims {
int map_x1, map_x2, menu_x1, menu_x2, area_x1, area_x2;
int y1, y2;
bool menu_on, area_on, menu_forced;
};
DFHACK_EXPORT DwarfmodeDims getDwarfmodeViewDims();
DFHACK_EXPORT void resetDwarfmodeView(bool pause = false);
DFHACK_EXPORT bool revealInDwarfmodeMap(df::coord pos, bool center = false);
DFHACK_EXPORT bool getViewCoords (int32_t &x, int32_t &y, int32_t &z); DFHACK_EXPORT bool getViewCoords (int32_t &x, int32_t &y, int32_t &z);
DFHACK_EXPORT bool setViewCoords (const int32_t x, const int32_t y, const int32_t z); DFHACK_EXPORT bool setViewCoords (const int32_t x, const int32_t y, const int32_t z);
@ -113,7 +137,11 @@ namespace DFHack
* Gui screens * Gui screens
*/ */
/// Get the current top-level view-screen /// Get the current top-level view-screen
DFHACK_EXPORT df::viewscreen * GetCurrentScreen(); DFHACK_EXPORT df::viewscreen *getCurViewscreen(bool skip_dismissed = false);
inline std::string getCurFocus(bool skip_dismissed = false) {
return getFocusString(getCurViewscreen(skip_dismissed));
}
/// get the size of the window buffer /// get the size of the window buffer
DFHACK_EXPORT bool getWindowSize(int32_t & width, int32_t & height); DFHACK_EXPORT bool getWindowSize(int32_t & width, int32_t & height);

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -40,10 +40,12 @@ distribution.
#include "df/building_actual.h" #include "df/building_actual.h"
#include "df/body_part_raw.h" #include "df/body_part_raw.h"
#include "df/unit_inventory_item.h" #include "df/unit_inventory_item.h"
#include "df/job_item_vector_id.h"
namespace df namespace df
{ {
struct itemdef; struct itemdef;
struct proj_itemst;
} }
namespace MapExtras { namespace MapExtras {
@ -85,7 +87,8 @@ namespace DFHack
bool find(const std::string &token); bool find(const std::string &token);
bool matches(const df::job_item &item, MaterialInfo *mat = NULL); bool matches(df::job_item_vector_id vec_id);
bool matches(const df::job_item &item, MaterialInfo *mat = NULL, bool skip_vector = false);
}; };
inline bool operator== (const ItemTypeInfo &a, const ItemTypeInfo &b) { inline bool operator== (const ItemTypeInfo &a, const ItemTypeInfo &b) {
@ -122,6 +125,10 @@ struct dfh_item
namespace Items namespace Items
{ {
DFHACK_EXPORT bool isCasteMaterial(df::item_type itype);
DFHACK_EXPORT int getSubtypeCount(df::item_type itype);
DFHACK_EXPORT df::itemdef *getSubtypeDef(df::item_type itype, int subtype);
/// Look for a particular item by ID /// Look for a particular item by ID
DFHACK_EXPORT df::item * findItemByID(int32_t id); DFHACK_EXPORT df::item * findItemByID(int32_t id);
@ -144,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);
@ -154,6 +166,12 @@ 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
DFHACK_EXPORT bool remove(MapExtras::MapCache &mc, df::item *item, bool no_uncat = false);
/// Detaches the items from its current location and turns it into a projectile
DFHACK_EXPORT df::proj_itemst *makeProjectile(MapExtras::MapCache &mc, df::item *item);
} }
} }

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -32,6 +32,7 @@ distribution.
#include "DataDefs.h" #include "DataDefs.h"
#include "df/job_item_ref.h" #include "df/job_item_ref.h"
#include "df/item_type.h"
namespace df namespace df
{ {
@ -69,6 +70,9 @@ namespace DFHack
DFHACK_EXPORT bool attachJobItem(df::job *job, df::item *item, DFHACK_EXPORT bool attachJobItem(df::job *job, df::item *item,
df::job_item_ref::T_role role, df::job_item_ref::T_role role,
int filter_idx = -1, int insert_idx = -1); int filter_idx = -1, int insert_idx = -1);
DFHACK_EXPORT bool isSuitableItem(df::job_item *item, df::item_type itype, int isubtype);
DFHACK_EXPORT bool isSuitableMaterial(df::job_item *item, int mat_type, int mat_index);
} }
DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b); DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b);

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -47,14 +47,6 @@ namespace MapExtras
class DFHACK_EXPORT MapCache; class DFHACK_EXPORT MapCache;
template<class R, class T> inline R index_tile(T &v, df::coord2d p) {
return v[p.x&15][p.y&15];
}
inline bool is_valid_tile_coord(df::coord2d p) {
return (p.x & ~15) == 0 && (p.y & ~15) == 0;
}
class Block; class Block;
class BlockInfo class BlockInfo
@ -253,6 +245,8 @@ public:
bool is_valid() { return valid; } bool is_valid() { return valid; }
df::map_block *getRaw() { return block; } df::map_block *getRaw() { return block; }
bool Allocate();
MapCache *getParent() { return parent; } MapCache *getParent() { return parent; }
private: private:
@ -262,6 +256,8 @@ private:
MapCache *parent; MapCache *parent;
df::map_block *block; df::map_block *block;
void init();
int biomeIndexAt(df::coord2d p); int biomeIndexAt(df::coord2d p);
bool valid; bool valid;
@ -347,6 +343,12 @@ class DFHACK_EXPORT MapCache
return BlockAt(df::coord(coord.x>>4,coord.y>>4,coord.z)); return BlockAt(df::coord(coord.x>>4,coord.y>>4,coord.z));
} }
bool ensureBlockAt(df::coord coord)
{
Block *b = BlockAtTile(coord);
return b ? b->Allocate() : false;
}
df::tiletype baseTiletypeAt (DFCoord tilecoord) df::tiletype baseTiletypeAt (DFCoord tilecoord)
{ {
Block *b = BlockAtTile(tilecoord); Block *b = BlockAtTile(tilecoord);

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -50,6 +50,7 @@ distribution.
#include "df/tile_dig_designation.h" #include "df/tile_dig_designation.h"
#include "df/tile_traffic.h" #include "df/tile_traffic.h"
#include "df/feature_init.h" #include "df/feature_init.h"
#include "df/flow_type.h"
/** /**
* \defgroup grp_maps Maps module and its types * \defgroup grp_maps Maps module and its types
@ -150,6 +151,21 @@ typedef uint8_t biome_indices40d [9];
*/ */
typedef uint16_t t_temperatures [16][16]; typedef uint16_t t_temperatures [16][16];
/**
* Index a tile array by a 2D coordinate, clipping it to mod 16
*/
template<class R, class T> inline R index_tile(T &v, df::coord2d p) {
return v[p.x&15][p.y&15];
}
/**
* Check if a 2D coordinate is in the 0-15 range.
*/
inline bool is_valid_tile_coord(df::coord2d p) {
return (p.x & ~15) == 0 && (p.y & ~15) == 0;
}
/** /**
* The Maps module * The Maps module
* \ingroup grp_modules * \ingroup grp_modules
@ -232,14 +248,19 @@ extern DFHACK_EXPORT void getSize(uint32_t& x, uint32_t& y, uint32_t& z);
/// get the position of the map on world map /// get the position of the map on world map
extern DFHACK_EXPORT void getPosition(int32_t& x, int32_t& y, int32_t& z); extern DFHACK_EXPORT void getPosition(int32_t& x, int32_t& y, int32_t& z);
extern DFHACK_EXPORT bool isValidTilePos(int32_t x, int32_t y, int32_t z);
inline bool isValidTilePos(df::coord pos) { return isValidTilePos(pos.x, pos.y, pos.z); }
/** /**
* Get the map block or NULL if block is not valid * Get the map block or NULL if block is not valid
*/ */
extern DFHACK_EXPORT df::map_block * getBlock (int32_t blockx, int32_t blocky, int32_t blockz); extern DFHACK_EXPORT df::map_block * getBlock (int32_t blockx, int32_t blocky, int32_t blockz);
extern DFHACK_EXPORT df::map_block * getTileBlock (int32_t x, int32_t y, int32_t z); extern DFHACK_EXPORT df::map_block * getTileBlock (int32_t x, int32_t y, int32_t z);
extern DFHACK_EXPORT df::map_block * ensureTileBlock (int32_t x, int32_t y, int32_t z);
inline df::map_block * getBlock (df::coord pos) { return getBlock(pos.x, pos.y, pos.z); } inline df::map_block * getBlock (df::coord pos) { return getBlock(pos.x, pos.y, pos.z); }
inline df::map_block * getTileBlock (df::coord pos) { return getTileBlock(pos.x, pos.y, pos.z); } inline df::map_block * getTileBlock (df::coord pos) { return getTileBlock(pos.x, pos.y, pos.z); }
inline df::map_block * ensureTileBlock (df::coord pos) { return ensureTileBlock(pos.x, pos.y, pos.z); }
extern DFHACK_EXPORT df::tiletype *getTileType(int32_t x, int32_t y, int32_t z); extern DFHACK_EXPORT df::tiletype *getTileType(int32_t x, int32_t y, int32_t z);
extern DFHACK_EXPORT df::tile_designation *getTileDesignation(int32_t x, int32_t y, int32_t z); extern DFHACK_EXPORT df::tile_designation *getTileDesignation(int32_t x, int32_t y, int32_t z);
@ -258,7 +279,7 @@ inline df::tile_occupancy *getTileOccupancy(df::coord pos) {
/** /**
* Returns biome info about the specified world region. * Returns biome info about the specified world region.
*/ */
DFHACK_EXPORT df::world_data::T_region_map *getRegionBiome(df::coord2d rgn_pos); DFHACK_EXPORT df::region_map_entry *getRegionBiome(df::coord2d rgn_pos);
/** /**
* Returns biome world region coordinates for the given tile within given block. * Returns biome world region coordinates for the given tile within given block.
@ -272,6 +293,8 @@ inline df::coord2d getTileBiomeRgn(df::coord pos) {
// Enables per-frame updates for liquid flow and/or temperature. // Enables per-frame updates for liquid flow and/or temperature.
DFHACK_EXPORT void enableBlockUpdates(df::map_block *blk, bool flow = false, bool temperature = false); DFHACK_EXPORT void enableBlockUpdates(df::map_block *blk, bool flow = false, bool temperature = false);
DFHACK_EXPORT df::flow_info *spawnFlow(df::coord pos, df::flow_type type, int mat_type = 0, int mat_index = -1, int density = 100);
/// sorts the block event vector into multiple vectors by type /// sorts the block event vector into multiple vectors by type
/// mineral veins, what's under ice, blood smears and mud /// mineral veins, what's under ice, blood smears and mud
extern DFHACK_EXPORT bool SortBlockEvents(df::map_block *block, extern DFHACK_EXPORT bool SortBlockEvents(df::map_block *block,

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -131,6 +131,11 @@ namespace DFHack
bool findPlant(const std::string &token, const std::string &subtoken); bool findPlant(const std::string &token, const std::string &subtoken);
bool findCreature(const std::string &token, const std::string &subtoken); bool findCreature(const std::string &token, const std::string &subtoken);
bool findProduct(df::material *material, const std::string &name);
bool findProduct(const MaterialInfo &info, const std::string &name) {
return findProduct(info.material, name);
}
std::string getToken(); std::string getToken();
std::string toString(uint16_t temp = 10015, bool named = true); std::string toString(uint16_t temp = 10015, bool named = true);

@ -0,0 +1,205 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#pragma once
#include "Export.h"
#include "Module.h"
#include "BitArray.h"
#include "ColorText.h"
#include <string>
#include "DataDefs.h"
#include "df/graphic.h"
#include "df/viewscreen.h"
namespace df
{
struct job;
struct item;
struct unit;
struct building;
}
/**
* \defgroup grp_screen utilities for painting to the screen
* @ingroup grp_screen
*/
namespace DFHack
{
class Core;
/**
* The Screen module
* \ingroup grp_modules
* \ingroup grp_screen
*/
namespace Screen
{
/// Data structure describing all properties a screen tile can have
struct Pen {
// Ordinary text symbol
char ch;
int8_t fg, bg;
bool bold;
// Graphics tile
int tile;
enum TileMode {
AsIs, // Tile colors used without modification
CharColor, // The fg/bg pair is used
TileColor // The fields below are used
} tile_mode;
int8_t tile_fg, tile_bg;
bool valid() const { return 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)
: ch(ch), fg(fg&7), bg(bg), bold(!!(fg&8)),
tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0)
{}
Pen(char ch, int8_t fg, int8_t bg, bool bold, int tile = 0, bool color_tile = false)
: ch(ch), fg(fg), bg(bg), bold(bold),
tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0)
{}
Pen(char ch, int8_t fg, int8_t bg, int tile, int8_t tile_fg, int8_t tile_bg)
: ch(ch), fg(fg&7), bg(bg), bold(!!(fg&8)),
tile(tile), tile_mode(TileColor), tile_fg(tile_fg), tile_bg(tile_bg)
{}
Pen(char ch, int8_t fg, int8_t bg, bool bold, int tile, int8_t tile_fg, int8_t tile_bg)
: ch(ch), fg(fg), bg(bg), bold(bold),
tile(tile), tile_mode(TileColor), tile_fg(tile_fg), tile_bg(tile_bg)
{}
};
DFHACK_EXPORT df::coord2d getMousePos();
DFHACK_EXPORT df::coord2d getWindowSize();
/// Returns the state of [GRAPHICS:YES/NO]
DFHACK_EXPORT bool inGraphicsMode();
/// Paint one screen tile with the given pen
DFHACK_EXPORT bool paintTile(const Pen &pen, int x, int y);
/// Retrieves one screen tile from the buffer
DFHACK_EXPORT Pen readTile(int x, int y);
/// Paint a string onto the screen. Ignores ch and tile of pen.
DFHACK_EXPORT bool paintString(const Pen &pen, int x, int y, const std::string &text);
/// Fills a rectangle with one pen. Possibly more efficient than a loop over paintTile.
DFHACK_EXPORT bool fillRect(const Pen &pen, int x1, int y1, int x2, int y2);
/// Draws a standard dark gray window border with a title string
DFHACK_EXPORT bool drawBorder(const std::string &title);
/// Wipes the screen to full black
DFHACK_EXPORT bool clear();
/// Requests repaint
DFHACK_EXPORT bool invalidate();
/// Find a loaded graphics tile from graphics raws.
DFHACK_EXPORT bool findGraphicsTile(const std::string &page, int x, int y, int *ptile, int *pgs = NULL);
// Push and remove viewscreens
DFHACK_EXPORT bool show(df::viewscreen *screen, df::viewscreen *before = NULL);
DFHACK_EXPORT void dismiss(df::viewscreen *screen, bool to_first = false);
DFHACK_EXPORT bool isDismissed(df::viewscreen *screen);
/// Retrieve the string representation of the bound key.
DFHACK_EXPORT std::string getKeyDisplay(df::interface_key key);
}
class DFHACK_EXPORT dfhack_viewscreen : public df::viewscreen {
df::coord2d last_size;
void check_resize();
protected:
bool text_input_mode;
public:
dfhack_viewscreen();
virtual ~dfhack_viewscreen();
static bool is_instance(df::viewscreen *screen);
static dfhack_viewscreen *try_cast(df::viewscreen *screen);
virtual void logic();
virtual void render();
virtual int8_t movies_okay() { return 1; }
virtual bool key_conflict(df::interface_key key);
virtual bool is_lua_screen() { return false; }
virtual std::string getFocusString() = 0;
virtual void onShow() {};
virtual void onDismiss() {};
virtual df::unit *getSelectedUnit() { return NULL; }
virtual df::item *getSelectedItem() { return NULL; }
virtual df::job *getSelectedJob() { return NULL; }
virtual df::building *getSelectedBuilding() { return NULL; }
};
class DFHACK_EXPORT dfhack_lua_viewscreen : public dfhack_viewscreen {
std::string focus;
void update_focus(lua_State *L, int idx);
bool safe_call_lua(int (*pf)(lua_State *), int args, int rvs);
static dfhack_lua_viewscreen *get_self(lua_State *L);
static int do_destroy(lua_State *L);
static int do_render(lua_State *L);
static int do_notify(lua_State *L);
static int do_input(lua_State *L);
public:
dfhack_lua_viewscreen(lua_State *L, int table_idx);
virtual ~dfhack_lua_viewscreen();
static df::viewscreen *get_pointer(lua_State *L, int idx, bool make);
virtual bool is_lua_screen() { return true; }
virtual std::string getFocusString() { return focus; }
virtual void render();
virtual void logic();
virtual void help();
virtual void resize(int w, int h);
virtual void feed(std::set<df::interface_key> *keys);
virtual void onShow();
virtual void onDismiss();
virtual df::unit *getSelectedUnit();
virtual df::item *getSelectedItem();
virtual df::job *getSelectedJob();
virtual df::building *getSelectedBuilding();
};
}

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -32,6 +32,10 @@ distribution.
#include "modules/Items.h" #include "modules/Items.h"
#include "DataDefs.h" #include "DataDefs.h"
#include "df/unit.h" #include "df/unit.h"
#include "df/misc_trait_type.h"
#include "df/physical_attribute_type.h"
#include "df/mental_attribute_type.h"
#include "df/job_skill.h"
namespace df namespace df
{ {
@ -41,6 +45,7 @@ namespace df
struct historical_entity; struct historical_entity;
struct entity_position_assignment; struct entity_position_assignment;
struct entity_position; struct entity_position;
struct unit_misc_trait;
} }
/** /**
@ -208,6 +213,18 @@ DFHACK_EXPORT df::language_name *getVisibleName(df::unit *unit);
DFHACK_EXPORT df::assumed_identity *getIdentity(df::unit *unit); DFHACK_EXPORT df::assumed_identity *getIdentity(df::unit *unit);
DFHACK_EXPORT df::nemesis_record *getNemesis(df::unit *unit); DFHACK_EXPORT df::nemesis_record *getNemesis(df::unit *unit);
DFHACK_EXPORT bool isHidingCurse(df::unit *unit);
DFHACK_EXPORT int getPhysicalAttrValue(df::unit *unit, df::physical_attribute_type attr);
DFHACK_EXPORT int getMentalAttrValue(df::unit *unit, df::mental_attribute_type attr);
DFHACK_EXPORT bool isCrazed(df::unit *unit);
DFHACK_EXPORT bool isOpposedToLife(df::unit *unit);
DFHACK_EXPORT bool hasExtravision(df::unit *unit);
DFHACK_EXPORT bool isBloodsucker(df::unit *unit);
DFHACK_EXPORT bool isMischievous(df::unit *unit);
DFHACK_EXPORT df::unit_misc_trait *getMiscTrait(df::unit *unit, df::misc_trait_type type, bool create = false);
DFHACK_EXPORT bool isDead(df::unit *unit); DFHACK_EXPORT bool isDead(df::unit *unit);
DFHACK_EXPORT bool isAlive(df::unit *unit); DFHACK_EXPORT bool isAlive(df::unit *unit);
DFHACK_EXPORT bool isSane(df::unit *unit); DFHACK_EXPORT bool isSane(df::unit *unit);
@ -216,6 +233,10 @@ DFHACK_EXPORT bool isDwarf(df::unit *unit);
DFHACK_EXPORT double getAge(df::unit *unit, bool true_age = false); 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 getEffectiveSkill(df::unit *unit, df::job_skill skill_id);
DFHACK_EXPORT int computeMovementSpeed(df::unit *unit);
struct NoblePosition { struct NoblePosition {
df::historical_entity *entity; df::historical_entity *entity;
df::entity_position_assignment *assignment; df::entity_position_assignment *assignment;
@ -226,6 +247,9 @@ DFHACK_EXPORT bool getNoblePositions(std::vector<NoblePosition> *pvec, df::unit
DFHACK_EXPORT std::string getProfessionName(df::unit *unit, bool ignore_noble = false, bool plural = false); DFHACK_EXPORT std::string getProfessionName(df::unit *unit, bool ignore_noble = false, bool plural = false);
DFHACK_EXPORT std::string getCasteProfessionName(int race, int caste, df::profession pid, bool plural = false); DFHACK_EXPORT std::string getCasteProfessionName(int race, int caste, df::profession pid, bool plural = false);
DFHACK_EXPORT int8_t getProfessionColor(df::unit *unit, bool ignore_noble = false);
DFHACK_EXPORT int8_t getCasteProfessionColor(int race, int caste, df::profession pid);
} }
} }
#endif #endif

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -37,6 +37,12 @@ distribution.
#include "DataDefs.h" #include "DataDefs.h"
namespace df
{
struct tile_bitmask;
struct map_block;
}
namespace DFHack namespace DFHack
{ {
typedef df::game_mode GameMode; typedef df::game_mode GameMode;
@ -55,8 +61,6 @@ namespace DFHack
class DFContextShared; class DFContextShared;
class DFHACK_EXPORT PersistentDataItem { class DFHACK_EXPORT PersistentDataItem {
friend class World;
int id; int id;
std::string key_value; std::string key_value;
@ -65,13 +69,17 @@ namespace DFHack
public: public:
static const int NumInts = 7; static const int NumInts = 7;
bool isValid() { return id != 0; } bool isValid() const { return id != 0; }
int entry_id() { return -id; } int entry_id() const { return -id; }
int raw_id() const { return id; }
const std::string &key() { return key_value; } const std::string &key() const { return key_value; }
std::string &val() { return *str_value; } std::string &val() { return *str_value; }
const std::string &val() const { return *str_value; }
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]; }
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)
@ -83,54 +91,45 @@ namespace DFHack
* \ingroup grp_modules * \ingroup grp_modules
* \ingroup grp_world * \ingroup grp_world
*/ */
class DFHACK_EXPORT World : public Module namespace World
{ {
public:
World();
~World();
bool Start();
bool Finish();
///true if paused, false if not ///true if paused, false if not
bool ReadPauseState(); DFHACK_EXPORT bool ReadPauseState();
///true if paused, false if not ///true if paused, false if not
void SetPauseState(bool paused); DFHACK_EXPORT void SetPauseState(bool paused);
uint32_t ReadCurrentTick(); DFHACK_EXPORT uint32_t ReadCurrentTick();
uint32_t ReadCurrentYear(); DFHACK_EXPORT uint32_t ReadCurrentYear();
uint32_t ReadCurrentMonth(); DFHACK_EXPORT uint32_t ReadCurrentMonth();
uint32_t ReadCurrentDay(); DFHACK_EXPORT uint32_t ReadCurrentDay();
uint8_t ReadCurrentWeather(); DFHACK_EXPORT uint8_t ReadCurrentWeather();
void SetCurrentWeather(uint8_t weather); DFHACK_EXPORT void SetCurrentWeather(uint8_t weather);
bool ReadGameMode(t_gamemodes& rd); DFHACK_EXPORT bool ReadGameMode(t_gamemodes& rd);
bool WriteGameMode(const t_gamemodes & wr); // this is very dangerous DFHACK_EXPORT bool WriteGameMode(const t_gamemodes & wr); // this is very dangerous
std::string ReadWorldFolder(); DFHACK_EXPORT std::string ReadWorldFolder();
// Store data in fake historical figure names. // Store data in fake historical figure names.
// This ensures that the values are stored in save games. // This ensures that the values are stored in save games.
PersistentDataItem AddPersistentData(const std::string &key); DFHACK_EXPORT PersistentDataItem AddPersistentData(const std::string &key);
PersistentDataItem GetPersistentData(const std::string &key); DFHACK_EXPORT PersistentDataItem GetPersistentData(const std::string &key);
PersistentDataItem GetPersistentData(int entry_id); DFHACK_EXPORT PersistentDataItem GetPersistentData(int entry_id);
// Calls GetPersistentData(key); if not found, adds and sets added to true. // Calls GetPersistentData(key); if not found, adds and sets added to true.
// The result can still be not isValid() e.g. if the world is not loaded. // The result can still be not isValid() e.g. if the world is not loaded.
PersistentDataItem GetPersistentData(const std::string &key, bool *added); DFHACK_EXPORT PersistentDataItem GetPersistentData(const std::string &key, bool *added);
// Lists all items with the given key. // Lists all items with the given key.
// If prefix is true, search for keys starting with key+"/". // If prefix is true, search for keys starting with key+"/".
// GetPersistentData(&vec,"",true) returns all items. // GetPersistentData(&vec,"",true) returns all items.
// Items have alphabetic order by key; same key ordering is undefined. // Items have alphabetic order by key; same key ordering is undefined.
void GetPersistentData(std::vector<PersistentDataItem> *vec, DFHACK_EXPORT void GetPersistentData(std::vector<PersistentDataItem> *vec,
const std::string &key, bool prefix = false); const std::string &key, bool prefix = false);
// Deletes the item; returns true if success. // Deletes the item; returns true if success.
bool DeletePersistentData(const PersistentDataItem &item); DFHACK_EXPORT bool DeletePersistentData(const PersistentDataItem &item);
void ClearPersistentCache();
private: DFHACK_EXPORT void ClearPersistentCache();
struct Private;
Private *d;
bool BuildPersistentCache(); DFHACK_EXPORT df::tile_bitmask *getPersistentTilemask(const PersistentDataItem &item, df::map_block *block, bool create = false);
}; DFHACK_EXPORT bool deletePersistentTilemask(const PersistentDataItem &item, df::map_block *block);
}
} }
#endif #endif

@ -1,6 +1,6 @@
/* /*
https://github.com/peterix/dfhack https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any

@ -0,0 +1,162 @@
-- A trivial reloadable class system
local _ENV = mkmodule('class')
-- Metatable template for a class
class_obj = class_obj or {}
-- Methods shared by all classes
common_methods = common_methods or {}
-- Forbidden names for class fields and methods.
reserved_names = { super = true, ATTRS = true }
-- Attribute table metatable
attrs_meta = attrs_meta or {}
-- Create or updates a class; a class has metamethods and thus own metatable.
function defclass(class,parent)
class = class or {}
local meta = getmetatable(class)
if not meta then
meta = {}
setmetatable(class, meta)
end
for k,v in pairs(class_obj) do meta[k] = v end
meta.__index = parent or common_methods
local attrs = rawget(class, 'ATTRS') or {}
setmetatable(attrs, attrs_meta)
rawset(class, 'super', parent)
rawset(class, 'ATTRS', attrs)
rawset(class, '__index', rawget(class, '__index') or class)
return class
end
-- An instance uses the class as metatable
function mkinstance(class,table)
table = table or {}
setmetatable(table, class)
return table
end
-- Patch the stubs in the global environment
dfhack.BASE_G.defclass = _ENV.defclass
dfhack.BASE_G.mkinstance = _ENV.mkinstance
-- Just verify the name, and then set.
function class_obj:__newindex(name,val)
if reserved_names[name] or common_methods[name] then
error('Method name '..name..' is reserved.')
end
rawset(self, name, val)
end
function attrs_meta:__call(attrs)
for k,v in pairs(attrs) do
self[k] = v
end
end
local function apply_attrs(obj, attrs, init_table)
for k,v in pairs(attrs) do
local init_v = init_table[k]
if init_v ~= nil then
obj[k] = init_v
elseif v == DEFAULT_NIL then
obj[k] = nil
else
obj[k] = v
end
end
end
local function invoke_before_rec(self, class, method, ...)
local meta = getmetatable(class)
if meta then
local fun = rawget(class, method)
if fun then
fun(self, ...)
end
invoke_before_rec(self, meta.__index, method, ...)
end
end
local function invoke_after_rec(self, class, method, ...)
local meta = getmetatable(class)
if meta then
invoke_after_rec(self, meta.__index, method, ...)
local fun = rawget(class, method)
if fun then
fun(self, ...)
end
end
end
local function init_attrs_rec(obj, class, init_table)
local meta = getmetatable(class)
if meta then
init_attrs_rec(obj, meta.__index, init_table)
apply_attrs(obj, rawget(class, 'ATTRS'), init_table)
end
end
-- Call metamethod constructs the object
function class_obj:__call(init_table)
-- The table is assumed to be a scratch temporary.
-- If it is not, copy it yourself before calling.
init_table = init_table or {}
local obj = mkinstance(self)
-- This initialization sequence is broadly based on how the
-- Common Lisp initialize-instance generic function works.
-- preinit screens input arguments in subclass to superclass order
invoke_before_rec(obj, self, 'preinit', init_table)
-- initialize the instance table from init table
init_attrs_rec(obj, self, init_table)
-- init in superclass -> subclass
invoke_after_rec(obj, self, 'init', init_table)
-- postinit in superclass -> subclass
invoke_after_rec(obj, self, 'postinit', init_table)
return obj
end
-- Common methods for all instances:
function common_methods:callback(method, ...)
return dfhack.curry(self[method], self, ...)
end
function common_methods:cb_getfield(field)
return function() return self[field] end
end
function common_methods:cb_setfield(field)
return function(val) self[field] = val end
end
function common_methods:assign(data)
for k,v in pairs(data) do
self[k] = v
end
end
function common_methods:invoke_before(method, ...)
return invoke_before_rec(self, getmetatable(self), method, ...)
end
function common_methods:invoke_after(method, ...)
return invoke_after_rec(self, getmetatable(self), method, ...)
end
return _ENV

@ -46,6 +46,7 @@ end
-- Error handling -- Error handling
safecall = dfhack.safecall safecall = dfhack.safecall
curry = dfhack.curry
function dfhack.pcall(f, ...) function dfhack.pcall(f, ...)
return xpcall(f, dfhack.onerror, ...) return xpcall(f, dfhack.onerror, ...)
@ -83,7 +84,7 @@ function mkmodule(module,env)
error("Not a table in package.loaded["..module.."]") error("Not a table in package.loaded["..module.."]")
end end
end end
local plugname = string.match(module,'^plugins%.(%w+)$') local plugname = string.match(module,'^plugins%.([%w%-]+)$')
if plugname then if plugname then
dfhack.open_plugin(pkg,plugname) dfhack.open_plugin(pkg,plugname)
end end
@ -102,11 +103,36 @@ function reload(module)
dofile(path) dofile(path)
end end
-- Trivial classes
function rawset_default(target,source)
for k,v in pairs(source) do
if rawget(target,k) == nil then
rawset(target,k,v)
end
end
end
DEFAULT_NIL = DEFAULT_NIL or {} -- Unique token
function defclass(...)
return require('class').defclass(...)
end
function mkinstance(...)
return require('class').mkinstance(...)
end
-- Misc functions -- Misc functions
NEWLINE = "\n"
COMMA = ","
PERIOD = "."
function printall(table) function printall(table)
if type(table) == 'table' or df.isvalid(table) == 'ref' then local ok,f,t,k = pcall(pairs,table)
for k,v in pairs(table) do if ok then
for k,v in f,t,k do
print(string.format("%-23s\t = %s",tostring(k),tostring(v))) print(string.format("%-23s\t = %s",tostring(k),tostring(v)))
end end
end end
@ -135,14 +161,39 @@ function xyz2pos(x,y,z)
end end
end end
function rawset_default(target,source) function same_xyz(a,b)
for k,v in pairs(source) do return a and b and a.x == b.x and a.y == b.y and a.z == b.z
if rawget(target,k) == nil then
rawset(target,k,v)
end end
function get_path_xyz(path,i)
return path.x[i], path.y[i], path.z[i]
end
function pos2xy(pos)
if pos then
local x = pos.x
if x and x ~= -30000 then
return x, pos.y
end
end
end
function xy2pos(x,y)
if x then
return {x=x,y=y}
else
return {x=-30000,y=-30000}
end end
end end
function same_xy(a,b)
return a and b and a.x == b.x and a.y == b.y
end
function get_path_xy(path,i)
return path.x[i], path.y[i]
end
function safe_index(obj,idx,...) function safe_index(obj,idx,...)
if obj == nil or idx == nil then if obj == nil or idx == nil then
return nil return nil
@ -160,10 +211,6 @@ end
-- String conversions -- String conversions
function dfhack.event:__tostring()
return "<event>"
end
function dfhack.persistent:__tostring() function dfhack.persistent:__tostring()
return "<persistent "..self.entry_id..":"..self.key.."=\"" return "<persistent "..self.entry_id..":"..self.key.."=\""
..self.value.."\":"..table.concat(self.ints,",")..">" ..self.value.."\":"..table.concat(self.ints,",")..">"

@ -0,0 +1,636 @@
-- Viewscreen implementation utility collection.
local _ENV = mkmodule('gui')
local dscreen = dfhack.screen
USE_GRAPHICS = dscreen.inGraphicsMode()
local to_pen = dfhack.pen.parse
CLEAR_PEN = to_pen{ch=32,fg=0,bg=0}
function simulateInput(screen,...)
local keys = {}
local function push_key(arg)
local kv = arg
if type(arg) == 'string' then
kv = df.interface_key[arg]
if kv == nil then
error('Invalid keycode: '..arg)
end
end
if type(kv) == 'number' then
keys[#keys+1] = kv
end
end
for i = 1,select('#',...) do
local arg = select(i,...)
if arg ~= nil then
local t = type(arg)
if type(arg) == 'table' then
for k,v in pairs(arg) do
if v == true then
push_key(k)
else
push_key(v)
end
end
else
push_key(arg)
end
end
end
dscreen._doSimulateInput(screen, keys)
end
function mkdims_xy(x1,y1,x2,y2)
return { x1=x1, y1=y1, x2=x2, y2=y2, width=x2-x1+1, height=y2-y1+1 }
end
function mkdims_wh(x1,y1,w,h)
return { x1=x1, y1=y1, x2=x1+w-1, y2=y1+h-1, width=w, height=h }
end
function is_in_rect(rect,x,y)
return x and y and x >= rect.x1 and x <= rect.x2 and y >= rect.y1 and y <= rect.y2
end
local function align_coord(gap,align,lv,rv)
if gap <= 0 then
return 0
end
if not align then
if rv and not lv then
align = 1.0
elseif lv and not rv then
align = 0.0
else
align = 0.5
end
end
return math.floor(gap*align)
end
function compute_frame_rect(wavail,havail,spec,xgap,ygap)
if not spec then
return mkdims_wh(0,0,wavail,havail)
end
local sw = wavail - (spec.l or 0) - (spec.r or 0)
local sh = havail - (spec.t or 0) - (spec.b or 0)
local rqw = math.min(sw, (spec.w or sw)+xgap)
local rqh = math.min(sh, (spec.h or sh)+ygap)
local ax = align_coord(sw - rqw, spec.xalign, spec.l, spec.r)
local ay = align_coord(sh - rqh, spec.yalign, spec.t, spec.b)
local rect = mkdims_wh((spec.l or 0) + ax, (spec.t or 0) + ay, rqw, rqh)
rect.wgap = sw - rqw
rect.hgap = sh - rqh
return rect
end
local function parse_inset(inset)
local l,r,t,b
if type(inset) == 'table' then
l,r = inset.l or inset.x, inset.r or inset.x
t,b = inset.t or inset.y, inset.b or inset.y
else
l = inset or 0
t,r,b = l,l,l
end
return l,r,t,b
end
function inset_frame(rect, inset, gap)
gap = gap or 0
local l,t,r,b = parse_inset(inset)
return mkdims_xy(rect.x1+l+gap, rect.y1+t+gap, rect.x2-r-gap, rect.y2-b-gap)
end
function compute_frame_body(wavail, havail, spec, inset, gap)
gap = gap or 0
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 body = mkdims_xy(rect.x1+l+gap, rect.y1+t+gap, rect.x2-r-gap, rect.y2-b-gap)
return rect, body
end
function blink_visible(delay)
return math.floor(dfhack.getTickCount()/delay) % 2 == 0
end
function getKeyDisplay(code)
if type(code) == 'string' then
code = df.interface_key[code]
end
return dscreen.getKeyDisplay(code)
end
-----------------------------------
-- Clipped view rectangle object --
-----------------------------------
ViewRect = defclass(ViewRect, nil)
function ViewRect:init(args)
if args.view_rect then
self:assign(args.view_rect)
else
local rect = args.rect or mkdims_wh(0,0,dscreen.getWindowSize())
local crect = args.clip_rect or rect
self:assign{
x1 = rect.x1, clip_x1 = crect.x1,
y1 = rect.y1, clip_y1 = crect.y1,
x2 = rect.x2, clip_x2 = crect.x2,
y2 = rect.y2, clip_y2 = crect.y2,
width = rect.x2-rect.x1+1,
height = rect.y2-rect.y1+1,
}
end
if args.clip_view then
local cr = args.clip_view
self:assign{
clip_x1 = math.max(self.clip_x1, cr.clip_x1),
clip_y1 = math.max(self.clip_y1, cr.clip_y1),
clip_x2 = math.min(self.clip_x2, cr.clip_x2),
clip_y2 = math.min(self.clip_y2, cr.clip_y2),
}
end
end
function ViewRect:isDefunct()
return (self.clip_x1 > self.clip_x2 or self.clip_y1 > self.clip_y2)
end
function ViewRect:inClipGlobalXY(x,y)
return x >= self.clip_x1 and x <= self.clip_x2
and y >= self.clip_y1 and y <= self.clip_y2
end
function ViewRect:inClipLocalXY(x,y)
return (x+self.x1) >= self.clip_x1 and (x+self.x1) <= self.clip_x2
and (y+self.y1) >= self.clip_y1 and (y+self.y1) <= self.clip_y2
end
function ViewRect:localXY(x,y)
return x-self.x1, y-self.y1
end
function ViewRect:globalXY(x,y)
return x+self.x1, y+self.y1
end
function ViewRect:viewport(x,y,w,h)
if type(x) == 'table' then
x,y,w,h = x.x1, x.y1, x.width, x.height
end
local x1,y1 = self.x1+x, self.y1+y
local x2,y2 = x1+w-1, y1+h-1
local vp = {
-- Logical viewport
x1 = x1, y1 = y1, x2 = x2, y2 = y2,
width = w, height = h,
-- Actual clipping rect
clip_x1 = math.max(self.clip_x1, x1),
clip_y1 = math.max(self.clip_y1, y1),
clip_x2 = math.min(self.clip_x2, x2),
clip_y2 = math.min(self.clip_y2, y2),
}
return mkinstance(ViewRect, vp)
end
----------------------------
-- Clipped painter object --
----------------------------
Painter = defclass(Painter, ViewRect)
function Painter:init(args)
self.x = self.x1
self.y = self.y1
self.cur_pen = to_pen(args.pen or COLOR_GREY)
self.cur_key_pen = to_pen(args.key_pen or COLOR_LIGHTGREEN)
end
function Painter.new(rect, pen)
return Painter{ rect = rect, pen = pen }
end
function Painter.new_view(view_rect, pen)
return Painter{ view_rect = view_rect, pen = pen }
end
function Painter.new_xy(x1,y1,x2,y2,pen)
return Painter{ rect = mkdims_xy(x1,y1,x2,y2), pen = pen }
end
function Painter.new_wh(x,y,w,h,pen)
return Painter{ rect = mkdims_wh(x,y,w,h), pen = pen }
end
function Painter:isValidPos()
return self:inClipGlobalXY(self.x, self.y)
end
function Painter:viewport(x,y,w,h)
local vp = ViewRect.viewport(x,y,w,h)
vp.cur_pen = self.cur_pen
vp.cur_key_pen = self.cur_key_pen
return mkinstance(Painter, vp):seek(0,0)
end
function Painter:cursor()
return self.x - self.x1, self.y - self.y1
end
function Painter:cursorX()
return self.x - self.x1
end
function Painter:cursorY()
return self.y - self.y1
end
function Painter:seek(x,y)
if x then self.x = self.x1 + x end
if y then self.y = self.y1 + y end
return self
end
function Painter:advance(dx,dy)
if dx then self.x = self.x + dx end
if dy then self.y = self.y + dy end
return self
end
function Painter:newline(dx)
self.y = self.y + 1
self.x = self.x1 + (dx or 0)
return self
end
function Painter:pen(pen,...)
self.cur_pen = to_pen(self.cur_pen, pen, ...)
return self
end
function Painter:color(fg,bold,bg)
self.cur_pen = to_pen(self.cur_pen, fg, bg, bold)
return self
end
function Painter:key_pen(pen,...)
self.cur_key_pen = to_pen(self.cur_key_pen, pen, ...)
return self
end
function Painter:clear()
dscreen.fillRect(CLEAR_PEN, self.clip_x1, self.clip_y1, self.clip_x2, self.clip_y2)
return self
end
function Painter:fill(x1,y1,x2,y2,pen,bg,bold)
if type(x1) == 'table' then
x1, y1, x2, y2, pen, bg, bold = x1.x1, x1.y1, x1.x2, x1.y2, y1, x2, y2
end
x1 = math.max(x1+self.x1,self.clip_x1)
y1 = math.max(y1+self.y1,self.clip_y1)
x2 = math.min(x2+self.x1,self.clip_x2)
y2 = math.min(y2+self.y1,self.clip_y2)
dscreen.fillRect(to_pen(self.cur_pen,pen,bg,bold),x1,y1,x2,y2)
return self
end
function Painter:char(char,pen,...)
if self:isValidPos() then
dscreen.paintTile(to_pen(self.cur_pen, pen, ...), self.x, self.y, char)
end
return self:advance(1, nil)
end
function Painter:tile(char,tile,pen,...)
if self:isValidPos() then
dscreen.paintTile(to_pen(self.cur_pen, pen, ...), self.x, self.y, char, tile)
end
return self:advance(1, nil)
end
function Painter:string(text,pen,...)
if self.y >= self.clip_y1 and self.y <= self.clip_y2 then
local dx = 0
if self.x < self.clip_x1 then
dx = self.clip_x1 - self.x
end
local len = #text
if self.x + len - 1 > self.clip_x2 then
len = self.clip_x2 - self.x + 1
end
if len > dx then
dscreen.paintString(
to_pen(self.cur_pen, pen, ...),
self.x+dx, self.y,
string.sub(text,dx+1,len)
)
end
end
return self:advance(#text, nil)
end
function Painter:key(code,pen,...)
return self:string(
getKeyDisplay(code),
to_pen(self.cur_key_pen, pen, ...)
)
end
--------------------------
-- Abstract view object --
--------------------------
View = defclass(View)
View.ATTRS {
active = true,
visible = true,
view_id = DEFAULT_NIL,
}
function View:init(args)
self.subviews = {}
end
function View:addviews(list)
if not list then return end
local sv = self.subviews
for _,obj in ipairs(list) do
table.insert(sv, obj)
local id = obj.view_id
if id and type(id) ~= 'number' and sv[id] == nil then
sv[id] = obj
end
end
for _,dir in ipairs(list) do
for id,obj in pairs(dir.subviews) do
if id and type(id) ~= 'number' and sv[id] == nil then
sv[id] = obj
end
end
end
end
function View:getWindowSize()
local rect = self.frame_body
return rect.width, rect.height
end
function View:getMousePos()
local rect = self.frame_body
local x,y = dscreen.getMousePos()
if rect and rect:inClipGlobalXY(x,y) then
return rect:localXY(x,y)
end
end
function View:computeFrame(parent_rect)
return mkdims_wh(0,0,parent_rect.width,parent_rect.height)
end
function View:updateSubviewLayout(frame_body)
for _,child in ipairs(self.subviews) do
child:updateLayout(frame_body)
end
end
function View:updateLayout(parent_rect)
if not parent_rect then
parent_rect = self.frame_parent_rect
else
self.frame_parent_rect = parent_rect
end
self:invoke_before('preUpdateLayout', parent_rect)
local frame_rect,body_rect = self:computeFrame(parent_rect)
self.frame_rect = frame_rect
self.frame_body = parent_rect:viewport(body_rect or frame_rect)
self:invoke_after('postComputeFrame', self.frame_body)
self:updateSubviewLayout(self.frame_body)
self:invoke_after('postUpdateLayout', self.frame_body)
end
function View:renderSubviews(dc)
for _,child in ipairs(self.subviews) do
if child.visible then
child:render(dc)
end
end
end
function View:render(dc)
self:onRenderFrame(dc, self.frame_rect)
local sub_dc = Painter{
view_rect = self.frame_body,
clip_view = dc
}
self:onRenderBody(sub_dc)
self:renderSubviews(sub_dc)
end
function View:onRenderFrame(dc,rect)
end
function View:onRenderBody(dc)
end
function View:inputToSubviews(keys)
local children = self.subviews
for i=#children,1,-1 do
local child = children[i]
if child.visible and child.active and child:onInput(keys) then
return true
end
end
return false
end
function View:onInput(keys)
return self:inputToSubviews(keys)
end
------------------------
-- Base screen object --
------------------------
Screen = defclass(Screen, View)
Screen.text_input_mode = false
function Screen:postinit()
self:onResize(dscreen.getWindowSize())
end
Screen.isDismissed = dscreen.isDismissed
function Screen:isShown()
return self._native ~= nil
end
function Screen:isActive()
return self:isShown() and not self:isDismissed()
end
function Screen:invalidate()
dscreen.invalidate()
end
function Screen:renderParent()
if self._native and self._native.parent then
self._native.parent:render()
else
dscreen.clear()
end
end
function Screen:sendInputToParent(...)
if self._native and self._native.parent then
simulateInput(self._native.parent, ...)
end
end
function Screen:show(parent)
if self._native then
error("This screen is already on display")
end
parent = parent or dfhack.gui.getCurViewscreen(true)
self:onAboutToShow(parent)
if not dscreen.show(self, parent.child) then
error('Could not show screen')
end
end
function Screen:onAboutToShow(parent)
end
function Screen:onShow()
self:onResize(dscreen.getWindowSize())
end
function Screen:dismiss()
if self._native then
dscreen.dismiss(self)
end
end
function Screen:onDismiss()
end
function Screen:onDestroy()
end
function Screen:onResize(w,h)
self:updateLayout(ViewRect{ rect = mkdims_wh(0,0,w,h) })
end
function Screen:onRender()
self:render(Painter.new())
end
------------------------
-- Framed screen object --
------------------------
-- Plain grey-colored frame.
GREY_FRAME = {
frame_pen = to_pen{ ch = ' ', fg = COLOR_BLACK, bg = COLOR_GREY },
title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_WHITE },
signature_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY },
}
-- The usual boundary used by the DF screens. Often has fancy pattern in tilesets.
BOUNDARY_FRAME = {
frame_pen = to_pen{ ch = 0xDB, fg = COLOR_DARKGREY, bg = COLOR_BLACK },
title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY },
signature_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_DARKGREY },
}
GREY_LINE_FRAME = {
frame_pen = to_pen{ ch = 206, fg = COLOR_GREY, bg = COLOR_BLACK },
h_frame_pen = to_pen{ ch = 205, fg = COLOR_GREY, bg = COLOR_BLACK },
v_frame_pen = to_pen{ ch = 186, fg = COLOR_GREY, bg = COLOR_BLACK },
lt_frame_pen = to_pen{ ch = 201, fg = COLOR_GREY, bg = COLOR_BLACK },
lb_frame_pen = to_pen{ ch = 200, fg = COLOR_GREY, bg = COLOR_BLACK },
rt_frame_pen = to_pen{ ch = 187, fg = COLOR_GREY, bg = COLOR_BLACK },
rb_frame_pen = to_pen{ ch = 188, fg = COLOR_GREY, bg = COLOR_BLACK },
title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY },
signature_pen = to_pen{ fg = COLOR_DARKGREY, bg = COLOR_BLACK },
}
function paint_frame(x1,y1,x2,y2,style,title)
local pen = style.frame_pen
dscreen.paintTile(style.lt_frame_pen or pen, x1, y1)
dscreen.paintTile(style.rt_frame_pen or pen, x2, y1)
dscreen.paintTile(style.lb_frame_pen or pen, x1, y2)
dscreen.paintTile(style.rb_frame_pen or pen, x2, y2)
dscreen.fillRect(style.t_frame_pen or style.h_frame_pen or pen,x1+1,y1,x2-1,y1)
dscreen.fillRect(style.b_frame_pen or style.h_frame_pen or pen,x1+1,y2,x2-1,y2)
dscreen.fillRect(style.l_frame_pen or style.v_frame_pen or pen,x1,y1+1,x1,y2-1)
dscreen.fillRect(style.r_frame_pen or style.v_frame_pen or pen,x2,y1+1,x2,y2-1)
dscreen.paintString(style.signature_pen or style.title_pen or pen,x2-7,y2,"DFHack")
if title then
local x = math.max(0,math.floor((x2-x1-3-#title)/2)) + x1
local tstr = ' '..title..' '
if #tstr > x2-x1-1 then
tstr = string.sub(tstr,1,x2-x1-1)
end
dscreen.paintString(style.title_pen or pen, x, y1, tstr)
end
end
FramedScreen = defclass(FramedScreen, Screen)
FramedScreen.ATTRS{
frame_style = BOUNDARY_FRAME,
frame_title = DEFAULT_NIL,
frame_width = DEFAULT_NIL,
frame_height = DEFAULT_NIL,
frame_inset = 0,
frame_background = CLEAR_PEN,
}
function FramedScreen:getWantedFrameSize()
return self.frame_width, self.frame_height
end
function FramedScreen:computeFrame(parent_rect)
local sw, sh = parent_rect.width, parent_rect.height
local fw, fh = self:getWantedFrameSize(parent_rect)
return compute_frame_body(sw, sh, { w = fw, h = fh }, self.frame_inset, 1)
end
function FramedScreen:onRenderFrame(dc, rect)
local x1,y1,x2,y2 = rect.x1, rect.y1, rect.x2, rect.y2
if rect.wgap <= 0 and rect.hgap <= 0 then
dc:clear()
else
self:renderParent()
dc:fill(rect, self.frame_background)
end
paint_frame(x1,y1,x2,y2,self.frame_style,self.frame_title)
end
return _ENV

@ -0,0 +1,221 @@
-- Some simple dialog screens
local _ENV = mkmodule('gui.dialogs')
local gui = require('gui')
local widgets = require('gui.widgets')
local utils = require('utils')
local dscreen = dfhack.screen
MessageBox = defclass(MessageBox, gui.FramedScreen)
MessageBox.focus_path = 'MessageBox'
MessageBox.ATTRS{
frame_style = gui.GREY_LINE_FRAME,
frame_inset = 1,
-- new attrs
on_accept = DEFAULT_NIL,
on_cancel = DEFAULT_NIL,
on_close = DEFAULT_NIL,
}
function MessageBox:init(info)
self:addviews{
widgets.Label{
view_id = 'label',
text = info.text,
text_pen = info.text_pen,
frame = { l = 0, t = 0 },
auto_height = true
}
}
end
function MessageBox:getWantedFrameSize()
local label = self.subviews.label
local width = math.max(self.frame_width or 0, 20, #(self.frame_title or '') + 4)
return math.max(width, label:getTextWidth()), label:getTextHeight()
end
function MessageBox:onRenderFrame(dc,rect)
MessageBox.super.onRenderFrame(self,dc,rect)
if self.on_accept then
dc:seek(rect.x1+2,rect.y2):key('LEAVESCREEN'):string('/'):key('MENU_CONFIRM')
end
end
function MessageBox:onDestroy()
if self.on_close then
self.on_close()
end
end
function MessageBox:onInput(keys)
if keys.MENU_CONFIRM then
self:dismiss()
if self.on_accept then
self.on_accept()
end
elseif keys.LEAVESCREEN or (keys.SELECT and not self.on_accept) then
self:dismiss()
if self.on_cancel then
self.on_cancel()
end
else
self:inputToSubviews(keys)
end
end
function showMessage(title, text, tcolor, on_close)
MessageBox{
frame_title = title,
text = text,
text_pen = tcolor,
on_close = on_close
}:show()
end
function showYesNoPrompt(title, text, tcolor, on_accept, on_cancel)
MessageBox{
frame_title = title,
text = text,
text_pen = tcolor,
on_accept = on_accept,
on_cancel = on_cancel,
}:show()
end
InputBox = defclass(InputBox, MessageBox)
InputBox.focus_path = 'InputBox'
InputBox.ATTRS{
on_input = DEFAULT_NIL,
}
function InputBox:preinit(info)
info.on_accept = nil
end
function InputBox:init(info)
self:addviews{
widgets.EditField{
view_id = 'edit',
text = info.input,
text_pen = info.input_pen,
frame = { l = 0, r = 0, h = 1 },
}
}
end
function InputBox:getWantedFrameSize()
local mw, mh = InputBox.super.getWantedFrameSize(self)
self.subviews.edit.frame.t = mh+1
return mw, mh+2
end
function InputBox:onInput(keys)
if keys.SELECT then
self:dismiss()
if self.on_input then
self.on_input(self.subviews.edit.text)
end
elseif keys.LEAVESCREEN then
self:dismiss()
if self.on_cancel then
self.on_cancel()
end
else
self:inputToSubviews(keys)
end
end
function showInputPrompt(title, text, tcolor, input, on_input, on_cancel, min_width)
InputBox{
frame_title = title,
text = text,
text_pen = tcolor,
input = input,
on_input = on_input,
on_cancel = on_cancel,
frame_width = min_width,
}:show()
end
ListBox = defclass(ListBox, MessageBox)
ListBox.focus_path = 'ListBox'
ListBox.ATTRS{
with_filter = false,
cursor_pen = DEFAULT_NIL,
select_pen = DEFAULT_NIL,
on_select = DEFAULT_NIL
}
function ListBox:preinit(info)
info.on_accept = nil
end
function ListBox:init(info)
local spen = dfhack.pen.parse(COLOR_CYAN, self.select_pen, nil, false)
local cpen = dfhack.pen.parse(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true)
local list_widget = widgets.List
if self.with_filter then
list_widget = widgets.FilteredList
end
self:addviews{
list_widget{
view_id = 'list',
selected = info.selected,
choices = info.choices,
icon_width = info.icon_width,
text_pen = spen,
cursor_pen = cpen,
on_submit = function(sel,obj)
self:dismiss()
if self.on_select then self.on_select(sel, obj) end
local cb = obj.on_select or obj[2]
if cb then cb(obj, sel) end
end,
frame = { l = 0, r = 0 },
}
}
end
function ListBox:getWantedFrameSize()
local mw, mh = InputBox.super.getWantedFrameSize(self)
local list = self.subviews.list
list.frame.t = mh+1
return math.max(mw, list:getContentWidth()), mh+1+math.min(20,list:getContentHeight())
end
function ListBox:onInput(keys)
if keys.LEAVESCREEN then
self:dismiss()
if self.on_cancel then
self.on_cancel()
end
else
self:inputToSubviews(keys)
end
end
function showListPrompt(title, text, tcolor, choices, on_select, on_cancel, min_width, filter)
ListBox{
frame_title = title,
text = text,
text_pen = tcolor,
choices = choices,
on_select = on_select,
on_cancel = on_cancel,
frame_width = min_width,
with_filter = filter,
}:show()
end
return _ENV

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