summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt5
-rw-r--r--Compile.html2
-rw-r--r--LUA_API.rst123
-rw-r--r--Lua API.html123
-rw-r--r--README.rst10
-rw-r--r--Readme.html7
-rw-r--r--depends/lua/include/lfunc.h5
-rw-r--r--depends/lua/include/lgc.h10
-rw-r--r--depends/lua/include/llimits.h17
-rw-r--r--depends/lua/include/lobject.h75
-rw-r--r--depends/lua/include/lparser.h6
-rw-r--r--depends/lua/include/lstate.h20
-rw-r--r--depends/lua/include/lstring.h17
-rw-r--r--depends/lua/include/lua.h8
-rw-r--r--depends/lua/include/luaconf.h103
-rw-r--r--depends/lua/include/lundump.h4
-rw-r--r--depends/lua/src/lapi.c20
-rw-r--r--depends/lua/src/lauxlib.c17
-rw-r--r--depends/lua/src/lbaselib.c3
-rw-r--r--depends/lua/src/lcorolib.c5
-rw-r--r--depends/lua/src/ldblib.c21
-rw-r--r--depends/lua/src/ldebug.c13
-rw-r--r--depends/lua/src/ldo.c35
-rw-r--r--depends/lua/src/ldump.c7
-rw-r--r--depends/lua/src/lfunc.c20
-rw-r--r--depends/lua/src/lgc.c456
-rw-r--r--depends/lua/src/llex.c27
-rw-r--r--depends/lua/src/lmathlib.c12
-rw-r--r--depends/lua/src/lmem.c18
-rw-r--r--depends/lua/src/loadlib.c97
-rw-r--r--depends/lua/src/lopcodes.c3
-rw-r--r--depends/lua/src/loslib.c17
-rw-r--r--depends/lua/src/lparser.c153
-rw-r--r--depends/lua/src/lstate.c42
-rw-r--r--depends/lua/src/lstring.c115
-rw-r--r--depends/lua/src/lstrlib.c35
-rw-r--r--depends/lua/src/ltable.c27
-rw-r--r--depends/lua/src/lua.c31
-rw-r--r--depends/lua/src/lundump.c38
-rw-r--r--depends/lua/src/lvm.c50
-rw-r--r--depends/lua/src/lzio.c4
-rw-r--r--dfhack.init-example3
-rw-r--r--library/CMakeLists.txt5
-rw-r--r--library/Console-linux.cpp6
-rw-r--r--library/Core.cpp119
-rw-r--r--library/Hooks-darwin.cpp6
-rw-r--r--library/Hooks-linux.cpp2
-rw-r--r--library/LuaApi.cpp233
-rw-r--r--library/LuaTools.cpp6
-rw-r--r--library/LuaTypes.cpp26
-rw-r--r--library/LuaWrapper.cpp89
-rw-r--r--library/Process-darwin.cpp113
-rw-r--r--library/RemoteClient.cpp21
-rw-r--r--library/RemoteServer.cpp2
-rw-r--r--library/RemoteTools.cpp16
-rw-r--r--library/VersionInfoFactory.cpp8
-rw-r--r--library/include/Console.h12
-rw-r--r--library/include/Core.h8
-rw-r--r--library/include/DataFuncs.h11
-rw-r--r--library/include/Hooks.h6
-rw-r--r--library/include/LuaWrapper.h1
-rw-r--r--library/include/PluginManager.h6
-rw-r--r--library/include/SDL_events.h210
-rw-r--r--library/include/SDL_keyboard.h61
-rw-r--r--library/include/SDL_keysym.h329
-rw-r--r--library/include/modules/World.h31
-rw-r--r--library/lua/dfhack.lua13
-rw-r--r--library/lua/memscan.lua455
-rw-r--r--library/lua/utils.lua46
-rw-r--r--library/modules/Gui.cpp45
-rw-r--r--library/modules/Materials.cpp4
-rw-r--r--library/modules/Units.cpp71
-rw-r--r--library/modules/World.cpp10
-rwxr-xr-xpackage/darwin/dfhack2
-rw-r--r--plugins/burrows.cpp4
-rw-r--r--plugins/devel/counters.cpp2
-rw-r--r--plugins/dwarfexport/dwarfexport.cpp4
-rw-r--r--plugins/forceequip.cpp38
-rw-r--r--plugins/mode.cpp48
-rw-r--r--plugins/reveal.cpp10
-rw-r--r--plugins/ruby/CMakeLists.txt62
-rw-r--r--plugins/ruby/README68
-rwxr-xr-xplugins/ruby/codegen.pl766
-rw-r--r--plugins/ruby/plugins/building.rb1
-rw-r--r--plugins/ruby/ruby-memstruct.rb747
-rw-r--r--plugins/ruby/ruby.cpp141
-rw-r--r--plugins/ruby/ruby.rb758
-rwxr-xr-xplugins/seedwatch.cpp4
-rw-r--r--plugins/showmood.cpp36
-rw-r--r--plugins/workflow.cpp3
-rw-r--r--scripts/devel/find-offsets.lua601
-rw-r--r--scripts/devel/nuke-items.lua16
-rw-r--r--scripts/fix/dead-units.lua29
-rw-r--r--scripts/fix/fat-dwarves.lua24
-rw-r--r--scripts/fix/stable-temp.lua49
-rw-r--r--scripts/putontable.lua2
96 files changed, 5205 insertions, 1985 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 62be3e8c..f77a6c3b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -60,7 +60,7 @@ endif()
# set up versioning.
set(DF_VERSION_MAJOR "0")
set(DF_VERSION_MINOR "34")
-set(DF_VERSION_PATCH "10")
+set(DF_VERSION_PATCH "11")
set(DF_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}")
SET(DFHACK_RELEASE "r1" CACHE STRING "Current release revision.")
@@ -139,9 +139,6 @@ include_directories(depends/tinyxml)
include_directories(depends/tthread)
include_directories(${ZLIB_INCLUDE_DIRS})
include_directories(depends/clsocket/src)
-if(APPLE)
- include_directories(${CMAKE_INSTALL_PREFIX}/libs/SDL.framework/Headers)
-endif()
add_subdirectory(depends)
diff --git a/Compile.html b/Compile.html
index 53fc959b..b0f9e9c6 100644
--- a/Compile.html
+++ b/Compile.html
@@ -3,7 +3,7 @@
<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.8.1: http://docutils.sourceforge.net/" />
+<meta name="generator" content="Docutils 0.9: http://docutils.sourceforge.net/" />
<title>Building DFHACK</title>
<style type="text/css">
diff --git a/LUA_API.rst b/LUA_API.rst
index b0f1a9f3..1723711d 100644
--- a/LUA_API.rst
+++ b/LUA_API.rst
@@ -121,6 +121,12 @@ or as a result of calling the ``_field()`` method.
They behave as structs with one field ``value`` of the right type.
+To make working with numeric buffers easier, they also allow
+numeric indices. Note that other than excluding negative values
+no bound checking is performed, since buffer length is not available.
+Index 0 is equivalent to the ``value`` field.
+
+
Struct references
-----------------
@@ -219,12 +225,21 @@ Bitfield references
-------------------
Bitfields behave like special fixed-size containers.
-The ``_enum`` property points to the bitfield type.
+Consider them to be something in between structs and
+fixed-size vectors.
+The ``_enum`` property points to the bitfield type.
Numerical indices correspond to the shift value,
and if a subfield occupies multiple bits, the
``ipairs`` order would have a gap.
+Since currently there is no API to allocate a bitfield
+object fully in GC-managed lua heap, consider using the
+lua table assignment feature outlined below in order to
+pass bitfield values to dfhack API functions that need
+them, e.g. ``matinfo:matches{metal=true}``.
+
+
Named types
===========
@@ -308,6 +323,24 @@ The ``df`` table itself contains the following functions and values:
Equivalent to the method, but also allows a reference as proxy for its type.
+* ``df.new(ptype[,count])``
+
+ Allocate a new instance, or an array of built-in types.
+ The ``ptype`` argument is a string from the following list:
+ ``string``, ``int8_t``, ``uint8_t``, ``int16_t``, ``uint16_t``,
+ ``int32_t``, ``uint32_t``, ``int64_t``, ``uint64_t``, ``bool``,
+ ``float``, ``double``. All of these except ``string`` can be
+ used with the count argument to allocate an array.
+
+* ``df.reinterpret_cast(type,ptr)``
+
+ Converts ptr to a ref of specified type. The type may be anything
+ acceptable to ``df.is_instance``. Ptr may be *nil*, a ref,
+ a lightuserdata, or a number.
+
+ Returns *nil* if NULL, or a ref.
+
+
Recursive table assignment
==========================
@@ -458,6 +491,13 @@ Currently it defines the following features:
Compares to coroutine.resume like dfhack.safecall vs pcall.
+* ``dfhack.run_script(name[,args...])``
+
+ Run a lua script in hack/scripts/, as if it was started from dfhack command-line.
+ The ``name`` argument should be the name stem, as would be used on the command line.
+ Note that the script is re-read from the file every time it is called, and errors
+ are propagated to the caller.
+
* ``dfhack.with_suspend(f[,args...])``
Calls ``f`` with arguments after grabbing the DF core suspend lock.
@@ -598,6 +638,22 @@ One notable difference is that these explicit wrappers allow argument count
adjustment according to the usual lua rules, so trailing false/nil arguments
can be omitted.
+* ``dfhack.getOSType()``
+
+ Returns the OS type string from ``symbols.xml``.
+
+* ``dfhack.getDFVersion()``
+
+ Returns the DF version string from ``symbols.xml``.
+
+* ``dfhack.getDFPath()``
+
+ Returns the DF directory path.
+
+* ``dfhack.getHackPath()``
+
+ Returns the dfhack directory path, i.e. ``".../df/hack/"``.
+
* ``dfhack.isWorldLoaded()``
Checks if the world is loaded.
@@ -726,7 +782,7 @@ Units module
* ``dfhack.units.isDead(unit)``
- The unit is completely dead and passive.
+ The unit is completely dead and passive, or a ghost.
* ``dfhack.units.isAlive(unit)``
@@ -734,7 +790,16 @@ Units module
* ``dfhack.units.isSane(unit)``
- The unit is capable of rational action, i.e. not dead, insane or zombie.
+ The unit is capable of rational action, i.e. not dead, insane, zombie, or active werewolf.
+
+* ``dfhack.units.isDwarf(unit)``
+
+ The unit is of the correct race of the fortress.
+
+* ``dfhack.units.isCitizen(unit)``
+
+ The unit is an alive sane citizen of the fortress; wraps the
+ same checks the game uses to decide game-over by extinction.
* ``dfhack.units.getAge(unit[,true_age])``
@@ -1084,6 +1149,58 @@ Constructions module
Returns *true, was_only_planned* if removed; or *false* if none found.
+Internal API
+------------
+
+These functions are intended for the use by dfhack developers,
+and are only documented here for completeness:
+
+* ``dfhack.internal.scripts``
+
+ The table used by ``dfhack.run_script()`` to give every script its own
+ global environment, persistent between calls to the script.
+
+* ``dfhack.internal.getAddress(name)``
+
+ Returns the global address ``name``, or *nil*.
+
+* ``dfhack.internal.setAddress(name, value)``
+
+ Sets the global address ``name``. Returns the value of ``getAddress`` before the change.
+
+* ``dfhack.internal.getVTable(name)``
+
+ Returns the pre-extracted vtable address ``name``, or *nil*.
+
+* ``dfhack.internal.getBase()``
+
+ Returns the base address of the process.
+
+* ``dfhack.internal.getMemRanges()``
+
+ Returns a sequence of tables describing virtual memory ranges of the process.
+
+* ``dfhack.internal.memmove(dest,src,count)``
+
+ Wraps the standard memmove function. Accepts both numbers and refs as pointers.
+
+* ``dfhack.internal.memcmp(ptr1,ptr2,count)``
+
+ Wraps the standard memcmp function.
+
+* ``dfhack.internal.memscan(haystack,count,step,needle,nsize)``
+
+ Searches for ``needle`` of ``nsize`` bytes in ``haystack``,
+ using ``count`` steps of ``step`` bytes.
+ Returns: *step_idx, sum_idx, found_ptr*, or *nil* if not found.
+
+* ``dfhack.internal.diffscan(old_data, new_data, start_idx, end_idx, eltsize[, oldval, newval, delta])``
+
+ Searches for differences between buffers at ptr1 and ptr2, as integers of size eltsize.
+ The oldval, newval or delta arguments may be used to specify additional constraints.
+ Returns: *found_index*, or *nil* if end reached.
+
+
Core interpreter context
========================
diff --git a/Lua API.html b/Lua API.html
index 4b763af2..f1bdd17d 100644
--- a/Lua API.html
+++ b/Lua API.html
@@ -3,7 +3,7 @@
<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.8.1: http://docutils.sourceforge.net/" />
+<meta name="generator" content="Docutils 0.9: http://docutils.sourceforge.net/" />
<title>DFHack Lua API</title>
<style type="text/css">
@@ -345,17 +345,18 @@ ul.auto-toc {
<li><a class="reference internal" href="#burrows-module" id="id19">Burrows module</a></li>
<li><a class="reference internal" href="#buildings-module" id="id20">Buildings module</a></li>
<li><a class="reference internal" href="#constructions-module" id="id21">Constructions module</a></li>
+<li><a class="reference internal" href="#internal-api" id="id22">Internal API</a></li>
</ul>
</li>
-<li><a class="reference internal" href="#core-interpreter-context" id="id22">Core interpreter context</a><ul>
-<li><a class="reference internal" href="#event-type" id="id23">Event type</a></li>
+<li><a class="reference internal" href="#core-interpreter-context" id="id23">Core interpreter context</a><ul>
+<li><a class="reference internal" href="#event-type" id="id24">Event type</a></li>
</ul>
</li>
</ul>
</li>
-<li><a class="reference internal" href="#plugins" id="id24">Plugins</a><ul>
-<li><a class="reference internal" href="#burrows" id="id25">burrows</a></li>
-<li><a class="reference internal" href="#sort" id="id26">sort</a></li>
+<li><a class="reference internal" href="#plugins" id="id25">Plugins</a><ul>
+<li><a class="reference internal" href="#burrows" id="id26">burrows</a></li>
+<li><a class="reference internal" href="#sort" id="id27">sort</a></li>
</ul>
</li>
</ul>
@@ -452,6 +453,10 @@ that don't fit any of the other reference types. Such
references can only appear as a value of a pointer field,
or as a result of calling the <tt class="docutils literal">_field()</tt> method.</p>
<p>They behave as structs with one field <tt class="docutils literal">value</tt> of the right type.</p>
+<p>To make working with numeric buffers easier, they also allow
+numeric indices. Note that other than excluding negative values
+no bound checking is performed, since buffer length is not available.
+Index 0 is equivalent to the <tt class="docutils literal">value</tt> field.</p>
</div>
<div class="section" id="struct-references">
<h3><a class="toc-backref" href="#id4">Struct references</a></h3>
@@ -534,10 +539,17 @@ use <tt class="docutils literal">#ref</tt>, or just <tt class="docutils literal"
<div class="section" id="bitfield-references">
<h3><a class="toc-backref" href="#id6">Bitfield references</a></h3>
<p>Bitfields behave like special fixed-size containers.
-The <tt class="docutils literal">_enum</tt> property points to the bitfield type.</p>
-<p>Numerical indices correspond to the shift value,
+Consider them to be something in between structs and
+fixed-size vectors.</p>
+<p>The <tt class="docutils literal">_enum</tt> property points to the bitfield type.
+Numerical indices correspond to the shift value,
and if a subfield occupies multiple bits, the
<tt class="docutils literal">ipairs</tt> order would have a gap.</p>
+<p>Since currently there is no API to allocate a bitfield
+object fully in GC-managed lua heap, consider using the
+lua table assignment feature outlined below in order to
+pass bitfield values to dfhack API functions that need
+them, e.g. <tt class="docutils literal">matinfo:matches{metal=true}</tt>.</p>
</div>
</div>
<div class="section" id="named-types">
@@ -607,6 +619,20 @@ lightuserdata (step is mandatory then).</p>
<li><p class="first"><tt class="docutils literal">df.is_instance(type,obj)</tt></p>
<p>Equivalent to the method, but also allows a reference as proxy for its type.</p>
</li>
+<li><p class="first"><tt class="docutils literal"><span class="pre">df.new(ptype[,count])</span></tt></p>
+<p>Allocate a new instance, or an array of built-in types.
+The <tt class="docutils literal">ptype</tt> argument is a string from the following list:
+<tt class="docutils literal">string</tt>, <tt class="docutils literal">int8_t</tt>, <tt class="docutils literal">uint8_t</tt>, <tt class="docutils literal">int16_t</tt>, <tt class="docutils literal">uint16_t</tt>,
+<tt class="docutils literal">int32_t</tt>, <tt class="docutils literal">uint32_t</tt>, <tt class="docutils literal">int64_t</tt>, <tt class="docutils literal">uint64_t</tt>, <tt class="docutils literal">bool</tt>,
+<tt class="docutils literal">float</tt>, <tt class="docutils literal">double</tt>. All of these except <tt class="docutils literal">string</tt> can be
+used with the count argument to allocate an array.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">df.reinterpret_cast(type,ptr)</tt></p>
+<p>Converts ptr to a ref of specified type. The type may be anything
+acceptable to <tt class="docutils literal">df.is_instance</tt>. Ptr may be <em>nil</em>, a ref,
+a lightuserdata, or a number.</p>
+<p>Returns <em>nil</em> if NULL, or a ref.</p>
+</li>
</ul>
</div>
<div class="section" id="recursive-table-assignment">
@@ -740,6 +766,12 @@ returning. Intended as a convenience function.</p>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.saferesume(coroutine[,args...])</span></tt></p>
<p>Compares to coroutine.resume like dfhack.safecall vs pcall.</p>
</li>
+<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.run_script(name[,args...])</span></tt></p>
+<p>Run a lua script in hack/scripts/, as if it was started from dfhack command-line.
+The <tt class="docutils literal">name</tt> argument should be the name stem, as would be used on the command line.
+Note that the script is re-read from the file every time it is called, and errors
+are propagated to the caller.</p>
+</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.with_suspend(f[,args...])</span></tt></p>
<p>Calls <tt class="docutils literal">f</tt> with arguments after grabbing the DF core suspend lock.
Suspending is necessary for accessing a consistent state of DF memory.</p>
@@ -855,6 +887,18 @@ One notable difference is that these explicit wrappers allow argument count
adjustment according to the usual lua rules, so trailing false/nil arguments
can be omitted.</p>
<ul>
+<li><p class="first"><tt class="docutils literal">dfhack.getOSType()</tt></p>
+<p>Returns the OS type string from <tt class="docutils literal">symbols.xml</tt>.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.getDFVersion()</tt></p>
+<p>Returns the DF version string from <tt class="docutils literal">symbols.xml</tt>.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.getDFPath()</tt></p>
+<p>Returns the DF directory path.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.getHackPath()</tt></p>
+<p>Returns the dfhack directory path, i.e. <tt class="docutils literal"><span class="pre">&quot;.../df/hack/&quot;</span></tt>.</p>
+</li>
<li><p class="first"><tt class="docutils literal">dfhack.isWorldLoaded()</tt></p>
<p>Checks if the world is loaded.</p>
</li>
@@ -960,13 +1004,20 @@ a lua list containing them.</p>
<p>Returns the nemesis record of the unit if it has one, or <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.isDead(unit)</tt></p>
-<p>The unit is completely dead and passive.</p>
+<p>The unit is completely dead and passive, or a ghost.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.isAlive(unit)</tt></p>
<p>The unit isn't dead or undead.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.isSane(unit)</tt></p>
-<p>The unit is capable of rational action, i.e. not dead, insane or zombie.</p>
+<p>The unit is capable of rational action, i.e. not dead, insane, zombie, or active werewolf.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.units.isDwarf(unit)</tt></p>
+<p>The unit is of the correct race of the fortress.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.units.isCitizen(unit)</tt></p>
+<p>The unit is an alive sane citizen of the fortress; wraps the
+same checks the game uses to decide game-over by extinction.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.units.getAge(unit[,true_age])</span></tt></p>
<p>Returns the age of the unit in years as a floating-point value.
@@ -1260,9 +1311,51 @@ Returns <em>true, was_only_planned</em> if removed; or <em>false</em> if none fo
</li>
</ul>
</div>
+<div class="section" id="internal-api">
+<h3><a class="toc-backref" href="#id22">Internal API</a></h3>
+<p>These functions are intended for the use by dfhack developers,
+and are only documented here for completeness:</p>
+<ul>
+<li><p class="first"><tt class="docutils literal">dfhack.internal.scripts</tt></p>
+<p>The table used by <tt class="docutils literal">dfhack.run_script()</tt> to give every script its own
+global environment, persistent between calls to the script.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.internal.getAddress(name)</tt></p>
+<p>Returns the global address <tt class="docutils literal">name</tt>, or <em>nil</em>.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.internal.setAddress(name, value)</tt></p>
+<p>Sets the global address <tt class="docutils literal">name</tt>. Returns the value of <tt class="docutils literal">getAddress</tt> before the change.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.internal.getVTable(name)</tt></p>
+<p>Returns the pre-extracted vtable address <tt class="docutils literal">name</tt>, or <em>nil</em>.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.internal.getBase()</tt></p>
+<p>Returns the base address of the process.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.internal.getMemRanges()</tt></p>
+<p>Returns a sequence of tables describing virtual memory ranges of the process.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.internal.memmove(dest,src,count)</tt></p>
+<p>Wraps the standard memmove function. Accepts both numbers and refs as pointers.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.internal.memcmp(ptr1,ptr2,count)</tt></p>
+<p>Wraps the standard memcmp function.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.internal.memscan(haystack,count,step,needle,nsize)</tt></p>
+<p>Searches for <tt class="docutils literal">needle</tt> of <tt class="docutils literal">nsize</tt> bytes in <tt class="docutils literal">haystack</tt>,
+using <tt class="docutils literal">count</tt> steps of <tt class="docutils literal">step</tt> bytes.
+Returns: <em>step_idx, sum_idx, found_ptr</em>, or <em>nil</em> if not found.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.internal.diffscan(old_data, new_data, start_idx, end_idx, eltsize[, oldval, newval, delta])</tt></p>
+<p>Searches for differences between buffers at ptr1 and ptr2, as integers of size eltsize.
+The oldval, newval or delta arguments may be used to specify additional constraints.
+Returns: <em>found_index</em>, or <em>nil</em> if end reached.</p>
+</li>
+</ul>
+</div>
</div>
<div class="section" id="core-interpreter-context">
-<h2><a class="toc-backref" href="#id22">Core interpreter context</a></h2>
+<h2><a class="toc-backref" href="#id23">Core interpreter context</a></h2>
<p>While plugins can create any number of interpreter instances,
there is one special context managed by dfhack core. It is the
only context that can receive events from DF and plugins.</p>
@@ -1293,7 +1386,7 @@ Using <tt class="docutils literal">timeout_active(id,nil)</tt> cancels the timer
</li>
</ul>
<div class="section" id="event-type">
-<h3><a class="toc-backref" href="#id23">Event type</a></h3>
+<h3><a class="toc-backref" href="#id24">Event type</a></h3>
<p>An event is just a lua table with a predefined metatable that
contains a __call metamethod. When it is invoked, it loops
through the table with next and calls all contained values.
@@ -1319,14 +1412,14 @@ order using <tt class="docutils literal">dfhack.safecall</tt>.</p>
</div>
</div>
<div class="section" id="plugins">
-<h1><a class="toc-backref" href="#id24">Plugins</a></h1>
+<h1><a class="toc-backref" href="#id25">Plugins</a></h1>
<p>DFHack plugins may export native functions and events
to lua contexts. They are automatically imported by
<tt class="docutils literal"><span class="pre">mkmodule('plugins.&lt;name&gt;')</span></tt>; this means that a lua
module file is still necessary for <tt class="docutils literal">require</tt> to read.</p>
<p>The following plugins have lua support.</p>
<div class="section" id="burrows">
-<h2><a class="toc-backref" href="#id25">burrows</a></h2>
+<h2><a class="toc-backref" href="#id26">burrows</a></h2>
<p>Implements extended burrow manipulations.</p>
<p>Events:</p>
<ul>
@@ -1364,7 +1457,7 @@ set is the same as used by the command line.</p>
<p>The lua module file also re-exports functions from <tt class="docutils literal">dfhack.burrows</tt>.</p>
</div>
<div class="section" id="sort">
-<h2><a class="toc-backref" href="#id26">sort</a></h2>
+<h2><a class="toc-backref" href="#id27">sort</a></h2>
<p>Does not export any native functions as of now. Instead, it
calls lua code to perform the actual ordering of list items.</p>
</div>
diff --git a/README.rst b/README.rst
index f1e95a74..d57cf2a7 100644
--- a/README.rst
+++ b/README.rst
@@ -27,7 +27,7 @@ Compatibility
DFHack works on Windows XP, Vista, 7 or any modern Linux distribution.
OSX is not supported due to lack of developers with a Mac.
-Currently, only versions 0.34.06 and 0.34.07 are supported. If you need DFHack
+Currently, versions 0.34.08 - 0.34.11 are supported. If you need DFHack
for older versions, look for older releases.
On Windows, you have to use the SDL version of DF.
@@ -633,8 +633,14 @@ produce undesirable results. There are a few good ones though.
You are in fort game mode, managing your fortress and paused.
You switch to the arena game mode, *assume control of a creature* and then
- switch to adventure game mode(1).
+ switch to adventure game mode(1).
You just lost a fortress and gained an adventurer.
+ You could also do this.
+ You are in fort game mode, managing your fortress and paused at the esc menu.
+ You switch to the adventure game mode, then use Dfusion to *assume control of a creature* and then
+ save or retire.
+ You just created a returnable mountain home and gained an adventurer.
+
I take no responsibility of anything that happens as a result of using this tool
diff --git a/Readme.html b/Readme.html
index 49ddb198..cd579d37 100644
--- a/Readme.html
+++ b/Readme.html
@@ -3,7 +3,7 @@
<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.8.1: http://docutils.sourceforge.net/" />
+<meta name="generator" content="Docutils 0.9: http://docutils.sourceforge.net/" />
<title></title>
<style type="text/css">
@@ -541,7 +541,7 @@ binaries at <a class="reference external" href="http://github.com/peterix/dfhac
<h1><a class="toc-backref" href="#id36">Compatibility</a></h1>
<p>DFHack works on Windows XP, Vista, 7 or any modern Linux distribution.
OSX is not supported due to lack of developers with a Mac.</p>
-<p>Currently, only versions 0.34.06 and 0.34.07 are supported. If you need DFHack
+<p>Currently, versions 0.34.08 - 0.34.11 are supported. If you need DFHack
for older versions, look for older releases.</p>
<p>On Windows, you have to use the SDL version of DF.</p>
<p>It is possible to use the Windows DFHack under wine/OSX.</p>
@@ -1569,12 +1569,13 @@ paint hidden 1
paint hidden 0
</pre>
<p>This will hide previously revealed tiles (or show hidden with the 0 option).</p>
-<p>Any paint or filter option can be disabled entirely by using the ANY keyword:</p>
+<p>Any paint or filter option (or the entire paint or filter) can be disabled entirely by using the ANY keyword:</p>
<pre class="literal-block">
paint hidden ANY
paint shape ANY
filter material any
filter shape any
+filter any
</pre>
<dl class="docutils">
<dt>You can use several different brushes for painting tiles:</dt>
diff --git a/depends/lua/include/lfunc.h b/depends/lua/include/lfunc.h
index da189231..e236a717 100644
--- a/depends/lua/include/lfunc.h
+++ b/depends/lua/include/lfunc.h
@@ -1,5 +1,5 @@
/*
-** $Id: lfunc.h,v 2.6 2010/06/04 13:06:15 roberto Exp $
+** $Id: lfunc.h,v 2.8 2012/05/08 13:53:33 roberto Exp $
** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h
*/
@@ -20,12 +20,11 @@
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems);
-LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, Proto *p);
+LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems);
LUAI_FUNC UpVal *luaF_newupval (lua_State *L);
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
LUAI_FUNC void luaF_close (lua_State *L, StkId level);
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
-LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c);
LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv);
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
int pc);
diff --git a/depends/lua/include/lgc.h b/depends/lua/include/lgc.h
index aa5dfce2..bdd5cce6 100644
--- a/depends/lua/include/lgc.h
+++ b/depends/lua/include/lgc.h
@@ -1,5 +1,5 @@
/*
-** $Id: lgc.h,v 2.52 2011/10/03 17:54:25 roberto Exp $
+** $Id: lgc.h,v 2.56 2012/05/23 15:43:14 roberto Exp $
** Garbage Collector
** See Copyright Notice in lua.h
*/
@@ -25,6 +25,14 @@
*/
+
+/* how much to allocate before next GC step */
+#if !defined(GCSTEPSIZE)
+/* ~100 small strings */
+#define GCSTEPSIZE (cast_int(100 * sizeof(TString)))
+#endif
+
+
/*
** Possible states of the Garbage Collector
*/
diff --git a/depends/lua/include/llimits.h b/depends/lua/include/llimits.h
index 48dc81f7..fc9de1a1 100644
--- a/depends/lua/include/llimits.h
+++ b/depends/lua/include/llimits.h
@@ -1,5 +1,5 @@
/*
-** $Id: llimits.h,v 1.95 2011/12/06 16:58:36 roberto Exp $
+** $Id: llimits.h,v 1.99 2012/05/28 20:32:28 roberto Exp $
** Limits, basic types, and some other `installation-dependent' definitions
** See Copyright Notice in lua.h
*/
@@ -31,6 +31,8 @@ typedef unsigned char lu_byte;
#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2)
+#define MAX_LMEM ((l_mem) ((MAX_LUMEM >> 1) - 2))
+
#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */
@@ -209,31 +211,36 @@ typedef lu_int32 Instruction;
#elif defined(LUA_IEEE754TRICK) /* }{ */
/* the next trick should work on any machine using IEEE754 with
- a 32-bit integer type */
+ a 32-bit int type */
union luai_Cast { double l_d; LUA_INT32 l_p[2]; };
#if !defined(LUA_IEEEENDIAN) /* { */
#define LUAI_EXTRAIEEE \
static const union luai_Cast ieeeendian = {-(33.0 + 6755399441055744.0)};
-#define LUA_IEEEENDIAN (ieeeendian.l_p[1] == 33)
+#define LUA_IEEEENDIANLOC (ieeeendian.l_p[1] == 33)
#else
+#define LUA_IEEEENDIANLOC LUA_IEEEENDIAN
#define LUAI_EXTRAIEEE /* empty */
#endif /* } */
#define lua_number2int32(i,n,t) \
{ LUAI_EXTRAIEEE \
volatile union luai_Cast u; u.l_d = (n) + 6755399441055744.0; \
- (i) = (t)u.l_p[LUA_IEEEENDIAN]; }
+ (i) = (t)u.l_p[LUA_IEEEENDIANLOC]; }
#define luai_hashnum(i,n) \
{ volatile union luai_Cast u; u.l_d = (n) + 1.0; /* avoid -0 */ \
(i) = u.l_p[0]; (i) += u.l_p[1]; } /* add double bits for his hash */
#define lua_number2int(i,n) lua_number2int32(i, n, int)
-#define lua_number2integer(i,n) lua_number2int32(i, n, lua_Integer)
#define lua_number2unsigned(i,n) lua_number2int32(i, n, lua_Unsigned)
+/* the trick can be expanded to lua_Integer when it is a 32-bit value */
+#if defined(LUA_IEEELL)
+#define lua_number2integer(i,n) lua_number2int32(i, n, lua_Integer)
+#endif
+
#endif /* } */
diff --git a/depends/lua/include/lobject.h b/depends/lua/include/lobject.h
index 06246bfa..ca75a028 100644
--- a/depends/lua/include/lobject.h
+++ b/depends/lua/include/lobject.h
@@ -1,5 +1,5 @@
/*
-** $Id: lobject.h,v 2.64 2011/10/31 17:48:22 roberto Exp $
+** $Id: lobject.h,v 2.70 2012/05/11 14:10:50 roberto Exp $
** Type definitions for Lua objects
** See Copyright Notice in lua.h
*/
@@ -36,6 +36,9 @@
** bit 6: whether value is collectable
*/
+#define VARBITS (3 << 4)
+
+
/*
** LUA_TFUNCTION variants:
** 0 - Lua function
@@ -49,6 +52,12 @@
#define LUA_TCCL (LUA_TFUNCTION | (2 << 4)) /* C closure */
+/*
+** LUA_TSTRING variants */
+#define LUA_TSHRSTR (LUA_TSTRING | (0 << 4)) /* short strings */
+#define LUA_TLNGSTR (LUA_TSTRING | (1 << 4)) /* long strings */
+
+
/* Bit mark for collectable types */
#define BIT_ISCOLLECTABLE (1 << 6)
@@ -109,23 +118,28 @@ typedef struct lua_TValue TValue;
/* raw type tag of a TValue */
#define rttype(o) ((o)->tt_)
+/* tag with no variants (bits 0-3) */
+#define novariant(x) ((x) & 0x0F)
+
/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */
#define ttype(o) (rttype(o) & 0x3F)
-
/* type tag of a TValue with no variants (bits 0-3) */
-#define ttypenv(o) (rttype(o) & 0x0F)
+#define ttypenv(o) (novariant(rttype(o)))
/* Macros to test type */
#define checktag(o,t) (rttype(o) == (t))
+#define checktype(o,t) (ttypenv(o) == (t))
#define ttisnumber(o) checktag((o), LUA_TNUMBER)
#define ttisnil(o) checktag((o), LUA_TNIL)
#define ttisboolean(o) checktag((o), LUA_TBOOLEAN)
#define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA)
-#define ttisstring(o) checktag((o), ctb(LUA_TSTRING))
+#define ttisstring(o) checktype((o), LUA_TSTRING)
+#define ttisshrstring(o) checktag((o), ctb(LUA_TSHRSTR))
+#define ttislngstring(o) checktag((o), ctb(LUA_TLNGSTR))
#define ttistable(o) checktag((o), ctb(LUA_TTABLE))
-#define ttisfunction(o) (ttypenv(o) == LUA_TFUNCTION)
+#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
#define ttisclosure(o) ((rttype(o) & 0x1F) == LUA_TFUNCTION)
#define ttisCclosure(o) checktag((o), ctb(LUA_TCCL))
#define ttisLclosure(o) checktag((o), ctb(LUA_TLCL))
@@ -161,7 +175,7 @@ typedef struct lua_TValue TValue;
/* Macros for internal tests */
-#define righttt(obj) (ttypenv(obj) == gcvalue(obj)->gch.tt)
+#define righttt(obj) (ttype(obj) == gcvalue(obj)->gch.tt)
#define checkliveness(g,obj) \
lua_longassert(!iscollectable(obj) || \
@@ -193,7 +207,8 @@ typedef struct lua_TValue TValue;
#define setsvalue(L,obj,x) \
{ TValue *io=(obj); \
- val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TSTRING)); \
+ TString *x_ = (x); \
+ val_(io).gc=cast(GCObject *, x_); settt_(io, ctb(x_->tsv.tt)); \
checkliveness(G(L),io); }
#define setuvalue(L,obj,x) \
@@ -221,11 +236,6 @@ typedef struct lua_TValue TValue;
val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TTABLE)); \
checkliveness(G(L),io); }
-#define setptvalue(L,obj,x) \
- { TValue *io=(obj); \
- val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TPROTO)); \
- checkliveness(G(L),io); }
-
#define setdeadvalue(obj) settt_(obj, LUA_TDEADKEY)
@@ -256,6 +266,8 @@ typedef struct lua_TValue TValue;
#define setsvalue2n setsvalue
+/* check whether a number is valid (useful only for NaN trick) */
+#define luai_checknum(L,o,c) { /* empty */ }
/*
@@ -263,10 +275,7 @@ typedef struct lua_TValue TValue;
** NaN Trick
** =======================================================
*/
-
-#if defined(LUA_NANTRICK) \
- || defined(LUA_NANTRICK_LE) \
- || defined(LUA_NANTRICK_BE)
+#if defined(LUA_NANTRICK)
/*
** numbers are represented in the 'd_' field. All other values have the
@@ -274,15 +283,23 @@ typedef struct lua_TValue TValue;
** a "signaled NaN", which is never generated by regular operations by
** the CPU (nor by 'strtod')
*/
-#if !defined(NNMARK)
+
+/* allows for external implementation for part of the trick */
+#if !defined(NNMARK) /* { */
+
+
+#if !defined(LUA_IEEEENDIAN)
+#error option 'LUA_NANTRICK' needs 'LUA_IEEEENDIAN'
+#endif
+
+
#define NNMARK 0x7FF7A500
#define NNMASK 0x7FFFFF00
-#endif
#undef TValuefields
#undef NILCONSTANT
-#if defined(LUA_NANTRICK_LE)
+#if (LUA_IEEEENDIAN == 0) /* { */
/* little endian */
#define TValuefields \
@@ -293,7 +310,7 @@ typedef struct lua_TValue TValue;
#define d_(o) ((o)->u.d__)
#define tt_(o) ((o)->u.i.tt__)
-#elif defined(LUA_NANTRICK_BE)
+#else /* }{ */
/* big endian */
#define TValuefields \
@@ -304,10 +321,9 @@ typedef struct lua_TValue TValue;
#define d_(o) ((o)->u.d__)
#define tt_(o) ((o)->u.i.tt__)
-#elif !defined(TValuefields)
-#error option 'LUA_NANTRICK' needs declaration for 'TValuefields'
+#endif /* } */
-#endif
+#endif /* } */
/* correspondence with standard representation */
@@ -348,21 +364,18 @@ typedef struct lua_TValue TValue;
*/
#undef checktag
+#undef checktype
#define checktag(o,t) (tt_(o) == tag2tt(t))
+#define checktype(o,t) (ctb(tt_(o) | VARBITS) == ctb(tag2tt(t) | VARBITS))
#undef ttisequal
#define ttisequal(o1,o2) \
(ttisnumber(o1) ? ttisnumber(o2) : (tt_(o1) == tt_(o2)))
-
+#undef luai_checknum
#define luai_checknum(L,o,c) { if (!ttisnumber(o)) c; }
-
-#else
-
-#define luai_checknum(L,o,c) { /* empty */ }
-
#endif
/* }====================================================== */
@@ -401,7 +414,7 @@ typedef union TString {
L_Umaxalign dummy; /* ensures maximum alignment for strings */
struct {
CommonHeader;
- lu_byte reserved;
+ lu_byte extra; /* reserved words for short strings; "has hash" for longs */
unsigned int hash;
size_t len; /* number of characters in string */
} tsv;
@@ -501,7 +514,7 @@ typedef struct UpVal {
*/
#define ClosureHeader \
- CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist
+ CommonHeader; lu_byte nupvalues; GCObject *gclist
typedef struct CClosure {
ClosureHeader;
diff --git a/depends/lua/include/lparser.h b/depends/lua/include/lparser.h
index caabf46c..301167d4 100644
--- a/depends/lua/include/lparser.h
+++ b/depends/lua/include/lparser.h
@@ -1,5 +1,5 @@
/*
-** $Id: lparser.h,v 1.69 2011/07/27 18:09:01 roberto Exp $
+** $Id: lparser.h,v 1.70 2012/05/08 13:53:33 roberto Exp $
** Lua Parser
** See Copyright Notice in lua.h
*/
@@ -112,8 +112,8 @@ typedef struct FuncState {
} FuncState;
-LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
- Dyndata *dyd, const char *name, int firstchar);
+LUAI_FUNC Closure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
+ Dyndata *dyd, const char *name, int firstchar);
#endif
diff --git a/depends/lua/include/lstate.h b/depends/lua/include/lstate.h
index 4743d741..29f810ba 100644
--- a/depends/lua/include/lstate.h
+++ b/depends/lua/include/lstate.h
@@ -1,5 +1,5 @@
/*
-** $Id: lstate.h,v 2.74 2011/09/30 12:45:07 roberto Exp $
+** $Id: lstate.h,v 2.81 2012/06/08 15:14:04 roberto Exp $
** Global State
** See Copyright Notice in lua.h
*/
@@ -72,6 +72,7 @@ typedef struct CallInfo {
struct CallInfo *previous, *next; /* dynamic call link */
short nresults; /* expected number of results from this function */
lu_byte callstatus;
+ ptrdiff_t extra;
union {
struct { /* only for Lua functions */
StkId base; /* base for this function */
@@ -81,7 +82,6 @@ typedef struct CallInfo {
int ctx; /* context info. in case of yields */
lua_CFunction k; /* continuation in case of yields */
ptrdiff_t old_errfunc;
- ptrdiff_t extra;
lu_byte old_allowhook;
lu_byte status;
} c;
@@ -100,6 +100,7 @@ typedef struct CallInfo {
#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */
#define CIST_STAT (1<<5) /* call has an error status (pcall) */
#define CIST_TAIL (1<<6) /* call was tail called */
+#define CIST_HOOKYIELD (1<<7) /* last hook called yielded */
#define isLua(ci) ((ci)->callstatus & CIST_LUA)
@@ -113,9 +114,11 @@ typedef struct global_State {
void *ud; /* auxiliary data to `frealloc' */
lu_mem totalbytes; /* number of bytes currently allocated - GCdebt */
l_mem GCdebt; /* bytes allocated not yet compensated by the collector */
- lu_mem lastmajormem; /* memory in use after last major collection */
+ lu_mem GCmemtrav; /* memory traversed by the GC */
+ lu_mem GCestimate; /* an estimate of the non-garbage memory in use */
stringtable strt; /* hash table for strings */
TValue l_registry;
+ unsigned int seed; /* randomized seed for hashes */
lu_byte currentwhite;
lu_byte gcstate; /* state of garbage collector */
lu_byte gckind; /* kind of GC running */
@@ -123,7 +126,8 @@ typedef struct global_State {
int sweepstrgc; /* position of sweep in `strt' */
GCObject *allgc; /* list of all collectable objects */
GCObject *finobj; /* list of collectable objects with finalizers */
- GCObject **sweepgc; /* current position of sweep */
+ GCObject **sweepgc; /* current position of sweep in list 'allgc' */
+ GCObject **sweepfin; /* current position of sweep in list 'finobj' */
GCObject *gray; /* list of gray objects */
GCObject *grayagain; /* list of objects to be traversed atomically */
GCObject *weak; /* list of tables with weak values */
@@ -193,11 +197,15 @@ union GCObject {
#define gch(o) (&(o)->gch)
/* macros to convert a GCObject into a specific value */
-#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))
+#define rawgco2ts(o) \
+ check_exp(novariant((o)->gch.tt) == LUA_TSTRING, &((o)->ts))
#define gco2ts(o) (&rawgco2ts(o)->tsv)
#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
#define gco2u(o) (&rawgco2u(o)->uv)
-#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))
+#define gco2lcl(o) check_exp((o)->gch.tt == LUA_TLCL, &((o)->cl.l))
+#define gco2ccl(o) check_exp((o)->gch.tt == LUA_TCCL, &((o)->cl.c))
+#define gco2cl(o) \
+ check_exp(novariant((o)->gch.tt) == LUA_TFUNCTION, &((o)->cl))
#define gco2t(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
diff --git a/depends/lua/include/lstring.h b/depends/lua/include/lstring.h
index d708a1b0..d312ff3d 100644
--- a/depends/lua/include/lstring.h
+++ b/depends/lua/include/lstring.h
@@ -1,5 +1,5 @@
/*
-** $Id: lstring.h,v 1.46 2010/04/05 16:26:37 roberto Exp $
+** $Id: lstring.h,v 1.49 2012/02/01 21:57:15 roberto Exp $
** String table (keep all strings handled by Lua)
** See Copyright Notice in lua.h
*/
@@ -23,11 +23,20 @@
/*
-** as all string are internalized, string equality becomes
-** pointer equality
+** test whether a string is a reserved word
*/
-#define eqstr(a,b) ((a) == (b))
+#define isreserved(s) ((s)->tsv.tt == LUA_TSHRSTR && (s)->tsv.extra > 0)
+
+/*
+** equality for short strings, which are always internalized
+*/
+#define eqshrstr(a,b) check_exp((a)->tsv.tt == LUA_TSHRSTR, (a) == (b))
+
+
+LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed);
+LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
+LUAI_FUNC int luaS_eqstr (TString *a, TString *b);
LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e);
LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
diff --git a/depends/lua/include/lua.h b/depends/lua/include/lua.h
index 1fafa45e..a3a3a70c 100644
--- a/depends/lua/include/lua.h
+++ b/depends/lua/include/lua.h
@@ -1,5 +1,5 @@
/*
-** $Id: lua.h,v 1.282 2011/11/29 15:55:08 roberto Exp $
+** $Id: lua.h,v 1.283 2012/04/20 13:18:26 roberto Exp $
** Lua - A Scripting Language
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file
@@ -19,11 +19,11 @@
#define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "2"
#define LUA_VERSION_NUM 502
-#define LUA_VERSION_RELEASE "0"
+#define LUA_VERSION_RELEASE "1"
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
-#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2011 Lua.org, PUC-Rio"
+#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2012 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
@@ -413,7 +413,7 @@ struct lua_Debug {
/******************************************************************************
-* Copyright (C) 1994-2011 Lua.org, PUC-Rio. All rights reserved.
+* Copyright (C) 1994-2012 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
diff --git a/depends/lua/include/luaconf.h b/depends/lua/include/luaconf.h
index fca66ce4..766752ff 100644
--- a/depends/lua/include/luaconf.h
+++ b/depends/lua/include/luaconf.h
@@ -1,5 +1,5 @@
/*
-** $Id: luaconf.h,v 1.170 2011/12/06 16:58:36 roberto Exp $
+** $Id: luaconf.h,v 1.172 2012/05/11 14:14:42 roberto Exp $
** Configuration file for Lua
** See Copyright Notice in lua.h
*/
@@ -223,6 +223,13 @@
(fprintf(stderr, (s), (p)), fflush(stderr))
+/*
+@@ LUAI_MAXSHORTLEN is the maximum length for short strings, that is,
+** strings that are internalized. (Cannot be smaller than reserved words
+** or tags for metamethods, as these strings must be internalized;
+** #("function") = 8, #("__newindex") = 10.)
+*/
+#define LUAI_MAXSHORTLEN 40
@@ -453,66 +460,76 @@
#define LUA_UNSIGNED unsigned LUA_INT32
-#if defined(LUA_CORE) /* { */
-#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) /* { */
+/*
+** Some tricks with doubles
+*/
-/* On a Microsoft compiler on a Pentium, use assembler to avoid clashes
- with a DirectX idiosyncrasy */
+#if defined(LUA_CORE) && \
+ defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) /* { */
+/*
+** The next definitions activate some tricks to speed up the
+** conversion from doubles to integer types, mainly to LUA_UNSIGNED.
+**
+@@ MS_ASMTRICK uses Microsoft assembler to avoid clashes with a
+** DirectX idiosyncrasy.
+**
+@@ LUA_IEEE754TRICK uses a trick that should work on any machine
+** using IEEE754 with a 32-bit integer type.
+**
+@@ LUA_IEEELL extends the trick to LUA_INTEGER; should only be
+** defined when LUA_INTEGER is a 32-bit integer.
+**
+@@ LUA_IEEEENDIAN is the endianness of doubles in your machine
+** (0 for little endian, 1 for big endian); if not defined, Lua will
+** check it dynamically for LUA_IEEE754TRICK (but not for LUA_NANTRICK).
+**
+@@ LUA_NANTRICK controls the use of a trick to pack all types into
+** a single double value, using NaN values to represent non-number
+** values. The trick only works on 32-bit machines (ints and pointers
+** are 32-bit values) with numbers represented as IEEE 754-2008 doubles
+** with conventional endianess (12345678 or 87654321), in CPUs that do
+** not produce signaling NaN values (all NaNs are quiet).
+*/
+
+/* Microsoft compiler on a Pentium (32 bit) ? */
#if defined(LUA_WIN) && defined(_MSC_VER) && defined(_M_IX86) /* { */
#define MS_ASMTRICK
+#define LUA_IEEEENDIAN 0
+#define LUA_NANTRICK
-#else /* }{ */
-/* the next definition uses a trick that should work on any machine
- using IEEE754 with a 32-bit integer type */
-
-#define LUA_IEEE754TRICK
-
-/*
-@@ LUA_IEEEENDIAN is the endianness of doubles in your machine
-** (0 for little endian, 1 for big endian); if not defined, Lua will
-** check it dynamically.
-*/
-/* check for known architectures */
-#if defined(__i386__) || defined(__i386) || defined(__X86__) || \
- defined (__x86_64)
-#define LUA_IEEEENDIAN 0
-#elif defined(__POWERPC__) || defined(__ppc__)
-#define LUA_IEEEENDIAN 1
-#endif
-#endif /* } */
+/* pentium 32 bits? */
+#elif defined(__i386__) || defined(__i386) || defined(__X86__) /* }{ */
-#endif /* } */
+#define LUA_IEEE754TRICK
+#define LUA_IEEELL
+#define LUA_IEEEENDIAN 0
+#define LUA_NANTRICK
-#endif /* } */
+/* pentium 64 bits? */
+#elif defined(__x86_64) /* }{ */
-/* }================================================================== */
+#define LUA_IEEE754TRICK
+#define LUA_IEEEENDIAN 0
+#elif defined(__POWERPC__) || defined(__ppc__) /* }{ */
-/*
-@@ LUA_NANTRICK_LE/LUA_NANTRICK_BE controls the use of a trick to
-** pack all types into a single double value, using NaN values to
-** represent non-number values. The trick only works on 32-bit machines
-** (ints and pointers are 32-bit values) with numbers represented as
-** IEEE 754-2008 doubles with conventional endianess (12345678 or
-** 87654321), in CPUs that do not produce signaling NaN values (all NaNs
-** are quiet).
-*/
-#if defined(LUA_CORE) && \
- defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) /* { */
+#define LUA_IEEE754TRICK
+#define LUA_IEEEENDIAN 1
-/* little-endian architectures that satisfy those conditions */
-#if defined(__i386__) || defined(__i386) || defined(__X86__) || \
- defined(_M_IX86)
+#else /* }{ */
-#define LUA_NANTRICK_LE
+/* assume IEEE754 and a 32-bit integer type */
+#define LUA_IEEE754TRICK
-#endif
+#endif /* } */
#endif /* } */
+/* }================================================================== */
+
diff --git a/depends/lua/include/lundump.h b/depends/lua/include/lundump.h
index b63993ff..2b8accec 100644
--- a/depends/lua/include/lundump.h
+++ b/depends/lua/include/lundump.h
@@ -1,5 +1,5 @@
/*
-** $Id: lundump.h,v 1.44 2011/05/06 13:35:17 lhf Exp $
+** $Id: lundump.h,v 1.39 2012/05/08 13:53:33 roberto Exp $
** load precompiled Lua chunks
** See Copyright Notice in lua.h
*/
@@ -11,7 +11,7 @@
#include "lzio.h"
/* load one chunk; from lundump.c */
-LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name);
+LUAI_FUNC Closure* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name);
/* make header; from lundump.c */
LUAI_FUNC void luaU_header (lu_byte* h);
diff --git a/depends/lua/src/lapi.c b/depends/lua/src/lapi.c
index e96ceb9e..1854fe61 100644
--- a/depends/lua/src/lapi.c
+++ b/depends/lua/src/lapi.c
@@ -1,5 +1,5 @@
/*
-** $Id: lapi.c,v 2.159 2011/11/30 12:32:05 roberto Exp $
+** $Id: lapi.c,v 2.164 2012/06/08 15:14:04 roberto Exp $
** Lua API
** See Copyright Notice in lua.h
*/
@@ -950,7 +950,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
ci->u.c.k = k; /* save continuation */
ci->u.c.ctx = ctx; /* save context */
/* save information for error recovery */
- ci->u.c.extra = savestack(L, c.func);
+ ci->extra = savestack(L, c.func);
ci->u.c.old_allowhook = L->allowhook;
ci->u.c.old_errfunc = L->errfunc;
L->errfunc = func;
@@ -1045,17 +1045,17 @@ LUA_API int lua_gc (lua_State *L, int what, int data) {
}
case LUA_GCSTEP: {
if (g->gckind == KGC_GEN) { /* generational mode? */
- res = (g->lastmajormem == 0); /* 1 if will do major collection */
+ res = (g->GCestimate == 0); /* true if it will do major collection */
luaC_forcestep(L); /* do a single step */
}
else {
- while (data-- >= 0) {
- luaC_forcestep(L);
- if (g->gcstate == GCSpause) { /* end of cycle? */
- res = 1; /* signal it */
- break;
- }
- }
+ lu_mem debt = cast(lu_mem, data) * 1024 - GCSTEPSIZE;
+ if (g->gcrunning)
+ debt += g->GCdebt; /* include current debt */
+ luaE_setdebt(g, debt);
+ luaC_forcestep(L);
+ if (g->gcstate == GCSpause) /* end of cycle? */
+ res = 1; /* signal it */
}
break;
}
diff --git a/depends/lua/src/lauxlib.c b/depends/lua/src/lauxlib.c
index c1b715f3..36ae7e62 100644
--- a/depends/lua/src/lauxlib.c
+++ b/depends/lua/src/lauxlib.c
@@ -1,5 +1,5 @@
/*
-** $Id: lauxlib.c,v 1.240 2011/12/06 16:33:55 roberto Exp $
+** $Id: lauxlib.c,v 1.244 2012/05/31 20:28:45 roberto Exp $
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
@@ -520,11 +520,11 @@ LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) {
LUALIB_API int luaL_ref (lua_State *L, int t) {
int ref;
- t = lua_absindex(L, t);
if (lua_isnil(L, -1)) {
lua_pop(L, 1); /* remove from stack */
return LUA_REFNIL; /* `nil' has a unique fixed reference */
}
+ t = lua_absindex(L, t);
lua_rawgeti(L, t, freelist); /* get first free element */
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
lua_pop(L, 1); /* remove it from stack */
@@ -616,8 +616,10 @@ static int skipBOM (LoadF *lf) {
static int skipcomment (LoadF *lf, int *cp) {
int c = *cp = skipBOM(lf);
if (c == '#') { /* first line is a comment (Unix exec. file)? */
- while ((c = getc(lf->f)) != EOF && c != '\n') ; /* skip first line */
- *cp = getc(lf->f); /* skip end-of-line */
+ do { /* skip first line */
+ c = getc(lf->f);
+ } while (c != EOF && c != '\n') ;
+ *cp = getc(lf->f); /* skip end-of-line, if present */
return 1; /* there was a comment */
}
else return 0; /* no comment */
@@ -843,6 +845,7 @@ LUALIB_API void luaL_openlib (lua_State *L, const char *libname,
** Returns with only the table at the stack.
*/
LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
+ luaL_checkversion(L);
luaL_checkstack(L, nup, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */
int i;
@@ -889,10 +892,8 @@ LUALIB_API void luaL_requiref (lua_State *L, const char *modname,
lua_setfield(L, -2, modname); /* _LOADED[modname] = module */
lua_pop(L, 1); /* remove _LOADED table */
if (glb) {
- lua_pushglobaltable(L);
- lua_pushvalue(L, -2); /* copy of 'mod' */
- lua_setfield(L, -2, modname); /* _G[modname] = module */
- lua_pop(L, 1); /* remove _G table */
+ lua_pushvalue(L, -1); /* copy of 'mod' */
+ lua_setglobal(L, modname); /* _G[modname] = module */
}
}
diff --git a/depends/lua/src/lbaselib.c b/depends/lua/src/lbaselib.c
index 1dfae301..dbfcb02c 100644
--- a/depends/lua/src/lbaselib.c
+++ b/depends/lua/src/lbaselib.c
@@ -1,5 +1,5 @@
/*
-** $Id: lbaselib.c,v 1.273 2011/11/30 13:03:24 roberto Exp $
+** $Id: lbaselib.c,v 1.274 2012/04/27 14:13:19 roberto Exp $
** Basic library
** See Copyright Notice in lua.h
*/
@@ -293,6 +293,7 @@ static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
lua_pushvalue(L, 1); /* get function */
lua_call(L, 0, 1); /* call it */
if (lua_isnil(L, -1)) {
+ lua_pop(L, 1); /* pop result */
*size = 0;
return NULL;
}
diff --git a/depends/lua/src/lcorolib.c b/depends/lua/src/lcorolib.c
index 0edde26d..c7932d90 100644
--- a/depends/lua/src/lcorolib.c
+++ b/depends/lua/src/lcorolib.c
@@ -1,5 +1,5 @@
/*
-** $Id: lcorolib.c,v 1.3 2011/08/23 17:24:34 roberto Exp $
+** $Id: lcorolib.c,v 1.4 2012/04/27 18:59:04 roberto Exp $
** Coroutine Library
** See Copyright Notice in lua.h
*/
@@ -80,8 +80,9 @@ static int luaB_auxwrap (lua_State *L) {
static int luaB_cocreate (lua_State *L) {
- lua_State *NL = lua_newthread(L);
+ lua_State *NL;
luaL_checktype(L, 1, LUA_TFUNCTION);
+ NL = lua_newthread(L);
lua_pushvalue(L, 1); /* move function to top */
lua_xmove(L, NL, 1); /* move function from L to NL */
return 1;
diff --git a/depends/lua/src/ldblib.c b/depends/lua/src/ldblib.c
index 3c2f1595..c0226945 100644
--- a/depends/lua/src/ldblib.c
+++ b/depends/lua/src/ldblib.c
@@ -1,5 +1,5 @@
/*
-** $Id: ldblib.c,v 1.131 2011/10/24 14:54:05 roberto Exp $
+** $Id: ldblib.c,v 1.132 2012/01/19 20:14:44 roberto Exp $
** Interface from Lua to its debug API
** See Copyright Notice in lua.h
*/
@@ -253,14 +253,15 @@ static int db_upvaluejoin (lua_State *L) {
}
-#define gethooktable(L) luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY);
+#define gethooktable(L) luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)
static void hookf (lua_State *L, lua_Debug *ar) {
static const char *const hooknames[] =
{"call", "return", "line", "count", "tail call"};
gethooktable(L);
- lua_rawgetp(L, -1, L);
+ lua_pushthread(L);
+ lua_rawget(L, -2);
if (lua_isfunction(L, -1)) {
lua_pushstring(L, hooknames[(int)ar->event]);
if (ar->currentline >= 0)
@@ -306,10 +307,15 @@ static int db_sethook (lua_State *L) {
count = luaL_optint(L, arg+3, 0);
func = hookf; mask = makemask(smask, count);
}
- gethooktable(L);
+ if (gethooktable(L) == 0) { /* creating hook table? */
+ lua_pushstring(L, "k");
+ lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
+ lua_pushvalue(L, -1);
+ lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */
+ }
+ lua_pushthread(L1); lua_xmove(L1, L, 1);
lua_pushvalue(L, arg+1);
- lua_rawsetp(L, -2, L1); /* set new hook */
- lua_pop(L, 1); /* remove hook table */
+ lua_rawset(L, -3); /* set new hook */
lua_sethook(L1, func, mask, count); /* set hooks */
return 0;
}
@@ -325,7 +331,8 @@ static int db_gethook (lua_State *L) {
lua_pushliteral(L, "external hook");
else {
gethooktable(L);
- lua_rawgetp(L, -1, L1); /* get hook */
+ lua_pushthread(L1); lua_xmove(L1, L, 1);
+ lua_rawget(L, -2); /* get hook */
lua_remove(L, -2); /* remove hook table */
}
lua_pushstring(L, unmakemask(mask, buff));
diff --git a/depends/lua/src/ldebug.c b/depends/lua/src/ldebug.c
index 31b7ae40..43f8f046 100644
--- a/depends/lua/src/ldebug.c
+++ b/depends/lua/src/ldebug.c
@@ -1,5 +1,5 @@
/*
-** $Id: ldebug.c,v 2.88 2011/11/30 12:43:51 roberto Exp $
+** $Id: ldebug.c,v 2.89 2012/01/20 22:05:50 roberto Exp $
** Debug Interface
** See Copyright Notice in lua.h
*/
@@ -30,6 +30,9 @@
+#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_TCCL)
+
+
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name);
@@ -173,7 +176,7 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
static void funcinfo (lua_Debug *ar, Closure *cl) {
- if (cl == NULL || cl->c.isC) {
+ if (noLuaClosure(cl)) {
ar->source = "=[C]";
ar->linedefined = -1;
ar->lastlinedefined = -1;
@@ -191,7 +194,7 @@ static void funcinfo (lua_Debug *ar, Closure *cl) {
static void collectvalidlines (lua_State *L, Closure *f) {
- if (f == NULL || f->c.isC) {
+ if (noLuaClosure(f)) {
setnilvalue(L->top);
incr_top(L);
}
@@ -210,7 +213,7 @@ static void collectvalidlines (lua_State *L, Closure *f) {
static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
- Closure *f, CallInfo *ci) {
+ Closure *f, CallInfo *ci) {
int status = 1;
for (; *what; what++) {
switch (*what) {
@@ -224,7 +227,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
}
case 'u': {
ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
- if (f == NULL || f->c.isC) {
+ if (noLuaClosure(f)) {
ar->isvararg = 1;
ar->nparams = 0;
}
diff --git a/depends/lua/src/ldo.c b/depends/lua/src/ldo.c
index e0f13c44..35d95ae4 100644
--- a/depends/lua/src/ldo.c
+++ b/depends/lua/src/ldo.c
@@ -1,5 +1,5 @@
/*
-** $Id: ldo.c,v 2.102 2011/11/29 15:55:08 roberto Exp $
+** $Id: ldo.c,v 2.105 2012/06/08 15:14:04 roberto Exp $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
@@ -402,8 +402,9 @@ static void finishCcall (lua_State *L) {
int n;
lua_assert(ci->u.c.k != NULL); /* must have a continuation */
lua_assert(L->nny == 0);
- /* finish 'luaD_call' */
- //L->nCcalls--;
+ /* finish 'lua_pcallk' */
+ if (ci->callstatus & CIST_YPCALL)
+ L->errfunc = ci->u.c.old_errfunc;
/* finish 'lua_callk' */
adjustresults(L, ci->nresults);
/* call continuation function */
@@ -453,7 +454,7 @@ static int recover (lua_State *L, int status) {
CallInfo *ci = findpcall(L);
if (ci == NULL) return 0; /* no recovery point */
/* "finish" luaD_pcall */
- oldtop = restorestack(L, ci->u.c.extra);
+ oldtop = restorestack(L, ci->extra);
luaF_close(L, oldtop);
seterrorobj(L, status, oldtop);
L->ci = ci;
@@ -484,9 +485,10 @@ static l_noret resume_error (lua_State *L, const char *msg, StkId firstArg) {
** do the work for 'lua_resume' in protected mode
*/
static void resume (lua_State *L, void *ud) {
+ int nCcalls = L->nCcalls;
StkId firstArg = cast(StkId, ud);
CallInfo *ci = L->ci;
- if (L->nCcalls >= LUAI_MAXCCALLS)
+ if (nCcalls >= LUAI_MAXCCALLS)
resume_error(L, "C stack overflow", firstArg);
if (L->status == LUA_OK) { /* may be starting a coroutine */
if (ci != &L->base_ci) /* not in base level? */
@@ -499,10 +501,10 @@ static void resume (lua_State *L, void *ud) {
resume_error(L, "cannot resume dead coroutine", firstArg);
else { /* resuming from previous yield */
L->status = LUA_OK;
+ ci->func = restorestack(L, ci->extra);
if (isLua(ci)) /* yielded inside a hook? */
luaV_execute(L); /* just continue running Lua code */
else { /* 'common' yield */
- ci->func = restorestack(L, ci->u.c.extra);
if (ci->u.c.k != NULL) { /* does it have a continuation? */
int n;
ci->u.c.status = LUA_YIELD; /* 'default' status */
@@ -513,11 +515,11 @@ static void resume (lua_State *L, void *ud) {
api_checknelems(L, n);
firstArg = L->top - n; /* yield results come from continuation */
}
- //L->nCcalls--; /* finish 'luaD_call' */
luaD_poscall(L, firstArg); /* finish 'luaD_precall' */
}
unroll(L, NULL);
}
+ lua_assert(nCcalls == L->nCcalls);
}
@@ -564,13 +566,13 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) {
luaG_runerror(L, "attempt to yield from outside a coroutine");
}
L->status = LUA_YIELD;
+ ci->extra = savestack(L, ci->func); /* save current 'func' */
if (isLua(ci)) { /* inside a hook? */
api_check(L, k == NULL, "hooks cannot continue after yielding");
}
else {
if ((ci->u.c.k = k) != NULL) /* is there a continuation? */
ci->u.c.ctx = ctx; /* save context */
- ci->u.c.extra = savestack(L, ci->func); /* save current 'func' */
ci->func = L->top - nresults - 1; /* protect stack below results */
luaD_throw(L, LUA_YIELD);
}
@@ -627,24 +629,23 @@ static void checkmode (lua_State *L, const char *mode, const char *x) {
static void f_parser (lua_State *L, void *ud) {
int i;
- Proto *tf;
Closure *cl;
struct SParser *p = cast(struct SParser *, ud);
int c = zgetc(p->z); /* read first character */
if (c == LUA_SIGNATURE[0]) {
checkmode(L, p->mode, "binary");
- tf = luaU_undump(L, p->z, &p->buff, p->name);
+ cl = luaU_undump(L, p->z, &p->buff, p->name);
}
else {
checkmode(L, p->mode, "text");
- tf = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c);
+ cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c);
+ }
+ lua_assert(cl->l.nupvalues == cl->l.p->sizeupvalues);
+ for (i = 0; i < cl->l.nupvalues; i++) { /* initialize upvalues */
+ UpVal *up = luaF_newupval(L);
+ cl->l.upvals[i] = up;
+ luaC_objbarrier(L, cl, up);
}
- setptvalue2s(L, L->top, tf);
- incr_top(L);
- cl = luaF_newLclosure(L, tf);
- setclLvalue(L, L->top - 1, cl);
- for (i = 0; i < tf->sizeupvalues; i++) /* initialize upvalues */
- cl->l.upvals[i] = luaF_newupval(L);
}
diff --git a/depends/lua/src/ldump.c b/depends/lua/src/ldump.c
index 699e1dc4..d5e6a47c 100644
--- a/depends/lua/src/ldump.c
+++ b/depends/lua/src/ldump.c
@@ -1,5 +1,5 @@
/*
-** $Id: ldump.c,v 1.19 2011/11/23 17:48:18 lhf Exp $
+** $Id: ldump.c,v 2.17 2012/01/23 23:02:10 roberto Exp $
** save precompiled Lua chunks
** See Copyright Notice in lua.h
*/
@@ -84,8 +84,8 @@ static void DumpConstants(const Proto* f, DumpState* D)
for (i=0; i<n; i++)
{
const TValue* o=&f->k[i];
- DumpChar(ttype(o),D);
- switch (ttype(o))
+ DumpChar(ttypenv(o),D);
+ switch (ttypenv(o))
{
case LUA_TNIL:
break;
@@ -98,6 +98,7 @@ static void DumpConstants(const Proto* f, DumpState* D)
case LUA_TSTRING:
DumpString(rawtsvalue(o),D);
break;
+ default: lua_assert(0);
}
}
n=f->sizep;
diff --git a/depends/lua/src/lfunc.c b/depends/lua/src/lfunc.c
index 1a1a8bb8..4fd27fe5 100644
--- a/depends/lua/src/lfunc.c
+++ b/depends/lua/src/lfunc.c
@@ -1,5 +1,5 @@
/*
-** $Id: lfunc.c,v 2.27 2010/06/30 14:11:17 roberto Exp $
+** $Id: lfunc.c,v 2.29 2012/05/08 13:53:33 roberto Exp $
** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h
*/
@@ -21,18 +21,15 @@
Closure *luaF_newCclosure (lua_State *L, int n) {
- Closure *c = &luaC_newobj(L, LUA_TFUNCTION, sizeCclosure(n), NULL, 0)->cl;
- c->c.isC = 1;
+ Closure *c = &luaC_newobj(L, LUA_TCCL, sizeCclosure(n), NULL, 0)->cl;
c->c.nupvalues = cast_byte(n);
return c;
}
-Closure *luaF_newLclosure (lua_State *L, Proto *p) {
- int n = p->sizeupvalues;
- Closure *c = &luaC_newobj(L, LUA_TFUNCTION, sizeLclosure(n), NULL, 0)->cl;
- c->l.isC = 0;
- c->l.p = p;
+Closure *luaF_newLclosure (lua_State *L, int n) {
+ Closure *c = &luaC_newobj(L, LUA_TLCL, sizeLclosure(n), NULL, 0)->cl;
+ c->l.p = NULL;
c->l.nupvalues = cast_byte(n);
while (n--) c->l.upvals[n] = NULL;
return c;
@@ -146,13 +143,6 @@ void luaF_freeproto (lua_State *L, Proto *f) {
}
-void luaF_freeclosure (lua_State *L, Closure *c) {
- int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) :
- sizeLclosure(c->l.nupvalues);
- luaM_freemem(L, c, size);
-}
-
-
/*
** Look for n-th local variable at line `line' in function `func'.
** Returns NULL if not found.
diff --git a/depends/lua/src/lgc.c b/depends/lua/src/lgc.c
index cdd92e52..06f972a7 100644
--- a/depends/lua/src/lgc.c
+++ b/depends/lua/src/lgc.c
@@ -1,5 +1,5 @@
/*
-** $Id: lgc.c,v 2.116 2011/12/02 13:18:41 roberto Exp $
+** $Id: lgc.c,v 2.133 2012/05/31 21:28:59 roberto Exp $
** Garbage Collector
** See Copyright Notice in lua.h
*/
@@ -24,34 +24,40 @@
-/* how much to allocate before next GC step */
-#define GCSTEPSIZE 1024
+/*
+** cost of sweeping one element (the size of a small object divided
+** by some adjust for the sweep speed)
+*/
+#define GCSWEEPCOST ((sizeof(TString) + 4) / 4)
/* maximum number of elements to sweep in each single step */
-#define GCSWEEPMAX 40
-
-/* cost of sweeping one element */
-#define GCSWEEPCOST 1
+#define GCSWEEPMAX (cast_int((GCSTEPSIZE / GCSWEEPCOST) / 4))
/* maximum number of finalizers to call in each GC step */
#define GCFINALIZENUM 4
-/* cost of marking the root set */
-#define GCROOTCOST 10
-/* cost of atomic step */
-#define GCATOMICCOST 1000
+/*
+** macro to adjust 'stepmul': 'stepmul' is actually used like
+** 'stepmul / STEPMULADJ' (value chosen by tests)
+*/
+#define STEPMULADJ 200
+
+/*
+** macro to adjust 'pause': 'pause' is actually used like
+** 'pause / PAUSEADJ' (value chosen by tests)
+*/
+#define PAUSEADJ 200
+
-/* basic cost to traverse one object (to be added to the links the
- object may have) */
-#define TRAVCOST 5
/*
** standard negative debt for GC; a reasonable "time" to wait before
** starting a new cycle
*/
-#define stddebt(g) (-cast(l_mem, gettotalbytes(g)/100) * g->gcpause)
+#define stddebtest(g,e) (-cast(l_mem, (e)/PAUSEADJ) * g->gcpause)
+#define stddebt(g) stddebtest(g, gettotalbytes(g))
/*
@@ -65,8 +71,6 @@
#define white2gray(x) resetbits(gch(x)->marked, WHITEBITS)
#define black2gray(x) resetbit(gch(x)->marked, BLACKBIT)
-#define stringmark(s) ((void)((s) && resetbits((s)->tsv.marked, WHITEBITS)))
-
#define isfinalized(x) testbit(gch(x)->marked, FINALIZEDBIT)
@@ -123,10 +127,10 @@ static void removeentry (Node *n) {
** other objects: if really collected, cannot keep them; for objects
** being finalized, keep them in keys, but not in values
*/
-static int iscleared (const TValue *o) {
+static int iscleared (global_State *g, const TValue *o) {
if (!iscollectable(o)) return 0;
else if (ttisstring(o)) {
- stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */
+ markobject(g, rawtsvalue(o)); /* strings are `values', so are never weak */
return 0;
}
else return iswhite(gcvalue(o));
@@ -217,7 +221,8 @@ void luaC_checkupvalcolor (global_State *g, UpVal *uv) {
GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, GCObject **list,
int offset) {
global_State *g = G(L);
- GCObject *o = obj2gco(cast(char *, luaM_newobject(L, tt, sz)) + offset);
+ char *raw = cast(char *, luaM_newobject(L, novariant(tt), sz));
+ GCObject *o = obj2gco(raw + offset);
if (list == NULL)
list = &g->allgc; /* standard list for collectable objects */
gch(o)->marked = luaC_white(g);
@@ -239,54 +244,63 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, GCObject **list,
/*
-** mark an object. Userdata and closed upvalues are visited and turned
-** black here. Strings remain gray (it is the same as making them
-** black). Other objects are marked gray and added to appropriate list
-** to be visited (and turned black) later. (Open upvalues are already
-** linked in 'headuv' list.)
+** mark an object. Userdata, strings, and closed upvalues are visited
+** and turned black here. Other objects are marked gray and added
+** to appropriate list to be visited (and turned black) later. (Open
+** upvalues are already linked in 'headuv' list.)
*/
static void reallymarkobject (global_State *g, GCObject *o) {
- lua_assert(iswhite(o) && !isdead(g, o));
+ lu_mem size;
white2gray(o);
switch (gch(o)->tt) {
- case LUA_TSTRING: {
- return; /* for strings, gray is as good as black */
+ case LUA_TSHRSTR:
+ case LUA_TLNGSTR: {
+ size = sizestring(gco2ts(o));
+ break; /* nothing else to mark; make it black */
}
case LUA_TUSERDATA: {
Table *mt = gco2u(o)->metatable;
markobject(g, mt);
markobject(g, gco2u(o)->env);
- gray2black(o); /* all pointers marked */
- return;
+ size = sizeudata(gco2u(o));
+ break;
}
case LUA_TUPVAL: {
UpVal *uv = gco2uv(o);
markvalue(g, uv->v);
- if (uv->v == &uv->u.value) /* closed? (open upvalues remain gray) */
- gray2black(o); /* make it black */
+ if (uv->v != &uv->u.value) /* open? */
+ return; /* open upvalues remain gray */
+ size = sizeof(UpVal);
+ break;
+ }
+ case LUA_TLCL: {
+ gco2lcl(o)->gclist = g->gray;
+ g->gray = o;
return;
}
- case LUA_TFUNCTION: {
- gco2cl(o)->c.gclist = g->gray;
+ case LUA_TCCL: {
+ gco2ccl(o)->gclist = g->gray;
g->gray = o;
- break;
+ return;
}
case LUA_TTABLE: {
linktable(gco2t(o), &g->gray);
- break;
+ return;
}
case LUA_TTHREAD: {
gco2th(o)->gclist = g->gray;
g->gray = o;
- break;
+ return;
}
case LUA_TPROTO: {
gco2p(o)->gclist = g->gray;
g->gray = o;
- break;
+ return;
}
- default: lua_assert(0);
+ default: lua_assert(0); return;
}
+ gray2black(o);
+ g->GCmemtrav += size;
}
@@ -359,7 +373,7 @@ static void traverseweakvalue (global_State *g, Table *h) {
else {
lua_assert(!ttisnil(gkey(n)));
markvalue(g, gkey(n)); /* mark key */
- if (!hasclears && iscleared(gval(n))) /* is there a white value? */
+ if (!hasclears && iscleared(g, gval(n))) /* is there a white value? */
hasclears = 1; /* table will have to be cleared */
}
}
@@ -388,7 +402,7 @@ static int traverseephemeron (global_State *g, Table *h) {
checkdeadkey(n);
if (ttisnil(gval(n))) /* entry is empty? */
removeentry(n); /* remove it */
- else if (iscleared(gkey(n))) { /* key is not marked (yet)? */
+ else if (iscleared(g, gkey(n))) { /* key is not marked (yet)? */
hasclears = 1; /* table must be cleared */
if (valiswhite(gval(n))) /* value not marked yet? */
prop = 1; /* must propagate again */
@@ -426,30 +440,26 @@ static void traversestrongtable (global_State *g, Table *h) {
}
-static int traversetable (global_State *g, Table *h) {
+static lu_mem traversetable (global_State *g, Table *h) {
+ const char *weakkey, *weakvalue;
const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
markobject(g, h->metatable);
- if (mode && ttisstring(mode)) { /* is there a weak mode? */
- int weakkey = (strchr(svalue(mode), 'k') != NULL);
- int weakvalue = (strchr(svalue(mode), 'v') != NULL);
- if (weakkey || weakvalue) { /* is really weak? */
- black2gray(obj2gco(h)); /* keep table gray */
- if (!weakkey) { /* strong keys? */
- traverseweakvalue(g, h);
- return TRAVCOST + sizenode(h);
- }
- else if (!weakvalue) { /* strong values? */
- traverseephemeron(g, h);
- return TRAVCOST + h->sizearray + sizenode(h);
- }
- else {
- linktable(h, &g->allweak); /* nothing to traverse now */
- return TRAVCOST;
- }
- } /* else go through */
+ if (mode && ttisstring(mode) && /* is there a weak mode? */
+ ((weakkey = strchr(svalue(mode), 'k')),
+ (weakvalue = strchr(svalue(mode), 'v')),
+ (weakkey || weakvalue))) { /* is really weak? */
+ black2gray(obj2gco(h)); /* keep table gray */
+ if (!weakkey) /* strong keys? */
+ traverseweakvalue(g, h);
+ else if (!weakvalue) /* strong values? */
+ traverseephemeron(g, h);
+ else /* all weak */
+ linktable(h, &g->allweak); /* nothing to traverse now */
}
- traversestrongtable(g, h);
- return TRAVCOST + h->sizearray + (2 * sizenode(h));
+ else /* not weak */
+ traversestrongtable(g, h);
+ return sizeof(Table) + sizeof(TValue) * h->sizearray +
+ sizeof(Node) * sizenode(h);
}
@@ -457,86 +467,101 @@ static int traverseproto (global_State *g, Proto *f) {
int i;
if (f->cache && iswhite(obj2gco(f->cache)))
f->cache = NULL; /* allow cache to be collected */
- stringmark(f->source);
+ markobject(g, f->source);
for (i = 0; i < f->sizek; i++) /* mark literals */
markvalue(g, &f->k[i]);
for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */
- stringmark(f->upvalues[i].name);
+ markobject(g, f->upvalues[i].name);
for (i = 0; i < f->sizep; i++) /* mark nested protos */
markobject(g, f->p[i]);
for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */
- stringmark(f->locvars[i].varname);
- return TRAVCOST + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars;
+ markobject(g, f->locvars[i].varname);
+ return sizeof(Proto) + sizeof(Instruction) * f->sizecode +
+ sizeof(Proto *) * f->sizep +
+ sizeof(TValue) * f->sizek +
+ sizeof(int) * f->sizelineinfo +
+ sizeof(LocVar) * f->sizelocvars +
+ sizeof(Upvaldesc) * f->sizeupvalues;
}
-static int traverseclosure (global_State *g, Closure *cl) {
- if (cl->c.isC) {
- int i;
- for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */
- markvalue(g, &cl->c.upvalue[i]);
- }
- else {
- int i;
- lua_assert(cl->l.nupvalues == cl->l.p->sizeupvalues);
- markobject(g, cl->l.p); /* mark its prototype */
- for (i=0; i<cl->l.nupvalues; i++) /* mark its upvalues */
- markobject(g, cl->l.upvals[i]);
- }
- return TRAVCOST + cl->c.nupvalues;
+static lu_mem traverseCclosure (global_State *g, CClosure *cl) {
+ int i;
+ for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */
+ markvalue(g, &cl->upvalue[i]);
+ return sizeCclosure(cl->nupvalues);
+}
+
+static lu_mem traverseLclosure (global_State *g, LClosure *cl) {
+ int i;
+ markobject(g, cl->p); /* mark its prototype */
+ for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */
+ markobject(g, cl->upvals[i]);
+ return sizeLclosure(cl->nupvalues);
}
-static int traversestack (global_State *g, lua_State *L) {
- StkId o = L->stack;
+static lu_mem traversestack (global_State *g, lua_State *th) {
+ StkId o = th->stack;
if (o == NULL)
return 1; /* stack not completely built yet */
- for (; o < L->top; o++)
+ for (; o < th->top; o++)
markvalue(g, o);
if (g->gcstate == GCSatomic) { /* final traversal? */
- StkId lim = L->stack + L->stacksize; /* real end of stack */
+ StkId lim = th->stack + th->stacksize; /* real end of stack */
for (; o < lim; o++) /* clear not-marked stack slice */
setnilvalue(o);
}
- return TRAVCOST + cast_int(o - L->stack);
+ return sizeof(lua_State) + sizeof(TValue) * th->stacksize;
}
/*
** traverse one gray object, turning it to black (except for threads,
** which are always gray).
-** Returns number of values traversed.
*/
-static int propagatemark (global_State *g) {
+static void propagatemark (global_State *g) {
+ lu_mem size;
GCObject *o = g->gray;
lua_assert(isgray(o));
gray2black(o);
switch (gch(o)->tt) {
case LUA_TTABLE: {
Table *h = gco2t(o);
- g->gray = h->gclist;
- return traversetable(g, h);
+ g->gray = h->gclist; /* remove from 'gray' list */
+ size = traversetable(g, h);
+ break;
+ }
+ case LUA_TLCL: {
+ LClosure *cl = gco2lcl(o);
+ g->gray = cl->gclist; /* remove from 'gray' list */
+ size = traverseLclosure(g, cl);
+ break;
}
- case LUA_TFUNCTION: {
- Closure *cl = gco2cl(o);
- g->gray = cl->c.gclist;
- return traverseclosure(g, cl);
+ case LUA_TCCL: {
+ CClosure *cl = gco2ccl(o);
+ g->gray = cl->gclist; /* remove from 'gray' list */
+ size = traverseCclosure(g, cl);
+ break;
}
case LUA_TTHREAD: {
lua_State *th = gco2th(o);
- g->gray = th->gclist;
+ g->gray = th->gclist; /* remove from 'gray' list */
th->gclist = g->grayagain;
- g->grayagain = o;
+ g->grayagain = o; /* insert into 'grayagain' list */
black2gray(o);
- return traversestack(g, th);
+ size = traversestack(g, th);
+ break;
}
case LUA_TPROTO: {
Proto *p = gco2p(o);
- g->gray = p->gclist;
- return traverseproto(g, p);
+ g->gray = p->gclist; /* remove from 'gray' list */
+ size = traverseproto(g, p);
+ break;
}
- default: lua_assert(0); return 0;
+ default: lua_assert(0); return;
}
+ g->GCmemtrav += size;
}
@@ -599,12 +624,12 @@ static void convergeephemerons (global_State *g) {
** clear entries with unmarked keys from all weaktables in list 'l' up
** to element 'f'
*/
-static void clearkeys (GCObject *l, GCObject *f) {
+static void clearkeys (global_State *g, GCObject *l, GCObject *f) {
for (; l != f; l = gco2t(l)->gclist) {
Table *h = gco2t(l);
Node *n, *limit = gnodelast(h);
for (n = gnode(h, 0); n < limit; n++) {
- if (!ttisnil(gval(n)) && (iscleared(gkey(n)))) {
+ if (!ttisnil(gval(n)) && (iscleared(g, gkey(n)))) {
setnilvalue(gval(n)); /* remove value ... */
removeentry(n); /* and remove entry from table */
}
@@ -617,18 +642,18 @@ static void clearkeys (GCObject *l, GCObject *f) {
** clear entries with unmarked values from all weaktables in list 'l' up
** to element 'f'
*/
-static void clearvalues (GCObject *l, GCObject *f) {
+static void clearvalues (global_State *g, GCObject *l, GCObject *f) {
for (; l != f; l = gco2t(l)->gclist) {
Table *h = gco2t(l);
Node *n, *limit = gnodelast(h);
int i;
for (i = 0; i < h->sizearray; i++) {
TValue *o = &h->array[i];
- if (iscleared(o)) /* value was collected? */
+ if (iscleared(g, o)) /* value was collected? */
setnilvalue(o); /* remove value */
}
for (n = gnode(h, 0); n < limit; n++) {
- if (!ttisnil(gval(n)) && iscleared(gval(n))) {
+ if (!ttisnil(gval(n)) && iscleared(g, gval(n))) {
setnilvalue(gval(n)); /* remove value ... */
removeentry(n); /* and remove entry from table */
}
@@ -640,13 +665,22 @@ static void clearvalues (GCObject *l, GCObject *f) {
static void freeobj (lua_State *L, GCObject *o) {
switch (gch(o)->tt) {
case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break;
- case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break;
+ case LUA_TLCL: {
+ luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues));
+ break;
+ }
+ case LUA_TCCL: {
+ luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues));
+ break;
+ }
case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break;
case LUA_TTABLE: luaH_free(L, gco2t(o)); break;
case LUA_TTHREAD: luaE_freethread(L, gco2th(o)); break;
case LUA_TUSERDATA: luaM_freemem(L, o, sizeudata(gco2u(o))); break;
- case LUA_TSTRING: {
+ case LUA_TSHRSTR:
G(L)->strt.nuse--;
+ /* go through */
+ case LUA_TLNGSTR: {
luaM_freemem(L, o, sizestring(gco2ts(o)));
break;
}
@@ -689,7 +723,6 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {
int ow = otherwhite(g);
int toclear, toset; /* bits to clear and to set in all live objects */
int tostop; /* stop sweep when this is true */
- l_mem debt = g->GCdebt; /* current debt */
if (isgenerational(g)) { /* generational mode? */
toclear = ~0; /* clear nothing */
toset = bitmask(OLDBIT); /* set the old bit of all surviving objects */
@@ -708,19 +741,30 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {
freeobj(L, curr); /* erase 'curr' */
}
else {
+ if (testbits(marked, tostop))
+ return NULL; /* stop sweeping this list */
if (gch(curr)->tt == LUA_TTHREAD)
sweepthread(L, gco2th(curr)); /* sweep thread's upvalues */
- if (testbits(marked, tostop)) {
- static GCObject *nullp = NULL;
- p = &nullp; /* stop sweeping this list */
- break;
- }
/* update marks */
gch(curr)->marked = cast_byte((marked & toclear) | toset);
p = &gch(curr)->next; /* go to next element */
}
}
- luaE_setdebt(g, debt); /* sweeping should not change debt */
+ return (*p == NULL) ? NULL : p;
+}
+
+
+/*
+** sweep a list until a live object (or end of list)
+*/
+static GCObject **sweeptolive (lua_State *L, GCObject **p, int *n) {
+ GCObject ** old = p;
+ int i = 0;
+ do {
+ i++;
+ p = sweeplist(L, p, 1);
+ } while (p == old);
+ if (n) *n += i;
return p;
}
@@ -783,12 +827,14 @@ static void GCTM (lua_State *L, int propagateerrors) {
L->allowhook = oldah; /* restore hooks */
g->gcrunning = running; /* restore state */
if (status != LUA_OK && propagateerrors) { /* error while running __gc? */
- if (status == LUA_ERRRUN) { /* is there an error msg.? */
- luaO_pushfstring(L, "error in __gc metamethod (%s)",
- lua_tostring(L, -1));
+ if (status == LUA_ERRRUN) { /* is there an error object? */
+ const char *msg = (ttisstring(L->top - 1))
+ ? svalue(L->top - 1)
+ : "no message";
+ luaO_pushfstring(L, "error in __gc metamethod (%s)", msg);
status = LUA_ERRGCMM; /* error in __gc metamethod */
}
- luaD_throw(L, status); /* re-send error */
+ luaD_throw(L, status); /* re-throw error */
}
}
}
@@ -834,12 +880,21 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
return; /* nothing to be done */
else { /* move 'o' to 'finobj' list */
GCObject **p;
- for (p = &g->allgc; *p != o; p = &gch(*p)->next) ;
- *p = gch(o)->next; /* remove 'o' from root list */
- gch(o)->next = g->finobj; /* link it in list 'finobj' */
+ GCheader *ho = gch(o);
+ if (g->sweepgc == &ho->next) { /* avoid removing current sweep object */
+ lua_assert(issweepphase(g));
+ g->sweepgc = sweeptolive(L, g->sweepgc, NULL);
+ }
+ /* search for pointer pointing to 'o' */
+ for (p = &g->allgc; *p != o; p = &gch(*p)->next) { /* empty */ }
+ *p = ho->next; /* remove 'o' from root list */
+ ho->next = g->finobj; /* link it in list 'finobj' */
g->finobj = o;
- l_setbit(gch(o)->marked, SEPARATED); /* mark it as such */
- resetoldbit(o); /* see MOVE OLD rule */
+ l_setbit(ho->marked, SEPARATED); /* mark it as such */
+ if (!keepinvariant(g)) /* not keeping invariant? */
+ makewhite(g, o); /* "sweep" object */
+ else
+ resetoldbit(o); /* see MOVE OLD rule */
}
}
@@ -856,6 +911,28 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
#define sweepphases \
(bitmask(GCSsweepstring) | bitmask(GCSsweepudata) | bitmask(GCSsweep))
+
+/*
+** enter first sweep phase (strings) and prepare pointers for other
+** sweep phases. The calls to 'sweeptolive' make pointers point to an
+** object inside the list (instead of to the header), so that the real
+** sweep do not need to skip objects created between "now" and the start
+** of the real sweep.
+** Returns how many objects it sweeped.
+*/
+static int entersweep (lua_State *L) {
+ global_State *g = G(L);
+ int n = 0;
+ g->gcstate = GCSsweepstring;
+ lua_assert(g->sweepgc == NULL && g->sweepfin == NULL);
+ /* prepare to sweep strings, finalizable objects, and regular objects */
+ g->sweepstrgc = 0;
+ g->sweepfin = sweeptolive(L, &g->finobj, &n);
+ g->sweepgc = sweeptolive(L, &g->allgc, &n);
+ return n;
+}
+
+
/*
** change GC mode
*/
@@ -865,15 +942,14 @@ void luaC_changemode (lua_State *L, int mode) {
if (mode == KGC_GEN) { /* change to generational mode */
/* make sure gray lists are consistent */
luaC_runtilstate(L, bitmask(GCSpropagate));
- g->lastmajormem = gettotalbytes(g);
+ g->GCestimate = gettotalbytes(g);
g->gckind = KGC_GEN;
}
else { /* change to incremental mode */
/* sweep all objects to turn them back to white
(as white has not changed, nothing extra will be collected) */
- g->sweepstrgc = 0;
- g->gcstate = GCSsweepstring;
g->gckind = KGC_NORMAL;
+ entersweep(L);
luaC_runtilstate(L, ~sweepphases);
}
}
@@ -907,8 +983,9 @@ void luaC_freeallobjects (lua_State *L) {
}
-static void atomic (lua_State *L) {
+static l_mem atomic (lua_State *L) {
global_State *g = G(L);
+ l_mem work = -g->GCmemtrav; /* start counting work */
GCObject *origweak, *origall;
lua_assert(!iswhite(obj2gco(g->mainthread)));
markobject(g, L); /* mark running thread */
@@ -917,77 +994,87 @@ static void atomic (lua_State *L) {
markmt(g); /* mark basic metatables */
/* remark occasional upvalues of (maybe) dead threads */
remarkupvals(g);
+ propagateall(g); /* propagate changes */
+ work += g->GCmemtrav; /* stop counting (do not (re)count grays) */
/* traverse objects caught by write barrier and by 'remarkupvals' */
retraversegrays(g);
+ work -= g->GCmemtrav; /* restart counting */
convergeephemerons(g);
/* at this point, all strongly accessible objects are marked. */
/* clear values from weak tables, before checking finalizers */
- clearvalues(g->weak, NULL);
- clearvalues(g->allweak, NULL);
+ clearvalues(g, g->weak, NULL);
+ clearvalues(g, g->allweak, NULL);
origweak = g->weak; origall = g->allweak;
+ work += g->GCmemtrav; /* stop counting (objects being finalized) */
separatetobefnz(L, 0); /* separate objects to be finalized */
- markbeingfnz(g); /* mark userdata that will be finalized */
+ markbeingfnz(g); /* mark objects that will be finalized */
propagateall(g); /* remark, to propagate `preserveness' */
+ work -= g->GCmemtrav; /* restart counting */
convergeephemerons(g);
/* at this point, all resurrected objects are marked. */
/* remove dead objects from weak tables */
- clearkeys(g->ephemeron, NULL); /* clear keys from all ephemeron tables */
- clearkeys(g->allweak, NULL); /* clear keys from all allweak tables */
+ clearkeys(g, g->ephemeron, NULL); /* clear keys from all ephemeron tables */
+ clearkeys(g, g->allweak, NULL); /* clear keys from all allweak tables */
/* clear values from resurrected weak tables */
- clearvalues(g->weak, origweak);
- clearvalues(g->allweak, origall);
- g->sweepstrgc = 0; /* prepare to sweep strings */
- g->gcstate = GCSsweepstring;
+ clearvalues(g, g->weak, origweak);
+ clearvalues(g, g->allweak, origall);
g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */
- /*lua_checkmemory(L);*/
+ work += g->GCmemtrav; /* complete counting */
+ return work; /* estimate of memory marked by 'atomic' */
}
-static l_mem singlestep (lua_State *L) {
+static lu_mem singlestep (lua_State *L) {
global_State *g = G(L);
switch (g->gcstate) {
case GCSpause: {
+ g->GCmemtrav = 0; /* start to count memory traversed */
if (!isgenerational(g))
markroot(g); /* start a new collection */
- /* in any case, root must be marked */
+ /* in any case, root must be marked at this point */
lua_assert(!iswhite(obj2gco(g->mainthread))
&& !iswhite(gcvalue(&g->l_registry)));
g->gcstate = GCSpropagate;
- return GCROOTCOST;
+ return g->GCmemtrav;
}
case GCSpropagate: {
- if (g->gray)
- return propagatemark(g);
+ if (g->gray) {
+ lu_mem oldtrav = g->GCmemtrav;
+ propagatemark(g);
+ return g->GCmemtrav - oldtrav; /* memory traversed in this step */
+ }
else { /* no more `gray' objects */
+ lu_mem work;
+ int sw;
g->gcstate = GCSatomic; /* finish mark phase */
- atomic(L);
- return GCATOMICCOST;
+ g->GCestimate = g->GCmemtrav; /* save what was counted */;
+ work = atomic(L); /* add what was traversed by 'atomic' */
+ g->GCestimate += work; /* estimate of total memory traversed */
+ sw = entersweep(L);
+ return work + sw * GCSWEEPCOST;
}
}
case GCSsweepstring: {
- if (g->sweepstrgc < g->strt.size) {
- sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]);
- return GCSWEEPCOST;
- }
- else { /* no more strings to sweep */
- g->sweepgc = &g->finobj; /* prepare to sweep finalizable objects */
+ int i;
+ for (i = 0; i < GCSWEEPMAX && g->sweepstrgc + i < g->strt.size; i++)
+ sweepwholelist(L, &g->strt.hash[g->sweepstrgc + i]);
+ g->sweepstrgc += i;
+ if (g->sweepstrgc >= g->strt.size) /* no more strings to sweep? */
g->gcstate = GCSsweepudata;
- return 0;
- }
+ return i * GCSWEEPCOST;
}
case GCSsweepudata: {
- if (*g->sweepgc) {
- g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
+ if (g->sweepfin) {
+ g->sweepfin = sweeplist(L, g->sweepfin, GCSWEEPMAX);
return GCSWEEPMAX*GCSWEEPCOST;
}
else {
- g->sweepgc = &g->allgc; /* go to next phase */
g->gcstate = GCSsweep;
- return GCSWEEPCOST;
+ return 0;
}
}
case GCSsweep: {
- if (*g->sweepgc) {
+ if (g->sweepgc) {
g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
return GCSWEEPMAX*GCSWEEPCOST;
}
@@ -1018,43 +1105,52 @@ void luaC_runtilstate (lua_State *L, int statesmask) {
static void generationalcollection (lua_State *L) {
global_State *g = G(L);
- if (g->lastmajormem == 0) { /* signal for another major collection? */
+ if (g->GCestimate == 0) { /* signal for another major collection? */
luaC_fullgc(L, 0); /* perform a full regular collection */
- g->lastmajormem = gettotalbytes(g); /* update control */
+ g->GCestimate = gettotalbytes(g); /* update control */
}
else {
+ lu_mem estimate = g->GCestimate;
luaC_runtilstate(L, ~bitmask(GCSpause)); /* run complete cycle */
luaC_runtilstate(L, bitmask(GCSpause));
- if (gettotalbytes(g) > g->lastmajormem/100 * g->gcmajorinc)
- g->lastmajormem = 0; /* signal for a major collection */
+ if (gettotalbytes(g) > (estimate / 100) * g->gcmajorinc)
+ g->GCestimate = 0; /* signal for a major collection */
}
luaE_setdebt(g, stddebt(g));
}
-static void step (lua_State *L) {
+static void incstep (lua_State *L) {
global_State *g = G(L);
- l_mem lim = g->gcstepmul; /* how much to work */
+ l_mem debt = g->GCdebt;
+ int stepmul = g->gcstepmul;
+ if (stepmul < 40) stepmul = 40; /* avoid ridiculous low values */
+ /* convert debt from Kb to 'work units' (avoid zero debt and overflows) */
+ debt = (debt / STEPMULADJ) + 1;
+ debt = (debt < MAX_LMEM / stepmul) ? debt * stepmul : MAX_LMEM;
do { /* always perform at least one single step */
- lim -= singlestep(L);
- } while (lim > 0 && g->gcstate != GCSpause);
- if (g->gcstate != GCSpause)
- luaE_setdebt(g, g->GCdebt - GCSTEPSIZE);
+ lu_mem work = singlestep(L); /* do some work */
+ debt -= work;
+ } while (debt > -GCSTEPSIZE && g->gcstate != GCSpause);
+ if (g->gcstate == GCSpause)
+ debt = stddebtest(g, g->GCestimate); /* pause until next cycle */
else
- luaE_setdebt(g, stddebt(g));
+ debt = (debt / stepmul) * STEPMULADJ; /* convert 'work units' to Kb */
+ luaE_setdebt(g, debt);
}
/*
-** performs a basic GC step even if the collector is stopped
+** performs a basic GC step
*/
void luaC_forcestep (lua_State *L) {
global_State *g = G(L);
int i;
if (isgenerational(g)) generationalcollection(L);
- else step(L);
- for (i = 0; i < GCFINALIZENUM && g->tobefnz; i++)
- GCTM(L, 1); /* Call a few pending finalizers */
+ else incstep(L);
+ /* run a few finalizers (or all of them at the end of a collect cycle) */
+ for (i = 0; g->tobefnz && (i < GCFINALIZENUM || g->gcstate == GCSpause); i++)
+ GCTM(L, 1); /* call one finalizer */
}
@@ -1062,10 +1158,13 @@ void luaC_forcestep (lua_State *L) {
** performs a basic GC step only if collector is running
*/
void luaC_step (lua_State *L) {
- if (G(L)->gcrunning) luaC_forcestep(L);
+ global_State *g = G(L);
+ if (g->gcrunning) luaC_forcestep(L);
+ else luaE_setdebt(g, -GCSTEPSIZE); /* avoid being called too often */
}
+
/*
** performs a full GC cycle; if "isemergency", does not call
** finalizers (which could change stack positions)
@@ -1073,16 +1172,19 @@ void luaC_step (lua_State *L) {
void luaC_fullgc (lua_State *L, int isemergency) {
global_State *g = G(L);
int origkind = g->gckind;
+ int someblack = keepinvariant(g);
lua_assert(origkind != KGC_EMERGENCY);
- if (!isemergency) /* do not run finalizers during emergency GC */
+ if (isemergency) /* do not run finalizers during emergency GC */
+ g->gckind = KGC_EMERGENCY;
+ else {
+ g->gckind = KGC_NORMAL;
callallpendingfinalizers(L, 1);
- if (keepinvariant(g)) { /* marking phase? */
+ }
+ if (someblack) { /* may there be some black objects? */
/* must sweep all objects to turn them back to white
(as white has not changed, nothing will be collected) */
- g->sweepstrgc = 0;
- g->gcstate = GCSsweepstring;
+ entersweep(L);
}
- g->gckind = isemergency ? KGC_EMERGENCY : KGC_NORMAL;
/* finish any pending sweep phase to start a new cycle */
luaC_runtilstate(L, bitmask(GCSpause));
/* run entire collector */
diff --git a/depends/lua/src/llex.c b/depends/lua/src/llex.c
index 74deebb9..c4d8c65b 100644
--- a/depends/lua/src/llex.c
+++ b/depends/lua/src/llex.c
@@ -1,5 +1,5 @@
/*
-** $Id: llex.c,v 2.59 2011/11/30 12:43:51 roberto Exp $
+** $Id: llex.c,v 2.61 2012/01/23 23:05:51 roberto Exp $
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
@@ -67,7 +67,7 @@ void luaX_init (lua_State *L) {
for (i=0; i<NUM_RESERVED; i++) {
TString *ts = luaS_new(L, luaX_tokens[i]);
luaS_fix(ts); /* reserved words are never collected */
- ts->tsv.reserved = cast_byte(i+1); /* reserved word */
+ ts->tsv.extra = cast_byte(i+1); /* reserved word */
}
}
@@ -222,13 +222,24 @@ static void trydecpoint (LexState *ls, SemInfo *seminfo) {
/* LUA_NUMBER */
+/*
+** this function is quite liberal in what it accepts, as 'luaO_str2d'
+** will reject ill-formed numerals.
+*/
static void read_numeral (LexState *ls, SemInfo *seminfo) {
+ const char *expo = "Ee";
+ int first = ls->current;
lua_assert(lisdigit(ls->current));
- do {
- save_and_next(ls);
- if (check_next(ls, "EePp")) /* exponent part? */
+ save_and_next(ls);
+ if (first == '0' && check_next(ls, "Xx")) /* hexadecimal? */
+ expo = "Pp";
+ for (;;) {
+ if (check_next(ls, expo)) /* exponent part? */
check_next(ls, "+-"); /* optional exponent sign */
- } while (lislalnum(ls->current) || ls->current == '.');
+ if (lisxdigit(ls->current) || ls->current == '.')
+ save_and_next(ls);
+ else break;
+ }
save(ls, '\0');
buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */
if (!buff2d(ls->buff, &seminfo->r)) /* format error? */
@@ -480,8 +491,8 @@ static int llex (LexState *ls, SemInfo *seminfo) {
ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
luaZ_bufflen(ls->buff));
seminfo->ts = ts;
- if (ts->tsv.reserved > 0) /* reserved word? */
- return ts->tsv.reserved - 1 + FIRST_RESERVED;
+ if (isreserved(ts)) /* reserved word? */
+ return ts->tsv.extra - 1 + FIRST_RESERVED;
else {
return TK_NAME;
}
diff --git a/depends/lua/src/lmathlib.c b/depends/lua/src/lmathlib.c
index b17237f0..c3c605e8 100644
--- a/depends/lua/src/lmathlib.c
+++ b/depends/lua/src/lmathlib.c
@@ -1,5 +1,5 @@
/*
-** $Id: lmathlib.c,v 1.80 2011/07/05 12:49:35 roberto Exp $
+** $Id: lmathlib.c,v 1.81 2012/05/18 17:47:53 roberto Exp $
** Standard mathematical library
** See Copyright Notice in lua.h
*/
@@ -17,17 +17,17 @@
#include "lualib.h"
-#undef PI
-#define PI (3.14159265358979323846)
-#define RADIANS_PER_DEGREE (PI/180.0)
-
-
/* macro 'l_tg' allows the addition of an 'l' or 'f' to all math operations */
#if !defined(l_tg)
#define l_tg(x) (x)
#endif
+#undef PI
+#define PI (l_tg(3.1415926535897932384626433832795))
+#define RADIANS_PER_DEGREE (PI/180.0)
+
+
static int math_abs (lua_State *L) {
lua_pushnumber(L, l_tg(fabs)(luaL_checknumber(L, 1)));
diff --git a/depends/lua/src/lmem.c b/depends/lua/src/lmem.c
index 792deb3c..3f88496e 100644
--- a/depends/lua/src/lmem.c
+++ b/depends/lua/src/lmem.c
@@ -1,5 +1,5 @@
/*
-** $Id: lmem.c,v 1.83 2011/11/30 12:42:49 roberto Exp $
+** $Id: lmem.c,v 1.84 2012/05/23 15:41:53 roberto Exp $
** Interface to Memory Manager
** See Copyright Notice in lua.h
*/
@@ -94,22 +94,6 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
}
lua_assert((nsize == 0) == (newblock == NULL));
g->GCdebt = (g->GCdebt + nsize) - realosize;
-#if defined(TRACEMEM)
- { /* auxiliary patch to monitor garbage collection.
- ** To plot, gnuplot with following command:
- ** plot TRACEMEM using 1:2 with lines, TRACEMEM using 1:3 with lines
- */
- static unsigned long total = 0; /* our "time" */
- static FILE *f = NULL; /* output file */
- total++; /* "time" always grows */
- if ((total % 200) == 0) {
- if (f == NULL) f = fopen(TRACEMEM, "w");
- fprintf(f, "%lu %u %d %d\n", total,
- gettotalbytes(g), g->GCdebt, g->gcstate * 10000);
- }
- }
-#endif
-
return newblock;
}
diff --git a/depends/lua/src/loadlib.c b/depends/lua/src/loadlib.c
index 783bc12b..a9959277 100644
--- a/depends/lua/src/loadlib.c
+++ b/depends/lua/src/loadlib.c
@@ -1,5 +1,5 @@
/*
-** $Id: loadlib.c,v 1.108 2011/12/12 16:34:03 roberto Exp $
+** $Id: loadlib.c,v 1.111 2012/05/30 12:33:44 roberto Exp $
** Dynamic library loader for Lua
** See Copyright Notice in lua.h
**
@@ -92,9 +92,9 @@
#define LUA_OFSEP "_"
-#define LIBPREFIX "LOADLIB: "
+/* table (in the registry) that keeps handles for all loaded C libraries */
+#define CLIBS "_CLIBS"
-#define POF LUA_POF
#define LIB_FAIL "open"
@@ -248,48 +248,54 @@ static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
#endif
-
-static void **ll_register (lua_State *L, const char *path) {
- void **plib;
- lua_pushfstring(L, "%s%s", LIBPREFIX, path);
- lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */
- if (!lua_isnil(L, -1)) /* is there an entry? */
- plib = (void **)lua_touserdata(L, -1);
- else { /* no entry yet; create one */
- lua_pop(L, 1); /* remove result from gettable */
- plib = (void **)lua_newuserdata(L, sizeof(const void *));
- *plib = NULL;
- luaL_setmetatable(L, "_LOADLIB");
- lua_pushfstring(L, "%s%s", LIBPREFIX, path);
- lua_pushvalue(L, -2);
- lua_settable(L, LUA_REGISTRYINDEX);
- }
+static void *ll_checkclib (lua_State *L, const char *path) {
+ void *plib;
+ lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
+ lua_getfield(L, -1, path);
+ plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */
+ lua_pop(L, 2); /* pop CLIBS table and 'plib' */
return plib;
}
+static void ll_addtoclib (lua_State *L, const char *path, void *plib) {
+ lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
+ lua_pushlightuserdata(L, plib);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, path); /* CLIBS[path] = plib */
+ lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */
+ lua_pop(L, 1); /* pop CLIBS table */
+}
+
+
/*
-** __gc tag method: calls library's `ll_unloadlib' function with the lib
-** handle
+** __gc tag method for CLIBS table: calls 'll_unloadlib' for all lib
+** handles in list CLIBS
*/
static int gctm (lua_State *L) {
- void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
- if (*lib) ll_unloadlib(*lib);
- *lib = NULL; /* mark library as closed */
+ int n = luaL_len(L, 1);
+ for (; n >= 1; n--) { /* for each handle, in reverse order */
+ lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */
+ ll_unloadlib(lua_touserdata(L, -1));
+ lua_pop(L, 1); /* pop handle */
+ }
return 0;
}
static int ll_loadfunc (lua_State *L, const char *path, const char *sym) {
- void **reg = ll_register(L, path);
- if (*reg == NULL) *reg = ll_load(L, path, *sym == '*');
- if (*reg == NULL) return ERRLIB; /* unable to load library */
+ void *reg = ll_checkclib(L, path); /* check loaded C libraries */
+ if (reg == NULL) { /* must load library? */
+ reg = ll_load(L, path, *sym == '*');
+ if (reg == NULL) return ERRLIB; /* unable to load library */
+ ll_addtoclib(L, path, reg);
+ }
if (*sym == '*') { /* loading only library (no function)? */
lua_pushboolean(L, 1); /* return 'true' */
return 0; /* no errors */
}
else {
- lua_CFunction f = ll_sym(L, *reg, sym);
+ lua_CFunction f = ll_sym(L, reg, sym);
if (f == NULL)
return ERRFUNC; /* unable to find function */
lua_pushcfunction(L, f); /* else create new function */
@@ -418,12 +424,12 @@ static int loadfunc (lua_State *L, const char *filename, const char *modname) {
if (mark) {
int stat;
funcname = lua_pushlstring(L, modname, mark - modname);
- funcname = lua_pushfstring(L, POF"%s", funcname);
+ funcname = lua_pushfstring(L, LUA_POF"%s", funcname);
stat = ll_loadfunc(L, filename, funcname);
if (stat != ERRFUNC) return stat;
modname = mark + 1; /* else go ahead and try old-style name */
}
- funcname = lua_pushfstring(L, POF"%s", modname);
+ funcname = lua_pushfstring(L, LUA_POF"%s", modname);
return ll_loadfunc(L, filename, funcname);
}
@@ -476,9 +482,9 @@ static void findloader (lua_State *L, const char *name) {
lua_getfield(L, lua_upvalueindex(1), "searchers"); /* will be at index 3 */
if (!lua_istable(L, 3))
luaL_error(L, LUA_QL("package.searchers") " must be a table");
- /* iterate over available seachers to find a loader */
+ /* iterate over available searchers to find a loader */
for (i = 1; ; i++) {
- lua_rawgeti(L, 3, i); /* get a seacher */
+ lua_rawgeti(L, 3, i); /* get a searcher */
if (lua_isnil(L, -1)) { /* no more searchers? */
lua_pop(L, 1); /* remove nil */
luaL_pushresult(&msg); /* create error message */
@@ -666,18 +672,10 @@ static const luaL_Reg ll_funcs[] = {
};
-static const lua_CFunction searchers[] =
- {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL};
-
-
-LUAMOD_API int luaopen_package (lua_State *L) {
+static void createsearcherstable (lua_State *L) {
+ static const lua_CFunction searchers[] =
+ {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL};
int i;
- /* create new type _LOADLIB */
- luaL_newmetatable(L, "_LOADLIB");
- lua_pushcfunction(L, gctm);
- lua_setfield(L, -2, "__gc");
- /* create `package' table */
- luaL_newlib(L, pk_funcs);
/* create 'searchers' table */
lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0);
/* fill it with pre-defined searchers */
@@ -686,6 +684,19 @@ LUAMOD_API int luaopen_package (lua_State *L) {
lua_pushcclosure(L, searchers[i], 1);
lua_rawseti(L, -2, i+1);
}
+}
+
+
+LUAMOD_API int luaopen_package (lua_State *L) {
+ /* create table CLIBS to keep track of loaded C libraries */
+ luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS);
+ lua_createtable(L, 0, 1); /* metatable for CLIBS */
+ lua_pushcfunction(L, gctm);
+ lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */
+ lua_setmetatable(L, -2);
+ /* create `package' table */
+ luaL_newlib(L, pk_funcs);
+ createsearcherstable(L);
#if defined(LUA_COMPAT_LOADERS)
lua_pushvalue(L, -1); /* make a copy of 'searchers' table */
lua_setfield(L, -3, "loaders"); /* put it in field `loaders' */
diff --git a/depends/lua/src/lopcodes.c b/depends/lua/src/lopcodes.c
index 2e346766..ef736927 100644
--- a/depends/lua/src/lopcodes.c
+++ b/depends/lua/src/lopcodes.c
@@ -1,5 +1,6 @@
/*
-** $Id: lopcodes.c,v 1.48 2011/04/19 16:22:13 roberto Exp $
+** $Id: lopcodes.c,v 1.49 2012/05/14 13:34:18 roberto Exp $
+** Opcodes for Lua virtual machine
** See Copyright Notice in lua.h
*/
diff --git a/depends/lua/src/loslib.c b/depends/lua/src/loslib.c
index 881667da..489755b6 100644
--- a/depends/lua/src/loslib.c
+++ b/depends/lua/src/loslib.c
@@ -1,5 +1,5 @@
/*
-** $Id: loslib.c,v 1.38 2011/11/30 12:35:05 roberto Exp $
+** $Id: loslib.c,v 1.39 2012/05/23 15:37:09 roberto Exp $
** Standard Operating System library
** See Copyright Notice in lua.h
*/
@@ -26,11 +26,12 @@
#if !defined(LUA_STRFTIMEOPTIONS)
#if !defined(LUA_USE_POSIX)
-#define LUA_STRFTIMEOPTIONS { "aAbBcdHIjmMpSUwWxXyYz%", "" }
+#define LUA_STRFTIMEOPTIONS { "aAbBcdHIjmMpSUwWxXyYz%", "" }
#else
-#define LUA_STRFTIMEOPTIONS { "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%", "", \
- "E", "cCxXyY", \
- "O", "deHImMSuUVwWy" }
+#define LUA_STRFTIMEOPTIONS \
+ { "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%", "" \
+ "", "E", "cCxXyY", \
+ "O", "deHImMSuUVwWy" }
#endif
#endif
@@ -43,7 +44,7 @@
*/
#if defined(LUA_USE_MKSTEMP)
#include <unistd.h>
-#define LUA_TMPNAMBUFSIZE 32
+#define LUA_TMPNAMBUFSIZE 32
#define lua_tmpnam(b,e) { \
strcpy(b, "/tmp/lua_XXXXXX"); \
e = mkstemp(b); \
@@ -52,8 +53,8 @@
#elif !defined(lua_tmpnam)
-#define LUA_TMPNAMBUFSIZE L_tmpnam
-#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); }
+#define LUA_TMPNAMBUFSIZE L_tmpnam
+#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); }
#endif
diff --git a/depends/lua/src/lparser.c b/depends/lua/src/lparser.c
index 4d689365..b3eb3ca9 100644
--- a/depends/lua/src/lparser.c
+++ b/depends/lua/src/lparser.c
@@ -1,5 +1,5 @@
/*
-** $Id: lparser.c,v 2.124 2011/12/02 13:23:56 roberto Exp $
+** $Id: lparser.c,v 2.128 2012/05/20 14:51:23 roberto Exp $
** Lua Parser
** See Copyright Notice in lua.h
*/
@@ -222,7 +222,7 @@ static int searchupvalue (FuncState *fs, TString *name) {
int i;
Upvaldesc *up = fs->f->upvalues;
for (i = 0; i < fs->nups; i++) {
- if (eqstr(up[i].name, name)) return i;
+ if (luaS_eqstr(up[i].name, name)) return i;
}
return -1; /* not found */
}
@@ -246,7 +246,7 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
static int searchvar (FuncState *fs, TString *n) {
int i;
for (i=fs->nactvar-1; i >= 0; i--) {
- if (eqstr(n, getlocvar(fs, i)->varname))
+ if (luaS_eqstr(n, getlocvar(fs, i)->varname))
return i;
}
return -1; /* not found */
@@ -342,7 +342,7 @@ static void closegoto (LexState *ls, int g, Labeldesc *label) {
FuncState *fs = ls->fs;
Labellist *gl = &ls->dyd->gt;
Labeldesc *gt = &gl->arr[g];
- lua_assert(eqstr(gt->name, label->name));
+ lua_assert(luaS_eqstr(gt->name, label->name));
if (gt->nactvar < label->nactvar) {
TString *vname = getlocvar(fs, gt->nactvar)->varname;
const char *msg = luaO_pushfstring(ls->L,
@@ -369,7 +369,7 @@ static int findlabel (LexState *ls, int g) {
/* check labels in current block for a match */
for (i = bl->firstlabel; i < dyd->label.n; i++) {
Labeldesc *lb = &dyd->label.arr[i];
- if (eqstr(lb->name, gt->name)) { /* correct label? */
+ if (luaS_eqstr(lb->name, gt->name)) { /* correct label? */
if (gt->nactvar > lb->nactvar &&
(bl->upval || dyd->label.n > bl->firstlabel))
luaK_patchclose(ls->fs, gt->pc, lb->nactvar);
@@ -403,7 +403,7 @@ static void findgotos (LexState *ls, Labeldesc *lb) {
Labellist *gl = &ls->dyd->gt;
int i = ls->fs->bl->firstgoto;
while (i < gl->n) {
- if (eqstr(gl->arr[i].name, lb->name))
+ if (luaS_eqstr(gl->arr[i].name, lb->name))
closegoto(ls, i, lb);
else
i++;
@@ -461,7 +461,7 @@ static void breaklabel (LexState *ls) {
** message when label name is a reserved word (which can only be 'break')
*/
static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
- const char *msg = (gt->name->tsv.reserved > 0)
+ const char *msg = isreserved(gt->name)
? "<%s> at line %d not inside a loop"
: "no visible label " LUA_QS " for <goto> at line %d";
msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line);
@@ -493,21 +493,30 @@ static void leaveblock (FuncState *fs) {
/*
-** adds prototype being created into its parent list of prototypes
-** and codes instruction to create new closure
+** adds a new prototype into list of prototypes
*/
-static void codeclosure (LexState *ls, Proto *clp, expdesc *v) {
- FuncState *fs = ls->fs->prev;
- Proto *f = fs->f; /* prototype of function creating new closure */
+static Proto *addprototype (LexState *ls) {
+ Proto *clp;
+ lua_State *L = ls->L;
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f; /* prototype of current function */
if (fs->np >= f->sizep) {
int oldsize = f->sizep;
- luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *,
- MAXARG_Bx, "functions");
+ luaM_growvector(L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "functions");
while (oldsize < f->sizep) f->p[oldsize++] = NULL;
}
- f->p[fs->np++] = clp;
- luaC_objbarrier(ls->L, f, clp);
- init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1));
+ f->p[fs->np++] = clp = luaF_newproto(L);
+ luaC_objbarrier(L, f, clp);
+ return clp;
+}
+
+
+/*
+** codes instruction to create new closure in parent function
+*/
+static void codeclosure (LexState *ls, expdesc *v) {
+ FuncState *fs = ls->fs->prev;
+ init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1));
luaK_exp2nextreg(fs, v); /* fix it at stack top (for GC) */
}
@@ -529,13 +538,9 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
fs->nactvar = 0;
fs->firstlocal = ls->dyd->actvar.n;
fs->bl = NULL;
- f = luaF_newproto(L);
- fs->f = f;
+ f = fs->f;
f->source = ls->source;
f->maxstacksize = 2; /* registers 0/1 are always valid */
- /* anchor prototype (to avoid being collected) */
- setptvalue2s(L, L->top, f);
- incr_top(L);
fs->h = luaH_new(L);
/* anchor table of constants (to avoid being collected) */
sethvalue2s(L, L->top, fs->h);
@@ -568,20 +573,6 @@ static void close_func (LexState *ls) {
anchor_token(ls);
L->top--; /* pop table of constants */
luaC_checkGC(L);
- L->top--; /* pop prototype (after possible collection) */
-}
-
-
-/*
-** opens the main function, which is a regular vararg function with an
-** upvalue named LUA_ENV
-*/
-static void open_mainfunc (LexState *ls, FuncState *fs, BlockCnt *bl) {
- expdesc v;
- open_func(ls, fs, bl);
- fs->f->is_vararg = 1; /* main function is always vararg */
- init_exp(&v, VLOCAL, 0);
- newupvalue(fs, ls->envn, &v); /* create environment upvalue */
}
@@ -795,8 +786,9 @@ static void body (LexState *ls, expdesc *e, int ismethod, int line) {
/* body -> `(' parlist `)' block END */
FuncState new_fs;
BlockCnt bl;
- open_func(ls, &new_fs, &bl);
+ new_fs.f = addprototype(ls);
new_fs.f->linedefined = line;
+ open_func(ls, &new_fs, &bl);
checknext(ls, '(');
if (ismethod) {
new_localvarliteral(ls, "self"); /* create 'self' parameter */
@@ -807,7 +799,7 @@ static void body (LexState *ls, expdesc *e, int ismethod, int line) {
statlist(ls);
new_fs.f->lastlinedefined = ls->linenumber;
check_match(ls, TK_END, TK_FUNCTION, line);
- codeclosure(ls, new_fs.f, e);
+ codeclosure(ls, e);
close_func(ls);
}
@@ -879,8 +871,8 @@ static void funcargs (LexState *ls, expdesc *f, int line) {
*/
-static void prefixexp (LexState *ls, expdesc *v) {
- /* prefixexp -> NAME | '(' expr ')' */
+static void primaryexp (LexState *ls, expdesc *v) {
+ /* primaryexp -> NAME | '(' expr ')' */
switch (ls->t.token) {
case '(': {
int line = ls->linenumber;
@@ -901,12 +893,12 @@ static void prefixexp (LexState *ls, expdesc *v) {
}
-static void primaryexp (LexState *ls, expdesc *v) {
- /* primaryexp ->
- prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */
+static void suffixedexp (LexState *ls, expdesc *v) {
+ /* suffixedexp ->
+ primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
FuncState *fs = ls->fs;
int line = ls->linenumber;
- prefixexp(ls, v);
+ primaryexp(ls, v);
for (;;) {
switch (ls->t.token) {
case '.': { /* fieldsel */
@@ -941,7 +933,7 @@ static void primaryexp (LexState *ls, expdesc *v) {
static void simpleexp (LexState *ls, expdesc *v) {
/* simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... |
- constructor | FUNCTION body | primaryexp */
+ constructor | FUNCTION body | suffixedexp */
switch (ls->t.token) {
case TK_NUMBER: {
init_exp(v, VKNUM, 0);
@@ -981,7 +973,7 @@ static void simpleexp (LexState *ls, expdesc *v) {
return;
}
default: {
- primaryexp(ls, v);
+ suffixedexp(ls, v);
return;
}
}
@@ -1141,10 +1133,10 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) {
expdesc e;
check_condition(ls, vkisvar(lh->v.k), "syntax error");
- if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */
+ if (testnext(ls, ',')) { /* assignment -> ',' suffixedexp assignment */
struct LHS_assign nv;
nv.prev = lh;
- primaryexp(ls, &nv.v);
+ suffixedexp(ls, &nv.v);
if (nv.v.k != VINDEXED)
check_conflict(ls, lh, &nv.v);
checklimit(ls->fs, nvars + ls->L->nCcalls, LUAI_MAXCCALLS,
@@ -1200,7 +1192,7 @@ static void gotostat (LexState *ls, int pc) {
static void checkrepeated (FuncState *fs, Labellist *ll, TString *label) {
int i;
for (i = fs->bl->firstlabel; i < ll->n; i++) {
- if (eqstr(label, ll->arr[i].name)) {
+ if (luaS_eqstr(label, ll->arr[i].name)) {
const char *msg = luaO_pushfstring(fs->ls->L,
"label " LUA_QS " already defined on line %d",
getstr(label), ll->arr[i].line);
@@ -1210,6 +1202,13 @@ static void checkrepeated (FuncState *fs, Labellist *ll, TString *label) {
}
+/* skip no-op statements */
+static void skipnoopstat (LexState *ls) {
+ while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
+ statement(ls);
+}
+
+
static void labelstat (LexState *ls, TString *label, int line) {
/* label -> '::' NAME '::' */
FuncState *fs = ls->fs;
@@ -1219,9 +1218,7 @@ static void labelstat (LexState *ls, TString *label, int line) {
checknext(ls, TK_DBCOLON); /* skip double colon */
/* create new entry for this label */
l = newlabelentry(ls, ll, label, line, fs->pc);
- /* skip other no-op statements */
- while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
- statement(ls);
+ skipnoopstat(ls); /* skip other no-op statements */
if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */
/* assume that locals are already out of scope */
ll->arr[l].nactvar = fs->bl->nactvar;
@@ -1384,6 +1381,7 @@ static void test_then_block (LexState *ls, int *escapelist) {
luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */
enterblock(fs, &bl, 0); /* must enter block before 'goto' */
gotostat(ls, v.t); /* handle goto/break */
+ skipnoopstat(ls); /* skip other no-op statements */
if (block_follow(ls, 0)) { /* 'goto' is the entire block? */
leaveblock(fs);
return; /* and that is it */
@@ -1480,13 +1478,15 @@ static void exprstat (LexState *ls) {
/* stat -> func | assignment */
FuncState *fs = ls->fs;
struct LHS_assign v;
- primaryexp(ls, &v.v);
- if (v.v.k == VCALL) /* stat -> func */
- SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */
- else { /* stat -> assignment */
+ suffixedexp(ls, &v.v);
+ if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */
v.prev = NULL;
assignment(ls, &v, 1);
}
+ else { /* stat -> func */
+ check_condition(ls, v.v.k == VCALL, "syntax error");
+ SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */
+ }
}
@@ -1594,27 +1594,42 @@ static void statement (LexState *ls) {
/* }====================================================================== */
-Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
- Dyndata *dyd, const char *name, int firstchar) {
+/*
+** compiles the main function, which is a regular vararg function with an
+** upvalue named LUA_ENV
+*/
+static void mainfunc (LexState *ls, FuncState *fs) {
+ BlockCnt bl;
+ expdesc v;
+ open_func(ls, fs, &bl);
+ fs->f->is_vararg = 1; /* main function is always vararg */
+ init_exp(&v, VLOCAL, 0); /* create and... */
+ newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */
+ luaX_next(ls); /* read first token */
+ statlist(ls); /* parse main body */
+ check(ls, TK_EOS);
+ close_func(ls);
+}
+
+
+Closure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
+ Dyndata *dyd, const char *name, int firstchar) {
LexState lexstate;
FuncState funcstate;
- BlockCnt bl;
- TString *tname = luaS_new(L, name);
- setsvalue2s(L, L->top, tname); /* push name to protect it */
+ Closure *cl = luaF_newLclosure(L, 1); /* create main closure */
+ /* anchor closure (to avoid being collected) */
+ setclLvalue(L, L->top, cl);
incr_top(L);
+ funcstate.f = cl->l.p = luaF_newproto(L);
+ funcstate.f->source = luaS_new(L, name); /* create and anchor TString */
lexstate.buff = buff;
lexstate.dyd = dyd;
dyd->actvar.n = dyd->gt.n = dyd->label.n = 0;
- luaX_setinput(L, &lexstate, z, tname, firstchar);
- open_mainfunc(&lexstate, &funcstate, &bl);
- luaX_next(&lexstate); /* read first token */
- statlist(&lexstate); /* main body */
- check(&lexstate, TK_EOS);
- close_func(&lexstate);
- L->top--; /* pop name */
+ luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar);
+ mainfunc(&lexstate, &funcstate);
lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
/* all scopes should be correctly finished */
lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
- return funcstate.f;
+ return cl; /* it's on the stack too */
}
diff --git a/depends/lua/src/lstate.c b/depends/lua/src/lstate.c
index 6e2801c4..3c00c285 100644
--- a/depends/lua/src/lstate.c
+++ b/depends/lua/src/lstate.c
@@ -1,11 +1,12 @@
/*
-** $Id: lstate.c,v 2.92 2011/10/03 17:54:25 roberto Exp $
+** $Id: lstate.c,v 2.98 2012/05/30 12:33:44 roberto Exp $
** Global State
** See Copyright Notice in lua.h
*/
#include <stddef.h>
+#include <string.h>
#define lstate_c
#define LUA_CORE
@@ -38,7 +39,18 @@
#endif
-#define MEMERRMSG "not enough memory"
+#define MEMERRMSG "not enough memory"
+
+
+/*
+** a macro to help the creation of a unique random seed when a state is
+** created; the seed is used to randomize hashes.
+*/
+#if !defined(luai_makeseed)
+#include <time.h>
+#define luai_makeseed() cast(size_t, time(NULL))
+#endif
+
/*
@@ -66,6 +78,28 @@ typedef struct LG {
/*
+** Compute an initial seed as random as possible. In ANSI, rely on
+** Address Space Layout Randomization (if present) to increase
+** randomness..
+*/
+#define addbuff(b,p,e) \
+ { size_t t = cast(size_t, e); \
+ memcpy(buff + p, &t, sizeof(t)); p += sizeof(t); }
+
+static unsigned int makeseed (lua_State *L) {
+ char buff[4 * sizeof(size_t)];
+ unsigned int h = luai_makeseed();
+ int p = 0;
+ addbuff(buff, p, L); /* heap variable */
+ addbuff(buff, p, &h); /* local variable */
+ addbuff(buff, p, luaO_nilobject); /* global variable */
+ addbuff(buff, p, &lua_newstate); /* public function */
+ lua_assert(p == sizeof(buff));
+ return luaS_hash(buff, p, h);
+}
+
+
+/*
** set GCdebt to a new value keeping the value (totalbytes + GCdebt)
** invariant
*/
@@ -242,10 +276,11 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
g->frealloc = f;
g->ud = ud;
g->mainthread = L;
+ g->seed = makeseed(L);
g->uvhead.u.l.prev = &g->uvhead;
g->uvhead.u.l.next = &g->uvhead;
g->gcrunning = 0; /* no GC while building state */
- g->lastmajormem = 0;
+ g->GCestimate = 0;
g->strt.size = 0;
g->strt.nuse = 0;
g->strt.hash = NULL;
@@ -257,6 +292,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
g->allgc = NULL;
g->finobj = NULL;
g->tobefnz = NULL;
+ g->sweepgc = g->sweepfin = NULL;
g->gray = g->grayagain = NULL;
g->weak = g->ephemeron = g->allweak = NULL;
g->totalbytes = sizeof(LG);
diff --git a/depends/lua/src/lstring.c b/depends/lua/src/lstring.c
index adec415e..8b959f19 100644
--- a/depends/lua/src/lstring.c
+++ b/depends/lua/src/lstring.c
@@ -1,5 +1,5 @@
/*
-** $Id: lstring.c,v 2.19 2011/05/03 16:01:57 roberto Exp $
+** $Id: lstring.c,v 2.24 2012/05/11 14:14:42 roberto Exp $
** String table (keeps all strings handled by Lua)
** See Copyright Notice in lua.h
*/
@@ -18,7 +18,49 @@
#include "lstring.h"
+/*
+** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a string to
+** compute its hash
+*/
+#if !defined(LUAI_HASHLIMIT)
+#define LUAI_HASHLIMIT 5
+#endif
+
+
+/*
+** equality for long strings
+*/
+int luaS_eqlngstr (TString *a, TString *b) {
+ size_t len = a->tsv.len;
+ lua_assert(a->tsv.tt == LUA_TLNGSTR && b->tsv.tt == LUA_TLNGSTR);
+ return (a == b) || /* same instance or... */
+ ((len == b->tsv.len) && /* equal length and ... */
+ (memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */
+}
+
+
+/*
+** equality for strings
+*/
+int luaS_eqstr (TString *a, TString *b) {
+ return (a->tsv.tt == b->tsv.tt) &&
+ (a->tsv.tt == LUA_TSHRSTR ? eqshrstr(a, b) : luaS_eqlngstr(a, b));
+}
+
+
+unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
+ unsigned int h = seed ^ l;
+ size_t l1;
+ size_t step = (l >> LUAI_HASHLIMIT) + 1;
+ for (l1 = l; l1 >= step; l1 -= step)
+ h = h ^ ((h<<5) + (h>>2) + cast_byte(str[l1 - 1]));
+ return h;
+}
+
+/*
+** resizes the string table
+*/
void luaS_resize (lua_State *L, int newsize) {
int i;
stringtable *tb = &G(L)->strt;
@@ -50,37 +92,49 @@ void luaS_resize (lua_State *L, int newsize) {
}
-static TString *newlstr (lua_State *L, const char *str, size_t l,
- unsigned int h) {
- size_t totalsize; /* total size of TString object */
- GCObject **list; /* (pointer to) list where it will be inserted */
+/*
+** creates a new string object
+*/
+static TString *createstrobj (lua_State *L, const char *str, size_t l,
+ int tag, unsigned int h, GCObject **list) {
TString *ts;
- stringtable *tb = &G(L)->strt;
- if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
- luaM_toobig(L);
- if (tb->nuse >= cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
- luaS_resize(L, tb->size*2); /* too crowded */
+ size_t totalsize; /* total size of TString object */
totalsize = sizeof(TString) + ((l + 1) * sizeof(char));
- list = &tb->hash[lmod(h, tb->size)];
- ts = &luaC_newobj(L, LUA_TSTRING, totalsize, list, 0)->ts;
+ ts = &luaC_newobj(L, tag, totalsize, list, 0)->ts;
ts->tsv.len = l;
ts->tsv.hash = h;
- ts->tsv.reserved = 0;
+ ts->tsv.extra = 0;
memcpy(ts+1, str, l*sizeof(char));
((char *)(ts+1))[l] = '\0'; /* ending 0 */
- tb->nuse++;
return ts;
}
-TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
+/*
+** creates a new short string, inserting it into string table
+*/
+static TString *newshrstr (lua_State *L, const char *str, size_t l,
+ unsigned int h) {
+ GCObject **list; /* (pointer to) list where it will be inserted */
+ stringtable *tb = &G(L)->strt;
+ TString *s;
+ if (tb->nuse >= cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
+ luaS_resize(L, tb->size*2); /* too crowded */
+ list = &tb->hash[lmod(h, tb->size)];
+ s = createstrobj(L, str, l, LUA_TSHRSTR, h, list);
+ tb->nuse++;
+ return s;
+}
+
+
+/*
+** checks whether short string exists and reuses it or creates a new one
+*/
+static TString *internshrstr (lua_State *L, const char *str, size_t l) {
GCObject *o;
- unsigned int h = cast(unsigned int, l); /* seed */
- size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */
- size_t l1;
- for (l1=l; l1>=step; l1-=step) /* compute hash */
- h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
- for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];
+ global_State *g = G(L);
+ unsigned int h = luaS_hash(str, l, g->seed);
+ for (o = g->strt.hash[lmod(h, g->strt.size)];
o != NULL;
o = gch(o)->next) {
TString *ts = rawgco2ts(o);
@@ -92,10 +146,27 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
return ts;
}
}
- return newlstr(L, str, l, h); /* not found; create a new string */
+ return newshrstr(L, str, l, h); /* not found; create a new string */
+}
+
+
+/*
+** new string (with explicit length)
+*/
+TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
+ if (l <= LUAI_MAXSHORTLEN) /* short string? */
+ return internshrstr(L, str, l);
+ else {
+ if (l + 1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
+ luaM_toobig(L);
+ return createstrobj(L, str, l, LUA_TLNGSTR, G(L)->seed, NULL);
+ }
}
+/*
+** new zero-terminated string
+*/
TString *luaS_new (lua_State *L, const char *str) {
return luaS_newlstr(L, str, strlen(str));
}
diff --git a/depends/lua/src/lstrlib.c b/depends/lua/src/lstrlib.c
index f5d61fd8..e13098bb 100644
--- a/depends/lua/src/lstrlib.c
+++ b/depends/lua/src/lstrlib.c
@@ -1,5 +1,5 @@
/*
-** $Id: lstrlib.c,v 1.173 2011/11/30 18:24:56 roberto Exp $
+** $Id: lstrlib.c,v 1.176 2012/05/23 15:37:09 roberto Exp $
** Standard library for string operations and pattern-matching
** See Copyright Notice in lua.h
*/
@@ -30,7 +30,7 @@
/* macro to `unsign' a character */
-#define uchar(c) ((unsigned char)(c))
+#define uchar(c) ((unsigned char)(c))
@@ -119,7 +119,9 @@ static int str_rep (lua_State *L) {
char *p = luaL_buffinitsize(L, &b, totallen);
while (n-- > 1) { /* first n-1 copies (followed by separator) */
memcpy(p, s, l * sizeof(char)); p += l;
- memcpy(p, sep, lsep * sizeof(char)); p += lsep;
+ if (lsep > 0) { /* avoid empty 'memcpy' (may be expensive) */
+ memcpy(p, sep, lsep * sizeof(char)); p += lsep;
+ }
}
memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */
luaL_pushresultsize(&b, totallen);
@@ -745,20 +747,17 @@ static int str_gsub (lua_State *L) {
#if !defined(LUA_INTFRMLEN) /* { */
#if defined(LUA_USE_LONGLONG)
-#define LUA_INTFRMLEN "ll"
-#define LUA_INTFRM_T long long
+#define LUA_INTFRMLEN "ll"
+#define LUA_INTFRM_T long long
#else
-#define LUA_INTFRMLEN "l"
-#define LUA_INTFRM_T long
+#define LUA_INTFRMLEN "l"
+#define LUA_INTFRM_T long
#endif
#endif /* } */
-#define MAX_UINTFRM ((lua_Number)(~(unsigned LUA_INTFRM_T)0))
-#define MAX_INTFRM ((lua_Number)((~(unsigned LUA_INTFRM_T)0)/2))
-#define MIN_INTFRM (-(lua_Number)((~(unsigned LUA_INTFRM_T)0)/2) - 1)
/*
** LUA_FLTFRMLEN is the length modifier for float conversions in
@@ -767,8 +766,8 @@ static int str_gsub (lua_State *L) {
*/
#if !defined(LUA_FLTFRMLEN)
-#define LUA_FLTFRMLEN ""
-#define LUA_FLTFRM_T double
+#define LUA_FLTFRMLEN ""
+#define LUA_FLTFRM_T double
#endif
@@ -870,18 +869,22 @@ static int str_format (lua_State *L) {
}
case 'd': case 'i': {
lua_Number n = luaL_checknumber(L, arg);
- luaL_argcheck(L, (MIN_INTFRM - 1) < n && n < (MAX_INTFRM + 1), arg,
+ LUA_INTFRM_T ni = (LUA_INTFRM_T)n;
+ lua_Number diff = n - (lua_Number)ni;
+ luaL_argcheck(L, -1 < diff && diff < 1, arg,
"not a number in proper range");
addlenmod(form, LUA_INTFRMLEN);
- nb = sprintf(buff, form, (LUA_INTFRM_T)n);
+ nb = sprintf(buff, form, ni);
break;
}
case 'o': case 'u': case 'x': case 'X': {
lua_Number n = luaL_checknumber(L, arg);
- luaL_argcheck(L, 0 <= n && n < (MAX_UINTFRM + 1), arg,
+ unsigned LUA_INTFRM_T ni = (unsigned LUA_INTFRM_T)n;
+ lua_Number diff = n - (lua_Number)ni;
+ luaL_argcheck(L, -1 < diff && diff < 1, arg,
"not a non-negative number in proper range");
addlenmod(form, LUA_INTFRMLEN);
- nb = sprintf(buff, form, (unsigned LUA_INTFRM_T)n);
+ nb = sprintf(buff, form, ni);
break;
}
case 'e': case 'E': case 'f':
diff --git a/depends/lua/src/ltable.c b/depends/lua/src/ltable.c
index 9581add9..ffa5ecb3 100644
--- a/depends/lua/src/ltable.c
+++ b/depends/lua/src/ltable.c
@@ -1,5 +1,5 @@
/*
-** $Id: ltable.c,v 2.67 2011/11/30 12:41:45 roberto Exp $
+** $Id: ltable.c,v 2.71 2012/05/23 15:37:09 roberto Exp $
** Lua tables (hash)
** See Copyright Notice in lua.h
*/
@@ -48,10 +48,10 @@
#define MAXASIZE (1 << MAXBITS)
-#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t))))
+#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t))))
-#define hashstr(t,str) hashpow2(t, (str)->tsv.hash)
-#define hashboolean(t,p) hashpow2(t, p)
+#define hashstr(t,str) hashpow2(t, (str)->tsv.hash)
+#define hashboolean(t,p) hashpow2(t, p)
/*
@@ -98,7 +98,15 @@ static Node *mainposition (const Table *t, const TValue *key) {
switch (ttype(key)) {
case LUA_TNUMBER:
return hashnum(t, nvalue(key));
- case LUA_TSTRING:
+ case LUA_TLNGSTR: {
+ TString *s = rawtsvalue(key);
+ if (s->tsv.extra == 0) { /* no hash? */
+ s->tsv.hash = luaS_hash(getstr(s), s->tsv.len, s->tsv.hash);
+ s->tsv.extra = 1; /* now it has its hash */
+ }
+ return hashstr(t, rawtsvalue(key));
+ }
+ case LUA_TSHRSTR:
return hashstr(t, rawtsvalue(key));
case LUA_TBOOLEAN:
return hashboolean(t, bvalue(key));
@@ -453,12 +461,13 @@ const TValue *luaH_getint (Table *t, int key) {
/*
-** search function for strings
+** search function for short strings
*/
const TValue *luaH_getstr (Table *t, TString *key) {
Node *n = hashstr(t, key);
+ lua_assert(key->tsv.tt == LUA_TSHRSTR);
do { /* check whether `key' is somewhere in the chain */
- if (ttisstring(gkey(n)) && eqstr(rawtsvalue(gkey(n)), key))
+ if (ttisshrstring(gkey(n)) && eqshrstr(rawtsvalue(gkey(n)), key))
return gval(n); /* that's it */
else n = gnext(n);
} while (n);
@@ -470,9 +479,9 @@ const TValue *luaH_getstr (Table *t, TString *key) {
** main search function
*/
const TValue *luaH_get (Table *t, const TValue *key) {
- switch (ttypenv(key)) {
+ switch (ttype(key)) {
case LUA_TNIL: return luaO_nilobject;
- case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key));
+ case LUA_TSHRSTR: return luaH_getstr(t, rawtsvalue(key));
case LUA_TNUMBER: {
int k;
lua_Number n = nvalue(key);
diff --git a/depends/lua/src/lua.c b/depends/lua/src/lua.c
index e20ab86d..7614c703 100644
--- a/depends/lua/src/lua.c
+++ b/depends/lua/src/lua.c
@@ -1,5 +1,5 @@
/*
-** $Id: lua.c,v 1.203 2011/12/12 16:34:03 roberto Exp $
+** $Id: lua.c,v 1.205 2012/05/23 15:37:09 roberto Exp $
** Lua stand-alone interpreter
** See Copyright Notice in lua.h
*/
@@ -45,13 +45,13 @@
*/
#if defined(LUA_USE_ISATTY)
#include <unistd.h>
-#define lua_stdin_is_tty() isatty(0)
+#define lua_stdin_is_tty() isatty(0)
#elif defined(LUA_WIN)
#include <io.h>
#include <stdio.h>
-#define lua_stdin_is_tty() _isatty(_fileno(stdin))
+#define lua_stdin_is_tty() _isatty(_fileno(stdin))
#else
-#define lua_stdin_is_tty() 1 /* assume stdin is a tty */
+#define lua_stdin_is_tty() 1 /* assume stdin is a tty */
#endif
@@ -66,19 +66,19 @@
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
-#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
+#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
#define lua_saveline(L,idx) \
if (lua_rawlen(L,idx) > 0) /* non-empty line? */ \
add_history(lua_tostring(L, idx)); /* add it to history */
-#define lua_freeline(L,b) ((void)L, free(b))
+#define lua_freeline(L,b) ((void)L, free(b))
#elif !defined(lua_readline)
-#define lua_readline(L,b,p) \
+#define lua_readline(L,b,p) \
((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \
fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */
-#define lua_saveline(L,idx) { (void)L; (void)idx; }
-#define lua_freeline(L,b) { (void)L; (void)b; }
+#define lua_saveline(L,idx) { (void)L; (void)idx; }
+#define lua_freeline(L,b) { (void)L; (void)b; }
#endif
@@ -223,16 +223,11 @@ static int dostring (lua_State *L, const char *s, const char *name) {
static int dolibrary (lua_State *L, const char *name) {
int status;
- lua_pushglobaltable(L);
- lua_getfield(L, -1, "require");
+ lua_getglobal(L, "require");
lua_pushstring(L, name);
- status = docall(L, 1, 1);
- if (status == LUA_OK) {
- lua_setfield(L, -2, name); /* global[name] = require return */
- lua_pop(L, 1); /* remove global table */
- }
- else
- lua_remove(L, -2); /* remove global table (below error msg.) */
+ status = docall(L, 1, 1); /* call 'require(name)' */
+ if (status == LUA_OK)
+ lua_setglobal(L, name); /* global[name] = require return */
return report(L, status);
}
diff --git a/depends/lua/src/lundump.c b/depends/lua/src/lundump.c
index 80c7aa39..54de011a 100644
--- a/depends/lua/src/lundump.c
+++ b/depends/lua/src/lundump.c
@@ -1,5 +1,5 @@
/*
-** $Id: lundump.c,v 1.71 2011/12/07 10:39:12 lhf Exp $
+** $Id: lundump.c,v 2.22 2012/05/08 13:53:33 roberto Exp $
** load precompiled Lua chunks
** See Copyright Notice in lua.h
*/
@@ -27,7 +27,7 @@ typedef struct {
const char* name;
} LoadState;
-static void error(LoadState* S, const char* why)
+static l_noret error(LoadState* S, const char* why)
{
luaO_pushfstring(S->L,"%s: %s precompiled chunk",S->name,why);
luaD_throw(S->L,LUA_ERRSYNTAX);
@@ -39,7 +39,7 @@ static void error(LoadState* S, const char* why)
#define LoadVector(S,b,n,size) LoadMem(S,b,n,size)
#if !defined(luai_verifycode)
-#define luai_verifycode(L,b,f) (f)
+#define luai_verifycode(L,b,f) /* empty */
#endif
static void LoadBlock(LoadState* S, void* b, size_t size)
@@ -91,7 +91,7 @@ static void LoadCode(LoadState* S, Proto* f)
LoadVector(S,f->code,n,sizeof(Instruction));
}
-static Proto* LoadFunction(LoadState* S);
+static void LoadFunction(LoadState* S, Proto* f);
static void LoadConstants(LoadState* S, Proto* f)
{
@@ -118,13 +118,18 @@ static void LoadConstants(LoadState* S, Proto* f)
case LUA_TSTRING:
setsvalue2n(S->L,o,LoadString(S));
break;
+ default: lua_assert(0);
}
}
n=LoadInt(S);
f->p=luaM_newvector(S->L,n,Proto*);
f->sizep=n;
for (i=0; i<n; i++) f->p[i]=NULL;
- for (i=0; i<n; i++) f->p[i]=LoadFunction(S);
+ for (i=0; i<n; i++)
+ {
+ f->p[i]=luaF_newproto(S->L);
+ LoadFunction(S,f->p[i]);
+ }
}
static void LoadUpvalues(LoadState* S, Proto* f)
@@ -163,10 +168,8 @@ static void LoadDebug(LoadState* S, Proto* f)
for (i=0; i<n; i++) f->upvalues[i].name=LoadString(S);
}
-static Proto* LoadFunction(LoadState* S)
+static void LoadFunction(LoadState* S, Proto* f)
{
- Proto* f=luaF_newproto(S->L);
- setptvalue2s(S->L,S->L->top,f); incr_top(S->L);
f->linedefined=LoadInt(S);
f->lastlinedefined=LoadInt(S);
f->numparams=LoadByte(S);
@@ -176,8 +179,6 @@ static Proto* LoadFunction(LoadState* S)
LoadConstants(S,f);
LoadUpvalues(S,f);
LoadDebug(S,f);
- S->L->top--;
- return f;
}
/* the code below must be consistent with the code in luaU_header */
@@ -202,9 +203,10 @@ static void LoadHeader(LoadState* S)
/*
** load precompiled chunk
*/
-Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name)
+Closure* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name)
{
LoadState S;
+ Closure* cl;
if (*name=='@' || *name=='=')
S.name=name+1;
else if (*name==LUA_SIGNATURE[0])
@@ -215,7 +217,19 @@ Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name)
S.Z=Z;
S.b=buff;
LoadHeader(&S);
- return luai_verifycode(L,buff,LoadFunction(&S));
+ cl=luaF_newLclosure(L,1);
+ setclLvalue(L,L->top,cl); incr_top(L);
+ cl->l.p=luaF_newproto(L);
+ LoadFunction(&S,cl->l.p);
+ if (cl->l.p->sizeupvalues != 1)
+ {
+ Proto* p=cl->l.p;
+ cl=luaF_newLclosure(L,cl->l.p->sizeupvalues);
+ cl->l.p=p;
+ setclLvalue(L,L->top-1,cl);
+ }
+ luai_verifycode(L,buff,cl->l.p);
+ return cl;
}
#define MYINT(s) (s[0]-'0')
diff --git a/depends/lua/src/lvm.c b/depends/lua/src/lvm.c
index 694971b1..b77eac26 100644
--- a/depends/lua/src/lvm.c
+++ b/depends/lua/src/lvm.c
@@ -1,5 +1,5 @@
/*
-** $Id: lvm.c,v 2.147 2011/12/07 14:43:55 roberto Exp $
+** $Id: lvm.c,v 2.152 2012/06/08 15:14:04 roberto Exp $
** Lua virtual machine
** See Copyright Notice in lua.h
*/
@@ -60,10 +60,15 @@ int luaV_tostring (lua_State *L, StkId obj) {
static void traceexec (lua_State *L) {
CallInfo *ci = L->ci;
lu_byte mask = L->hookmask;
- if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) {
- resethookcount(L);
- luaD_hook(L, LUA_HOOKCOUNT, -1);
+ int counthook = ((mask & LUA_MASKCOUNT) && L->hookcount == 0);
+ if (counthook)
+ resethookcount(L); /* reset count */
+ if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */
+ ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
+ return; /* do not call hook again (VM yielded, so it did not move) */
}
+ if (counthook)
+ luaD_hook(L, LUA_HOOKCOUNT, -1); /* call count hook */
if (mask & LUA_MASKLINE) {
Proto *p = ci_func(ci)->p;
int npc = pcRel(ci->u.l.savedpc, p);
@@ -71,11 +76,15 @@ static void traceexec (lua_State *L) {
if (npc == 0 || /* call linehook when enter a new function, */
ci->u.l.savedpc <= L->oldpc || /* when jump back (loop), or when */
newline != getfuncline(p, pcRel(L->oldpc, p))) /* enter a new line */
- luaD_hook(L, LUA_HOOKLINE, newline);
+ luaD_hook(L, LUA_HOOKLINE, newline); /* call line hook */
}
L->oldpc = ci->u.l.savedpc;
if (L->status == LUA_YIELD) { /* did hook yield? */
+ if (counthook)
+ L->hookcount = 1; /* undo decrement to zero */
ci->u.l.savedpc--; /* undo increment (resume will increment it again) */
+ ci->callstatus |= CIST_HOOKYIELD; /* mark that it yieled */
+ ci->func = L->top - 1; /* protect stack below results */
luaD_throw(L, LUA_YIELD);
}
}
@@ -258,7 +267,8 @@ int luaV_equalobj_ (lua_State *L, const TValue *t1, const TValue *t2) {
case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */
case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
case LUA_TLCF: return fvalue(t1) == fvalue(t2);
- case LUA_TSTRING: return eqstr(rawtsvalue(t1), rawtsvalue(t2));
+ case LUA_TSHRSTR: return eqshrstr(rawtsvalue(t1), rawtsvalue(t2));
+ case LUA_TLNGSTR: return luaS_eqlngstr(rawtsvalue(t1), rawtsvalue(t2));
case LUA_TUSERDATA: {
if (uvalue(t1) == uvalue(t2)) return 1;
else if (L == NULL) return 0;
@@ -293,7 +303,7 @@ void luaV_concat (lua_State *L, int total) {
else if (tsvalue(top-1)->len == 0) /* second operand is empty? */
(void)tostring(L, top - 2); /* result is first operand */
else if (ttisstring(top-2) && tsvalue(top-2)->len == 0) {
- setsvalue2s(L, top-2, rawtsvalue(top-1)); /* result is second op. */
+ setobjs2s(L, top - 2, top - 1); /* result is second op. */
}
else {
/* at least two non-empty string values; get as many as possible */
@@ -394,7 +404,8 @@ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base,
int nup = p->sizeupvalues;
Upvaldesc *uv = p->upvalues;
int i;
- Closure *ncl = luaF_newLclosure(L, p);
+ Closure *ncl = luaF_newLclosure(L, nup);
+ ncl->l.p = p;
setclLvalue(L, ra, ncl); /* anchor new closure in stack */
for (i = 0; i < nup; i++) { /* fill in its upvalues */
if (uv[i].instack) /* upvalue refers to local variable? */
@@ -500,7 +511,11 @@ void luaV_finishOp (lua_State *L) {
#define Protect(x) { {x;}; base = ci->u.l.base; }
-#define checkGC(L,c) Protect(luaC_condGC(L, c); luai_threadyield(L);)
+#define checkGC(L,c) \
+ Protect( luaC_condGC(L,{L->top = (c); /* limit of live values */ \
+ luaC_step(L); \
+ L->top = ci->top;}) /* restore top */ \
+ luai_threadyield(L); )
#define arith_op(op,tm) { \
@@ -593,11 +608,7 @@ void luaV_execute (lua_State *L) {
sethvalue(L, ra, t);
if (b != 0 || c != 0)
luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c));
- checkGC(L,
- L->top = ra + 1; /* limit of live values */
- luaC_step(L);
- L->top = ci->top; /* restore top */
- )
+ checkGC(L, ra + 1);
)
vmcase(OP_SELF,
StkId rb = RB(i);
@@ -649,10 +660,7 @@ void luaV_execute (lua_State *L) {
ra = RA(i); /* 'luav_concat' may invoke TMs and move the stack */
rb = b + base;
setobjs2s(L, ra, rb);
- checkGC(L,
- L->top = (ra >= rb ? ra + 1 : rb); /* limit of live values */
- luaC_step(L);
- )
+ checkGC(L, (ra >= rb ? ra + 1 : rb));
L->top = ci->top; /* restore top */
)
vmcase(OP_JMP,
@@ -830,11 +838,7 @@ void luaV_execute (lua_State *L) {
pushclosure(L, p, cl->upvals, base, ra); /* create a new one */
else
setclLvalue(L, ra, ncl); /* push cashed closure */
- checkGC(L,
- L->top = ra + 1; /* limit of live values */
- luaC_step(L);
- L->top = ci->top; /* restore top */
- )
+ checkGC(L, ra + 1);
)
vmcase(OP_VARARG,
int b = GETARG_B(i) - 1;
diff --git a/depends/lua/src/lzio.c b/depends/lua/src/lzio.c
index 354f94e7..8b77054e 100644
--- a/depends/lua/src/lzio.c
+++ b/depends/lua/src/lzio.c
@@ -1,6 +1,6 @@
/*
-** $Id: lzio.c,v 1.34 2011/07/15 12:35:32 roberto Exp $
-** a generic input stream interface
+** $Id: lzio.c,v 1.35 2012/05/14 13:34:18 roberto Exp $
+** Buffered streams
** See Copyright Notice in lua.h
*/
diff --git a/dfhack.init-example b/dfhack.init-example
index 7bceda2d..4af4246f 100644
--- a/dfhack.init-example
+++ b/dfhack.init-example
@@ -13,6 +13,9 @@ keybinding add Ctrl-Shift-K autodump-destroy-here
# any item:
keybinding add Ctrl-K autodump-destroy-item
+# quicksave, only in main dwarfmode screen and menu page
+keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave
+
##############################
# Generic adv mode bindings #
##############################
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index 2035a5ce..73f6c9ec 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -267,8 +267,11 @@ SET_TARGET_PROPERTIES(dfhack PROPERTIES DEBUG_POSTFIX "-debug" )
IF(APPLE)
SET(SDL_LIBRARY ${CMAKE_INSTALL_PREFIX}/libs/SDL.framework)
+ SET(CXX_LIBRARY ${CMAKE_INSTALL_PREFIX}/libs/libstdc++.6.dylib)
+ SET(ZIP_LIBRARY /usr/lib/libz.dylib)
TARGET_LINK_LIBRARIES(dfhack ${SDL_LIBRARY})
-# TARGET_LINK_LIBRARIES(dfhack /usr/lib/libc++.dylib)
+ TARGET_LINK_LIBRARIES(dfhack ${CXX_LIBRARY})
+ TARGET_LINK_LIBRARIES(dfhack ${ZIP_LIBRARY})
SET_TARGET_PROPERTIES(dfhack PROPERTIES VERSION 1.0.0)
SET_TARGET_PROPERTIES(dfhack PROPERTIES SOVERSION 1.0.0)
ENDIF()
diff --git a/library/Console-linux.cpp b/library/Console-linux.cpp
index c547f841..6b4de736 100644
--- a/library/Console-linux.cpp
+++ b/library/Console-linux.cpp
@@ -264,7 +264,7 @@ namespace DFHack
void color(Console::color_value index)
{
if(!rawmode)
- fprintf(dfout_C,getANSIColor(index));
+ fprintf(dfout_C, "%s", getANSIColor(index));
else
{
const char * colstr = getANSIColor(index);
@@ -716,6 +716,8 @@ void Console::add_text(color_value color, const std::string &text)
lock_guard <recursive_mutex> g(*wlock);
if (inited)
d->print_text(color, text);
+ else
+ fwrite(text.data(), 1, text.size(), stderr);
}
int Console::get_columns(void)
@@ -770,4 +772,4 @@ void Console::msleep (unsigned int msec)
{
if (msec > 1000) sleep(msec/1000000);
usleep((msec % 1000000) * 1000);
-} \ No newline at end of file
+}
diff --git a/library/Core.cpp b/library/Core.cpp
index 94d408b8..f30e19c2 100644
--- a/library/Core.cpp
+++ b/library/Core.cpp
@@ -68,6 +68,8 @@ using namespace DFHack;
#include <fstream>
#include "tinythread.h"
+#include "SDL_events.h"
+
using namespace tthread;
using namespace df::enums;
using df::global::init;
@@ -262,16 +264,12 @@ static bool init_run_script(color_ostream &out, lua_State *state, void *info)
return true;
}
-static command_result runLuaScript(color_ostream &out, std::string filename, vector<string> &args)
+static command_result runLuaScript(color_ostream &out, std::string name, vector<string> &args)
{
ScriptArgs data;
- data.pcmd = &filename;
+ data.pcmd = &name;
data.pargs = &args;
-#ifndef LINUX_BUILD
- filename = toLower(filename);
-#endif
-
bool ok = Lua::RunCoreQueryLoop(out, Lua::Core::State, init_run_script, &data);
return ok ? CR_OK : CR_FAILURE;
@@ -279,29 +277,29 @@ static command_result runLuaScript(color_ostream &out, std::string filename, vec
command_result Core::runCommand(color_ostream &out, const std::string &command)
{
- fprintf(stderr,"Inside runCommand");
- fprintf(stderr," with command %s\n",command.c_str());
+ //fprintf(stderr,"Inside runCommand");
+ //fprintf(stderr," with command %s\n",command.c_str());
if (!command.empty())
{
- fprintf(stderr,"Command is not empty, tokenizing\n");
+ //fprintf(stderr,"Command is not empty, tokenizing\n");
vector <string> parts;
Core::cheap_tokenise(command,parts);
- fprintf(stderr,"Tokenized, got %d parts\n",parts.size());
+ //fprintf(stderr,"Tokenized, got %d parts\n",parts.size());
if(parts.size() == 0)
return CR_NOT_IMPLEMENTED;
string first = parts[0];
- fprintf(stderr,"Erasing beginning\n");
+ //fprintf(stderr,"Erasing beginning\n");
parts.erase(parts.begin());
- fprintf(stderr,"I think we're about there\n");
+ //fprintf(stderr,"I think we're about there\n");
if (first[0] == '#')
return CR_OK;
cerr << "Invoking: " << command << endl;
- fprintf(stderr,"Returning with the next recursion\n");
+ //fprintf(stderr,"Returning with the next recursion\n");
return runCommand(out, first, parts);
}
else
@@ -617,7 +615,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
{
auto filename = getHackPath() + "scripts/" + first + ".lua";
if (fileExists(filename))
- res = runLuaScript(con, filename, parts);
+ res = runLuaScript(con, first, parts);
else
con.printerr("%s is not a recognized command.\n", first.c_str());
}
@@ -682,7 +680,7 @@ void fIOthread(void * iodata)
{
string command = "";
int ret = con.lineedit("[DFHack]# ",command, main_history);
- fprintf(stderr,"Command: [%s]\n",command.c_str());
+ //fprintf(stderr,"Command: [%s]\n",command.c_str());
if(ret == -2)
{
cerr << "Console is shutting down properly." << endl;
@@ -696,13 +694,13 @@ void fIOthread(void * iodata)
else if(ret)
{
// a proper, non-empty command was entered
- fprintf(stderr,"Adding command to history\n");
+ //fprintf(stderr,"Adding command to history\n");
main_history.add(command);
- fprintf(stderr,"Saving history\n");
+ //fprintf(stderr,"Saving history\n");
main_history.save("dfhack.history");
}
- fprintf(stderr,"Running command\n");
+ //fprintf(stderr,"Running command\n");
auto rv = core->runCommand(con, command);
@@ -813,14 +811,19 @@ bool Core::Init()
}
cerr << "Version: " << vinfo->getVersion() << endl;
+ // Init global object pointers
+ df::global::InitGlobals();
+
cerr << "Initializing Console.\n";
// init the console.
bool is_text_mode = false;
if(init && init->display.flag.is_set(init_display_flags::TEXT))
{
is_text_mode = true;
+ con.init(true);
+ cerr << "Console is not available. Use dfhack-run to send commands.\n";
}
- if(con.init(is_text_mode))
+ else if(con.init(false))
cerr << "Console is running.\n";
else
fatal ("Console has failed to initialize!\n", false);
@@ -835,7 +838,6 @@ bool Core::Init()
*/
// initialize data defs
virtual_identity::Init(this);
- df::global::InitGlobals();
// initialize common lua context
Lua::Core::Init(con);
@@ -845,12 +847,15 @@ bool Core::Init()
cerr << "Initializing Plugins.\n";
// create plugin manager
plug_mgr = new PluginManager(this);
- cerr << "Starting IO thread.\n";
- // create IO thread
IODATA *temp = new IODATA;
temp->core = this;
temp->plug_mgr = plug_mgr;
- thread * IO = new thread(fIOthread, (void *) temp);
+ if (!is_text_mode)
+ {
+ cerr << "Starting IO thread.\n";
+ // create IO thread
+ thread * IO = new thread(fIOthread, (void *) temp);
+ }
cerr << "Starting DF input capture thread.\n";
// set up hotkey capture
HotkeyMutex = new mutex();
@@ -1218,29 +1223,30 @@ bool Core::ncurses_wgetch(int in, int & out)
return true;
}
-int Core::UnicodeAwareSym(const SDL_KeyboardEvent& ke)
+int UnicodeAwareSym(const SDL::KeyboardEvent& ke)
{
// Assume keyboard layouts don't change the order of numbers:
- if( '0' <= ke.keysym.sym && ke.keysym.sym <= '9') return ke.keysym.sym;
- if(SDLK_F1 <= ke.keysym.sym && ke.keysym.sym <= SDLK_F12) return ke.keysym.sym;
+ if( '0' <= ke.ksym.sym && ke.ksym.sym <= '9') return ke.ksym.sym;
+ if(SDL::K_F1 <= ke.ksym.sym && ke.ksym.sym <= SDL::K_F12) return ke.ksym.sym;
// These keys are mapped to the same control codes as Ctrl-?
- switch (ke.keysym.sym) {
- case SDLK_RETURN:
- case SDLK_KP_ENTER:
- case SDLK_TAB:
- case SDLK_ESCAPE:
- case SDLK_DELETE:
- return ke.keysym.sym;
- default:
- break;
+ switch (ke.ksym.sym)
+ {
+ case SDL::K_RETURN:
+ case SDL::K_KP_ENTER:
+ case SDL::K_TAB:
+ case SDL::K_ESCAPE:
+ case SDL::K_DELETE:
+ return ke.ksym.sym;
+ default:
+ break;
}
- int unicode = ke.keysym.unicode;
+ int unicode = ke.ksym.unicode;
// convert Ctrl characters to their 0x40-0x5F counterparts:
if (unicode < ' ')
- {
+ {
unicode += 'A' - 1;
}
@@ -1248,7 +1254,7 @@ int Core::UnicodeAwareSym(const SDL_KeyboardEvent& ke)
if('A' < unicode && unicode < 'Z')
{
unicode += 'a' - 'A';
- }
+ }
// convert various other punctuation marks:
if('\"' == unicode) unicode = '\'';
@@ -1265,29 +1271,30 @@ int Core::UnicodeAwareSym(const SDL_KeyboardEvent& ke)
return unicode;
}
+
//MEMO: return false if event is consumed
-int Core::DFH_SDL_Event(SDL_Event* ev)
+int Core::DFH_SDL_Event(SDL::Event* ev)
{
// do NOT process events before we are ready.
if(!started) return true;
if(!ev)
return true;
- if(ev && (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP))
+ if(ev && (ev->type == SDL::ET_KEYDOWN || ev->type == SDL::ET_KEYUP))
{
- SDL_KeyboardEvent * ke = (SDL_KeyboardEvent *)ev;
+ auto ke = (SDL::KeyboardEvent *)ev;
- if(ke->state == SDL_PRESSED && !hotkey_states[ke->keysym.sym])
+ if(ke->state == SDL::BTN_PRESSED && !hotkey_states[ke->ksym.sym])
{
- hotkey_states[ke->keysym.sym] = true;
+ hotkey_states[ke->ksym.sym] = true;
int mod = 0;
- if (ke->keysym.mod & KMOD_SHIFT) mod |= 1;
- if (ke->keysym.mod & KMOD_CTRL) mod |= 2;
- if (ke->keysym.mod & KMOD_ALT) mod |= 4;
+ if (ke->ksym.mod & SDL::KMOD_SHIFT) mod |= 1;
+ if (ke->ksym.mod & SDL::KMOD_CTRL) mod |= 2;
+ if (ke->ksym.mod & SDL::KMOD_ALT) mod |= 4;
// Use unicode so Windows gives the correct value for the
// user's Input Language
- if((ke->keysym.unicode & 0xff80) == 0)
+ if((ke->ksym.unicode & 0xff80) == 0)
{
int key = UnicodeAwareSym(*ke);
SelectHotkey(key, mod);
@@ -1295,12 +1302,12 @@ int Core::DFH_SDL_Event(SDL_Event* ev)
else
{
// Pretend non-ascii characters don't happen:
- SelectHotkey(ke->keysym.sym, mod);
+ SelectHotkey(ke->ksym.sym, mod);
}
}
- else if(ke->state == SDL_RELEASED)
+ else if(ke->state == SDL::BTN_RELEASED)
{
- hotkey_states[ke->keysym.sym] = false;
+ hotkey_states[ke->ksym.sym] = false;
}
}
return true;
@@ -1317,8 +1324,8 @@ bool Core::SelectHotkey(int sym, int modifiers)
while (screen->child)
screen = screen->child;
- if (sym == SDLK_KP_ENTER)
- sym = SDLK_RETURN;
+ if (sym == SDL::K_KP_ENTER)
+ sym = SDL::K_RETURN;
std::string cmd;
@@ -1341,7 +1348,7 @@ bool Core::SelectHotkey(int sym, int modifiers)
if (cmd.empty()) {
// Check the hotkey keybindings
- int idx = sym - SDLK_F1;
+ int idx = sym - SDL::K_F1;
if(idx >= 0 && idx < 8)
{
if (modifiers & 1)
@@ -1396,13 +1403,13 @@ static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string
}
if (keyspec.size() == 1 && keyspec[0] >= 'A' && keyspec[0] <= 'Z') {
- *psym = SDLK_a + (keyspec[0]-'A');
+ *psym = SDL::K_a + (keyspec[0]-'A');
return true;
} else if (keyspec.size() == 2 && keyspec[0] == 'F' && keyspec[1] >= '1' && keyspec[1] <= '9') {
- *psym = SDLK_F1 + (keyspec[1]-'1');
+ *psym = SDL::K_F1 + (keyspec[1]-'1');
return true;
} else if (keyspec == "Enter") {
- *psym = SDLK_RETURN;
+ *psym = SDL::K_RETURN;
return true;
} else
return false;
diff --git a/library/Hooks-darwin.cpp b/library/Hooks-darwin.cpp
index 347881ee..ef89105d 100644
--- a/library/Hooks-darwin.cpp
+++ b/library/Hooks-darwin.cpp
@@ -83,8 +83,8 @@ DFhackCExport void SDL_Quit(void)
}
// called by DF to check input events
-static int (*_SDL_PollEvent)(SDL_Event* event) = 0;
-DFhackCExport int SDL_PollEvent(SDL_Event* event)
+static int (*_SDL_PollEvent)(SDL::Event* event) = 0;
+DFhackCExport int SDL_PollEvent(SDL::Event* event)
{
pollevent_again:
// if SDL returns 0 here, it means there are no more events. return 0
@@ -140,7 +140,7 @@ DFhackCExport int SDL_Init(uint32_t flags)
fprintf(stderr,"dfhack: saving real SDL functions\n");
_SDL_Init = (int (*)( uint32_t )) dlsym(RTLD_NEXT, "SDL_Init");
_SDL_Quit = (void (*)( void )) dlsym(RTLD_NEXT, "SDL_Quit");
- _SDL_PollEvent = (int (*)(SDL_Event*))dlsym(RTLD_NEXT,"SDL_PollEvent");
+ _SDL_PollEvent = (int (*)(SDL::Event*))dlsym(RTLD_NEXT,"SDL_PollEvent");
fprintf(stderr,"dfhack: saved real SDL functions\n");
// check if we got them
diff --git a/library/Hooks-linux.cpp b/library/Hooks-linux.cpp
index 3628aab6..ef1a765c 100644
--- a/library/Hooks-linux.cpp
+++ b/library/Hooks-linux.cpp
@@ -79,7 +79,7 @@ DFhackCExport int SDL_PollEvent(SDL::Event* event)
{
DFHack::Core & c = DFHack::Core::getInstance();
// if we consume the event, ask SDL for more.
- if(!c.SDL_Event(event))
+ if(!c.DFH_SDL_Event(event))
goto pollevent_again;
}
return orig_return;
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp
index 6a550db8..631b3c49 100644
--- a/library/LuaApi.cpp
+++ b/library/LuaApi.cpp
@@ -84,6 +84,8 @@ distribution.
using namespace DFHack;
using namespace DFHack::LuaWrapper;
+void dfhack_printerr(lua_State *S, const std::string &str);
+
void Lua::Push(lua_State *state, const Units::NoblePosition &pos)
{
lua_createtable(state, 0, 3);
@@ -640,10 +642,37 @@ static void OpenModule(lua_State *state, const char *mname,
/***** DFHack module *****/
+static std::string getOSType()
+{
+ switch (Core::getInstance().vinfo->getOS())
+ {
+ case OS_WINDOWS:
+ return "windows";
+
+ case OS_LINUX:
+ return "linux";
+
+ case OS_APPLE:
+ return "darwin";
+
+ default:
+ return "unknown";
+ }
+}
+
+static std::string getDFVersion() { return Core::getInstance().vinfo->getVersion(); }
+
+static std::string getDFPath() { return Core::getInstance().p->getPath(); }
+static std::string getHackPath() { return Core::getInstance().getHackPath(); }
+
static bool isWorldLoaded() { return Core::getInstance().isWorldLoaded(); }
static bool isMapLoaded() { return Core::getInstance().isMapLoaded(); }
static const LuaWrapper::FunctionReg dfhack_module[] = {
+ WRAP(getOSType),
+ WRAP(getDFVersion),
+ WRAP(getDFPath),
+ WRAP(getHackPath),
WRAP(isWorldLoaded),
WRAP(isMapLoaded),
WRAPM(Translation, TranslateName),
@@ -715,6 +744,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, isDead),
WRAPM(Units, isAlive),
WRAPM(Units, isSane),
+ WRAPM(Units, isDwarf),
+ WRAPM(Units, isCitizen),
WRAPM(Units, getAge),
WRAPM(Units, getProfessionName),
WRAPM(Units, getCasteProfessionName),
@@ -988,6 +1019,207 @@ static const luaL_Reg dfhack_constructions_funcs[] = {
{ NULL, NULL }
};
+/***** Internal module *****/
+
+static void *checkaddr(lua_State *L, int idx, bool allow_null = false)
+{
+ luaL_checkany(L, idx);
+ void *rv;
+ if (lua_isnil(L, idx))
+ rv = NULL;
+ else if (lua_type(L, idx) == LUA_TNUMBER)
+ rv = (void*)lua_tounsigned(L, idx);
+ else
+ rv = Lua::CheckDFObject(L, NULL, idx);
+ if (!rv && !allow_null)
+ luaL_argerror(L, idx, "null pointer");
+ return rv;
+}
+
+static uint32_t getBase() { return Core::getInstance().p->getBase(); }
+
+static const LuaWrapper::FunctionReg dfhack_internal_module[] = {
+ WRAP(getBase),
+ { NULL, NULL }
+};
+
+static int internal_getAddress(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ uint32_t addr = Core::getInstance().vinfo->getAddress(name);
+ if (addr)
+ lua_pushnumber(L, addr);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+static int internal_setAddress(lua_State *L)
+{
+ std::string name = luaL_checkstring(L, 1);
+ uint32_t addr = (uint32_t)checkaddr(L, 2, true);
+ internal_getAddress(L);
+
+ // Set the address
+ Core::getInstance().vinfo->setAddress(name, addr);
+
+ auto fields = df::global::_identity.getFields();
+
+ for (int i = 0; fields && fields[i].mode != struct_field_info::END; ++i)
+ {
+ if (fields[i].name != name)
+ continue;
+
+ *(void**)fields[i].offset = (void*)addr;
+ }
+
+ // Print via printerr, so that it is definitely logged to stderr.log.
+ std::string msg = stl_sprintf("<global-address name='%s' value='0x%x'/>", name.c_str(), addr);
+ dfhack_printerr(L, msg);
+
+ return 1;
+}
+
+static int internal_getVTable(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ uint32_t addr = (uint32_t)Core::getInstance().vinfo->getVTable(name);
+ if (addr)
+ lua_pushnumber(L, addr);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+static int internal_getMemRanges(lua_State *L)
+{
+ std::vector<DFHack::t_memrange> ranges;
+ Core::getInstance().p->getMemRanges(ranges);
+
+ lua_newtable(L);
+
+ for(size_t i = 0; i < ranges.size(); i++)
+ {
+ lua_newtable(L);
+ lua_pushnumber(L, (uint32_t)ranges[i].start);
+ lua_setfield(L, -2, "start_addr");
+ lua_pushnumber(L, (uint32_t)ranges[i].end);
+ lua_setfield(L, -2, "end_addr");
+ lua_pushstring(L, ranges[i].name);
+ lua_setfield(L, -2, "name");
+ lua_pushboolean(L, ranges[i].read);
+ lua_setfield(L, -2, "read");
+ lua_pushboolean(L, ranges[i].write);
+ lua_setfield(L, -2, "write");
+ lua_pushboolean(L, ranges[i].execute);
+ lua_setfield(L, -2, "execute");
+ lua_pushboolean(L, ranges[i].shared);
+ lua_setfield(L, -2, "shared");
+ lua_pushboolean(L, ranges[i].valid);
+ lua_setfield(L, -2, "valid");
+ lua_rawseti(L, -2, i+1);
+ }
+
+ return 1;
+}
+
+static int internal_memmove(lua_State *L)
+{
+ void *dest = checkaddr(L, 1);
+ void *src = checkaddr(L, 2);
+ int size = luaL_checkint(L, 3);
+ if (size < 0) luaL_argerror(L, 1, "negative size");
+ memmove(dest, src, size);
+ return 0;
+}
+
+static int internal_memcmp(lua_State *L)
+{
+ void *dest = checkaddr(L, 1);
+ void *src = checkaddr(L, 2);
+ int size = luaL_checkint(L, 3);
+ if (size < 0) luaL_argerror(L, 1, "negative size");
+ lua_pushinteger(L, memcmp(dest, src, size));
+ return 1;
+}
+
+static int internal_memscan(lua_State *L)
+{
+ uint8_t *haystack = (uint8_t*)checkaddr(L, 1);
+ int hcount = luaL_checkint(L, 2);
+ int hstep = luaL_checkint(L, 3);
+ if (hstep == 0) luaL_argerror(L, 3, "zero step");
+ void *needle = checkaddr(L, 4);
+ int nsize = luaL_checkint(L, 5);
+ if (nsize < 0) luaL_argerror(L, 5, "negative size");
+
+ for (int i = 0; i <= hcount; i++)
+ {
+ uint8_t *p = haystack + i*hstep;
+ if (memcmp(p, needle, nsize) == 0) {
+ lua_pushinteger(L, i);
+ lua_pushinteger(L, (lua_Integer)p);
+ return 2;
+ }
+ }
+
+ lua_pushnil(L);
+ return 1;
+}
+
+static int internal_diffscan(lua_State *L)
+{
+ lua_settop(L, 8);
+ void *old_data = checkaddr(L, 1);
+ void *new_data = checkaddr(L, 2);
+ int start_idx = luaL_checkint(L, 3);
+ int end_idx = luaL_checkint(L, 4);
+ int eltsize = luaL_checkint(L, 5);
+ bool has_oldv = !lua_isnil(L, 6);
+ bool has_newv = !lua_isnil(L, 7);
+ bool has_diffv = !lua_isnil(L, 8);
+
+#define LOOP(esz, etype) \
+ case esz: { \
+ etype *pold = (etype*)old_data; \
+ etype *pnew = (etype*)new_data; \
+ etype oldv = (etype)luaL_optint(L, 6, 0); \
+ etype newv = (etype)luaL_optint(L, 7, 0); \
+ etype diffv = (etype)luaL_optint(L, 8, 0); \
+ for (int i = start_idx; i < end_idx; i++) { \
+ if (pold[i] == pnew[i]) continue; \
+ if (has_oldv && pold[i] != oldv) continue; \
+ if (has_newv && pnew[i] != newv) continue; \
+ if (has_diffv && etype(pnew[i]-pold[i]) != diffv) continue; \
+ lua_pushinteger(L, i); return 1; \
+ } \
+ break; \
+ }
+ switch (eltsize) {
+ LOOP(1, uint8_t);
+ LOOP(2, uint16_t);
+ LOOP(4, uint32_t);
+ default:
+ luaL_argerror(L, 5, "invalid element size");
+ }
+#undef LOOP
+
+ lua_pushnil(L);
+ return 1;
+}
+
+static const luaL_Reg dfhack_internal_funcs[] = {
+ { "getAddress", internal_getAddress },
+ { "setAddress", internal_setAddress },
+ { "getVTable", internal_getVTable },
+ { "getMemRanges", internal_getMemRanges },
+ { "memmove", internal_memmove },
+ { "memcmp", internal_memcmp },
+ { "memscan", internal_memscan },
+ { "diffscan", internal_diffscan },
+ { NULL, NULL }
+};
+
/************************
* Main Open function *
@@ -1007,4 +1239,5 @@ void OpenDFHackApi(lua_State *state)
OpenModule(state, "burrows", dfhack_burrows_module, dfhack_burrows_funcs);
OpenModule(state, "buildings", dfhack_buildings_module, dfhack_buildings_funcs);
OpenModule(state, "constructions", dfhack_constructions_module);
+ OpenModule(state, "internal", dfhack_internal_module, dfhack_internal_funcs);
}
diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp
index f794f6b4..752c341b 100644
--- a/library/LuaTools.cpp
+++ b/library/LuaTools.cpp
@@ -66,6 +66,8 @@ using namespace DFHack::LuaWrapper;
lua_State *DFHack::Lua::Core::State = NULL;
+void dfhack_printerr(lua_State *S, const std::string &str);
+
inline void AssertCoreSuspend(lua_State *state)
{
assert(!Lua::IsCoreContext(state) || DFHack::Core::getInstance().isSuspended());
@@ -96,8 +98,6 @@ static void check_valid_ptr_index(lua_State *state, int val_index)
}
}
-static void dfhack_printerr(lua_State *S, const std::string &str);
-
static void signal_typeid_error(color_ostream *out, lua_State *state,
type_identity *type, const char *msg,
int val_index, bool perr, bool signal)
@@ -233,7 +233,7 @@ static int lua_dfhack_println(lua_State *S)
return 0;
}
-static void dfhack_printerr(lua_State *S, const std::string &str)
+void dfhack_printerr(lua_State *S, const std::string &str)
{
if (color_ostream *out = Lua::GetOutput(S))
out->printerr("%s\n", str.c_str());
diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp
index c58c25d1..8548c5d0 100644
--- a/library/LuaTypes.cpp
+++ b/library/LuaTypes.cpp
@@ -636,13 +636,35 @@ static int meta_struct_next(lua_State *state)
}
/**
+ * Field lookup for primitive refs: behave as a quasi-array with numeric indices.
+ */
+static type_identity *find_primitive_field(lua_State *state, int field, const char *mode, uint8_t **ptr)
+{
+ if (lua_type(state, field) == LUA_TNUMBER)
+ {
+ int idx = lua_tointeger(state, field);
+ if (idx < 0)
+ field_error(state, 2, "negative index", mode);
+
+ lua_rawgetp(state, UPVAL_METATABLE, &DFHACK_IDENTITY_FIELD_TOKEN);
+ auto id = (type_identity *)lua_touserdata(state, -1);
+ lua_pop(state, 1);
+
+ *ptr += int(id->byte_size()) * idx;
+ return id;
+ }
+
+ return (type_identity*)find_field(state, field, mode);
+}
+
+/**
* Metamethod: __index for primitives, i.e. simple object references.
* Fields point to identity, or NULL for metafields.
*/
static int meta_primitive_index(lua_State *state)
{
uint8_t *ptr = get_object_addr(state, 1, 2, "read");
- auto type = (type_identity*)find_field(state, 2, "read");
+ auto type = find_primitive_field(state, 2, "read", &ptr);
if (!type)
return 1;
type->lua_read(state, 2, ptr);
@@ -655,7 +677,7 @@ static int meta_primitive_index(lua_State *state)
static int meta_primitive_newindex(lua_State *state)
{
uint8_t *ptr = get_object_addr(state, 1, 2, "write");
- auto type = (type_identity*)find_field(state, 2, "write");
+ auto type = find_primitive_field(state, 2, "write", &ptr);
if (!type)
field_error(state, 2, "builtin property or method", "write");
type->lua_write(state, 2, ptr, 3);
diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp
index 471dba64..9f2fc2f1 100644
--- a/library/LuaWrapper.cpp
+++ b/library/LuaWrapper.cpp
@@ -447,10 +447,21 @@ Lua::ObjectClass Lua::IsDFObject(lua_State *state, int val_index)
}
static const char *const primitive_types[] = {
- "string", NULL
+ "string",
+ "int8_t", "uint8_t", "int16_t", "uint16_t",
+ "int32_t", "uint32_t", "int64_t", "uint64_t",
+ "bool", "float", "double",
+ NULL
};
static type_identity *const primitive_identities[] = {
- df::identity_traits<std::string>::get(), NULL
+ df::identity_traits<std::string>::get(),
+ df::identity_traits<int8_t>::get(), df::identity_traits<uint8_t>::get(),
+ df::identity_traits<int16_t>::get(), df::identity_traits<uint16_t>::get(),
+ df::identity_traits<int32_t>::get(), df::identity_traits<uint32_t>::get(),
+ df::identity_traits<int64_t>::get(), df::identity_traits<uint64_t>::get(),
+ df::identity_traits<bool>::get(),
+ df::identity_traits<float>::get(), df::identity_traits<double>::get(),
+ NULL
};
/**
@@ -644,12 +655,32 @@ static int meta_new(lua_State *state)
{
int argc = lua_gettop(state);
- if (argc != 1)
- luaL_error(state, "Usage: object:new() or df.new(object)");
+ if (argc != 1 && argc != 2)
+ luaL_error(state, "Usage: object:new() or df.new(object) or df.new(ptype,count)");
type_identity *id = get_object_identity(state, 1, "df.new()", true);
- void *ptr = id->allocate();
+ void *ptr;
+
+ // Support arrays of primitive types
+ if (argc == 2)
+ {
+ int cnt = luaL_checkint(state, 2);
+ if (cnt <= 0)
+ luaL_error(state, "Invalid array size in df.new()");
+ if (id->type() != IDTYPE_PRIMITIVE)
+ luaL_error(state, "Cannot allocate arrays of non-primitive types.");
+
+ size_t sz = id->byte_size() * cnt;
+ ptr = malloc(sz);
+ if (ptr)
+ memset(ptr, 0, sz);
+ }
+ else
+ {
+ ptr = id->allocate();
+ }
+
if (!ptr)
luaL_error(state, "Cannot allocate %s", id->getFullName().c_str());
@@ -666,6 +697,48 @@ static int meta_new(lua_State *state)
return 1;
}
+/**
+ * Method: type casting of pointers.
+ */
+static int meta_reinterpret_cast(lua_State *state)
+{
+ int argc = lua_gettop(state);
+
+ if (argc != 2)
+ luaL_error(state, "Usage: df.reinterpret_cast(type,ptr)");
+
+ type_identity *id = get_object_identity(state, 1, "df.reinterpret_cast()", true);
+
+ // Find the raw pointer value
+ void *ptr;
+
+ if (lua_isnil(state, 2))
+ ptr = NULL;
+ else if (lua_isnumber(state, 2))
+ ptr = (void*)lua_tounsigned(state, 2);
+ else
+ {
+ ptr = get_object_internal(state, NULL, 2, false, true);
+ if (!ptr)
+ luaL_error(state, "Invalid pointer argument in df.reinterpret_cast.\n");
+ }
+
+ // Convert it to the appropriate representation
+ if (ptr == NULL)
+ {
+ lua_pushnil(state);
+ }
+ else if (lua_isuserdata(state, 1))
+ {
+ lua_getmetatable(state, 1);
+ push_object_ref(state, ptr);
+ }
+ else
+ push_object_internal(state, id, ptr);
+
+ return 1;
+}
+
static void invoke_resize(lua_State *state, int table, lua_Integer size)
{
lua_getfield(state, table, "resize");
@@ -1433,6 +1506,10 @@ static int DoAttach(lua_State *state)
lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME);
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
+ lua_pushcclosure(state, meta_reinterpret_cast, 1);
+ lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_CAST_NAME);
+
+ lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
lua_pushcclosure(state, meta_assign, 1);
lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME);
@@ -1463,6 +1540,8 @@ static int DoAttach(lua_State *state)
lua_setfield(state, -2, "assign");
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME);
lua_setfield(state, -2, "is_instance");
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_CAST_NAME);
+ lua_setfield(state, -2, "reinterpret_cast");
lua_pushlightuserdata(state, NULL);
lua_setfield(state, -2, "NULL");
diff --git a/library/Process-darwin.cpp b/library/Process-darwin.cpp
index a4b95186..5a97d9e0 100644
--- a/library/Process-darwin.cpp
+++ b/library/Process-darwin.cpp
@@ -124,34 +124,99 @@ string Process::doReadClassName (void * vptr)
return raw.substr(start,end-start);
}
-//FIXME: cross-reference with ELF segment entries?
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <mach/vm_region.h>
+#include <mach/vm_statistics.h>
+#include <dlfcn.h>
+
+const char *
+inheritance_strings[] = {
+ "SHARE", "COPY", "NONE", "DONATE_COPY",
+};
+
+const char *
+behavior_strings[] = {
+ "DEFAULT", "RANDOM", "SEQUENTIAL", "RESQNTL", "WILLNEED", "DONTNEED",
+};
+
void Process::getMemRanges( vector<t_memrange> & ranges )
{
- char buffer[1024];
- char permissions[5]; // r/-, w/-, x/-, p/s, 0
- FILE *mapFile = ::fopen("/proc/self/maps", "r");
- size_t start, end, offset, device1, device2, node;
+ kern_return_t kr;
+ task_t the_task;
- while (fgets(buffer, 1024, mapFile))
- {
- t_memrange temp;
- temp.name[0] = 0;
- sscanf(buffer, "%zx-%zx %s %zx %2zx:%2zx %zu %[^\n]",
- &start,
- &end,
- (char*)&permissions,
- &offset, &device1, &device2, &node,
- (char*)temp.name);
- temp.start = (void *) start;
- temp.end = (void *) end;
- temp.read = permissions[0] == 'r';
- temp.write = permissions[1] == 'w';
- temp.execute = permissions[2] == 'x';
- temp.shared = permissions[3] == 's';
- temp.valid = true;
- ranges.push_back(temp);
- }
+ the_task = mach_task_self();
+
+ vm_size_t vmsize;
+ vm_address_t address;
+ vm_region_basic_info_data_t info;
+ mach_msg_type_number_t info_count;
+ vm_region_flavor_t flavor;
+ memory_object_name_t object;
+
+ kr = KERN_SUCCESS;
+ address = 0;
+
+ do {
+ flavor = VM_REGION_BASIC_INFO;
+ info_count = VM_REGION_BASIC_INFO_COUNT;
+ kr = vm_region(the_task, &address, &vmsize, flavor,
+ (vm_region_info_t)&info, &info_count, &object);
+ if (kr == KERN_SUCCESS) {
+ if (info.reserved==1) {
+ address += vmsize;
+ continue;
+ }
+ Dl_info dlinfo;
+ int dlcheck;
+ dlcheck = dladdr((const void*)address, &dlinfo);
+ if (dlcheck==0) {
+ dlinfo.dli_fname = "";
+ }
+
+ t_memrange temp;
+ strncpy( temp.name, dlinfo.dli_fname, 1023 );
+ temp.name[1023] = 0;
+ temp.start = (void *) address;
+ temp.end = (void *) (address+vmsize);
+ temp.read = (info.protection & VM_PROT_READ);
+ temp.write = (info.protection & VM_PROT_WRITE);
+ temp.execute = (info.protection & VM_PROT_EXECUTE);
+ temp.shared = info.shared;
+ temp.valid = true;
+ ranges.push_back(temp);
+
+ fprintf(stderr,
+ "%08x-%08x %8uK %c%c%c/%c%c%c %11s %6s %10s uwir=%hu sub=%u dlname: %s\n",
+ address, (address + vmsize), (vmsize >> 10),
+ (info.protection & VM_PROT_READ) ? 'r' : '-',
+ (info.protection & VM_PROT_WRITE) ? 'w' : '-',
+ (info.protection & VM_PROT_EXECUTE) ? 'x' : '-',
+ (info.max_protection & VM_PROT_READ) ? 'r' : '-',
+ (info.max_protection & VM_PROT_WRITE) ? 'w' : '-',
+ (info.max_protection & VM_PROT_EXECUTE) ? 'x' : '-',
+ inheritance_strings[info.inheritance],
+ (info.shared) ? "shared" : "-",
+ behavior_strings[info.behavior],
+ info.user_wired_count,
+ info.reserved,
+ dlinfo.dli_fname);
+
+ address += vmsize;
+ } else if (kr != KERN_INVALID_ADDRESS) {
+
+ /*if (the_task != MACH_PORT_NULL) {
+ mach_port_deallocate(mach_task_self(), the_task);
+ }*/
+ return;
+ }
+ } while (kr != KERN_INVALID_ADDRESS);
+
+
+/* if (the_task != MACH_PORT_NULL) {
+ mach_port_deallocate(mach_task_self(), the_task);
+ }*/
}
uint32_t Process::getBase()
diff --git a/library/RemoteClient.cpp b/library/RemoteClient.cpp
index a1ac2ec9..4d30988c 100644
--- a/library/RemoteClient.cpp
+++ b/library/RemoteClient.cpp
@@ -329,17 +329,19 @@ bool sendRemoteMessage(CSimpleSocket *socket, int16_t id, const MessageLite *msg
int size = size_ready ? msg->GetCachedSize() : msg->ByteSize();
int fullsz = size + sizeof(RPCMessageHeader);
- std::auto_ptr<uint8_t> data(new uint8_t[fullsz]);
- RPCMessageHeader *hdr = (RPCMessageHeader*)data.get();
+ uint8_t *data = new uint8_t[fullsz];
+ RPCMessageHeader *hdr = (RPCMessageHeader*)data;
hdr->id = id;
hdr->size = size;
- uint8_t *pstart = data.get() + sizeof(RPCMessageHeader);
+ uint8_t *pstart = data + sizeof(RPCMessageHeader);
uint8_t *pend = msg->SerializeWithCachedSizesToArray(pstart);
assert((pend - pstart) == size);
- return (socket->Send(data.get(), fullsz) == fullsz);
+ int got = socket->Send(data, fullsz);
+ delete[] data;
+ return (got == fullsz);
}
command_result RemoteFunctionBase::execute(color_ostream &out,
@@ -402,9 +404,9 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
return CR_LINK_FAILURE;
}
- std::auto_ptr<uint8_t> buf(new uint8_t[header.size]);
+ uint8_t *buf = new uint8_t[header.size];
- if (!readFullBuffer(p_client->socket, buf.get(), header.size))
+ if (!readFullBuffer(p_client->socket, buf, header.size))
{
out.printerr("In call to %s::%s: I/O error in receive %d bytes of data.\n",
this->proto.c_str(), this->name.c_str(), header.size);
@@ -413,18 +415,20 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
switch (header.id) {
case RPC_REPLY_RESULT:
- if (!output->ParseFromArray(buf.get(), header.size))
+ if (!output->ParseFromArray(buf, header.size))
{
out.printerr("In call to %s::%s: error parsing received result.\n",
this->proto.c_str(), this->name.c_str());
+ delete[] buf;
return CR_LINK_FAILURE;
}
+ delete[] buf;
return CR_OK;
case RPC_REPLY_TEXT:
text_data.Clear();
- if (text_data.ParseFromArray(buf.get(), header.size))
+ if (text_data.ParseFromArray(buf, header.size))
text_decoder.decode(&text_data);
else
out.printerr("In call to %s::%s: received invalid text data.\n",
@@ -434,5 +438,6 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
default:
break;
}
+ delete[] buf;
}
}
diff --git a/library/RemoteServer.cpp b/library/RemoteServer.cpp
index ed47890f..53428f2b 100644
--- a/library/RemoteServer.cpp
+++ b/library/RemoteServer.cpp
@@ -220,7 +220,7 @@ void ServerConnection::threadFn()
}
if (memcmp(header.magic, RPCHandshakeHeader::REQUEST_MAGIC, sizeof(header.magic)) ||
- header.version != 1)
+ header.version < 1 || header.version > 255)
{
out << "In RPC server: invalid handshake header." << endl;
return;
diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp
index 689c783a..95c495e9 100644
--- a/library/RemoteTools.cpp
+++ b/library/RemoteTools.cpp
@@ -379,19 +379,19 @@ static command_result GetWorldInfo(color_ostream &stream,
if (!ui || !world || !Core::getInstance().isWorldLoaded())
return CR_NOT_FOUND;
- t_gamemodes mode;
- if (!Core::getInstance().getWorld()->ReadGameMode(mode))
- mode.g_type = GAMETYPE_DWARF_MAIN;
+ df::game_type gt = game_type::DWARF_MAIN;
+ if (df::global::gametype)
+ gt = *df::global::gametype;
out->set_save_dir(world->cur_savegame.save_dir);
if (world->world_data->name.has_name)
describeName(out->mutable_world_name(), &world->world_data->name);
- switch (mode.g_type)
+ switch (gt)
{
- case GAMETYPE_DWARF_MAIN:
- case GAMETYPE_DWARF_RECLAIM:
+ case game_type::DWARF_MAIN:
+ case game_type::DWARF_RECLAIM:
out->set_mode(GetWorldInfoOut::MODE_DWARF);
out->set_civ_id(ui->civ_id);
out->set_site_id(ui->site_id);
@@ -399,7 +399,7 @@ static command_result GetWorldInfo(color_ostream &stream,
out->set_race_id(ui->race_id);
break;
- case GAMETYPE_ADVENTURE_MAIN:
+ case game_type::ADVENTURE_MAIN:
out->set_mode(GetWorldInfoOut::MODE_ADVENTURE);
if (auto unit = vector_get(world->units.active, 0))
@@ -423,7 +423,7 @@ static command_result GetWorldInfo(color_ostream &stream,
}
break;
- case GAMETYPE_VIEW_LEGENDS:
+ case game_type::VIEW_LEGENDS:
out->set_mode(GetWorldInfoOut::MODE_LEGENDS);
break;
diff --git a/library/VersionInfoFactory.cpp b/library/VersionInfoFactory.cpp
index 36dbd9aa..4ac53dd1 100644
--- a/library/VersionInfoFactory.cpp
+++ b/library/VersionInfoFactory.cpp
@@ -105,9 +105,15 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem)
// this is wrong... I'm not going to do base image relocation on linux though.
mem->setBase(0x0);
}
+ else if(os == "darwin")
+ {
+ mem->setOS(OS_APPLE);
+ // this is wrong... I'm not going to do base image relocation on linux though.
+ mem->setBase(0x0);
+ }
else
{
- throw Error::SymbolsXmlBadAttribute("os-type");
+ return; // ignore it if it's invalid
}
// process additional entries
diff --git a/library/include/Console.h b/library/include/Console.h
index 196a1c27..ba59324e 100644
--- a/library/include/Console.h
+++ b/library/include/Console.h
@@ -65,20 +65,20 @@ namespace DFHack
bool save (const char * filename)
{
std::ofstream outfile (filename);
- fprintf(stderr,"Save: Initialized stream\n");
+ //fprintf(stderr,"Save: Initialized stream\n");
if(outfile.bad())
return false;
- fprintf(stderr,"Save: Iterating...\n");
+ //fprintf(stderr,"Save: Iterating...\n");
for(auto iter = history.begin();iter < history.end(); iter++)
{
- fprintf(stderr,"Save: Dumping %s\n",(*iter).c_str());
+ //fprintf(stderr,"Save: Dumping %s\n",(*iter).c_str());
outfile << *iter << std::endl;
- fprintf(stderr,"Save: Flushing\n");
+ //fprintf(stderr,"Save: Flushing\n");
outfile.flush();
}
- fprintf(stderr,"Save: Closing\n");
+ //fprintf(stderr,"Save: Closing\n");
outfile.close();
- fprintf(stderr,"Save: Done\n");
+ //fprintf(stderr,"Save: Done\n");
return true;
}
/// add a command to the history
diff --git a/library/include/Core.h b/library/include/Core.h
index 2810ff14..e4d1080d 100644
--- a/library/include/Core.h
+++ b/library/include/Core.h
@@ -33,7 +33,6 @@ distribution.
#include <stdint.h>
#include "Console.h"
#include "modules/Graphic.h"
-#include "SDL_events.h"
#include "RemoteClient.h"
@@ -86,14 +85,14 @@ namespace DFHack
{
friend int ::SDL_NumJoysticks(void);
friend void ::SDL_Quit(void);
- friend int ::SDL_PollEvent(SDL_Event *);
+ friend int ::SDL_PollEvent(SDL::Event *);
friend int ::SDL_Init(uint32_t flags);
friend int ::wgetch(WINDOW * w);
friend int ::egg_init(void);
friend int ::egg_shutdown(void);
friend int ::egg_tick(void);
friend int ::egg_prerender(void);
- friend int ::egg_sdl_event(SDL_Event* event);
+ friend int ::egg_sdl_event(SDL::Event* event);
friend int ::egg_curses_event(int orig_return);
public:
/// Get the single Core instance or make one.
@@ -170,7 +169,7 @@ namespace DFHack
int Update (void);
int TileUpdate (void);
int Shutdown (void);
- int DFH_SDL_Event(SDL_Event* event);
+ int DFH_SDL_Event(SDL::Event* event);
bool ncurses_wgetch(int in, int & out);
void onUpdate(color_ostream &out);
@@ -215,7 +214,6 @@ namespace DFHack
tthread::mutex * HotkeyMutex;
tthread::condition_variable * HotkeyCond;
- int UnicodeAwareSym(const SDL_KeyboardEvent& ke);
bool SelectHotkey(int key, int modifiers);
// for state change tracking
diff --git a/library/include/DataFuncs.h b/library/include/DataFuncs.h
index aff04128..637a532f 100644
--- a/library/include/DataFuncs.h
+++ b/library/include/DataFuncs.h
@@ -160,6 +160,17 @@ INSTANTIATE_WRAPPERS(6, (OSTREAM_ARG,A1,A2,A3,A4,A5,A6), (out,vA1,vA2,vA3,vA4,vA
#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7))
+INSTANTIATE_WRAPPERS(7, (A1,A2,A3,A4,A5,A6,A7), (vA1,vA2,vA3,vA4,vA5,vA6,vA7),
+ LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);
+ LOAD_ARG(A4); LOAD_ARG(A5); LOAD_ARG(A6);
+ LOAD_ARG(A7);)
+INSTANTIATE_WRAPPERS(7, (OSTREAM_ARG,A1,A2,A3,A4,A5,A6,A7), (out,vA1,vA2,vA3,vA4,vA5,vA6,vA7),
+ LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);
+ LOAD_ARG(A4); LOAD_ARG(A5); LOAD_ARG(A6); LOAD_ARG(A7);)
+#undef FW_TARGS
+
+#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8
+INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7,A8))
#undef FW_TARGS
#undef FW_TARGSC
diff --git a/library/include/Hooks.h b/library/include/Hooks.h
index 83a39ea1..418a5ce3 100644
--- a/library/include/Hooks.h
+++ b/library/include/Hooks.h
@@ -33,8 +33,6 @@ distribution.
#include <string>
#include <stdint.h>
-#include "SDL.h"
-
// function and variable pointer... we don't try to understand what SDL does here
typedef void * fPtr;
typedef void * vPtr;
@@ -48,7 +46,7 @@ namespace SDL
// be declared as friend functions/known
DFhackCExport int SDL_NumJoysticks(void);
DFhackCExport void SDL_Quit(void);
-DFhackCExport int SDL_PollEvent(SDL_Event* event);
+DFhackCExport int SDL_PollEvent(SDL::Event* event);
DFhackCExport int SDL_Init(uint32_t flags);
DFhackCExport int wgetch(WINDOW * win);
@@ -65,7 +63,7 @@ DFhackCExport int egg_tick(void);
DFhackCExport int egg_prerender(void);
// hook - called for each SDL event, can filter both the event and the return value
-DFhackCExport int egg_sdl_event(SDL_Event* event);
+DFhackCExport int egg_sdl_event(SDL::Event* event);
// hook - ncurses event. return -1 to consume
DFhackCExport int egg_curses_event(int orig_return);
diff --git a/library/include/LuaWrapper.h b/library/include/LuaWrapper.h
index 97b2e698..9e449022 100644
--- a/library/include/LuaWrapper.h
+++ b/library/include/LuaWrapper.h
@@ -77,6 +77,7 @@ namespace DFHack { namespace LuaWrapper {
#define DFHACK_ASSIGN_NAME "DFHack::Assign"
#define DFHACK_IS_INSTANCE_NAME "DFHack::IsInstance"
#define DFHACK_DELETE_NAME "DFHack::Delete"
+#define DFHACK_CAST_NAME "DFHack::Cast"
extern LuaToken DFHACK_EMPTY_TABLE_TOKEN;
diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h
index 31633dfe..b76df437 100644
--- a/library/include/PluginManager.h
+++ b/library/include/PluginManager.h
@@ -61,11 +61,11 @@ namespace DFHack
struct DFLibrary;
// Open a plugin library
- DFLibrary * OpenPlugin (const char * filename);
+ DFHACK_EXPORT DFLibrary * OpenPlugin (const char * filename);
// find a symbol inside plugin
- void * LookupPlugin (DFLibrary * plugin ,const char * function);
+ DFHACK_EXPORT void * LookupPlugin (DFLibrary * plugin ,const char * function);
// Close a plugin library
- void ClosePlugin (DFLibrary * plugin);
+ DFHACK_EXPORT void ClosePlugin (DFLibrary * plugin);
struct DFHACK_EXPORT CommandReg {
const char *name;
diff --git a/library/include/SDL_events.h b/library/include/SDL_events.h
new file mode 100644
index 00000000..0457dbca
--- /dev/null
+++ b/library/include/SDL_events.h
@@ -0,0 +1,210 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2009 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+
+// Fake - only structs. Shamelessly pilfered from the SDL library.
+// Needed for processing its event types without polluting our namespaces with C garbage
+
+#pragma once
+#include "SDL_keyboard.h"
+
+namespace SDL
+{
+ enum ButtonState
+ {
+ BTN_RELEASED = 0,
+ BTN_PRESSED = 1
+ };
+
+ /** Event enumerations */
+ enum EventType
+ {
+ ET_NOEVENT = 0, /**< Unused (do not remove) */
+ ET_ACTIVEEVENT, /**< Application loses/gains visibility */
+ ET_KEYDOWN, /**< Keys pressed */
+ ET_KEYUP, /**< Keys released */
+ ET_MOUSEMOTION, /**< Mouse moved */
+ ET_MOUSEBUTTONDOWN, /**< Mouse button pressed */
+ ET_MOUSEBUTTONUP, /**< Mouse button released */
+ ET_JOYAXISMOTION, /**< Joystick axis motion */
+ ET_JOYBALLMOTION, /**< Joystick trackball motion */
+ ET_JOYHATMOTION, /**< Joystick hat position change */
+ ET_JOYBUTTONDOWN, /**< Joystick button pressed */
+ ET_JOYBUTTONUP, /**< Joystick button released */
+ ET_QUIT, /**< User-requested quit */
+ ET_SYSWMEVENT, /**< System specific event */
+ ET_EVENT_RESERVEDA, /**< Reserved for future use.. */
+ ET_EVENT_RESERVEDB, /**< Reserved for future use.. */
+ ET_VIDEORESIZE, /**< User resized video mode */
+ ET_VIDEOEXPOSE, /**< Screen needs to be redrawn */
+ ET_EVENT_RESERVED2, /**< Reserved for future use.. */
+ ET_EVENT_RESERVED3, /**< Reserved for future use.. */
+ ET_EVENT_RESERVED4, /**< Reserved for future use.. */
+ ET_EVENT_RESERVED5, /**< Reserved for future use.. */
+ ET_EVENT_RESERVED6, /**< Reserved for future use.. */
+ ET_EVENT_RESERVED7, /**< Reserved for future use.. */
+ /** Events ET_USEREVENT through ET_MAXEVENTS-1 are for your use */
+ ET_USEREVENT = 24,
+ /** This last event is only for bounding internal arrays
+ * It is the number of bits in the event mask datatype -- Uint32
+ */
+ ET_NUMEVENTS = 32
+ };
+
+ /** Application visibility event structure */
+ struct ActiveEvent
+ {
+ uint8_t type; /**< ET_ACTIVEEVENT */
+ uint8_t gain; /**< Whether given states were gained or lost (1/0) */
+ uint8_t state; /**< A mask of the focus states */
+ };
+
+ /** Keyboard event structure */
+ struct KeyboardEvent
+ {
+ uint8_t type; /**< ET_KEYDOWN or ET_KEYUP */
+ uint8_t which; /**< The keyboard device index */
+ uint8_t state; /**< BTN_PRESSED or BTN_RELEASED */
+ keysym ksym;
+ };
+
+ /** Mouse motion event structure */
+ struct MouseMotionEvent
+ {
+ uint8_t type; /**< ET_MOUSEMOTION */
+ uint8_t which; /**< The mouse device index */
+ uint8_t state; /**< The current button state */
+ uint16_t x, y; /**< The X/Y coordinates of the mouse */
+ int16_t xrel; /**< The relative motion in the X direction */
+ int16_t yrel; /**< The relative motion in the Y direction */
+ };
+
+ /** Mouse button event structure */
+ struct MouseButtonEvent
+ {
+ uint8_t type; /**< ET_MOUSEBUTTONDOWN or ET_MOUSEBUTTONUP */
+ uint8_t which; /**< The mouse device index */
+ uint8_t button; /**< The mouse button index */
+ uint8_t state; /**< BTN_PRESSED or BTN_RELEASED */
+ uint16_t x, y; /**< The X/Y coordinates of the mouse at press time */
+ };
+
+ /** Joystick axis motion event structure */
+ struct JoyAxisEvent
+ {
+ uint8_t type; /**< ET_JOYAXISMOTION */
+ uint8_t which; /**< The joystick device index */
+ uint8_t axis; /**< The joystick axis index */
+ int16_t value; /**< The axis value (range: -32768 to 32767) */
+ };
+
+ /** Joystick trackball motion event structure */
+ struct JoyBallEvent
+ {
+ uint8_t type; /**< ET_JOYBALLMOTION */
+ uint8_t which; /**< The joystick device index */
+ uint8_t ball; /**< The joystick trackball index */
+ int16_t xrel; /**< The relative motion in the X direction */
+ int16_t yrel; /**< The relative motion in the Y direction */
+ };
+
+ /** Joystick hat position change event structure */
+ struct JoyHatEvent
+ {
+ uint8_t type; /**< ET_JOYHATMOTION */
+ uint8_t which; /**< The joystick device index */
+ uint8_t hat; /**< The joystick hat index */
+ uint8_t value; /**< The hat position value:
+ * SDL_HAT_LEFTUP SDL_HAT_UP SDL_HAT_RIGHTUP
+ * SDL_HAT_LEFT SDL_HAT_CENTERED SDL_HAT_RIGHT
+ * SDL_HAT_LEFTDOWN SDL_HAT_DOWN SDL_HAT_RIGHTDOWN
+ * Note that zero means the POV is centered.
+ */
+ };
+
+ /** Joystick button event structure */
+ struct JoyButtonEvent
+ {
+ uint8_t type; /**< ET_JOYBUTTONDOWN or ET_JOYBUTTONUP */
+ uint8_t which; /**< The joystick device index */
+ uint8_t button; /**< The joystick button index */
+ uint8_t state; /**< BTN_PRESSED or BTN_RELEASED */
+ };
+
+ /** The "window resized" event
+ * When you get this event, you are responsible for setting a new video
+ * mode with the new width and height.
+ */
+ struct ResizeEvent
+ {
+ uint8_t type; /**< ET_VIDEORESIZE */
+ int w; /**< New width */
+ int h; /**< New height */
+ };
+
+ /** The "screen redraw" event */
+ struct ExposeEvent
+ {
+ uint8_t type; /**< ET_VIDEOEXPOSE */
+ };
+
+ /** The "quit requested" event */
+ struct QuitEvent
+ {
+ uint8_t type; /**< ET_QUIT */
+ };
+
+ /** A user-defined event type */
+ struct UserEvent
+ {
+ uint8_t type; /**< ETL_USEREVENT through ET_NUMEVENTS-1 */
+ int code; /**< User defined event code */
+ void *data1; /**< User defined data pointer */
+ void *data2; /**< User defined data pointer */
+ };
+
+ /** If you want to use this event, you should include SDL_syswm.h */
+ struct SysWMmsg;
+ struct SysWMEvent
+ {
+ uint8_t type;
+ SysWMmsg *msg;
+ };
+
+ /** General event structure */
+ union Event
+ {
+ uint8_t type;
+ ActiveEvent active;
+ KeyboardEvent key;
+ MouseMotionEvent motion;
+ MouseButtonEvent button;
+ JoyAxisEvent jaxis;
+ JoyBallEvent jball;
+ JoyHatEvent jhat;
+ JoyButtonEvent jbutton;
+ ResizeEvent resize;
+ ExposeEvent expose;
+ QuitEvent quit;
+ UserEvent user;
+ SysWMEvent syswm;
+ };
+} \ No newline at end of file
diff --git a/library/include/SDL_keyboard.h b/library/include/SDL_keyboard.h
new file mode 100644
index 00000000..f0d325f5
--- /dev/null
+++ b/library/include/SDL_keyboard.h
@@ -0,0 +1,61 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2009 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+
+// Fake - only structs. Shamelessly pilfered from the SDL library.
+// Needed for processing its event types without polluting our namespaces with C garbage
+
+#pragma once
+#include "SDL_keysym.h"
+#include <stdint.h>
+
+namespace SDL
+{
+ /** Keysym structure
+ *
+ * - The scancode is hardware dependent, and should not be used by general
+ * applications. If no hardware scancode is available, it will be 0.
+ *
+ * - The 'unicode' translated character is only available when character
+ * translation is enabled by the SDL_EnableUNICODE() API. If non-zero,
+ * this is a UNICODE character corresponding to the keypress. If the
+ * high 9 bits of the character are 0, then this maps to the equivalent
+ * ASCII character:
+ * @code
+ * char ch;
+ * if ( (keysym.unicode & 0xFF80) == 0 ) {
+ * ch = keysym.unicode & 0x7F;
+ * } else {
+ * An international character..
+ * }
+ * @endcode
+ */
+ typedef struct keysym
+ {
+ uint8_t scancode; /**< hardware specific scancode */
+ Key sym; /**< SDL virtual keysym */
+ Mod mod; /**< current key modifiers */
+ uint16_t unicode; /**< translated character */
+ } keysym;
+
+ /** This is the mask which refers to all hotkey bindings */
+ #define ALL_HOTKEYS 0xFFFFFFFF
+} \ No newline at end of file
diff --git a/library/include/SDL_keysym.h b/library/include/SDL_keysym.h
new file mode 100644
index 00000000..4f01cfa9
--- /dev/null
+++ b/library/include/SDL_keysym.h
@@ -0,0 +1,329 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2009 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+
+// Fake - only structs. Shamelessly pilfered from the SDL library.
+// Needed for processing its event types without polluting our namespaces with C garbage
+
+#pragma once
+
+namespace SDL
+{
+ /** What we really want is a mapping of every raw key on the keyboard.
+ * To support international keyboards, we use the range 0xA1 - 0xFF
+ * as international virtual keycodes. We'll follow in the footsteps of X11...
+ * @brief The names of the keys
+ */
+ enum Key
+ {
+ /** @name ASCII mapped keysyms
+ * The keyboard syms have been cleverly chosen to map to ASCII
+ */
+ /*@{*/
+ K_UNKNOWN = 0,
+ K_FIRST = 0,
+ K_BACKSPACE = 8,
+ K_TAB = 9,
+ K_CLEAR = 12,
+ K_RETURN = 13,
+ K_PAUSE = 19,
+ K_ESCAPE = 27,
+ K_SPACE = 32,
+ K_EXCLAIM = 33,
+ K_QUOTEDBL = 34,
+ K_HASH = 35,
+ K_DOLLAR = 36,
+ K_AMPERSAND = 38,
+ K_QUOTE = 39,
+ K_LEFTPAREN = 40,
+ K_RIGHTPAREN = 41,
+ K_ASTERISK = 42,
+ K_PLUS = 43,
+ K_COMMA = 44,
+ K_MINUS = 45,
+ K_PERIOD = 46,
+ K_SLASH = 47,
+ K_0 = 48,
+ K_1 = 49,
+ K_2 = 50,
+ K_3 = 51,
+ K_4 = 52,
+ K_5 = 53,
+ K_6 = 54,
+ K_7 = 55,
+ K_8 = 56,
+ K_9 = 57,
+ K_COLON = 58,
+ K_SEMICOLON = 59,
+ K_LESS = 60,
+ K_EQUALS = 61,
+ K_GREATER = 62,
+ K_QUESTION = 63,
+ K_AT = 64,
+ /*
+ Skip uppercase letters
+ */
+ K_LEFTBRACKET = 91,
+ K_BACKSLASH = 92,
+ K_RIGHTBRACKET = 93,
+ K_CARET = 94,
+ K_UNDERSCORE = 95,
+ K_BACKQUOTE = 96,
+ K_a = 97,
+ K_b = 98,
+ K_c = 99,
+ K_d = 100,
+ K_e = 101,
+ K_f = 102,
+ K_g = 103,
+ K_h = 104,
+ K_i = 105,
+ K_j = 106,
+ K_k = 107,
+ K_l = 108,
+ K_m = 109,
+ K_n = 110,
+ K_o = 111,
+ K_p = 112,
+ K_q = 113,
+ K_r = 114,
+ K_s = 115,
+ K_t = 116,
+ K_u = 117,
+ K_v = 118,
+ K_w = 119,
+ K_x = 120,
+ K_y = 121,
+ K_z = 122,
+ K_DELETE = 127,
+ /* End of ASCII mapped keysyms */
+ /*@}*/
+
+ /** @name International keyboard syms */
+ /*@{*/
+ K_WORLD_0 = 160, /* 0xA0 */
+ K_WORLD_1 = 161,
+ K_WORLD_2 = 162,
+ K_WORLD_3 = 163,
+ K_WORLD_4 = 164,
+ K_WORLD_5 = 165,
+ K_WORLD_6 = 166,
+ K_WORLD_7 = 167,
+ K_WORLD_8 = 168,
+ K_WORLD_9 = 169,
+ K_WORLD_10 = 170,
+ K_WORLD_11 = 171,
+ K_WORLD_12 = 172,
+ K_WORLD_13 = 173,
+ K_WORLD_14 = 174,
+ K_WORLD_15 = 175,
+ K_WORLD_16 = 176,
+ K_WORLD_17 = 177,
+ K_WORLD_18 = 178,
+ K_WORLD_19 = 179,
+ K_WORLD_20 = 180,
+ K_WORLD_21 = 181,
+ K_WORLD_22 = 182,
+ K_WORLD_23 = 183,
+ K_WORLD_24 = 184,
+ K_WORLD_25 = 185,
+ K_WORLD_26 = 186,
+ K_WORLD_27 = 187,
+ K_WORLD_28 = 188,
+ K_WORLD_29 = 189,
+ K_WORLD_30 = 190,
+ K_WORLD_31 = 191,
+ K_WORLD_32 = 192,
+ K_WORLD_33 = 193,
+ K_WORLD_34 = 194,
+ K_WORLD_35 = 195,
+ K_WORLD_36 = 196,
+ K_WORLD_37 = 197,
+ K_WORLD_38 = 198,
+ K_WORLD_39 = 199,
+ K_WORLD_40 = 200,
+ K_WORLD_41 = 201,
+ K_WORLD_42 = 202,
+ K_WORLD_43 = 203,
+ K_WORLD_44 = 204,
+ K_WORLD_45 = 205,
+ K_WORLD_46 = 206,
+ K_WORLD_47 = 207,
+ K_WORLD_48 = 208,
+ K_WORLD_49 = 209,
+ K_WORLD_50 = 210,
+ K_WORLD_51 = 211,
+ K_WORLD_52 = 212,
+ K_WORLD_53 = 213,
+ K_WORLD_54 = 214,
+ K_WORLD_55 = 215,
+ K_WORLD_56 = 216,
+ K_WORLD_57 = 217,
+ K_WORLD_58 = 218,
+ K_WORLD_59 = 219,
+ K_WORLD_60 = 220,
+ K_WORLD_61 = 221,
+ K_WORLD_62 = 222,
+ K_WORLD_63 = 223,
+ K_WORLD_64 = 224,
+ K_WORLD_65 = 225,
+ K_WORLD_66 = 226,
+ K_WORLD_67 = 227,
+ K_WORLD_68 = 228,
+ K_WORLD_69 = 229,
+ K_WORLD_70 = 230,
+ K_WORLD_71 = 231,
+ K_WORLD_72 = 232,
+ K_WORLD_73 = 233,
+ K_WORLD_74 = 234,
+ K_WORLD_75 = 235,
+ K_WORLD_76 = 236,
+ K_WORLD_77 = 237,
+ K_WORLD_78 = 238,
+ K_WORLD_79 = 239,
+ K_WORLD_80 = 240,
+ K_WORLD_81 = 241,
+ K_WORLD_82 = 242,
+ K_WORLD_83 = 243,
+ K_WORLD_84 = 244,
+ K_WORLD_85 = 245,
+ K_WORLD_86 = 246,
+ K_WORLD_87 = 247,
+ K_WORLD_88 = 248,
+ K_WORLD_89 = 249,
+ K_WORLD_90 = 250,
+ K_WORLD_91 = 251,
+ K_WORLD_92 = 252,
+ K_WORLD_93 = 253,
+ K_WORLD_94 = 254,
+ K_WORLD_95 = 255, /* 0xFF */
+ /*@}*/
+
+ /** @name Numeric keypad */
+ /*@{*/
+ K_KP0 = 256,
+ K_KP1 = 257,
+ K_KP2 = 258,
+ K_KP3 = 259,
+ K_KP4 = 260,
+ K_KP5 = 261,
+ K_KP6 = 262,
+ K_KP7 = 263,
+ K_KP8 = 264,
+ K_KP9 = 265,
+ K_KP_PERIOD = 266,
+ K_KP_DIVIDE = 267,
+ K_KP_MULTIPLY = 268,
+ K_KP_MINUS = 269,
+ K_KP_PLUS = 270,
+ K_KP_ENTER = 271,
+ K_KP_EQUALS = 272,
+ /*@}*/
+
+ /** @name Arrows + Home/End pad */
+ /*@{*/
+ K_UP = 273,
+ K_DOWN = 274,
+ K_RIGHT = 275,
+ K_LEFT = 276,
+ K_INSERT = 277,
+ K_HOME = 278,
+ K_END = 279,
+ K_PAGEUP = 280,
+ K_PAGEDOWN = 281,
+ /*@}*/
+
+ /** @name Function keys */
+ /*@{*/
+ K_F1 = 282,
+ K_F2 = 283,
+ K_F3 = 284,
+ K_F4 = 285,
+ K_F5 = 286,
+ K_F6 = 287,
+ K_F7 = 288,
+ K_F8 = 289,
+ K_F9 = 290,
+ K_F10 = 291,
+ K_F11 = 292,
+ K_F12 = 293,
+ K_F13 = 294,
+ K_F14 = 295,
+ K_F15 = 296,
+ /*@}*/
+
+ /** @name Key state modifier keys */
+ /*@{*/
+ K_NUMLOCK = 300,
+ K_CAPSLOCK = 301,
+ K_SCROLLOCK = 302,
+ K_RSHIFT = 303,
+ K_LSHIFT = 304,
+ K_RCTRL = 305,
+ K_LCTRL = 306,
+ K_RALT = 307,
+ K_LALT = 308,
+ K_RMETA = 309,
+ K_LMETA = 310,
+ K_LSUPER = 311, /**< Left "Windows" key */
+ K_RSUPER = 312, /**< Right "Windows" key */
+ K_MODE = 313, /**< "Alt Gr" key */
+ K_COMPOSE = 314, /**< Multi-key compose key */
+ /*@}*/
+
+ /** @name Miscellaneous function keys */
+ /*@{*/
+ K_HELP = 315,
+ K_PRINT = 316,
+ K_SYSREQ = 317,
+ K_BREAK = 318,
+ K_MENU = 319,
+ K_POWER = 320, /**< Power Macintosh power key */
+ K_EURO = 321, /**< Some european keyboards */
+ K_UNDO = 322, /**< Atari keyboard has Undo */
+ /*@}*/
+
+ /* Add any other keys here */
+
+ K_LAST
+ };
+
+ /** Enumeration of valid key mods (possibly OR'd together) */
+ enum Mod {
+ KMOD_NONE = 0x0000,
+ KMOD_LSHIFT= 0x0001,
+ KMOD_RSHIFT= 0x0002,
+ KMOD_LCTRL = 0x0040,
+ KMOD_RCTRL = 0x0080,
+ KMOD_LALT = 0x0100,
+ KMOD_RALT = 0x0200,
+ KMOD_LMETA = 0x0400,
+ KMOD_RMETA = 0x0800,
+ KMOD_NUM = 0x1000,
+ KMOD_CAPS = 0x2000,
+ KMOD_MODE = 0x4000,
+ KMOD_RESERVED = 0x8000,
+ KMOD_CTRL = (KMOD_LCTRL|KMOD_RCTRL),
+ KMOD_SHIFT = (KMOD_LSHIFT|KMOD_RSHIFT),
+ KMOD_ALT = (KMOD_LALT|KMOD_RALT),
+ KMOD_META = (KMOD_LMETA|KMOD_RMETA)
+ };
+} \ No newline at end of file
diff --git a/library/include/modules/World.h b/library/include/modules/World.h
index 81ebfbd6..8b0fe3f5 100644
--- a/library/include/modules/World.h
+++ b/library/include/modules/World.h
@@ -35,32 +35,15 @@ distribution.
#include "Module.h"
#include <ostream>
+#include "DataDefs.h"
+
namespace DFHack
{
- /**
- * \ingroup grp_world
- */
- enum GameMode
- {
- GAMEMODE_DWARF,
- GAMEMODE_ADVENTURE,
- GAMEMODENUM,
- GAMEMODE_NONE
- };
- /**
- * \ingroup grp_world
- */
- enum GameType
- {
- GAMETYPE_DWARF_MAIN,
- GAMETYPE_ADVENTURE_MAIN,
- GAMETYPE_VIEW_LEGENDS,
- GAMETYPE_DWARF_RECLAIM,
- GAMETYPE_DWARF_ARENA,
- GAMETYPE_ADVENTURE_ARENA,
- GAMETYPENUM,
- GAMETYPE_NONE
- };
+ typedef df::game_mode GameMode;
+ typedef df::game_type GameType;
+
+#define GAMEMODE_ADVENTURE df::enums::game_mode::ADVENTURE
+
/**
* \ingroup grp_world
*/
diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua
index 926600c0..4cdb4c95 100644
--- a/library/lua/dfhack.lua
+++ b/library/lua/dfhack.lua
@@ -273,19 +273,24 @@ end
-- Command scripts
-dfhack.scripts = dfhack.scripts or {}
+dfhack.internal.scripts = dfhack.internal.scripts or {}
-function dfhack.run_script(file,...)
- local env = dfhack.scripts[file]
+local scripts = dfhack.internal.scripts
+local hack_path = dfhack.getHackPath()
+
+function dfhack.run_script(name,...)
+ local key = string.lower(name)
+ local file = hack_path..'scripts/'..name..'.lua'
+ local env = scripts[key]
if env == nil then
env = {}
setmetatable(env, { __index = base_env })
- dfhack.scripts[file] = env
end
local f,perr = loadfile(file, 't', env)
if f == nil then
error(perr)
end
+ scripts[key] = env
return f(...)
end
diff --git a/library/lua/memscan.lua b/library/lua/memscan.lua
new file mode 100644
index 00000000..95b9197b
--- /dev/null
+++ b/library/lua/memscan.lua
@@ -0,0 +1,455 @@
+-- Utilities for offset scan scripts.
+
+local _ENV = mkmodule('memscan')
+
+local utils = require('utils')
+
+-- A length-checked view on a memory buffer
+
+CheckedArray = CheckedArray or {}
+
+function CheckedArray.new(type,saddr,eaddr)
+ local data = df.reinterpret_cast(type,saddr)
+ local esize = data:sizeof()
+ local count = math.floor((eaddr-saddr)/esize)
+ local obj = {
+ type = type, start = saddr, size = count*esize,
+ esize = esize, data = data, count = count
+ }
+ setmetatable(obj, CheckedArray)
+ return obj
+end
+
+function CheckedArray:__len()
+ return self.count
+end
+function CheckedArray:__index(idx)
+ if type(idx) == number then
+ if idx >= self.count then
+ error('Index out of bounds: '..tostring(idx))
+ end
+ return self.data[idx]
+ else
+ return CheckedArray[idx]
+ end
+end
+function CheckedArray:__newindex(idx, val)
+ if idx >= self.count then
+ error('Index out of bounds: '..tostring(idx))
+ end
+ self.data[idx] = val
+end
+function CheckedArray:addr2idx(addr, round)
+ local off = addr - self.start
+ if off >= 0 and off < self.size and (round or (off % self.esize) == 0) then
+ return math.floor(off / self.esize), off
+ end
+end
+function CheckedArray:idx2addr(idx)
+ if idx >= 0 and idx < self.count then
+ return self.start + idx*self.esize
+ end
+end
+
+-- Search methods
+
+function CheckedArray:find(data,sidx,eidx,reverse)
+ local dcnt = #data
+ sidx = math.max(0, sidx or 0)
+ eidx = math.min(self.count, eidx or self.count)
+ if (eidx - sidx) >= dcnt and dcnt > 0 then
+ return dfhack.with_temp_object(
+ df.new(self.type, dcnt),
+ function(buffer)
+ for i = 1,dcnt do
+ buffer[i-1] = data[i]
+ end
+ local cnt = eidx - sidx - dcnt
+ local step = self.esize
+ local sptr = self.start + sidx*step
+ local ksize = dcnt*step
+ if reverse then
+ local idx, addr = dfhack.internal.memscan(sptr + cnt*step, cnt, -step, buffer, ksize)
+ if idx then
+ return sidx + cnt - idx, addr
+ end
+ else
+ local idx, addr = dfhack.internal.memscan(sptr, cnt, step, buffer, ksize)
+ if idx then
+ return sidx + idx, addr
+ end
+ end
+ end
+ )
+ end
+end
+function CheckedArray:find_one(data,sidx,eidx,reverse)
+ local idx, addr = self:find(data,sidx,eidx,reverse)
+ if idx then
+ -- Verify this is the only match
+ if reverse then
+ eidx = idx+#data-1
+ else
+ sidx = idx+1
+ end
+ if self:find(data,sidx,eidx,reverse) then
+ return nil
+ end
+ end
+ return idx, addr
+end
+function CheckedArray:list_changes(old_arr,old_val,new_val,delta)
+ if old_arr.type ~= self.type or old_arr.count ~= self.count then
+ error('Incompatible arrays')
+ end
+ local eidx = self.count
+ local optr = old_arr.start
+ local nptr = self.start
+ local esize = self.esize
+ local rv
+ local sidx = 0
+ while true do
+ local idx = dfhack.internal.diffscan(optr, nptr, sidx, eidx, esize, old_val, new_val, delta)
+ if not idx then
+ break
+ end
+ rv = rv or {}
+ rv[#rv+1] = idx
+ sidx = idx+1
+ end
+ return rv
+end
+function CheckedArray:filter_changes(prev_list,old_arr,old_val,new_val,delta)
+ if old_arr.type ~= self.type or old_arr.count ~= self.count then
+ error('Incompatible arrays')
+ end
+ local eidx = self.count
+ local optr = old_arr.start
+ local nptr = self.start
+ local esize = self.esize
+ local rv
+ for i=1,#prev_list do
+ local idx = prev_list[i]
+ if idx < 0 or idx >= eidx then
+ error('Index out of bounds: '..idx)
+ end
+ if dfhack.internal.diffscan(optr, nptr, idx, idx+1, esize, old_val, new_val, delta) then
+ rv = rv or {}
+ rv[#rv+1] = idx
+ end
+ end
+ return rv
+end
+
+-- A raw memory area class
+
+MemoryArea = MemoryArea or {}
+MemoryArea.__index = MemoryArea
+
+function MemoryArea.new(astart, aend)
+ local obj = {
+ start_addr = astart, end_addr = aend, size = aend - astart,
+ int8_t = CheckedArray.new('int8_t',astart,aend),
+ uint8_t = CheckedArray.new('uint8_t',astart,aend),
+ int16_t = CheckedArray.new('int16_t',astart,aend),
+ uint16_t = CheckedArray.new('uint16_t',astart,aend),
+ int32_t = CheckedArray.new('int32_t',astart,aend),
+ uint32_t = CheckedArray.new('uint32_t',astart,aend)
+ }
+ setmetatable(obj, MemoryArea)
+ return obj
+end
+
+function MemoryArea:__gc()
+ df.delete(self.buffer)
+end
+
+function MemoryArea:__tostring()
+ return string.format('<MemoryArea: %x..%x>', self.start_addr, self.end_addr)
+end
+function MemoryArea:contains_range(start,size)
+ return start >= self.start_addr and (start+size) <= self.end_addr
+end
+function MemoryArea:contains_obj(obj,count)
+ local size, base = df.sizeof(obj)
+ return size and base and self:contains_range(base, size*(count or 1))
+end
+
+function MemoryArea:clone()
+ local buffer = df.new('int8_t', self.size)
+ local _, base = buffer:sizeof()
+ local rv = MemoryArea.new(base, base+self.size)
+ rv.buffer = buffer
+ return rv
+end
+function MemoryArea:copy_from(area2)
+ if area2.size ~= self.size then
+ error('Size mismatch')
+ end
+ dfhack.internal.memmove(self.start_addr, area2.start_addr, self.size)
+end
+function MemoryArea:delete()
+ setmetatable(self, nil)
+ df.delete(self.buffer)
+ for k,v in pairs(self) do self[k] = nil end
+end
+
+-- Static data segment search
+
+local function find_data_segment()
+ local data_start, data_end
+
+ for i,mem in ipairs(dfhack.internal.getMemRanges()) do
+ if data_end then
+ if mem.start_addr == data_end and mem.read and mem.write then
+ data_end = mem.end_addr
+ else
+ break
+ end
+ elseif mem.read and mem.write
+ and (string.match(mem.name,'/dwarfort%.exe$')
+ or string.match(mem.name,'/Dwarf_Fortress$'))
+ then
+ data_start = mem.start_addr
+ data_end = mem.end_addr
+ end
+ end
+
+ return data_start, data_end
+end
+
+function get_data_segment()
+ local s, e = find_data_segment()
+ if s and e then
+ return MemoryArea.new(s, e)
+ end
+end
+
+-- Register a found offset, or report an error.
+
+function found_offset(name,val)
+ local cval = dfhack.internal.getAddress(name)
+
+ if not val then
+ print('Could not find offset '..name)
+ if not cval and not utils.prompt_yes_no('Continue with the script?') then
+ error('User quit')
+ end
+ return
+ end
+
+ if df.isvalid(val) then
+ _,val = val:sizeof()
+ end
+
+ print(string.format('Found offset %s: %x', name, val))
+
+ if cval then
+ if cval ~= val then
+ error(string.format('Mismatch with the current value: %x',val))
+ end
+ else
+ dfhack.internal.setAddress(name, val)
+ end
+end
+
+-- Offset of a field within struct
+
+function field_ref(handle,...)
+ local items = table.pack(...)
+ for i=1,items.n-1 do
+ handle = handle[items[i]]
+ end
+ return handle:_field(items[items.n])
+end
+
+function field_offset(type,...)
+ local handle = df.reinterpret_cast(type,1)
+ local _,addr = df.sizeof(field_ref(handle,...))
+ return addr-1
+end
+
+function MemoryArea:object_by_field(addr,type,...)
+ if not addr then
+ return nil
+ end
+ local base = addr - field_offset(type,...)
+ local obj = df.reinterpret_cast(type, base)
+ if not self:contains_obj(obj) then
+ obj = nil
+ end
+ return obj, base
+end
+
+-- Validation
+
+function is_valid_vector(ref,size)
+ local ints = df.reinterpret_cast('uint32_t', ref)
+ return ints[0] <= ints[1] and ints[1] <= ints[2]
+ and (size == nil or (ints[1] - ints[0]) % size == 0)
+end
+
+-- Difference search helper
+
+DiffSearcher = DiffSearcher or {}
+DiffSearcher.__index = DiffSearcher
+
+function DiffSearcher.new(area)
+ local obj = { area = area }
+ setmetatable(obj, DiffSearcher)
+ return obj
+end
+
+function DiffSearcher:begin_search(type)
+ self.type = type
+ self.old_value = nil
+ self.search_sets = nil
+ if not self.save_area then
+ self.save_area = self.area:clone()
+ end
+end
+function DiffSearcher:advance_search(new_value,delta)
+ if self.search_sets then
+ local nsets = #self.search_sets
+ local ovec = self.save_area[self.type]
+ local nvec = self.area[self.type]
+ local new_set
+ if nsets > 0 then
+ local last_set = self.search_sets[nsets]
+ new_set = nvec:filter_changes(last_set,ovec,self.old_value,new_value,delta)
+ else
+ new_set = nvec:list_changes(ovec,self.old_value,new_value,delta)
+ end
+ if new_set then
+ self.search_sets[nsets+1] = new_set
+ self.old_value = new_value
+ self.save_area:copy_from(self.area)
+ return #new_set, new_set
+ end
+ else
+ self.old_value = new_value
+ self.search_sets = {}
+ self.save_area:copy_from(self.area)
+ return #self.save_area[self.type], nil
+ end
+end
+function DiffSearcher:reset()
+ self.search_sets = nil
+ if self.save_area then
+ self.save_area:delete()
+ self.save_area = nil
+ end
+end
+function DiffSearcher:idx2addr(idx)
+ return self.area[self.type]:idx2addr(idx)
+end
+
+-- Interactive search utility
+
+function DiffSearcher:find_interactive(prompt,data_type,condition_cb)
+ enum = enum or {}
+
+ -- Loop for restarting search from scratch
+ while true do
+ print('\n'..prompt)
+
+ self:begin_search(data_type)
+
+ local found = false
+ local ccursor = 0
+
+ -- Loop through choices
+ while true do
+ print('')
+
+ local ok, value, delta = condition_cb(ccursor)
+
+ ccursor = ccursor + 1
+
+ if not ok then
+ break
+ end
+
+ -- Search for it in the memory
+ local cnt, set = self:advance_search(value, delta)
+ if not cnt then
+ dfhack.printerr(' Converged to zero candidates; probably a mistake somewhere.')
+ break
+ elseif set and cnt == 1 then
+ -- To confirm, wait for two 1-candidate results in a row
+ if found then
+ local addr = self:idx2addr(set[1])
+ print(string.format(' Confirmed address: %x\n', addr))
+ return addr, set[1]
+ else
+ found = true
+ end
+ end
+
+ print(' '..cnt..' candidates remaining.')
+ end
+
+ if not utils.prompt_yes_no('\nRetry search from the start?') then
+ return nil
+ end
+ end
+end
+
+function DiffSearcher:find_menu_cursor(prompt,data_type,choices,enum)
+ enum = enum or {}
+
+ return self:find_interactive(
+ prompt, data_type,
+ function(ccursor)
+ local choice
+
+ -- Select the next value to search for
+ if type(choices) == 'function' then
+ choice = choices(ccursor)
+
+ if not choice then
+ return false
+ end
+ else
+ choice = choices[(ccursor % #choices) + 1]
+ end
+
+ -- Ask the user to select it
+ if enum ~= 'noprompt' then
+ local cname = enum[choice] or choice
+ if type(choice) == 'string' and type(cname) == 'number' then
+ choice, cname = cname, choice
+ end
+ if cname ~= choice then
+ cname = cname..' ('..choice..')'
+ end
+
+ print(' Please select: '..cname)
+ if not utils.prompt_yes_no(' Continue?', true) then
+ return false
+ end
+ end
+
+ return true, choice
+ end
+ )
+end
+
+function DiffSearcher:find_counter(prompt,data_type,delta,action_prompt)
+ delta = delta or 1
+
+ return self:find_interactive(
+ prompt, data_type,
+ function(ccursor)
+ if ccursor > 0 then
+ print(" "..(action_prompt or 'Please do the action.'))
+ end
+ if not utils.prompt_yes_no(' Continue?', true) then
+ return false
+ end
+ return true, nil, delta
+ end
+ )
+end
+
+return _ENV
diff --git a/library/lua/utils.lua b/library/lua/utils.lua
index e67801f4..93ee840c 100644
--- a/library/lua/utils.lua
+++ b/library/lua/utils.lua
@@ -361,4 +361,50 @@ function insert_or_update(vector,item,field,cmp)
return added,cur,pos
end
+-- Ask a yes-no question
+function prompt_yes_no(msg,default)
+ local prompt = msg
+ if default == nil then
+ prompt = prompt..' (y/n): '
+ elseif default then
+ prompt = prompt..' (y/n)[y]: '
+ else
+ prompt = prompt..' (y/n)[n]: '
+ end
+ while true do
+ local rv = dfhack.lineedit(prompt)
+ if rv then
+ if string.match(rv,'^[Yy]') then
+ return true
+ elseif string.match(rv,'^[Nn]') then
+ return false
+ elseif rv == 'abort' then
+ error('User abort in utils.prompt_yes_no()')
+ elseif rv == '' and default ~= nil then
+ return default
+ end
+ end
+ end
+end
+
+-- Ask for input with check function
+function prompt_input(prompt,check,quit_str)
+ quit_str = quit_str or '~~~'
+ while true do
+ local rv = dfhack.lineedit(prompt)
+ if rv == quit_str then
+ return nil
+ end
+ local rtbl = table.pack(check(rv))
+ if rtbl[1] then
+ return table.unpack(rtbl,2,rtbl.n)
+ end
+ end
+end
+
+function check_number(text)
+ local nv = tonumber(text)
+ return nv ~= nil, nv
+end
+
return _ENV \ No newline at end of file
diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp
index e1810522..cd44401f 100644
--- a/library/modules/Gui.cpp
+++ b/library/modules/Gui.cpp
@@ -58,12 +58,14 @@ using namespace DFHack;
#include "df/viewscreen_layer_overall_healthst.h"
#include "df/viewscreen_layer_assigntradest.h"
#include "df/viewscreen_layer_militaryst.h"
+#include "df/viewscreen_layer_stockpilest.h"
#include "df/viewscreen_petst.h"
#include "df/viewscreen_tradegoodsst.h"
#include "df/viewscreen_storesst.h"
#include "df/ui_unit_view_mode.h"
#include "df/ui_sidebar_menus.h"
#include "df/ui_look_list.h"
+#include "df/ui_advmode.h"
#include "df/job.h"
#include "df/ui_build_selector.h"
#include "df/building_workshopst.h"
@@ -273,7 +275,9 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode)
break;
case Burrows:
- if (ui->burrows.in_add_units_mode)
+ if (ui->burrows.in_confirm_delete)
+ focus += "/ConfirmDelete";
+ else if (ui->burrows.in_add_units_mode)
focus += "/AddUnits";
else if (ui->burrows.in_edit_name_mode)
focus += "/EditName";
@@ -288,6 +292,16 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode)
}
}
+DEFINE_GET_FOCUS_STRING_HANDLER(dungeonmode)
+{
+ using df::global::ui_advmode;
+
+ if (!ui_advmode)
+ return;
+
+ focus += "/" + enum_item_key(ui_advmode->menu);
+}
+
DEFINE_GET_FOCUS_STRING_HANDLER(unitlist)
{
focus += "/" + enum_item_key(screen->page);
@@ -407,6 +421,35 @@ DEFINE_GET_FOCUS_STRING_HANDLER(stores)
focus += "/Items";
}
+DEFINE_GET_FOCUS_STRING_HANDLER(layer_stockpile)
+{
+ auto list1 = getLayerList(screen, 0);
+ auto list2 = getLayerList(screen, 1);
+ auto list3 = getLayerList(screen, 2);
+ if (!list1 || !list2 || !list3 || !screen->settings) return;
+
+ auto group = screen->cur_group;
+ if (group != vector_get(screen->group_ids, list1->cursor))
+ return;
+
+ focus += "/" + enum_item_key(group);
+
+ auto bits = vector_get(screen->group_bits, list1->cursor);
+ if (bits.whole && !(bits.whole & screen->settings->flags.whole))
+ {
+ focus += "/Off";
+ return;
+ }
+
+ focus += "/On";
+
+ if (list2->bright || list3->bright || screen->list_ids.empty()) {
+ focus += "/" + enum_item_key(screen->cur_list);
+
+ if (list3->bright)
+ focus += (screen->item_names.empty() ? "/None" : "/Item");
+ }
+}
std::string Gui::getFocusString(df::viewscreen *top)
{
diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp
index f49dd82e..f8f99f81 100644
--- a/library/modules/Materials.cpp
+++ b/library/modules/Materials.cpp
@@ -761,8 +761,8 @@ bool Materials::ReadCreatureTypesEx (void)
{
df::body_part_raw *bp = ca->body_info.body_parts[k];
t_bodypart part;
- part.id = bp->part_code;
- part.category = bp->part_name;
+ part.id = bp->token;
+ part.category = bp->category;
caste.bodypart.push_back(part);
}
using namespace df::enums::mental_attribute_type;
diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp
index dd26109e..ee383cc0 100644
--- a/library/modules/Units.cpp
+++ b/library/modules/Units.cpp
@@ -613,12 +613,45 @@ df::nemesis_record *Units::getNemesis(df::unit *unit)
return NULL;
}
+static bool casteFlagSet(int race, int caste, df::caste_raw_flags flag)
+{
+ auto creature = df::creature_raw::find(race);
+ if (!creature)
+ return false;
+
+ auto craw = vector_get(creature->caste, caste);
+ if (!craw)
+ return false;
+
+ return craw->flags.is_set(flag);
+}
+
+static bool isCrazed(df::unit *unit)
+{
+ if (unit->flags3.bits.scuttle)
+ return false;
+ if (unit->curse.rem_tags1.bits.CRAZED)
+ return false;
+ if (unit->curse.add_tags1.bits.CRAZED)
+ return true;
+ return casteFlagSet(unit->race, unit->caste, caste_raw_flags::CRAZED);
+}
+
+static bool isOpposedToLife(df::unit *unit)
+{
+ if (unit->curse.rem_tags1.bits.OPPOSED_TO_LIFE)
+ return false;
+ if (unit->curse.add_tags1.bits.OPPOSED_TO_LIFE)
+ return true;
+ return casteFlagSet(unit->race, unit->caste, caste_raw_flags::CANNOT_UNDEAD);
+}
bool DFHack::Units::isDead(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
- return unit->flags1.bits.dead;
+ return unit->flags1.bits.dead ||
+ unit->flags3.bits.ghostly;
}
bool DFHack::Units::isAlive(df::unit *unit)
@@ -636,8 +669,11 @@ bool DFHack::Units::isSane(df::unit *unit)
if (unit->flags1.bits.dead ||
unit->flags3.bits.ghostly ||
- unit->curse.add_tags1.bits.OPPOSED_TO_LIFE ||
- unit->curse.add_tags1.bits.CRAZED)
+ isOpposedToLife(unit) ||
+ unit->unknown8.unk2)
+ return false;
+
+ if (unit->unknown8.normal_race == unit->unknown8.were_race && isCrazed(unit))
return false;
switch (unit->mood)
@@ -657,19 +693,38 @@ bool DFHack::Units::isCitizen(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
+ // Copied from the conditions used to decide game over,
+ // except that the game appears to let melancholy/raving
+ // dwarves count as citizens.
+
+ if (!isDwarf(unit) || !isSane(unit))
+ return false;
+
+ if (unit->flags1.bits.marauder ||
+ unit->flags1.bits.invader_origin ||
+ unit->flags1.bits.active_invader ||
+ unit->flags1.bits.forest ||
+ unit->flags1.bits.merchant ||
+ unit->flags1.bits.diplomat)
+ return false;
+
+ if (unit->flags1.bits.tame)
+ return true;
+
return unit->civ_id == ui->civ_id &&
- !unit->flags1.bits.merchant &&
- !unit->flags1.bits.diplomat &&
+ unit->civ_id != -1 &&
+ !unit->flags2.bits.underworld &&
!unit->flags2.bits.resident &&
- !unit->flags1.bits.dead &&
- !unit->flags3.bits.ghostly;
+ !unit->flags2.bits.visitor_uninvited &&
+ !unit->flags2.bits.visitor;
}
bool DFHack::Units::isDwarf(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
- return unit->race == ui->race_id;
+ return unit->race == ui->race_id ||
+ unit->unknown8.normal_race == ui->race_id;
}
double DFHack::Units::getAge(df::unit *unit, bool true_age)
diff --git a/library/modules/World.cpp b/library/modules/World.cpp
index 4b50b44c..393e7cbf 100644
--- a/library/modules/World.cpp
+++ b/library/modules/World.cpp
@@ -85,7 +85,7 @@ World::World()
if(df::global::current_weather)
d->StartedWeather = true;
- if (df::global::game_mode && df::global::control_mode)
+ if (df::global::gamemode && df::global::gametype)
d->StartedMode = true;
d->Inited = true;
@@ -132,8 +132,8 @@ bool World::ReadGameMode(t_gamemodes& rd)
{
if(d->Inited && d->StartedMode)
{
- rd.g_mode = (DFHack::GameMode)*df::global::control_mode;
- rd.g_type = (DFHack::GameType)*df::global::game_mode;
+ rd.g_mode = (DFHack::GameMode)*df::global::gamemode;
+ rd.g_type = (DFHack::GameType)*df::global::gametype;
return true;
}
return false;
@@ -142,8 +142,8 @@ bool World::WriteGameMode(const t_gamemodes & wr)
{
if(d->Inited && d->StartedMode)
{
- *df::global::control_mode = wr.g_mode;
- *df::global::game_mode = wr.g_type;
+ *df::global::gamemode = wr.g_mode;
+ *df::global::gametype = wr.g_type;
return true;
}
return false;
diff --git a/package/darwin/dfhack b/package/darwin/dfhack
index 6f54d15d..62407342 100755
--- a/package/darwin/dfhack
+++ b/package/darwin/dfhack
@@ -5,7 +5,7 @@ OSREV=`uname -r | cut -d. -f1`
if [ "$OSREV" -ge 11 ] ; then
export DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib
export DYLD_LIBRARY_PATH=${PWD}/hack:${PWD}/libs
- export DYLD_FRAMEWORK_PATH=${PWD}/hack${PWD}/libs
+ export DYLD_FRAMEWORK_PATH=${PWD}/hack:${PWD}/libs
else
export DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib
export DYLD_FALLBACK_LIBRARY_PATH=${PWD}/hack:${PWD}/libs
diff --git a/plugins/burrows.cpp b/plugins/burrows.cpp
index 9ff902c3..0b66a7b9 100644
--- a/plugins/burrows.cpp
+++ b/plugins/burrows.cpp
@@ -101,8 +101,8 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan
switch (event) {
case SC_MAP_LOADED:
deinit_map(out);
- if (df::global::game_mode &&
- *df::global::game_mode == GAMEMODE_DWARF)
+ if (df::global::gamemode &&
+ *df::global::gamemode == game_mode::DWARF)
init_map(out);
break;
case SC_MAP_UNLOADED:
diff --git a/plugins/devel/counters.cpp b/plugins/devel/counters.cpp
index 66d8c63a..33294567 100644
--- a/plugins/devel/counters.cpp
+++ b/plugins/devel/counters.cpp
@@ -25,7 +25,7 @@ command_result df_counters (color_ostream &out, vector <string> & parameters)
for (size_t i = 0; i < counters.size(); i++)
{
auto counter = counters[i];
- out.print("%i (%s): %i\n", counter->id, ENUM_KEY_STR(unit_misc_trait::T_id, counter->id).c_str(), counter->value);
+ out.print("%i (%s): %i\n", counter->id, ENUM_KEY_STR(misc_trait_type, counter->id).c_str(), counter->value);
}
return CR_OK;
diff --git a/plugins/dwarfexport/dwarfexport.cpp b/plugins/dwarfexport/dwarfexport.cpp
index cb6e9f19..4a78332c 100644
--- a/plugins/dwarfexport/dwarfexport.cpp
+++ b/plugins/dwarfexport/dwarfexport.cpp
@@ -89,13 +89,13 @@ static void element(const char* name, const uint32_t content, ostream& out, cons
static void printAttributes(color_ostream &con, df::unit* cre, ostream& out) {
out << " <Attributes>" << endl;
for (int i = 0; i < NUM_CREATURE_PHYSICAL_ATTRIBUTES; i++) {
- element(physicals[i], cre->body.physical_attrs[i].unk1, out, " ");
+ element(physicals[i], cre->body.physical_attrs[i].value, out, " ");
}
df::unit_soul * s = cre->status.current_soul;
if (s) {
for (int i = 0; i < NUM_CREATURE_MENTAL_ATTRIBUTES; i++) {
- element(mentals[i], s->mental_attrs[i].unk1, out, " ");
+ element(mentals[i], s->mental_attrs[i].value, out, " ");
}
}
out << " </Attributes>" << endl;
diff --git a/plugins/forceequip.cpp b/plugins/forceequip.cpp
index 0d493143..abf4a95d 100644
--- a/plugins/forceequip.cpp
+++ b/plugins/forceequip.cpp
@@ -28,7 +28,7 @@ using namespace std;
#include "df/body_part_raw.h"
#include "MiscUtils.h"
#include "df/unit_inventory_item.h"
-#include "df/body_part_template_flags.h"
+#include "df/body_part_raw_flags.h"
#include "df/creature_raw.h"
#include "df/caste_raw.h"
#include "df/body_detail_plan.h"
@@ -292,7 +292,7 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u
else if (bpIndex < unit->body.body_plan->body_parts.size())
{
// The current body part is not the one that was specified in the function call, but we can keep searching
- if (verbose) { Core::printerr("Found bodypart %s; not a match; continuing search.\n", currPart->part_code.c_str()); }
+ if (verbose) { Core::printerr("Found bodypart %s; not a match; continuing search.\n", currPart->token.c_str()); }
continue;
}
else
@@ -302,35 +302,35 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u
return false;
}
- if (verbose) { Core::print("Inspecting bodypart %s.\n", currPart->part_code.c_str()); }
+ if (verbose) { Core::print("Inspecting bodypart %s.\n", currPart->token.c_str()); }
// Inspect the current bodypart
- if (item->getType() == df::enums::item_type::GLOVES && currPart->flags.is_set(df::body_part_template_flags::GRASP) &&
- ((item->getGloveHandedness() == const_GloveLeftHandedness && currPart->flags.is_set(df::body_part_template_flags::LEFT)) ||
- (item->getGloveHandedness() == const_GloveRightHandedness && currPart->flags.is_set(df::body_part_template_flags::RIGHT))))
+ if (item->getType() == df::enums::item_type::GLOVES && currPart->flags.is_set(df::body_part_raw_flags::GRASP) &&
+ ((item->getGloveHandedness() == const_GloveLeftHandedness && currPart->flags.is_set(df::body_part_raw_flags::LEFT)) ||
+ (item->getGloveHandedness() == const_GloveRightHandedness && currPart->flags.is_set(df::body_part_raw_flags::RIGHT))))
{
- if (verbose) { Core::print("Hand found (%s)...", currPart->part_code.c_str()); }
+ if (verbose) { Core::print("Hand found (%s)...", currPart->token.c_str()); }
}
- else if (item->getType() == df::enums::item_type::HELM && currPart->flags.is_set(df::body_part_template_flags::HEAD))
+ else if (item->getType() == df::enums::item_type::HELM && currPart->flags.is_set(df::body_part_raw_flags::HEAD))
{
- if (verbose) { Core::print("Head found (%s)...", currPart->part_code.c_str()); }
+ if (verbose) { Core::print("Head found (%s)...", currPart->token.c_str()); }
}
- else if (item->getType() == df::enums::item_type::ARMOR && currPart->flags.is_set(df::body_part_template_flags::UPPERBODY))
+ else if (item->getType() == df::enums::item_type::ARMOR && currPart->flags.is_set(df::body_part_raw_flags::UPPERBODY))
{
- if (verbose) { Core::print("Upper body found (%s)...", currPart->part_code.c_str()); }
+ if (verbose) { Core::print("Upper body found (%s)...", currPart->token.c_str()); }
}
- else if (item->getType() == df::enums::item_type::PANTS && currPart->flags.is_set(df::body_part_template_flags::LOWERBODY))
+ else if (item->getType() == df::enums::item_type::PANTS && currPart->flags.is_set(df::body_part_raw_flags::LOWERBODY))
{
- if (verbose) { Core::print("Lower body found (%s)...", currPart->part_code.c_str()); }
+ if (verbose) { Core::print("Lower body found (%s)...", currPart->token.c_str()); }
}
- else if (item->getType() == df::enums::item_type::SHOES && currPart->flags.is_set(df::body_part_template_flags::STANCE))
+ else if (item->getType() == df::enums::item_type::SHOES && currPart->flags.is_set(df::body_part_raw_flags::STANCE))
{
- if (verbose) { Core::print("Foot found (%s)...", currPart->part_code.c_str()); }
+ if (verbose) { Core::print("Foot found (%s)...", currPart->token.c_str()); }
}
else if (targetBodyPart && ignoreRestrictions)
{
// The BP in question would normally be considered ineligible for equipment. But since it was deliberately specified by the user, we'll proceed anyways.
- if (verbose) { Core::print("Non-standard bodypart found (%s)...", targetBodyPart->part_code.c_str()); }
+ if (verbose) { Core::print("Non-standard bodypart found (%s)...", targetBodyPart->token.c_str()); }
}
else if (targetBodyPart)
{
@@ -538,16 +538,16 @@ command_result df_forceequip(color_ostream &out, vector <string> & parameters)
{
// Tentatively assume that the part is a match
targetBodyPart = targetUnit->body.body_plan->body_parts.at(bpIndex);
- if (targetBodyPart->part_code.compare(targetBodyPartCode) == 0)
+ if (targetBodyPart->token.compare(targetBodyPartCode) == 0)
{
// It is indeed a match; exit the loop (while leaving the variable populated)
- if (verbose) { out.print("Matching bodypart (%s) found.\n", targetBodyPart->part_name.c_str()); }
+ if (verbose) { out.print("Matching bodypart (%s) found.\n", targetBodyPart->token.c_str()); }
break;
}
else
{
// Not a match; nullify the variable (it will get re-populated on the next pass through the loop)
- if (verbose) { out.printerr("Bodypart \"%s\" does not match \"%s\".\n", targetBodyPart->part_code.c_str(), targetBodyPartCode.c_str()); }
+ if (verbose) { out.printerr("Bodypart \"%s\" does not match \"%s\".\n", targetBodyPart->token.c_str(), targetBodyPartCode.c_str()); }
targetBodyPart = NULL;
}
}
diff --git a/plugins/mode.cpp b/plugins/mode.cpp
index 33dd5875..f9e6cd10 100644
--- a/plugins/mode.cpp
+++ b/plugins/mode.cpp
@@ -10,7 +10,7 @@ using namespace std;
#include "modules/World.h"
#include <stdlib.h>
using namespace DFHack;
-
+using namespace df::enums;
command_result mode (color_ostream &out, vector <string> & parameters);
@@ -44,28 +44,28 @@ void printCurrentModes(t_gamemodes gm, Console & con)
con << "Current game type:\t" << gm.g_type << " (";
switch(gm.g_type)
{
- case GAMETYPE_DWARF_MAIN:
+ case game_type::DWARF_MAIN:
con << "Fortress)" << endl;
break;
- case GAMETYPE_ADVENTURE_MAIN:
+ case game_type::ADVENTURE_MAIN:
con << "Adventurer)" << endl;
break;
- case GAMETYPE_VIEW_LEGENDS:
+ case game_type::VIEW_LEGENDS:
con << "Legends)" << endl;
break;
- case GAMETYPE_DWARF_RECLAIM:
+ case game_type::DWARF_RECLAIM:
con << "Reclaim)" << endl;
break;
- case GAMETYPE_DWARF_ARENA:
+ case game_type::DWARF_ARENA:
con << "Arena)" << endl;
break;
- case GAMETYPE_ADVENTURE_ARENA:
+ case game_type::ADVENTURE_ARENA:
con << "Arena - control creature)" << endl;
break;
- case GAMETYPENUM:
+ case game_type::num:
con << "INVALID)" << endl;
break;
- case GAMETYPE_NONE:
+ case game_type::NONE:
con << "NONE)" << endl;
break;
default:
@@ -75,16 +75,16 @@ void printCurrentModes(t_gamemodes gm, Console & con)
con << "Current game mode:\t" << gm.g_mode << " (";
switch (gm.g_mode)
{
- case GAMEMODE_DWARF:
+ case game_mode::DWARF:
con << "Dwarf)" << endl;
break;
- case GAMEMODE_ADVENTURE:
+ case game_mode::ADVENTURE:
con << "Adventure)" << endl;
break;
- case GAMEMODENUM:
+ case game_mode::num:
con << "INVALID)" << endl;
break;
- case GAMEMODE_NONE:
+ case game_mode::NONE:
con << "NONE)" << endl;
break;
default:
@@ -132,7 +132,7 @@ command_result mode (color_ostream &out_, vector <string> & parameters)
{
if(!abuse)
{
- if( gm.g_mode == GAMEMODE_NONE || gm.g_type == GAMETYPE_VIEW_LEGENDS)
+ if( gm.g_mode == game_mode::NONE || gm.g_type == game_type::VIEW_LEGENDS)
{
out.printerr("It is not safe to set modes in menus.\n");
return CR_FAILURE;
@@ -163,24 +163,24 @@ command_result mode (color_ostream &out_, vector <string> & parameters)
switch(select)
{
case 0:
- gm.g_mode = GAMEMODE_DWARF;
- gm.g_type = GAMETYPE_DWARF_MAIN;
+ gm.g_mode = game_mode::DWARF;
+ gm.g_type = game_type::DWARF_MAIN;
break;
case 1:
- gm.g_mode = GAMEMODE_ADVENTURE;
- gm.g_type = GAMETYPE_ADVENTURE_MAIN;
+ gm.g_mode = game_mode::ADVENTURE;
+ gm.g_type = game_type::ADVENTURE_MAIN;
break;
case 2:
- gm.g_mode = GAMEMODE_DWARF;
- gm.g_type = GAMETYPE_DWARF_ARENA;
+ gm.g_mode = game_mode::DWARF;
+ gm.g_type = game_type::DWARF_ARENA;
break;
case 3:
- gm.g_mode = GAMEMODE_ADVENTURE;
- gm.g_type = GAMETYPE_ADVENTURE_ARENA;
+ gm.g_mode = game_mode::ADVENTURE;
+ gm.g_type = game_type::ADVENTURE_ARENA;
break;
case 4:
- gm.g_mode = GAMEMODE_DWARF;
- gm.g_type = GAMETYPE_DWARF_RECLAIM;
+ gm.g_mode = game_mode::DWARF;
+ gm.g_type = game_type::DWARF_RECLAIM;
break;
}
}
diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp
index 513eeded..f3542430 100644
--- a/plugins/reveal.cpp
+++ b/plugins/reveal.cpp
@@ -90,7 +90,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
World *World = Core::getInstance().getWorld();
t_gamemodes gm;
World->ReadGameMode(gm);
- if(gm.g_mode == GAMEMODE_DWARF)
+ if(gm.g_mode == game_mode::DWARF)
{
// if the map is revealed and we're in fortress mode, force the game to pause.
if(revealed == REVEALED)
@@ -193,12 +193,12 @@ command_result reveal(color_ostream &out, vector<string> & params)
}
t_gamemodes gm;
World->ReadGameMode(gm);
- if(gm.g_mode == GAMEMODE_ADVENTURE)
+ if(gm.g_mode == game_mode::ADVENTURE)
{
revealAdventure(out);
return CR_OK;
}
- if(gm.g_mode != GAMEMODE_DWARF)
+ if(gm.g_mode != game_mode::DWARF)
{
con.printerr("Only in fortress mode.\n");
return CR_FAILURE;
@@ -272,7 +272,7 @@ command_result unreveal(color_ostream &out, vector<string> & params)
}
t_gamemodes gm;
World->ReadGameMode(gm);
- if(gm.g_mode != GAMEMODE_DWARF)
+ if(gm.g_mode != game_mode::DWARF)
{
con.printerr("Only in fortress mode.\n");
return CR_FAILURE;
@@ -350,7 +350,7 @@ command_result revflood(color_ostream &out, vector<string> & params)
}
t_gamemodes gm;
World->ReadGameMode(gm);
- if(gm.g_type != GAMETYPE_DWARF_MAIN && gm.g_mode != GAMEMODE_DWARF )
+ if(gm.g_type != game_type::DWARF_MAIN && gm.g_mode != game_mode::DWARF )
{
out.printerr("Only in proper dwarf mode.\n");
return CR_FAILURE;
diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt
index 7057cb2d..e69632e6 100644
--- a/plugins/ruby/CMakeLists.txt
+++ b/plugins/ruby/CMakeLists.txt
@@ -1,29 +1,33 @@
-find_package(Ruby)
-if(RUBY_FOUND)
- ADD_CUSTOM_COMMAND(
- OUTPUT ruby-autogen.cpp
- COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.cpp
- DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl
- )
- ADD_EXECUTABLE(ruby-autogen ruby-autogen.cpp)
- if(CMAKE_COMPILER_IS_GNUCC)
- set_target_properties (ruby-autogen PROPERTIES COMPILE_FLAGS "-Wno-invalid-offsetof")
- endif(CMAKE_COMPILER_IS_GNUCC)
- ADD_CUSTOM_COMMAND(
- OUTPUT ruby-autogen.offsets
- COMMAND ruby-autogen ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets
- DEPENDS ruby-autogen
- )
- ADD_CUSTOM_COMMAND(
- OUTPUT ruby-autogen.rb
- COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets ${CMAKE_CURRENT_SOURCE_DIR}/ruby-memstruct.rb
- DEPENDS ruby-autogen.offsets ruby-memstruct.rb
- )
- ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb)
- include_directories("${dfhack_SOURCE_DIR}/depends/tthread" ${RUBY_INCLUDE_PATH})
- DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread)
- target_link_libraries(ruby ${RUBY_LIBRARY})
- install(FILES ruby.rb ruby-autogen.rb DESTINATION ${DFHACK_LIBRARY_DESTINATION})
-else(RUBY_FOUND)
- MESSAGE(STATUS "Required library (ruby) not found - ruby plugin can't be built.")
-endif(RUBY_FOUND)
+OPTION(DL_RUBY "download libruby from the internet" ON)
+IF (DL_RUBY)
+ IF (UNIX)
+ FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/libruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/libruby187.tar.gz
+ EXPECTED_MD5 eb2adea59911f68e6066966c1352f291)
+ EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf libruby187.tar.gz
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+ FILE(RENAME libruby1.8.so.1.8.7 libruby.so)
+ SET(RUBYLIB libruby.so)
+ ELSE (UNIX)
+ FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/msvcrtruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/msvcrtruby187.tar.gz
+ EXPECTED_MD5 9f4a1659ac3a5308f32d3a1937bbeeae)
+ EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf msvcrtruby187.tar.gz
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+ FILE(RENAME msvcrt-ruby18.dll libruby.dll)
+ SET(RUBYLIB libruby.dll)
+ ENDIF(UNIX)
+ENDIF(DL_RUBY)
+
+ADD_CUSTOM_COMMAND(
+ OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb
+ COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb
+ DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl
+ COMMENT ruby-autogen.rb
+)
+ADD_CUSTOM_TARGET(ruby-autogen-rb DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb)
+
+INCLUDE_DIRECTORIES("${dfhack_SOURCE_DIR}/depends/tthread")
+
+DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread)
+ADD_DEPENDENCIES(ruby ruby-autogen-rb)
+
+INSTALL(FILES ruby.rb ruby-autogen.rb ${RUBYLIB} DESTINATION ${DFHACK_LIBRARY_DESTINATION})
diff --git a/plugins/ruby/README b/plugins/ruby/README
index 7238c161..9dc7d49f 100644
--- a/plugins/ruby/README
+++ b/plugins/ruby/README
@@ -3,7 +3,7 @@ This plugins embeds a ruby interpreter inside DFHack (ie inside Dwarf Fortress).
The plugin maps all the structures available in library/xml/ to ruby objects.
These objects are described in ruby-autogen.rb, they are all in the DFHack::
-module. The toplevel 'df' method returs the DFHack module.
+module. The toplevel 'df' method is a shortcut to the DFHack module.
The plugin does *not* map most of dfhack methods (MapCache, ...) ; only direct
access to the raw DF data structures in memory is provided.
@@ -11,8 +11,7 @@ access to the raw DF data structures in memory is provided.
Some library methods are stored in the ruby.rb file, with shortcuts to read a
map block, find an unit or an item, etc.
-Global objects are stored in the GlobalObjects class ; each object accessor is
-mirrored as a DFHack module method.
+Global objects are accessible through the 'df' accessor (eg df.world).
The ruby plugin defines 2 dfhack console commands:
rb_load <filename> ; load a ruby script. Ex: rb_load hack/plants.rb (no quotes)
@@ -21,17 +20,25 @@ console. Ex: rb_eval df.find_unit.name.first_name
You can use single-quotes for strings ; avoid double-quotes that are parsed
and removed by the dfhack console.
+If dfhack reports 'rb_eval is not a recognized command', check stderr.log. You
+need a valid 32-bit ruby library to work, and ruby1.8 is prefered (ruby1.9 may
+crash DF on startup for now). Install the library in the df root folder (or
+hack/ on linux), the library should be named 'libruby.dll' (.so on linux).
+You can download a tested version at http://github.com/jjyg/dfhack/downloads/
+
The plugin also interfaces with dfhack 'onupdate' hook.
To register ruby code to be run every graphic frame, use:
- handle = df.onupdate_register { puts 'i love flood' }
+ handle = df.onupdate_register { puts 'i love flooding the console' }
To stop being called, use:
df.onupdate_unregister handle
+The same mechanism is available for onstatechange.
+
Exemples
--------
-For more complex exemples, check the ruby/plugins/ folder.
+For more complex exemples, check the ruby/plugins/ source folder.
Show info on the currently selected unit ('v' or 'k' DF menu)
p df.find_unit.flags1
@@ -42,6 +49,9 @@ Set a custom nickname to unit with id '123'
Show current unit profession
p df.find_unit.profession
+Change current unit profession
+ df.find_unit.profession = :MASON
+
Center the screen on unit '123'
df.center_viewscreen(df.find_unit(123))
@@ -49,54 +59,42 @@ Find an item at a given position, show its C++ classname
df.find_item(df.cursor)._rtti_classname
Find the raws name of the plant under cursor
- plant = df.world.plants.all.find { |p| df.at_cursor?(p) }
- df.world.raws.plants.all[plant.mat_index].id
+ plant = df.world.plants.all.find { |plt| df.at_cursor?(plt) }
+ p df.world.raws.plants.all[plant.mat_index].id
Dig a channel under the cursor
- df.map_designation_at(df.cursor).dig = TileDigDesignation::Channel
+ df.map_designation_at(df.cursor).dig = :Channel
df.map_block_at(df.cursor).flags.designated = true
Compilation
-----------
-The plugin consists of the ruby.rb file including user comfort functions ;
-ruby-memstruct.rb describing basic classes used by the autogenerated code, and
-embedded at the beginnig of ruby-autogen.rb, and the generated code.
+The plugin consists of the ruby.rb file including user comfort functions and
+describing basic classes used by the autogenerated code, and ruby-autogen.rb,
+the auto-generated code.
The generated code is generated by codegen.pl, which takes the codegen.out.xml
file as input.
-One of the limitations of the xml file is that it does not include structure
-offsets, as they depend on the compiler. To overcome that, codegen runs in two
-passes. The first pass generates a ruby-autogen.cpp file, that will output the
-structure offsets ; the second pass will generate the ruby-autogen.rb using the
-output of the compiled ruby-autogen.cpp.
-
-For exemple, from
+For exemple,
<ld:global-type ld:meta="struct-type" type-name="unit">
<ld:field type-name="language_name" name="name" ld:meta="global"/>
<ld:field name="custom_profession" ld:meta="primitive" ld:subtype="stl-string"/>
<ld:field ld:subtype="enum" base-type="int16_t" name="profession" type-name="profession" ld:meta="global"/>
-We generate the cpp
- printf("%s = %d", "offsetof(df::unit, language_name)", offsetof(df::unit, language_name));
- printf("%s = %d", "offsetof(df::unit, custom_profession)", offsetof(df::unit, custom_profession));
- printf("%s = %d", "offsetof(df::unit, profession)", offsetof(df::unit, profession));
-
-Which generates (on linux)
- offsetof(df::unit, name) = 0
- offsetof(df::unit, custom_profession) = 60
- offsetof(df::unit, profession) = 64
-
-Which generates
+Will generate
class Unit < MemHack::Compound
field(:name, 0) { global :LanguageName }
field(:custom_profession, 60) { stl_string }
field(:profession, 64) { number 16, true }
-The field method has 2 arguments: the name of the method and the member offset ;
-the block specifies the member type. See ruby-memstruct.rb for more information.
+The syntax for the 'field' method is:
+1st argument = name of the method
+2nd argument = offset of this field from the beginning of the struct.
+
+The block argument describes the type of the field: uint32, ptr to global...
+
Primitive type access is done through native methods in ruby.cpp (vector length,
raw memory access, etc)
@@ -104,10 +102,4 @@ MemHack::Pointers are automatically dereferenced ; so a vector of pointer to
Units will yield Units directly. Null pointers yield the 'nil' value.
This allows to use code such as 'df.world.units.all[0].pos', with 'all' being
-really a vector of pointer.
-
-
-Todo
-----
-
-Correct c++ object (de)allocation (call ctor etc) ; ability to call vtable methods
+in fact a vector of *pointers* to DFHack::Unit objects.
diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl
index 5c60fcb4..5cdeeedd 100755
--- a/plugins/ruby/codegen.pl
+++ b/plugins/ruby/codegen.pl
@@ -6,9 +6,13 @@ use warnings;
use XML::LibXML;
our @lines_rb;
-my @lines_cpp;
-my @include_cpp;
-my %offsets;
+
+my $os;
+if ($^O =~ /linux/i) {
+ $os = 'linux';
+} else {
+ $os = 'windows';
+}
sub indent_rb(&) {
my ($sub) = @_;
@@ -27,6 +31,7 @@ sub rb_ucase {
return join("", map { ucfirst $_ } (split('_', $name)));
}
+
my %item_renderer = (
'global' => \&render_item_global,
'number' => \&render_item_number,
@@ -38,7 +43,9 @@ my %item_renderer = (
'bytes' => \&render_item_bytes,
);
+
my %global_types;
+our $current_typename;
sub render_global_enum {
my ($name, $type) = @_;
@@ -50,72 +57,94 @@ sub render_global_enum {
};
push @lines_rb, "end\n";
}
+
sub render_enum_fields {
my ($type) = @_;
- my $value = -1;
push @lines_rb, "ENUM = Hash.new";
push @lines_rb, "NUME = Hash.new";
my %attr_type;
my %attr_list;
- for my $attr ($type->findnodes('child::enum-attr')) {
+ render_enum_initattrs($type, \%attr_type, \%attr_list);
+
+ my $value = -1;
+ for my $item ($type->findnodes('child::enum-item'))
+ {
+ $value = $item->getAttribute('value') || ($value+1);
+ my $elemname = $item->getAttribute('name'); # || "unk_$value";
+
+ if ($elemname)
+ {
+ my $rbelemname = rb_ucase($elemname);
+ push @lines_rb, "ENUM[$value] = :$rbelemname ; NUME[:$rbelemname] = $value";
+ for my $iattr ($item->findnodes('child::item-attr'))
+ {
+ my $attr = render_enum_attr($rbelemname, $iattr, \%attr_type, \%attr_list);
+ $lines_rb[$#lines_rb] .= ' ; ' . $attr;
+ }
+ }
+ }
+}
+
+sub render_enum_initattrs {
+ my ($type, $attr_type, $attr_list) = @_;
+
+ for my $attr ($type->findnodes('child::enum-attr'))
+ {
my $rbattr = rb_ucase($attr->getAttribute('name'));
my $typeattr = $attr->getAttribute('type-name');
# find how we need to encode the attribute values: string, symbol (for enums), raw (number, bool)
if ($typeattr) {
if ($global_types{$typeattr}) {
- $attr_type{$rbattr} = 'symbol';
+ $attr_type->{$rbattr} = 'symbol';
} else {
- $attr_type{$rbattr} = 'naked';
+ $attr_type->{$rbattr} = 'naked';
}
} else {
- $attr_type{$rbattr} = 'quote';
+ $attr_type->{$rbattr} = 'quote';
}
my $def = $attr->getAttribute('default-value');
- if ($attr->getAttribute('is-list')) {
+ if ($attr->getAttribute('is-list'))
+ {
push @lines_rb, "$rbattr = Hash.new { |h, k| h[k] = [] }";
- $attr_list{$rbattr} = 1;
- } elsif ($def) {
- $def = ":$def" if ($attr_type{$rbattr} eq 'symbol');
- $def =~ s/'/\\'/g if ($attr_type{$rbattr} eq 'quote');
- $def = "'$def'" if ($attr_type{$rbattr} eq 'quote');
+ $attr_list->{$rbattr} = 1;
+ }
+ elsif ($def)
+ {
+ $def = ":$def" if ($attr_type->{$rbattr} eq 'symbol');
+ $def =~ s/'/\\'/g if ($attr_type->{$rbattr} eq 'quote');
+ $def = "'$def'" if ($attr_type->{$rbattr} eq 'quote');
push @lines_rb, "$rbattr = Hash.new($def)";
- } else {
+ }
+ else
+ {
push @lines_rb, "$rbattr = Hash.new";
}
}
+}
- for my $item ($type->findnodes('child::enum-item')) {
- $value = $item->getAttribute('value') || ($value+1);
- my $elemname = $item->getAttribute('name'); # || "unk_$value";
+sub render_enum_attr {
+ my ($rbelemname, $iattr, $attr_type, $attr_list) = @_;
- if ($elemname) {
- my $rbelemname = rb_ucase($elemname);
- push @lines_rb, "ENUM[$value] = :$rbelemname ; NUME[:$rbelemname] = $value";
- for my $iattr ($item->findnodes('child::item-attr')) {
- my $ian = $iattr->getAttribute('name');
- my $iav = $iattr->getAttribute('value');
- my $rbattr = rb_ucase($ian);
- my $op = ($attr_list{$rbattr} ? '<<' : '=');
- $iav = ":$iav" if ($attr_type{$rbattr} eq 'symbol');
- $iav =~ s/'/\\'/g if ($attr_type{$rbattr} eq 'quote');
- $iav = "'$iav'" if ($attr_type{$rbattr} eq 'quote');
- $lines_rb[$#lines_rb] .= " ; ${rbattr}[:$rbelemname] $op $iav";
- }
- }
- }
+ my $ian = $iattr->getAttribute('name');
+ my $iav = $iattr->getAttribute('value');
+ my $rbattr = rb_ucase($ian);
+
+ my $op = ($attr_list->{$rbattr} ? '<<' : '=');
+
+ $iav = ":$iav" if ($attr_type->{$rbattr} eq 'symbol');
+ $iav =~ s/'/\\'/g if ($attr_type->{$rbattr} eq 'quote');
+ $iav = "'$iav'" if ($attr_type->{$rbattr} eq 'quote');
+
+ return "${rbattr}[:$rbelemname] $op $iav";
}
sub render_global_bitfield {
my ($name, $type) = @_;
- push @lines_cpp, "}" if @include_cpp;
- push @lines_cpp, "void cpp_$name(FILE *fout) {";
- push @include_cpp, $name;
-
my $rbname = rb_ucase($name);
push @lines_rb, "class $rbname < MemHack::Compound";
indent_rb {
@@ -123,57 +152,50 @@ sub render_global_bitfield {
};
push @lines_rb, "end\n";
}
+
sub render_bitfield_fields {
my ($type) = @_;
push @lines_rb, "field(:_whole, 0) {";
indent_rb {
- render_item_number($type, '');
+ render_item_number($type);
};
push @lines_rb, "}";
my $shift = 0;
- for my $field ($type->findnodes('child::ld:field')) {
+ for my $field ($type->findnodes('child::ld:field'))
+ {
my $count = $field->getAttribute('count') || 1;
my $name = $field->getAttribute('name');
my $type = $field->getAttribute('type-name');
my $enum = rb_ucase($type) if ($type and $global_types{$type});
$name = $field->getAttribute('ld:anon-name') if (!$name);
print "bitfield $name !number\n" if (!($field->getAttribute('ld:meta') eq 'number'));
- if ($count == 1) {
- push @lines_rb, "field(:$name, 0) { bit $shift }" if ($name);
- } elsif ($enum) {
- push @lines_rb, "field(:$name, 0) { bits $shift, $count, $enum }" if ($name);
- } else {
- push @lines_rb, "field(:$name, 0) { bits $shift, $count }" if ($name);
+
+ if ($name)
+ {
+ if ($count == 1) {
+ push @lines_rb, "field(:$name, 0) { bit $shift }";
+ } elsif ($enum) {
+ push @lines_rb, "field(:$name, 0) { bits $shift, $count, $enum }";
+ } else {
+ push @lines_rb, "field(:$name, 0) { bits $shift, $count }";
+ }
}
+
$shift += $count;
}
}
-sub render_global_struct {
- my ($name, $type) = @_;
-
- my $rbname = rb_ucase($name);
-
- my $cppns = "df::$name";
- push @lines_cpp, "}" if @include_cpp;
- push @lines_cpp, "void cpp_$name(FILE *fout) {";
- push @include_cpp, $name;
-
- push @lines_rb, "class $rbname < MemHack::Compound";
- indent_rb {
- my $sz = query_cpp("sizeof($cppns)");
- push @lines_rb, "sizeof $sz";
- render_struct_fields($type, "$cppns");
- };
- push @lines_rb, "end\n";
-}
my %seen_class;
+our $compound_off;
+our $compound_pointer;
+
sub render_global_class {
my ($name, $type) = @_;
+ my $meta = $type->getAttribute('ld:meta');
my $rbname = rb_ucase($name);
# ensure pre-definition of ancestors
@@ -183,68 +205,96 @@ sub render_global_class {
return if $seen_class{$name};
$seen_class{$name}++;
+ local $compound_off = 0;
+ $compound_off = 4 if ($meta eq 'class-type');
+ $compound_off = sizeof($global_types{$parent}) if $parent;
+ local $current_typename = $rbname;
+
my $rtti_name;
- if ($type->getAttribute('ld:meta') eq 'class-type') {
+ if ($meta eq 'class-type')
+ {
$rtti_name = $type->getAttribute('original-name') ||
- $type->getAttribute('type-name') ||
- $name;
+ $type->getAttribute('type-name') ||
+ $name;
}
my $rbparent = ($parent ? rb_ucase($parent) : 'MemHack::Compound');
-
- my $cppns = "df::$name";
- push @lines_cpp, "}" if @include_cpp;
- push @lines_cpp, "void cpp_$name(FILE *fout) {";
- push @include_cpp, $name;
-
push @lines_rb, "class $rbname < $rbparent";
indent_rb {
- my $sz = query_cpp("sizeof($cppns)");
- push @lines_rb, "sizeof $sz";
- push @lines_rb, "rtti_classname :$rtti_name" if $rtti_name;
- render_struct_fields($type, "$cppns");
+ my $sz = sizeof($type);
+ # see comment is sub sizeof ; but gcc has sizeof(cls) aligned
+ $sz = align_field($sz, 4) if $os eq 'linux' and $meta eq 'class-type';
+ push @lines_rb, "sizeof $sz\n";
+
+ push @lines_rb, "rtti_classname :$rtti_name\n" if $rtti_name;
+
+ render_struct_fields($type);
+
my $vms = $type->findnodes('child::virtual-methods')->[0];
render_class_vmethods($vms) if $vms;
};
push @lines_rb, "end\n";
}
+
sub render_struct_fields {
- my ($type, $cppns) = @_;
+ my ($type) = @_;
+
+ my $isunion = $type->getAttribute('is-union');
- for my $field ($type->findnodes('child::ld:field')) {
+ for my $field ($type->findnodes('child::ld:field'))
+ {
my $name = $field->getAttribute('name');
$name = $field->getAttribute('ld:anon-name') if (!$name);
- if (!$name and $field->getAttribute('ld:anon-compound')) {
- render_struct_fields($field, $cppns);
+
+ if (!$name and $field->getAttribute('ld:anon-compound'))
+ {
+ render_struct_fields($field);
+ }
+ else
+ {
+ $compound_off = align_field($compound_off, get_field_align($field));
+ if ($name)
+ {
+ push @lines_rb, "field(:$name, $compound_off) {";
+ indent_rb {
+ render_item($field);
+ };
+ push @lines_rb, "}";
+ }
}
- next if (!$name);
- my $offset = get_offset($cppns, $name);
- push @lines_rb, "field(:$name, $offset) {";
- indent_rb {
- render_item($field, "$cppns");
- };
- push @lines_rb, "}";
+ $compound_off += sizeof($field) if (!$isunion);
}
}
+
sub render_class_vmethods {
my ($vms) = @_;
my $voff = 0;
- for my $meth ($vms->findnodes('child::vmethod')) {
+
+ for my $meth ($vms->findnodes('child::vmethod'))
+ {
my $name = $meth->getAttribute('name');
- if ($name) {
+
+ if ($name)
+ {
my @argnames;
my @argargs;
- for my $arg ($meth->findnodes('child::ld:field')) {
+
+ # check if arguments need special treatment (eg auto-convert from symbol to enum value)
+ for my $arg ($meth->findnodes('child::ld:field'))
+ {
my $nr = $#argnames + 1;
my $argname = lcfirst($arg->getAttribute('name') || "arg$nr");
push @argnames, $argname;
+
if ($arg->getAttribute('ld:meta') eq 'global' and $arg->getAttribute('ld:subtype') eq 'enum') {
- push @argargs, rb_ucase($arg->getAttribute('type-name')) . ".to_i($argname)";
+ push @argargs, rb_ucase($arg->getAttribute('type-name')) . ".int($argname)";
} else {
push @argargs, $argname;
}
}
+
+ # write vmethod ruby wrapper
push @lines_rb, "def $name(" . join(', ', @argnames) . ')';
indent_rb {
my $args = join('', map { ", $_" } @argargs);
@@ -254,48 +304,74 @@ sub render_class_vmethods {
};
push @lines_rb, 'end';
}
+
# on linux, the destructor uses 2 entries
- $voff += 4 if $meth->getAttribute('is-destructor') and $^O =~ /linux/i;
+ $voff += 4 if $meth->getAttribute('is-destructor') and $os eq 'linux';
$voff += 4;
}
}
sub render_class_vmethod_ret {
my ($call, $ret) = @_;
- if (!$ret) {
+
+ if (!$ret)
+ {
+ # method returns void, hide return value
push @lines_rb, "$call ; nil";
return;
}
+
my $retmeta = $ret->getAttribute('ld:meta') || '';
- if ($retmeta eq 'global') { # enum
+ if ($retmeta eq 'global')
+ {
+ # method returns an enum value: auto-convert to symbol
my $retname = $ret->getAttribute('type-name');
if ($retname and $global_types{$retname} and
- $global_types{$retname}->getAttribute('ld:meta') eq 'enum-type') {
- push @lines_rb, rb_ucase($retname) . ".to_sym($call)";
- } else {
+ $global_types{$retname}->getAttribute('ld:meta') eq 'enum-type')
+ {
+ push @lines_rb, rb_ucase($retname) . ".sym($call)";
+ }
+ else
+ {
print "vmethod global nonenum $call\n";
push @lines_rb, $call;
}
- } elsif ($retmeta eq 'number') {
+
+ }
+ elsif ($retmeta eq 'number')
+ {
+ # raw method call returns an int32, mask according to actual return type
my $retsubtype = $ret->getAttribute('ld:subtype');
my $retbits = $ret->getAttribute('ld:bits');
push @lines_rb, "val = $call";
- if ($retsubtype eq 'bool') {
+ if ($retsubtype eq 'bool')
+ {
push @lines_rb, "(val & 1) != 0";
- } elsif ($ret->getAttribute('ld:unsigned')) {
+ }
+ elsif ($ret->getAttribute('ld:unsigned'))
+ {
push @lines_rb, "val & ((1 << $retbits) - 1)";
- } else { # signed
+ }
+ elsif ($retbits != 32)
+ {
+ # manual sign extension
push @lines_rb, "val &= ((1 << $retbits) - 1)";
push @lines_rb, "((val >> ($retbits-1)) & 1) == 0 ? val : val - (1 << $retbits)";
}
- } elsif ($retmeta eq 'pointer') {
+
+ }
+ elsif ($retmeta eq 'pointer')
+ {
+ # method returns a pointer to some struct, create the correct ruby wrapper
push @lines_rb, "ptr = $call";
push @lines_rb, "class << self";
indent_rb {
render_item($ret->findnodes('child::ld:item')->[0]);
};
push @lines_rb, "end._at(ptr) if ptr != 0";
- } else {
+ }
+ else
+ {
print "vmethod unkret $call\n";
push @lines_rb, $call;
}
@@ -305,17 +381,17 @@ sub render_global_objects {
my (@objects) = @_;
my @global_objects;
- my $sname = 'global_objects';
- my $rbname = rb_ucase($sname);
-
- push @lines_cpp, "}" if @include_cpp;
- push @lines_cpp, "void cpp_$sname(FILE *fout) {";
- push @include_cpp, $sname;
+ local $compound_off = 0;
+ local $current_typename = 'Global';
- push @lines_rb, "class $rbname < MemHack::Compound";
+ # define all globals as 'fields' of a virtual globalobject wrapping the whole address space
+ push @lines_rb, 'class GlobalObjects < MemHack::Compound';
indent_rb {
- for my $obj (@objects) {
+ for my $obj (@objects)
+ {
my $oname = $obj->getAttribute('name');
+
+ # check if the symbol is defined in xml to avoid NULL deref
my $addr = "DFHack.get_global_address('$oname')";
push @lines_rb, "addr = $addr";
push @lines_rb, "if addr != 0";
@@ -323,7 +399,7 @@ sub render_global_objects {
push @lines_rb, "field(:$oname, addr) {";
my $item = $obj->findnodes('child::ld:item')->[0];
indent_rb {
- render_item($item, 'df::global');
+ render_item($item);
};
push @lines_rb, "}";
};
@@ -334,9 +410,11 @@ sub render_global_objects {
};
push @lines_rb, "end";
+ # define friendlier accessors, eg df.world -> DFHack::GlobalObjects.new._at(0).world
indent_rb {
push @lines_rb, "Global = GlobalObjects.new._at(0)";
- for my $obj (@global_objects) {
+ for my $obj (@global_objects)
+ {
push @lines_rb, "def self.$obj ; Global.$obj ; end";
push @lines_rb, "def self.$obj=(v) ; Global.$obj = v ; end";
}
@@ -344,28 +422,257 @@ sub render_global_objects {
}
+my %align_cache;
+my %sizeof_cache;
+
+sub align_field {
+ my ($off, $fldalign) = @_;
+ my $dt = $off % $fldalign;
+ $off += $fldalign - $dt if $dt > 0;
+ return $off;
+}
+
+sub get_field_align {
+ my ($field) = @_;
+ my $al = 4;
+ my $meta = $field->getAttribute('ld:meta');
+
+ if ($meta eq 'number') {
+ $al = $field->getAttribute('ld:bits')/8;
+ $al = 4 if $al > 4;
+ } elsif ($meta eq 'global') {
+ $al = get_global_align($field);
+ } elsif ($meta eq 'compound') {
+ $al = get_compound_align($field);
+ } elsif ($meta eq 'static-array') {
+ my $tg = $field->findnodes('child::ld:item')->[0];
+ $al = get_field_align($tg);
+ } elsif ($meta eq 'bytes') {
+ $al = $field->getAttribute('alignment') || 1;
+ }
+
+ return $al;
+}
+
+sub get_global_align {
+ my ($field) = @_;
+
+ my $typename = $field->getAttribute('type-name');
+ return $align_cache{$typename} if $align_cache{$typename};
+
+ my $g = $global_types{$typename};
+
+ my $st = $field->getAttribute('ld:subtype') || '';
+ if ($st eq 'bitfield' or $st eq 'enum' or $g->getAttribute('ld:meta') eq 'bitfield-type')
+ {
+ my $base = $field->getAttribute('base-type') || $g->getAttribute('base-type') || 'uint32_t';
+ print "$st type $base\n" if $base !~ /int(\d+)_t/;
+ # dont cache, field->base-type may differ
+ return $1/8;
+ }
+
+ my $al = 1;
+ for my $gf ($g->findnodes('child::ld:field')) {
+ my $fld_al = get_field_align($gf);
+ $al = $fld_al if $fld_al > $al;
+ }
+ $align_cache{$typename} = $al;
+
+ return $al;
+}
+
+sub get_compound_align {
+ my ($field) = @_;
+
+ my $st = $field->getAttribute('ld:subtype') || '';
+ if ($st eq 'bitfield' or $st eq 'enum')
+ {
+ my $base = $field->getAttribute('base-type') || 'uint32_t';
+ print "$st type $base\n" if $base !~ /int(\d+)_t/;
+ return $1/8;
+ }
+
+ my $al = 1;
+ for my $f ($field->findnodes('child::ld:field')) {
+ my $fal = get_field_align($f);
+ $al = $fal if $fal > $al;
+ }
+
+ return $al;
+}
+
+sub sizeof {
+ my ($field) = @_;
+ my $meta = $field->getAttribute('ld:meta');
+
+ if ($meta eq 'number') {
+ return $field->getAttribute('ld:bits')/8;
+
+ } elsif ($meta eq 'pointer') {
+ return 4;
+
+ } elsif ($meta eq 'static-array') {
+ my $count = $field->getAttribute('count');
+ my $tg = $field->findnodes('child::ld:item')->[0];
+ return $count * sizeof($tg);
+
+ } elsif ($meta eq 'bitfield-type' or $meta eq 'enum-type') {
+ my $base = $field->getAttribute('base-type') || 'uint32_t';
+ print "$meta type $base\n" if $base !~ /int(\d+)_t/;
+ return $1/8;
+
+ } elsif ($meta eq 'global') {
+ my $typename = $field->getAttribute('type-name');
+ return $sizeof_cache{$typename} if $sizeof_cache{$typename};
+
+ my $g = $global_types{$typename};
+ my $st = $field->getAttribute('ld:subtype') || '';
+ if ($st eq 'bitfield' or $st eq 'enum' or $g->getAttribute('ld:meta') eq 'bitfield-type')
+ {
+ my $base = $field->getAttribute('base-type') || $g->getAttribute('base-type') || 'uint32_t';
+ print "$st type $base\n" if $base !~ /int(\d+)_t/;
+ return $1/8;
+ }
+
+ return sizeof($g);
+
+ } elsif ($meta eq 'class-type' or $meta eq 'struct-type' or $meta eq 'compound') {
+ return sizeof_compound($field);
+
+ } elsif ($meta eq 'container') {
+ my $subtype = $field->getAttribute('ld:subtype');
+
+ if ($subtype eq 'stl-vector') {
+ if ($os eq 'linux') {
+ return 12;
+ } elsif ($os eq 'windows') {
+ return 16;
+ } else {
+ print "sizeof stl-vector on $os\n";
+ }
+ } elsif ($subtype eq 'stl-bit-vector') {
+ if ($os eq 'linux') {
+ return 20;
+ } elsif ($os eq 'windows') {
+ return 20;
+ } else {
+ print "sizeof stl-bit-vector on $os\n";
+ }
+ } elsif ($subtype eq 'stl-deque') {
+ if ($os eq 'linux') {
+ return 40;
+ } elsif ($os eq 'windows') {
+ return 24;
+ } else {
+ print "sizeof stl-deque on $os\n";
+ }
+ } elsif ($subtype eq 'df-linked-list') {
+ return 12;
+ } elsif ($subtype eq 'df-flagarray') {
+ return 8;
+ } elsif ($subtype eq 'df-array') {
+ return 8; # XXX 6 ?
+ } else {
+ print "sizeof container $subtype\n";
+ }
+
+ } elsif ($meta eq 'primitive') {
+ my $subtype = $field->getAttribute('ld:subtype');
+
+ if ($subtype eq 'stl-string') { if ($os eq 'linux') {
+ return 4;
+ } elsif ($os eq 'windows') {
+ return 28;
+ } else {
+ print "sizeof stl-string on $os\n";
+ }
+ print "sizeof stl-string\n";
+ } else {
+ print "sizeof primitive $subtype\n";
+ }
+
+ } elsif ($meta eq 'bytes') {
+ return $field->getAttribute('size');
+ } else {
+ print "sizeof $meta\n";
+ }
+}
+
+sub sizeof_compound {
+ my ($field) = @_;
+
+ my $typename = $field->getAttribute('type-name');
+ return $sizeof_cache{$typename} if $typename and $sizeof_cache{$typename};
+
+ my $meta = $field->getAttribute('ld:meta');
+
+ my $st = $field->getAttribute('ld:subtype') || '';
+ if ($st eq 'bitfield' or $st eq 'enum')
+ {
+ my $base = $field->getAttribute('base-type') || 'uint32_t';
+ print "$st type $base\n" if $base !~ /int(\d+)_t/;
+ $sizeof_cache{$typename} = $1/8 if $typename;
+ return $1/8;
+ }
+
+ if ($field->getAttribute('is-union'))
+ {
+ my $sz = 0;
+ for my $f ($field->findnodes('child::ld:field'))
+ {
+ my $fsz = sizeof($f);
+ $sz = $fsz if $fsz > $sz;
+ }
+ return $sz;
+ }
+
+ my $parent = $field->getAttribute('inherits-from');
+ my $off = 0;
+ $off = 4 if ($meta eq 'class-type');
+ $off = sizeof($global_types{$parent}) if ($parent);
+
+ my $al = 1;
+ $al = 4 if ($meta eq 'class-type');
+
+ for my $f ($field->findnodes('child::ld:field'))
+ {
+ my $fa = get_field_align($f);
+ $al = $fa if $fa > $al;
+ $off = align_field($off, $fa);
+ $off += sizeof($f);
+ }
+
+ # GCC: class a { vtable; char; } ; class b:a { char c2; } -> c2 has offset 5 (Windows MSVC: offset 8)
+ $al = 1 if ($meta eq 'class-type' and $os eq 'linux');
+ $off = align_field($off, $al);
+ $sizeof_cache{$typename} = $off if $typename;
+
+ return $off;
+}
+
+
sub render_item {
- my ($item, $pns) = @_;
+ my ($item) = @_;
return if (!$item);
my $meta = $item->getAttribute('ld:meta');
my $renderer = $item_renderer{$meta};
if ($renderer) {
- $renderer->($item, $pns);
+ $renderer->($item);
} else {
print "no render item $meta\n";
}
}
sub render_item_global {
- my ($item, $pns) = @_;
+ my ($item) = @_;
my $typename = $item->getAttribute('type-name');
my $subtype = $item->getAttribute('ld:subtype');
if ($subtype and $subtype eq 'enum') {
- render_item_number($item, $pns);
+ render_item_number($item);
} else {
my $rbname = rb_ucase($typename);
push @lines_rb, "global :$rbname";
@@ -373,7 +680,7 @@ sub render_item_global {
}
sub render_item_number {
- my ($item, $pns) = @_;
+ my ($item, $classname) = @_;
my $subtype = $item->getAttribute('ld:subtype');
my $meta = $item->getAttribute('ld:meta');
@@ -381,13 +688,13 @@ sub render_item_number {
my $typename = $item->getAttribute('type-name');
undef $typename if ($meta and $meta eq 'bitfield-type');
$typename = rb_ucase($typename) if $typename;
- $typename = $pns if (!$typename and $subtype and $subtype eq 'enum'); # compound enum
+ $typename = $classname if (!$typename and $subtype and $subtype eq 'enum'); # compound enum
$initvalue = 1 if ($initvalue and $initvalue eq 'true');
$initvalue = ":$initvalue" if ($initvalue and $typename and $initvalue =~ /[a-zA-Z]/);
$initvalue ||= 'nil' if $typename;
- $subtype = $item->getAttribute('base-type') if (!$subtype or $subtype eq 'enum' or $subtype eq 'bitfield');
+ $subtype = $item->getAttribute('base-type') if (!$subtype or $subtype eq 'bitfield' or $subtype eq 'enum');
$subtype = 'int32_t' if (!$subtype);
if ($subtype eq 'int64_t') {
@@ -406,6 +713,8 @@ sub render_item_number {
push @lines_rb, 'number 8, false';
} elsif ($subtype eq 'bool') {
push @lines_rb, 'number 8, true';
+ $initvalue ||= 'nil';
+ $typename ||= 'BooleanEnum';
} elsif ($subtype eq 's-float') {
push @lines_rb, 'float';
return;
@@ -413,31 +722,40 @@ sub render_item_number {
print "no render number $subtype\n";
return;
}
+
$lines_rb[$#lines_rb] .= ", $initvalue" if ($initvalue);
$lines_rb[$#lines_rb] .= ", $typename" if ($typename);
}
sub render_item_compound {
- my ($item, $pns) = @_;
+ my ($item) = @_;
- my $cppns = $pns . '::' . $item->getAttribute('ld:typedef-name');
my $subtype = $item->getAttribute('ld:subtype');
- my @namecomponents = split('::', $cppns);
- shift @namecomponents;
- my $classname = join('_', map { rb_ucase($_) } @namecomponents);
+ local $compound_off = 0;
+ my $classname = $current_typename . '_' . rb_ucase($item->getAttribute('ld:typedef-name'));
+ local $current_typename = $classname;
- if (!$subtype || $subtype eq 'bitfield') {
+ if (!$subtype || $subtype eq 'bitfield')
+ {
push @lines_rb, "compound(:$classname) {";
indent_rb {
+ # declare sizeof() only for useful compound, eg the one behind pointers
+ # that the user may want to allocate
+ my $sz = sizeof($item);
+ push @lines_rb, "sizeof $sz\n" if $compound_pointer;
+
if (!$subtype) {
- render_struct_fields($item, $cppns);
+ local $compound_pointer = 0;
+ render_struct_fields($item);
} else {
render_bitfield_fields($item);
}
};
push @lines_rb, "}"
- } elsif ($subtype eq 'enum') {
+ }
+ elsif ($subtype eq 'enum')
+ {
push @lines_rb, "class ::DFHack::$classname < MemHack::Enum";
indent_rb {
# declare constants
@@ -447,61 +765,71 @@ sub render_item_compound {
# actual field
render_item_number($item, $classname);
- } else {
+ }
+ else
+ {
print "no render compound $subtype\n";
}
}
sub render_item_container {
- my ($item, $pns) = @_;
+ my ($item) = @_;
my $subtype = $item->getAttribute('ld:subtype');
my $rbmethod = join('_', split('-', $subtype));
my $tg = $item->findnodes('child::ld:item')->[0];
my $indexenum = $item->getAttribute('index-enum');
- if ($tg) {
+ if ($tg)
+ {
if ($rbmethod eq 'df_linked_list') {
push @lines_rb, "$rbmethod {";
} else {
- my $tglen = get_tglen($tg, $pns);
+ my $tglen = sizeof($tg) if $tg;
push @lines_rb, "$rbmethod($tglen) {";
}
indent_rb {
- render_item($tg, $pns);
+ render_item($tg);
};
push @lines_rb, "}";
- } elsif ($indexenum) {
+ }
+ elsif ($indexenum)
+ {
$indexenum = rb_ucase($indexenum);
push @lines_rb, "$rbmethod($indexenum)";
- } else {
+ }
+ else
+ {
push @lines_rb, "$rbmethod";
}
}
sub render_item_pointer {
- my ($item, $pns) = @_;
+ my ($item) = @_;
my $tg = $item->findnodes('child::ld:item')->[0];
- my $ary = $item->getAttribute('is-array');
- if ($ary and $ary eq 'true') {
- my $tglen = get_tglen($tg, $pns);
+ my $ary = $item->getAttribute('is-array') || '';
+
+ if ($ary eq 'true') {
+ my $tglen = sizeof($tg) if $tg;
push @lines_rb, "pointer_ary($tglen) {";
} else {
push @lines_rb, "pointer {";
}
indent_rb {
- render_item($tg, $pns);
+ local $compound_pointer = 1;
+ render_item($tg);
};
push @lines_rb, "}";
}
sub render_item_staticarray {
- my ($item, $pns) = @_;
+ my ($item) = @_;
my $count = $item->getAttribute('count');
my $tg = $item->findnodes('child::ld:item')->[0];
- my $tglen = get_tglen($tg, $pns);
+ my $tglen = sizeof($tg) if $tg;
my $indexenum = $item->getAttribute('index-enum');
+
if ($indexenum) {
$indexenum = rb_ucase($indexenum);
push @lines_rb, "static_array($count, $tglen, $indexenum) {";
@@ -509,13 +837,13 @@ sub render_item_staticarray {
push @lines_rb, "static_array($count, $tglen) {";
}
indent_rb {
- render_item($tg, $pns);
+ render_item($tg);
};
push @lines_rb, "}";
}
sub render_item_primitive {
- my ($item, $pns) = @_;
+ my ($item) = @_;
my $subtype = $item->getAttribute('ld:subtype');
if ($subtype eq 'stl-string') {
@@ -526,7 +854,7 @@ sub render_item_primitive {
}
sub render_item_bytes {
- my ($item, $pns) = @_;
+ my ($item) = @_;
my $subtype = $item->getAttribute('ld:subtype');
if ($subtype eq 'padding') {
@@ -538,119 +866,18 @@ sub render_item_bytes {
}
}
-sub get_offset {
- my ($cppns, $fname) = @_;
-
- return query_cpp("offsetof($cppns, $fname)");
-}
-
-sub get_tglen {
- my ($tg, $cppns) = @_;
- if (!$tg) {
- return 'nil';
- }
- my $meta = $tg->getAttribute('ld:meta');
- if ($meta eq 'number') {
- return $tg->getAttribute('ld:bits')/8;
- } elsif ($meta eq 'pointer') {
- return 4;
- } elsif ($meta eq 'container') {
- my $subtype = $tg->getAttribute('ld:subtype');
- if ($subtype eq 'stl-vector') {
- return query_cpp("sizeof(std::vector<int>)");
- } elsif ($subtype eq 'df-linked-list') {
- return 12;
- } else {
- print "cannot tglen container $subtype\n";
- }
- } elsif ($meta eq 'compound') {
- my $cname = $tg->getAttribute('ld:typedef-name');
- return query_cpp("sizeof(${cppns}::$cname)");
- } elsif ($meta eq 'static-array') {
- my $count = $tg->getAttribute('count');
- my $ttg = $tg->findnodes('child::ld:item')->[0];
- my $ttgl = get_tglen($ttg, $cppns);
- if ($ttgl =~ /^\d+$/) {
- return $count * $ttgl;
- } else {
- return "$count*$ttgl";
- }
- } elsif ($meta eq 'global') {
- my $typename = $tg->getAttribute('type-name');
- my $subtype = $tg->getAttribute('ld:subtype');
- if ($subtype and $subtype eq 'enum') {
- my $base = $tg->getAttribute('base-type') || 'int32_t';
- if ($base eq 'int32_t') {
- return 4;
- } elsif ($base eq 'int16_t') {
- return 2;
- } elsif ($base eq 'int8_t') {
- return 1;
- } else {
- print "cannot tglen enum $base\n";
- }
- } else {
- return query_cpp("sizeof(df::$typename)");
- }
- } elsif ($meta eq 'primitive') {
- my $subtype = $tg->getAttribute('ld:subtype');
- if ($subtype eq 'stl-string') {
- return query_cpp("sizeof(std::string)");
- } else {
- print "cannot tglen primitive $subtype\n";
- }
- } else {
- print "cannot tglen $meta\n";
- }
-}
-
-my %query_cpp_cache;
-sub query_cpp {
- my ($query) = @_;
-
- my $ans = $offsets{$query};
- return $ans if (defined($ans));
-
- my $cached = $query_cpp_cache{$query};
- return $cached if (defined($cached));
- $query_cpp_cache{$query} = 1;
-
- push @lines_cpp, " fprintf(fout, \"%s = %d\\n\", \"$query\", $query);";
- return "'$query'";
-}
-
-
-
-my $input = $ARGV[0] || '../../library/include/df/codegen.out.xml';
-
-# run once with output = 'ruby-autogen.cpp'
-# compile
-# execute, save output to 'ruby-autogen.offsets'
-# re-run this script with output = 'ruby-autogen.rb' and offsetfile = 'ruby-autogen.offsets'
-# delete binary
-# delete offsets
+my $input = $ARGV[0] or die "need input xml";
my $output = $ARGV[1] or die "need output file";
-my $offsetfile = $ARGV[2];
-my $memstruct = $ARGV[3];
-
-if ($offsetfile) {
- open OF, "<$offsetfile";
- while (my $line = <OF>) {
- chomp($line);
- my ($key, $val) = split(' = ', $line);
- $offsets{$key} = $val;
- }
- close OF;
-}
-
my $doc = XML::LibXML->new()->parse_file($input);
$global_types{$_->getAttribute('type-name')} = $_ foreach $doc->findnodes('/ld:data-definition/ld:global-type');
+# render enums first, this allows later code to refer to them directly
my @nonenums;
-for my $name (sort { $a cmp $b } keys %global_types) {
+for my $name (sort { $a cmp $b } keys %global_types)
+{
my $type = $global_types{$name};
my $meta = $type->getAttribute('ld:meta');
if ($meta eq 'enum-type') {
@@ -660,12 +887,12 @@ for my $name (sort { $a cmp $b } keys %global_types) {
}
}
-for my $name (@nonenums) {
+# render other structs/bitfields/classes
+for my $name (@nonenums)
+{
my $type = $global_types{$name};
my $meta = $type->getAttribute('ld:meta');
- if ($meta eq 'struct-type') {
- render_global_struct($name, $type);
- } elsif ($meta eq 'class-type') {
+ if ($meta eq 'struct-type' or $meta eq 'class-type') {
render_global_class($name, $type);
} elsif ($meta eq 'bitfield-type') {
render_global_bitfield($name, $type);
@@ -674,35 +901,12 @@ for my $name (@nonenums) {
}
}
-
+# render globals
render_global_objects($doc->findnodes('/ld:data-definition/ld:global-object'));
open FH, ">$output";
-if ($output =~ /\.cpp$/) {
- print FH "#include \"DataDefs.h\"\n";
- print FH "#include \"df/$_.h\"\n" for @include_cpp;
- print FH "#include <stdio.h>\n";
- print FH "#include <stddef.h>\n";
- print FH "$_\n" for @lines_cpp;
- print FH "}\n";
- print FH "int main(int argc, char **argv) {\n";
- print FH " FILE *fout;\n";
- print FH " if (argc < 2) return 1;\n";
- print FH " fout = fopen(argv[1], \"w\");\n";
- print FH " cpp_$_(fout);\n" for @include_cpp;
- print FH " fclose(fout);\n";
- print FH " return 0;\n";
- print FH "}\n";
-
-} else {
- if ($memstruct) {
- open MH, "<$memstruct";
- print FH "$_" while(<MH>);
- close MH;
- }
- print FH "module DFHack\n";
- print FH "$_\n" for @lines_rb;
- print FH "end\n";
-}
+print FH "module DFHack\n";
+print FH "$_\n" for @lines_rb;
+print FH "end\n";
close FH;
diff --git a/plugins/ruby/plugins/building.rb b/plugins/ruby/plugins/building.rb
index 15212092..5dfbcdac 100644
--- a/plugins/ruby/plugins/building.rb
+++ b/plugins/ruby/plugins/building.rb
@@ -2,7 +2,6 @@ module DFHack
# allocate a new building object
def self.building_alloc(type, subtype=-1, custom=-1)
- type = BuildingType.to_sym(type)
cls = rtti_n2c[BuildingType::Classname[type].to_sym]
raise "invalid building type #{type.inspect}" if not cls
bld = cls.cpp_new
diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb
deleted file mode 100644
index 40f35bb4..00000000
--- a/plugins/ruby/ruby-memstruct.rb
+++ /dev/null
@@ -1,747 +0,0 @@
-module DFHack
-module MemHack
-INSPECT_SIZE_LIMIT=16384
-class MemStruct
- attr_accessor :_memaddr
- def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end
- def _get ; self ; end
- def _cpp_init ; end
-end
-
-class Compound < MemStruct
- class << self
- attr_accessor :_fields, :_rtti_classname, :_sizeof
- def field(name, offset)
- struct = yield
- return if not struct
- @_fields ||= []
- @_fields << [name, offset, struct]
- define_method(name) { struct._at(@_memaddr+offset)._get }
- define_method("#{name}=") { |v| struct._at(@_memaddr+offset)._set(v) }
- end
- def _fields_ancestors
- if superclass.respond_to?(:_fields_ancestors)
- superclass._fields_ancestors + _fields.to_a
- else
- _fields.to_a
- end
- end
-
- def number(bits, signed, initvalue=nil, enum=nil)
- Number.new(bits, signed, initvalue, enum)
- end
- def float
- Float.new
- end
- def bit(shift)
- BitField.new(shift, 1)
- end
- def bits(shift, len, enum=nil)
- BitField.new(shift, len, enum)
- end
- def pointer
- Pointer.new((yield if block_given?))
- end
- def pointer_ary(tglen)
- PointerAry.new(tglen, yield)
- end
- def static_array(len, tglen, indexenum=nil)
- StaticArray.new(tglen, len, indexenum, yield)
- end
- def static_string(len)
- StaticString.new(len)
- end
-
- def stl_vector(tglen=nil)
- tg = yield if tglen
- case tglen
- when 1; StlVector8.new(tg)
- when 2; StlVector16.new(tg)
- else StlVector32.new(tg)
- end
- end
- def stl_string
- StlString.new
- end
- def stl_bit_vector
- StlBitVector.new
- end
- def stl_deque(tglen)
- StlDeque.new(tglen, yield)
- end
-
- def df_flagarray(indexenum=nil)
- DfFlagarray.new(indexenum)
- end
- def df_array(tglen)
- DfArray.new(tglen, yield)
- end
- def df_linked_list
- DfLinkedList.new(yield)
- end
-
- def global(glob)
- Global.new(glob)
- end
- def compound(name=nil, &b)
- m = Class.new(Compound)
- DFHack.const_set(name, m) if name
- m.instance_eval(&b)
- m.new
- end
- def rtti_classname(n)
- DFHack.rtti_register(n, self)
- @_rtti_classname = n
- end
- def sizeof(n)
- @_sizeof = n
- end
-
- # allocate a new c++ object, return its ruby wrapper
- def cpp_new
- ptr = DFHack.malloc(_sizeof)
- if _rtti_classname and vt = DFHack.rtti_getvtable(_rtti_classname)
- DFHack.memory_write_int32(ptr, vt)
- # TODO call constructor
- end
- o = new._at(ptr)
- o._cpp_init
- o
- end
- end
- def _cpp_init
- _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_init }
- end
- def _set(h)
- case h
- when Hash; h.each { |k, v| send("_#{k}=", v) }
- when Array; names = _field_names ; raise 'bad size' if names.length != h.length ; names.zip(h).each { |n, a| send("#{n}=", a) }
- when Compound; _field_names.each { |n| send("#{n}=", h.send(n)) }
- else raise 'wut?'
- end
- end
- def _fields ; self.class._fields.to_a ; end
- def _fields_ancestors ; self.class._fields_ancestors.to_a ; end
- def _field_names ; _fields_ancestors.map { |n, o, s| n } ; end
- def _rtti_classname ; self.class._rtti_classname ; end
- def _sizeof ; self.class._sizeof ; end
- @@inspecting = {} # avoid infinite recursion on mutually-referenced objects
- def inspect
- cn = self.class.name.sub(/^DFHack::/, '')
- cn << ' @' << ('0x%X' % _memaddr) if cn != ''
- out = "#<#{cn}"
- return out << ' ...>' if @@inspecting[_memaddr]
- @@inspecting[_memaddr] = true
- _fields_ancestors.each { |n, o, s|
- out << ' ' if out.length != 0 and out[-1, 1] != ' '
- if out.length > INSPECT_SIZE_LIMIT
- out << '...'
- break
- end
- out << inspect_field(n, o, s)
- }
- out.chomp!(' ')
- @@inspecting.delete _memaddr
- out << '>'
- end
- def inspect_field(n, o, s)
- if s.kind_of?(BitField) and s._len == 1
- send(n) ? n.to_s : ''
- elsif s.kind_of?(Pointer)
- "#{n}=#{s._at(@_memaddr+o).inspect}"
- elsif n == :_whole
- "_whole=0x#{_whole.to_s(16)}"
- else
- v = send(n).inspect
- "#{n}=#{v}"
- end
- rescue
- "#{n}=ERR(#{$!})"
- end
-end
-
-class Enum
- # number -> symbol
- def self.enum
- # ruby weirdness, needed to make the constants 'virtual'
- @enum ||= const_get(:ENUM)
- end
- # symbol -> number
- def self.nume
- @nume ||= const_get(:NUME)
- end
-
- def self.to_i(i)
- nume[i] || i
- end
- def self.to_sym(i)
- enum[i] || i
- end
-end
-
-class Number < MemStruct
- attr_accessor :_bits, :_signed, :_initvalue, :_enum
- def initialize(bits, signed, initvalue, enum)
- @_bits = bits
- @_signed = signed
- @_initvalue = initvalue
- @_enum = enum
- end
-
- def _get
- v = case @_bits
- when 32; DFHack.memory_read_int32(@_memaddr)
- when 16; DFHack.memory_read_int16(@_memaddr)
- when 8; DFHack.memory_read_int8( @_memaddr)
- when 64;(DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + (DFHack.memory_read_int32(@_memaddr+4) << 32)
- end
- v &= (1 << @_bits) - 1 if not @_signed
- v = @_enum.to_sym(v) if @_enum
- v
- end
-
- def _set(v)
- v = @_enum.to_i(v) if @_enum
- case @_bits
- when 32; DFHack.memory_write_int32(@_memaddr, v)
- when 16; DFHack.memory_write_int16(@_memaddr, v)
- when 8; DFHack.memory_write_int8( @_memaddr, v)
- when 64; DFHack.memory_write_int32(@_memaddr, v & 0xffffffff) ; DFHack.memory_write_int32(@memaddr+4, v>>32)
- end
- end
-
- def _cpp_init
- _set(@_initvalue) if @_initvalue
- end
-end
-class Float < MemStruct
- def _get
- DFHack.memory_read_float(@_memaddr)
- end
-
- def _set(v)
- DFHack.memory_write_float(@_memaddr, v)
- end
-
- def _cpp_init
- _set(0.0)
- end
-end
-class BitField < MemStruct
- attr_accessor :_shift, :_len, :_enum
- def initialize(shift, len, enum=nil)
- @_shift = shift
- @_len = len
- @_enum = enum
- end
- def _mask
- (1 << @_len) - 1
- end
-
- def _get
- v = DFHack.memory_read_int32(@_memaddr) >> @_shift
- if @_len == 1
- ((v & 1) == 0) ? false : true
- else
- v &= _mask
- v = @_enum.to_sym(v) if @_enum
- v
- end
- end
-
- def _set(v)
- if @_len == 1
- # allow 'bit = 0'
- v = (v && v != 0 ? 1 : 0)
- end
- v = @_enum.to_i(v) if @_enum
- v = (v & _mask) << @_shift
-
- ori = DFHack.memory_read_int32(@_memaddr) & 0xffffffff
- DFHack.memory_write_int32(@_memaddr, ori - (ori & ((-1 & _mask) << @_shift)) + v)
- end
-end
-
-class Pointer < MemStruct
- attr_accessor :_tg
- def initialize(tg)
- @_tg = tg
- end
-
- def _getp
- DFHack.memory_read_int32(@_memaddr) & 0xffffffff
- end
-
- def _get
- addr = _getp
- return if addr == 0
- @_tg._at(addr)._get
- end
-
- # XXX shaky...
- def _set(v)
- if v.kind_of?(Pointer)
- DFHack.memory_write_int32(@_memaddr, v._getp)
- elsif v.kind_of?(MemStruct)
- DFHack.memory_write_int32(@_memaddr, v._memaddr)
- else
- _get._set(v)
- end
- end
-
- def inspect
- ptr = _getp
- if ptr == 0
- 'NULL'
- else
- cn = ''
- cn = @_tg.class.name.sub(/^DFHack::/, '').sub(/^MemHack::/, '') if @_tg
- cn = @_tg._glob if cn == 'Global'
- "#<Pointer #{cn} #{'0x%X' % _getp}>"
- end
- end
-end
-class PointerAry < MemStruct
- attr_accessor :_tglen, :_tg
- def initialize(tglen, tg)
- @_tglen = tglen
- @_tg = tg
- end
-
- def _getp(i=0)
- delta = (i != 0 ? i*@_tglen : 0)
- (DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + delta
- end
-
- def _get
- addr = _getp
- return if addr == 0
- self
- end
-
- def [](i)
- addr = _getp(i)
- return if addr == 0
- @_tg._at(addr)._get
- end
- def []=(i, v)
- addr = _getp(i)
- raise 'null pointer' if addr == 0
- @_tg._at(addr)._set(v)
- end
-
- def inspect ; ptr = _getp ; (ptr == 0) ? 'NULL' : "#<PointerAry #{'0x%X' % ptr}>" ; end
-end
-module Enumerable
- include ::Enumerable
- attr_accessor :_indexenum
- def each ; (0...length).each { |i| yield self[i] } ; end
- def inspect
- out = '['
- each_with_index { |e, idx|
- out << ', ' if out.length > 1
- if out.length > INSPECT_SIZE_LIMIT
- out << '...'
- break
- end
- out << "#{_indexenum.to_sym(idx)}=" if _indexenum
- out << e.inspect
- }
- out << ']'
- end
- def empty? ; length == 0 ; end
- def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end
-end
-class StaticArray < MemStruct
- attr_accessor :_tglen, :_length, :_indexenum, :_tg
- def initialize(tglen, length, indexenum, tg)
- @_tglen = tglen
- @_length = length
- @_indexenum = indexenum
- @_tg = tg
- end
- def _set(a)
- a.each_with_index { |v, i| self[i] = v }
- end
- def _cpp_init
- _length.times { |i| _tgat(i)._cpp_init }
- end
- alias length _length
- alias size _length
- def _tgat(i)
- @_tg._at(@_memaddr + i*@_tglen) if i >= 0 and i < @_length
- end
- def [](i)
- i = _indexenum.to_i(i) if _indexenum
- i += @_length if i < 0
- _tgat(i)._get
- end
- def []=(i, v)
- i = _indexenum.to_i(i) if _indexenum
- i += @_length if i < 0
- _tgat(i)._set(v)
- end
-
- include Enumerable
-end
-class StaticString < MemStruct
- attr_accessor :_length
- def initialize(length)
- @_length = length
- end
- def _get
- DFHack.memory_read(@_memaddr, @_length)
- end
- def _set(v)
- DFHack.memory_write(@_memaddr, v[0, @_length])
- end
-end
-
-class StlVector32 < MemStruct
- attr_accessor :_tg
- def initialize(tg)
- @_tg = tg
- end
-
- def length
- DFHack.memory_vector32_length(@_memaddr)
- end
- def size ; length ; end # alias wouldnt work for subclasses
- def valueptr_at(idx)
- DFHack.memory_vector32_ptrat(@_memaddr, idx)
- end
- def insert_at(idx, val)
- DFHack.memory_vector32_insert(@_memaddr, idx, val)
- end
- def delete_at(idx)
- DFHack.memory_vector32_delete(@_memaddr, idx)
- end
-
- def _set(v)
- delete_at(length-1) while length > v.length # match lengthes
- v.each_with_index { |e, i| self[i] = e } # patch entries
- end
-
- def _cpp_init
- DFHack.memory_vector_init(@_memaddr)
- end
-
- def clear
- delete_at(length-1) while length > 0
- end
- def [](idx)
- idx += length if idx < 0
- @_tg._at(valueptr_at(idx))._get if idx >= 0 and idx < length
- end
- def []=(idx, v)
- idx += length if idx < 0
- if idx >= length
- insert_at(idx, 0)
- elsif idx < 0
- raise 'invalid idx'
- end
- @_tg._at(valueptr_at(idx))._set(v)
- end
- def push(v)
- self[length] = v
- self
- end
- def <<(v) ; push(v) ; end
- def pop
- l = length
- if l > 0
- v = self[l-1]
- delete_at(l-1)
- end
- v
- end
-
- include Enumerable
- # do a binary search in an ordered vector for a specific target attribute
- # ex: world.history.figures.binsearch(unit.hist_figure_id)
- def binsearch(target, field=:id)
- o_start = 0
- o_end = length - 1
- while o_end >= o_start
- o_half = o_start + (o_end-o_start)/2
- obj = self[o_half]
- oval = obj.send(field)
- if oval == target
- return obj
- elsif oval < target
- o_start = o_half+1
- else
- o_end = o_half-1
- end
- end
- end
-end
-class StlVector16 < StlVector32
- def length
- DFHack.memory_vector16_length(@_memaddr)
- end
- def valueptr_at(idx)
- DFHack.memory_vector16_ptrat(@_memaddr, idx)
- end
- def insert_at(idx, val)
- DFHack.memory_vector16_insert(@_memaddr, idx, val)
- end
- def delete_at(idx)
- DFHack.memory_vector16_delete(@_memaddr, idx)
- end
-end
-class StlVector8 < StlVector32
- def length
- DFHack.memory_vector8_length(@_memaddr)
- end
- def valueptr_at(idx)
- DFHack.memory_vector8_ptrat(@_memaddr, idx)
- end
- def insert_at(idx, val)
- DFHack.memory_vector8_insert(@_memaddr, idx, val)
- end
- def delete_at(idx)
- DFHack.memory_vector8_delete(@_memaddr, idx)
- end
-end
-class StlBitVector < StlVector32
- def initialize ; end
- def length
- DFHack.memory_vectorbool_length(@_memaddr)
- end
- def insert_at(idx, val)
- DFHack.memory_vectorbool_insert(@_memaddr, idx, val)
- end
- def delete_at(idx)
- DFHack.memory_vectorbool_delete(@_memaddr, idx)
- end
- def [](idx)
- idx += length if idx < 0
- DFHack.memory_vectorbool_at(@_memaddr, idx) if idx >= 0 and idx < length
- end
- def []=(idx, v)
- idx += length if idx < 0
- if idx >= length
- insert_at(idx, v)
- elsif idx < 0
- raise 'invalid idx'
- else
- DFHack.memory_vectorbool_setat(@_memaddr, idx, v)
- end
- end
-end
-class StlString < MemStruct
- def _get
- DFHack.memory_read_stlstring(@_memaddr)
- end
-
- def _set(v)
- DFHack.memory_write_stlstring(@_memaddr, v)
- end
-
- def _cpp_init
- DFHack.memory_stlstring_init(@_memaddr)
- end
-end
-class StlDeque < MemStruct
- attr_accessor :_tglen, :_tg
- def initialize(tglen, tg)
- @_tglen = tglen
- @_tg = tg
- end
- # XXX DF uses stl::deque<some_struct>, so to have a C binding we'd need to single-case every
- # possible struct size, like for StlVector. Just ignore it for now, deque are rare enough.
- def inspect ; "#<StlDeque>" ; end
-end
-
-class DfFlagarray < MemStruct
- attr_accessor :_indexenum
- def initialize(indexenum)
- @_indexenum = indexenum
- end
- def length
- DFHack.memory_bitarray_length(@_memaddr)
- end
- # TODO _cpp_init
- def size ; length ; end
- def resize(len)
- DFHack.memory_bitarray_resize(@_memaddr, len)
- end
- def [](idx)
- idx = _indexenum.to_i(idx) if _indexenum
- idx += length if idx < 0
- DFHack.memory_bitarray_isset(@_memaddr, idx) if idx >= 0 and idx < length
- end
- def []=(idx, v)
- idx = _indexenum.to_i(idx) if _indexenum
- idx += length if idx < 0
- if idx >= length or idx < 0
- raise 'invalid idx'
- else
- DFHack.memory_bitarray_set(@_memaddr, idx, v)
- end
- end
-
- include Enumerable
-end
-class DfArray < Compound
- attr_accessor :_tglen, :_tg
- def initialize(tglen, tg)
- @_tglen = tglen
- @_tg = tg
- end
-
- field(:_ptr, 0) { number 32, false }
- field(:_length, 4) { number 16, false }
-
- def length ; _length ; end
- def size ; _length ; end
- # TODO _cpp_init
- def _tgat(i)
- @_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length
- end
- def [](i)
- i += _length if i < 0
- _tgat(i)._get
- end
- def []=(i, v)
- i += _length if i < 0
- _tgat(i)._set(v)
- end
- def _set(a)
- a.each_with_index { |v, i| self[i] = v }
- end
-
- include Enumerable
-end
-class DfLinkedList < Compound
- attr_accessor :_tg
- def initialize(tg)
- @_tg = tg
- end
-
- field(:_ptr, 0) { number 32, false }
- field(:_prev, 4) { number 32, false }
- field(:_next, 8) { number 32, false }
-
- def item
- # With the current xml structure, currently _tg designate
- # the type of the 'next' and 'prev' fields, not 'item'.
- # List head has item == NULL, so we can safely return nil.
-
- #addr = _ptr
- #return if addr == 0
- #@_tg._at(addr)._get
- end
-
- def item=(v)
- #addr = _ptr
- #raise 'null pointer' if addr == 0
- #@_tg.at(addr)._set(v)
- raise 'null pointer'
- end
-
- def prev
- addr = _prev
- return if addr == 0
- @_tg._at(addr)._get
- end
-
- def next
- addr = _next
- return if addr == 0
- @_tg._at(addr)._get
- end
-
- include Enumerable
- def each
- o = self
- while o
- yield o.item if o.item
- o = o.next
- end
- end
- def inspect ; "#<DfLinkedList #{item.inspect} prev=#{'0x%X' % _prev} next=#{'0x%X' % _next}>" ; end
-end
-
-class Global < MemStruct
- attr_accessor :_glob
- def initialize(glob)
- @_glob = glob
- end
- def _at(addr)
- g = DFHack.const_get(@_glob)
- g = DFHack.rtti_getclassat(g, addr)
- g.new._at(addr)
- end
- def inspect ; "#<#{@_glob}>" ; end
-end
-end # module MemHack
-
-
-# cpp rtti name -> rb class
-@rtti_n2c = {}
-@rtti_c2n = {}
-
-# cpp rtti name -> vtable ptr
-@rtti_n2v = {}
-@rtti_v2n = {}
-
-def self.rtti_n2c ; @rtti_n2c ; end
-def self.rtti_c2n ; @rtti_c2n ; end
-def self.rtti_n2v ; @rtti_n2v ; end
-def self.rtti_v2n ; @rtti_v2n ; end
-
-# register a ruby class with a cpp rtti class name
-def self.rtti_register(cppname, cls)
- @rtti_n2c[cppname] = cls
- @rtti_c2n[cls] = cppname
-end
-
-# return the ruby class to use for the cpp object at address if rtti info is available
-def self.rtti_getclassat(cls, addr)
- if addr != 0 and @rtti_c2n[cls]
- # rtti info exist for class => cpp object has a vtable
- @rtti_n2c[rtti_readclassname(get_vtable_ptr(addr))] || cls
- else
- cls
- end
-end
-
-# try to read the rtti classname from an object vtable pointer
-def self.rtti_readclassname(vptr)
- unless n = @rtti_v2n[vptr]
- n = @rtti_v2n[vptr] = get_rtti_classname(vptr).to_sym
- @rtti_n2v[n] = vptr
- end
- n
-end
-
-# return the vtable pointer from the cpp rtti name
-def self.rtti_getvtable(cppname)
- unless v = @rtti_n2v[cppname]
- v = get_vtable(cppname.to_s)
- @rtti_n2v[cppname] = v
- @rtti_v2n[v] = cppname if v != 0
- end
- v if v != 0
-end
-
-def self.vmethod_call(obj, voff, a0=0, a1=0, a2=0, a3=0, a4=0)
- vmethod_do_call(obj._memaddr, voff, vmethod_arg(a0), vmethod_arg(a1), vmethod_arg(a2), vmethod_arg(a3))
-end
-
-def self.vmethod_arg(arg)
- case arg
- when nil, false; 0
- when true; 1
- when Integer; arg
- #when String; [arg].pack('p').unpack('L')[0] # raw pointer to buffer
- when MemHack::Compound; arg._memaddr
- else raise "bad vmethod arg #{arg.class}"
- end
-end
-
-end
-
-
diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp
index 76d6a043..49119c9a 100644
--- a/plugins/ruby/ruby.cpp
+++ b/plugins/ruby/ruby.cpp
@@ -11,8 +11,6 @@
#include "tinythread.h"
-#include <ruby.h>
-
using namespace DFHack;
@@ -20,6 +18,8 @@ using namespace DFHack;
// DFHack stuff
+static int df_loadruby(void);
+static void df_unloadruby(void);
static void df_rubythread(void*);
static command_result df_rubyload(color_ostream &out, std::vector <std::string> & parameters);
static command_result df_rubyeval(color_ostream &out, std::vector <std::string> & parameters);
@@ -45,6 +45,11 @@ DFHACK_PLUGIN("ruby")
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
+ // fail silently instead of spamming the console with 'failed to initialize' if libruby is not present
+ // the error is still logged in stderr.log
+ if (!df_loadruby())
+ return CR_OK;
+
m_irun = new tthread::mutex();
m_mutex = new tthread::mutex();
r_type = RB_INIT;
@@ -74,10 +79,11 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
- m_mutex->lock();
if (!r_thread)
return CR_OK;
+ m_mutex->lock();
+
r_type = RB_DIE;
r_command = 0;
m_irun->unlock();
@@ -90,6 +96,8 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
m_mutex->unlock();
delete m_mutex;
+ df_unloadruby();
+
return CR_OK;
}
@@ -128,6 +136,9 @@ static command_result plugin_eval_rb(std::string &command)
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
{
+ if (!r_thread)
+ return CR_OK;
+
if (!onupdate_active)
return CR_OK;
@@ -136,6 +147,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event e)
{
+ if (!r_thread)
+ return CR_OK;
+
std::string cmd = "DFHack.onstatechange ";
switch (e) {
#define SCASE(s) case SC_ ## s : cmd += ":" # s ; break
@@ -146,6 +160,7 @@ DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_ch
SCASE(VIEWSCREEN_CHANGED);
SCASE(CORE_INITIALIZED);
SCASE(BEGIN_UNLOAD);
+#undef SCASE
}
return plugin_eval_rb(cmd);
@@ -191,6 +206,113 @@ static command_result df_rubyeval(color_ostream &out, std::vector <std::string>
// ruby stuff
+// ruby-dev on windows is messy
+// ruby.h on linux 64 is broken
+// so we dynamically load libruby instead of linking it at compile time
+// lib path can be set in dfhack.ini to use the system libruby, but by
+// default we'll embed our own (downloaded by cmake)
+
+// these ruby definitions are invalid for windows 64bit
+typedef unsigned long VALUE;
+typedef unsigned long ID;
+
+#define Qfalse ((VALUE)0)
+#define Qtrue ((VALUE)2)
+#define Qnil ((VALUE)4)
+
+#define INT2FIX(i) ((VALUE)((((long)i) << 1) | 1))
+#define FIX2INT(i) (((long)i) >> 1)
+#define RUBY_METHOD_FUNC(func) ((VALUE(*)(...))func)
+
+VALUE *rb_eRuntimeError;
+
+void (*ruby_sysinit)(int *, const char ***);
+void (*ruby_init)(void);
+void (*ruby_init_loadpath)(void);
+void (*ruby_script)(const char*);
+void (*ruby_finalize)(void);
+ID (*rb_intern)(const char*);
+VALUE (*rb_raise)(VALUE, const char*, ...);
+VALUE (*rb_funcall)(VALUE, ID, int, ...);
+VALUE (*rb_define_module)(const char*);
+void (*rb_define_singleton_method)(VALUE, const char*, VALUE(*)(...), int);
+void (*rb_define_const)(VALUE, const char*, VALUE);
+void (*rb_load_protect)(VALUE, int, int*);
+VALUE (*rb_gv_get)(const char*);
+VALUE (*rb_str_new)(const char*, long);
+VALUE (*rb_str_new2)(const char*);
+char* (*rb_string_value_ptr)(VALUE*);
+VALUE (*rb_eval_string_protect)(const char*, int*);
+VALUE (*rb_ary_shift)(VALUE);
+VALUE (*rb_float_new)(double);
+double (*rb_num2dbl)(VALUE);
+VALUE (*rb_int2inum)(long);
+VALUE (*rb_uint2inum)(unsigned long);
+unsigned long (*rb_num2ulong)(VALUE);
+// end of rip(ruby.h)
+
+DFHack::DFLibrary *libruby_handle;
+
+// load the ruby library, initialize function pointers
+static int df_loadruby(void)
+{
+ const char *libpath =
+#ifdef WIN32
+ "./libruby.dll";
+#else
+ "hack/libruby.so";
+#endif
+
+ libruby_handle = OpenPlugin(libpath);
+ if (!libruby_handle) {
+ fprintf(stderr, "Cannot initialize ruby plugin: failed to load %s\n", libpath);
+ return 0;
+ }
+
+ if (!(rb_eRuntimeError = (VALUE*)LookupPlugin(libruby_handle, "rb_eRuntimeError")))
+ return 0;
+
+ // XXX does msvc support decltype ? might need a #define decltype typeof
+ // or just assign to *(void**)(&s) = ...
+ // ruby_sysinit is optional (ruby1.9 only)
+ ruby_sysinit = (decltype(ruby_sysinit))LookupPlugin(libruby_handle, "ruby_sysinit");
+#define rbloadsym(s) if (!(s = (decltype(s))LookupPlugin(libruby_handle, #s))) return 0
+ rbloadsym(ruby_init);
+ rbloadsym(ruby_init_loadpath);
+ rbloadsym(ruby_script);
+ rbloadsym(ruby_finalize);
+ rbloadsym(rb_intern);
+ rbloadsym(rb_raise);
+ rbloadsym(rb_funcall);
+ rbloadsym(rb_define_module);
+ rbloadsym(rb_define_singleton_method);
+ rbloadsym(rb_define_const);
+ rbloadsym(rb_load_protect);
+ rbloadsym(rb_gv_get);
+ rbloadsym(rb_str_new);
+ rbloadsym(rb_str_new2);
+ rbloadsym(rb_string_value_ptr);
+ rbloadsym(rb_eval_string_protect);
+ rbloadsym(rb_ary_shift);
+ rbloadsym(rb_float_new);
+ rbloadsym(rb_num2dbl);
+ rbloadsym(rb_int2inum);
+ rbloadsym(rb_uint2inum);
+ rbloadsym(rb_num2ulong);
+#undef rbloadsym
+
+ return 1;
+}
+
+static void df_unloadruby(void)
+{
+ if (libruby_handle) {
+ ClosePlugin(libruby_handle);
+ libruby_handle = 0;
+ }
+}
+
+
// ruby thread code
static void dump_rb_error(void)
{
@@ -218,6 +340,13 @@ static void df_rubythread(void *p)
{
int state, running;
+ if (ruby_sysinit) {
+ // ruby1.9 specific API
+ static int argc;
+ static const char *argv[] = { "dfhack", 0 };
+ ruby_sysinit(&argc, (const char ***)&argv);
+ }
+
// initialize the ruby interpreter
ruby_init();
ruby_init_loadpath();
@@ -353,7 +482,7 @@ static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr)
*/
static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr)
{
- rb_raise(rb_eRuntimeError, "not implemented");
+ rb_raise(*rb_eRuntimeError, "not implemented");
}
static VALUE rb_dfget_global_address(VALUE self, VALUE name)
@@ -402,7 +531,7 @@ static VALUE rb_dfmalloc(VALUE self, VALUE len)
{
char *ptr = (char*)malloc(FIX2INT(len));
if (!ptr)
- rb_raise(rb_eRuntimeError, "no memory");
+ rb_raise(*rb_eRuntimeError, "no memory");
memset(ptr, 0, FIX2INT(len));
return rb_uint2inum((long)ptr);
}
@@ -641,7 +770,7 @@ static VALUE rb_dfvcall(VALUE self, VALUE cppobj, VALUE cppvoff, VALUE a0, VALUE
int ret;
fptr = (decltype(fptr))*(void**)(*that + rb_num2ulong(cppvoff));
ret = fptr(that, rb_num2ulong(a0), rb_num2ulong(a1), rb_num2ulong(a2), rb_num2ulong(a3));
- return rb_uint2inum(ret);
+ return rb_int2inum(ret);
}
diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb
index 8c218eb6..fd597e43 100644
--- a/plugins/ruby/ruby.rb
+++ b/plugins/ruby/ruby.rb
@@ -1,5 +1,3 @@
-require 'hack/ruby-autogen'
-
module DFHack
class << self
# update the ruby.cpp version to accept a block
@@ -298,5 +296,759 @@ def df
DFHack
end
-# load user-specified startup file
+# following: definitions used by ruby-autogen.rb
+module DFHack
+module MemHack
+INSPECT_SIZE_LIMIT=16384
+class MemStruct
+ attr_accessor :_memaddr
+ def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end
+ def _get ; self ; end
+ def _cpp_init ; end
+end
+
+class Compound < MemStruct
+ class << self
+ attr_accessor :_fields, :_rtti_classname, :_sizeof
+ def field(name, offset)
+ struct = yield
+ return if not struct
+ @_fields ||= []
+ @_fields << [name, offset, struct]
+ define_method(name) { struct._at(@_memaddr+offset)._get }
+ define_method("#{name}=") { |v| struct._at(@_memaddr+offset)._set(v) }
+ end
+ def _fields_ancestors
+ if superclass.respond_to?(:_fields_ancestors)
+ superclass._fields_ancestors + _fields.to_a
+ else
+ _fields.to_a
+ end
+ end
+
+ def number(bits, signed, initvalue=nil, enum=nil)
+ Number.new(bits, signed, initvalue, enum)
+ end
+ def float
+ Float.new
+ end
+ def bit(shift)
+ BitField.new(shift, 1)
+ end
+ def bits(shift, len, enum=nil)
+ BitField.new(shift, len, enum)
+ end
+ def pointer
+ Pointer.new((yield if block_given?))
+ end
+ def pointer_ary(tglen)
+ PointerAry.new(tglen, yield)
+ end
+ def static_array(len, tglen, indexenum=nil)
+ StaticArray.new(tglen, len, indexenum, yield)
+ end
+ def static_string(len)
+ StaticString.new(len)
+ end
+
+ def stl_vector(tglen=nil)
+ tg = yield if tglen
+ case tglen
+ when 1; StlVector8.new(tg)
+ when 2; StlVector16.new(tg)
+ else StlVector32.new(tg)
+ end
+ end
+ def stl_string
+ StlString.new
+ end
+ def stl_bit_vector
+ StlBitVector.new
+ end
+ def stl_deque(tglen)
+ StlDeque.new(tglen, yield)
+ end
+
+ def df_flagarray(indexenum=nil)
+ DfFlagarray.new(indexenum)
+ end
+ def df_array(tglen)
+ DfArray.new(tglen, yield)
+ end
+ def df_linked_list
+ DfLinkedList.new(yield)
+ end
+
+ def global(glob)
+ Global.new(glob)
+ end
+ def compound(name=nil, &b)
+ m = Class.new(Compound)
+ DFHack.const_set(name, m) if name
+ m.instance_eval(&b)
+ m.new
+ end
+ def rtti_classname(n)
+ DFHack.rtti_register(n, self)
+ @_rtti_classname = n
+ end
+ def sizeof(n)
+ @_sizeof = n
+ end
+
+ # allocate a new c++ object, return its ruby wrapper
+ def cpp_new
+ ptr = DFHack.malloc(_sizeof)
+ if _rtti_classname and vt = DFHack.rtti_getvtable(_rtti_classname)
+ DFHack.memory_write_int32(ptr, vt)
+ # TODO call constructor
+ end
+ o = new._at(ptr)
+ o._cpp_init
+ o
+ end
+ end
+ def _cpp_init
+ _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_init }
+ end
+ def _set(h)
+ case h
+ when Hash; h.each { |k, v| send("_#{k}=", v) }
+ when Array; names = _field_names ; raise 'bad size' if names.length != h.length ; names.zip(h).each { |n, a| send("#{n}=", a) }
+ when Compound; _field_names.each { |n| send("#{n}=", h.send(n)) }
+ else raise 'wut?'
+ end
+ end
+ def _fields ; self.class._fields.to_a ; end
+ def _fields_ancestors ; self.class._fields_ancestors.to_a ; end
+ def _field_names ; _fields_ancestors.map { |n, o, s| n } ; end
+ def _rtti_classname ; self.class._rtti_classname ; end
+ def _sizeof ; self.class._sizeof ; end
+ @@inspecting = {} # avoid infinite recursion on mutually-referenced objects
+ def inspect
+ cn = self.class.name.sub(/^DFHack::/, '')
+ cn << ' @' << ('0x%X' % _memaddr) if cn != ''
+ out = "#<#{cn}"
+ return out << ' ...>' if @@inspecting[_memaddr]
+ @@inspecting[_memaddr] = true
+ _fields_ancestors.each { |n, o, s|
+ out << ' ' if out.length != 0 and out[-1, 1] != ' '
+ if out.length > INSPECT_SIZE_LIMIT
+ out << '...'
+ break
+ end
+ out << inspect_field(n, o, s)
+ }
+ out.chomp!(' ')
+ @@inspecting.delete _memaddr
+ out << '>'
+ end
+ def inspect_field(n, o, s)
+ if s.kind_of?(BitField) and s._len == 1
+ send(n) ? n.to_s : ''
+ elsif s.kind_of?(Pointer)
+ "#{n}=#{s._at(@_memaddr+o).inspect}"
+ elsif n == :_whole
+ "_whole=0x#{_whole.to_s(16)}"
+ else
+ v = send(n).inspect
+ "#{n}=#{v}"
+ end
+ rescue
+ "#{n}=ERR(#{$!})"
+ end
+end
+
+class Enum
+ # number -> symbol
+ def self.enum
+ # ruby weirdness, needed to make the constants 'virtual'
+ @enum ||= const_get(:ENUM)
+ end
+ # symbol -> number
+ def self.nume
+ @nume ||= const_get(:NUME)
+ end
+
+ def self.int(i)
+ nume[i] || i
+ end
+ def self.sym(i)
+ enum[i] || i
+ end
+end
+
+class Number < MemStruct
+ attr_accessor :_bits, :_signed, :_initvalue, :_enum
+ def initialize(bits, signed, initvalue, enum)
+ @_bits = bits
+ @_signed = signed
+ @_initvalue = initvalue
+ @_enum = enum
+ end
+
+ def _get
+ v = case @_bits
+ when 32; DFHack.memory_read_int32(@_memaddr)
+ when 16; DFHack.memory_read_int16(@_memaddr)
+ when 8; DFHack.memory_read_int8( @_memaddr)
+ when 64;(DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + (DFHack.memory_read_int32(@_memaddr+4) << 32)
+ end
+ v &= (1 << @_bits) - 1 if not @_signed
+ v = @_enum.sym(v) if @_enum
+ v
+ end
+
+ def _set(v)
+ v = @_enum.int(v) if @_enum
+ case @_bits
+ when 32; DFHack.memory_write_int32(@_memaddr, v)
+ when 16; DFHack.memory_write_int16(@_memaddr, v)
+ when 8; DFHack.memory_write_int8( @_memaddr, v)
+ when 64; DFHack.memory_write_int32(@_memaddr, v & 0xffffffff) ; DFHack.memory_write_int32(@memaddr+4, v>>32)
+ end
+ end
+
+ def _cpp_init
+ _set(@_initvalue) if @_initvalue
+ end
+end
+class Float < MemStruct
+ def _get
+ DFHack.memory_read_float(@_memaddr)
+ end
+
+ def _set(v)
+ DFHack.memory_write_float(@_memaddr, v)
+ end
+
+ def _cpp_init
+ _set(0.0)
+ end
+end
+class BitField < MemStruct
+ attr_accessor :_shift, :_len, :_enum
+ def initialize(shift, len, enum=nil)
+ @_shift = shift
+ @_len = len
+ @_enum = enum
+ end
+ def _mask
+ (1 << @_len) - 1
+ end
+
+ def _get
+ v = DFHack.memory_read_int32(@_memaddr) >> @_shift
+ if @_len == 1
+ ((v & 1) == 0) ? false : true
+ else
+ v &= _mask
+ v = @_enum.sym(v) if @_enum
+ v
+ end
+ end
+
+ def _set(v)
+ if @_len == 1
+ # allow 'bit = 0'
+ v = (v && v != 0 ? 1 : 0)
+ end
+ v = @_enum.int(v) if @_enum
+ v = (v & _mask) << @_shift
+
+ ori = DFHack.memory_read_int32(@_memaddr) & 0xffffffff
+ DFHack.memory_write_int32(@_memaddr, ori - (ori & ((-1 & _mask) << @_shift)) + v)
+ end
+end
+
+class Pointer < MemStruct
+ attr_accessor :_tg
+ def initialize(tg)
+ @_tg = tg
+ end
+
+ def _getp
+ DFHack.memory_read_int32(@_memaddr) & 0xffffffff
+ end
+
+ def _get
+ addr = _getp
+ return if addr == 0
+ @_tg._at(addr)._get
+ end
+
+ # XXX shaky...
+ def _set(v)
+ if v.kind_of?(Pointer)
+ DFHack.memory_write_int32(@_memaddr, v._getp)
+ elsif v.kind_of?(MemStruct)
+ DFHack.memory_write_int32(@_memaddr, v._memaddr)
+ else
+ _get._set(v)
+ end
+ end
+
+ def inspect
+ ptr = _getp
+ if ptr == 0
+ 'NULL'
+ else
+ cn = ''
+ cn = @_tg.class.name.sub(/^DFHack::/, '').sub(/^MemHack::/, '') if @_tg
+ cn = @_tg._glob if cn == 'Global'
+ "#<Pointer #{cn} #{'0x%X' % _getp}>"
+ end
+ end
+end
+class PointerAry < MemStruct
+ attr_accessor :_tglen, :_tg
+ def initialize(tglen, tg)
+ @_tglen = tglen
+ @_tg = tg
+ end
+
+ def _getp(i=0)
+ delta = (i != 0 ? i*@_tglen : 0)
+ (DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + delta
+ end
+
+ def _get
+ addr = _getp
+ return if addr == 0
+ self
+ end
+
+ def [](i)
+ addr = _getp(i)
+ return if addr == 0
+ @_tg._at(addr)._get
+ end
+ def []=(i, v)
+ addr = _getp(i)
+ raise 'null pointer' if addr == 0
+ @_tg._at(addr)._set(v)
+ end
+
+ def inspect ; ptr = _getp ; (ptr == 0) ? 'NULL' : "#<PointerAry #{'0x%X' % ptr}>" ; end
+end
+module Enumerable
+ include ::Enumerable
+ attr_accessor :_indexenum
+ def each ; (0...length).each { |i| yield self[i] } ; end
+ def inspect
+ out = '['
+ each_with_index { |e, idx|
+ out << ', ' if out.length > 1
+ if out.length > INSPECT_SIZE_LIMIT
+ out << '...'
+ break
+ end
+ out << "#{_indexenum.sym(idx)}=" if _indexenum
+ out << e.inspect
+ }
+ out << ']'
+ end
+ def empty? ; length == 0 ; end
+ def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end
+end
+class StaticArray < MemStruct
+ attr_accessor :_tglen, :_length, :_indexenum, :_tg
+ def initialize(tglen, length, indexenum, tg)
+ @_tglen = tglen
+ @_length = length
+ @_indexenum = indexenum
+ @_tg = tg
+ end
+ def _set(a)
+ a.each_with_index { |v, i| self[i] = v }
+ end
+ def _cpp_init
+ _length.times { |i| _tgat(i)._cpp_init }
+ end
+ alias length _length
+ alias size _length
+ def _tgat(i)
+ @_tg._at(@_memaddr + i*@_tglen) if i >= 0 and i < @_length
+ end
+ def [](i)
+ i = _indexenum.int(i) if _indexenum
+ i += @_length if i < 0
+ _tgat(i)._get
+ end
+ def []=(i, v)
+ i = _indexenum.int(i) if _indexenum
+ i += @_length if i < 0
+ _tgat(i)._set(v)
+ end
+
+ include Enumerable
+end
+class StaticString < MemStruct
+ attr_accessor :_length
+ def initialize(length)
+ @_length = length
+ end
+ def _get
+ DFHack.memory_read(@_memaddr, @_length)
+ end
+ def _set(v)
+ DFHack.memory_write(@_memaddr, v[0, @_length])
+ end
+end
+
+class StlVector32 < MemStruct
+ attr_accessor :_tg
+ def initialize(tg)
+ @_tg = tg
+ end
+
+ def length
+ DFHack.memory_vector32_length(@_memaddr)
+ end
+ def size ; length ; end # alias wouldnt work for subclasses
+ def valueptr_at(idx)
+ DFHack.memory_vector32_ptrat(@_memaddr, idx)
+ end
+ def insert_at(idx, val)
+ DFHack.memory_vector32_insert(@_memaddr, idx, val)
+ end
+ def delete_at(idx)
+ DFHack.memory_vector32_delete(@_memaddr, idx)
+ end
+
+ def _set(v)
+ delete_at(length-1) while length > v.length # match lengthes
+ v.each_with_index { |e, i| self[i] = e } # patch entries
+ end
+
+ def _cpp_init
+ DFHack.memory_vector_init(@_memaddr)
+ end
+
+ def clear
+ delete_at(length-1) while length > 0
+ end
+ def [](idx)
+ idx += length if idx < 0
+ @_tg._at(valueptr_at(idx))._get if idx >= 0 and idx < length
+ end
+ def []=(idx, v)
+ idx += length if idx < 0
+ if idx >= length
+ insert_at(idx, 0)
+ elsif idx < 0
+ raise 'invalid idx'
+ end
+ @_tg._at(valueptr_at(idx))._set(v)
+ end
+ def push(v)
+ self[length] = v
+ self
+ end
+ def <<(v) ; push(v) ; end
+ def pop
+ l = length
+ if l > 0
+ v = self[l-1]
+ delete_at(l-1)
+ end
+ v
+ end
+
+ include Enumerable
+ # do a binary search in an ordered vector for a specific target attribute
+ # ex: world.history.figures.binsearch(unit.hist_figure_id)
+ def binsearch(target, field=:id)
+ o_start = 0
+ o_end = length - 1
+ while o_end >= o_start
+ o_half = o_start + (o_end-o_start)/2
+ obj = self[o_half]
+ oval = obj.send(field)
+ if oval == target
+ return obj
+ elsif oval < target
+ o_start = o_half+1
+ else
+ o_end = o_half-1
+ end
+ end
+ end
+end
+class StlVector16 < StlVector32
+ def length
+ DFHack.memory_vector16_length(@_memaddr)
+ end
+ def valueptr_at(idx)
+ DFHack.memory_vector16_ptrat(@_memaddr, idx)
+ end
+ def insert_at(idx, val)
+ DFHack.memory_vector16_insert(@_memaddr, idx, val)
+ end
+ def delete_at(idx)
+ DFHack.memory_vector16_delete(@_memaddr, idx)
+ end
+end
+class StlVector8 < StlVector32
+ def length
+ DFHack.memory_vector8_length(@_memaddr)
+ end
+ def valueptr_at(idx)
+ DFHack.memory_vector8_ptrat(@_memaddr, idx)
+ end
+ def insert_at(idx, val)
+ DFHack.memory_vector8_insert(@_memaddr, idx, val)
+ end
+ def delete_at(idx)
+ DFHack.memory_vector8_delete(@_memaddr, idx)
+ end
+end
+class StlBitVector < StlVector32
+ def initialize ; end
+ def length
+ DFHack.memory_vectorbool_length(@_memaddr)
+ end
+ def insert_at(idx, val)
+ DFHack.memory_vectorbool_insert(@_memaddr, idx, val)
+ end
+ def delete_at(idx)
+ DFHack.memory_vectorbool_delete(@_memaddr, idx)
+ end
+ def [](idx)
+ idx += length if idx < 0
+ DFHack.memory_vectorbool_at(@_memaddr, idx) if idx >= 0 and idx < length
+ end
+ def []=(idx, v)
+ idx += length if idx < 0
+ if idx >= length
+ insert_at(idx, v)
+ elsif idx < 0
+ raise 'invalid idx'
+ else
+ DFHack.memory_vectorbool_setat(@_memaddr, idx, v)
+ end
+ end
+end
+class StlString < MemStruct
+ def _get
+ DFHack.memory_read_stlstring(@_memaddr)
+ end
+
+ def _set(v)
+ DFHack.memory_write_stlstring(@_memaddr, v)
+ end
+
+ def _cpp_init
+ DFHack.memory_stlstring_init(@_memaddr)
+ end
+end
+class StlDeque < MemStruct
+ attr_accessor :_tglen, :_tg
+ def initialize(tglen, tg)
+ @_tglen = tglen
+ @_tg = tg
+ end
+ # XXX DF uses stl::deque<some_struct>, so to have a C binding we'd need to single-case every
+ # possible struct size, like for StlVector. Just ignore it for now, deque are rare enough.
+ def inspect ; "#<StlDeque>" ; end
+end
+
+class DfFlagarray < MemStruct
+ attr_accessor :_indexenum
+ def initialize(indexenum)
+ @_indexenum = indexenum
+ end
+ def length
+ DFHack.memory_bitarray_length(@_memaddr)
+ end
+ # TODO _cpp_init
+ def size ; length ; end
+ def resize(len)
+ DFHack.memory_bitarray_resize(@_memaddr, len)
+ end
+ def [](idx)
+ idx = _indexenum.int(idx) if _indexenum
+ idx += length if idx < 0
+ DFHack.memory_bitarray_isset(@_memaddr, idx) if idx >= 0 and idx < length
+ end
+ def []=(idx, v)
+ idx = _indexenum.int(idx) if _indexenum
+ idx += length if idx < 0
+ if idx >= length or idx < 0
+ raise 'invalid idx'
+ else
+ DFHack.memory_bitarray_set(@_memaddr, idx, v)
+ end
+ end
+
+ include Enumerable
+end
+class DfArray < Compound
+ attr_accessor :_tglen, :_tg
+ def initialize(tglen, tg)
+ @_tglen = tglen
+ @_tg = tg
+ end
+
+ field(:_ptr, 0) { number 32, false }
+ field(:_length, 4) { number 16, false }
+
+ def length ; _length ; end
+ def size ; _length ; end
+ # TODO _cpp_init
+ def _tgat(i)
+ @_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length
+ end
+ def [](i)
+ i += _length if i < 0
+ _tgat(i)._get
+ end
+ def []=(i, v)
+ i += _length if i < 0
+ _tgat(i)._set(v)
+ end
+ def _set(a)
+ a.each_with_index { |v, i| self[i] = v }
+ end
+
+ include Enumerable
+end
+class DfLinkedList < Compound
+ attr_accessor :_tg
+ def initialize(tg)
+ @_tg = tg
+ end
+
+ field(:_ptr, 0) { number 32, false }
+ field(:_prev, 4) { number 32, false }
+ field(:_next, 8) { number 32, false }
+
+ def item
+ # With the current xml structure, currently _tg designate
+ # the type of the 'next' and 'prev' fields, not 'item'.
+ # List head has item == NULL, so we can safely return nil.
+
+ #addr = _ptr
+ #return if addr == 0
+ #@_tg._at(addr)._get
+ end
+
+ def item=(v)
+ #addr = _ptr
+ #raise 'null pointer' if addr == 0
+ #@_tg.at(addr)._set(v)
+ raise 'null pointer'
+ end
+
+ def prev
+ addr = _prev
+ return if addr == 0
+ @_tg._at(addr)._get
+ end
+
+ def next
+ addr = _next
+ return if addr == 0
+ @_tg._at(addr)._get
+ end
+
+ include Enumerable
+ def each
+ o = self
+ while o
+ yield o.item if o.item
+ o = o.next
+ end
+ end
+ def inspect ; "#<DfLinkedList #{item.inspect} prev=#{'0x%X' % _prev} next=#{'0x%X' % _next}>" ; end
+end
+
+class Global < MemStruct
+ attr_accessor :_glob
+ def initialize(glob)
+ @_glob = glob
+ end
+ def _at(addr)
+ g = DFHack.const_get(@_glob)
+ g = DFHack.rtti_getclassat(g, addr)
+ g.new._at(addr)
+ end
+ def inspect ; "#<#{@_glob}>" ; end
+end
+end # module MemHack
+
+class BooleanEnum
+ def self.int(v) ; ((v == true) || (v == 1)) ? 1 : 0 ; end
+ def self.sym(v) ; (!v || (v == 0)) ? false : true ; end
+end
+
+# cpp rtti name -> rb class
+@rtti_n2c = {}
+@rtti_c2n = {}
+
+# cpp rtti name -> vtable ptr
+@rtti_n2v = {}
+@rtti_v2n = {}
+
+def self.rtti_n2c ; @rtti_n2c ; end
+def self.rtti_c2n ; @rtti_c2n ; end
+def self.rtti_n2v ; @rtti_n2v ; end
+def self.rtti_v2n ; @rtti_v2n ; end
+
+# register a ruby class with a cpp rtti class name
+def self.rtti_register(cppname, cls)
+ @rtti_n2c[cppname] = cls
+ @rtti_c2n[cls] = cppname
+end
+
+# return the ruby class to use for the cpp object at address if rtti info is available
+def self.rtti_getclassat(cls, addr)
+ if addr != 0 and @rtti_c2n[cls]
+ # rtti info exist for class => cpp object has a vtable
+ @rtti_n2c[rtti_readclassname(get_vtable_ptr(addr))] || cls
+ else
+ cls
+ end
+end
+
+# try to read the rtti classname from an object vtable pointer
+def self.rtti_readclassname(vptr)
+ unless n = @rtti_v2n[vptr]
+ n = @rtti_v2n[vptr] = get_rtti_classname(vptr).to_sym
+ @rtti_n2v[n] = vptr
+ end
+ n
+end
+
+# return the vtable pointer from the cpp rtti name
+def self.rtti_getvtable(cppname)
+ unless v = @rtti_n2v[cppname]
+ v = get_vtable(cppname.to_s)
+ @rtti_n2v[cppname] = v
+ @rtti_v2n[v] = cppname if v != 0
+ end
+ v if v != 0
+end
+
+def self.vmethod_call(obj, voff, a0=0, a1=0, a2=0, a3=0, a4=0)
+ vmethod_do_call(obj._memaddr, voff, vmethod_arg(a0), vmethod_arg(a1), vmethod_arg(a2), vmethod_arg(a3))
+end
+
+def self.vmethod_arg(arg)
+ case arg
+ when nil, false; 0
+ when true; 1
+ when Integer; arg
+ #when String; [arg].pack('p').unpack('L')[0] # raw pointer to buffer
+ when MemHack::Compound; arg._memaddr
+ else raise "bad vmethod arg #{arg.class}"
+ end
+end
+
+end
+
+# load autogen'd file
+require 'hack/ruby-autogen'
+
+# load optional user-specified startup file
load 'ruby_custom.rb' if File.exist?('ruby_custom.rb')
diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp
index b6097f62..ae9ff0e1 100755
--- a/plugins/seedwatch.cpp
+++ b/plugins/seedwatch.cpp
@@ -111,7 +111,7 @@ command_result df_seedwatch(color_ostream &out, vector<string>& parameters)
w->ReadGameMode(gm);// FIXME: check return value
// if game mode isn't fortress mode
- if(gm.g_mode != GAMEMODE_DWARF || gm.g_type != GAMETYPE_DWARF_MAIN)
+ if(gm.g_mode != game_mode::DWARF || gm.g_type != game_type::DWARF_MAIN)
{
// just print the help
printHelp(out);
@@ -299,7 +299,7 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out)
t_gamemodes gm;
w->ReadGameMode(gm);// FIXME: check return value
// if game mode isn't fortress mode
- if(gm.g_mode != GAMEMODE_DWARF || gm.g_type != GAMETYPE_DWARF_MAIN)
+ if(gm.g_mode != game_mode::DWARF || gm.g_type != game_type::DWARF_MAIN)
{
// stop running.
running = false;
diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp
index 7407e6af..7926e2ac 100644
--- a/plugins/showmood.cpp
+++ b/plugins/showmood.cpp
@@ -100,37 +100,37 @@ command_result df_showmood (color_ostream &out, vector <string> & parameters)
switch (job->job_type)
{
case job_type::StrangeMoodCrafter:
- out.print("become a Craftsdwarf (or Engraver)");
+ out.print("claim a Craftsdwarf's Workshop");
break;
case job_type::StrangeMoodJeweller:
- out.print("become a Jeweler");
+ out.print("claim a Jeweler's Workshop");
break;
case job_type::StrangeMoodForge:
- out.print("become a Metalworker");
+ out.print("claim a Metalsmith's Forge");
break;
case job_type::StrangeMoodMagmaForge:
- out.print("become a Metalworker using a Magma Forge");
+ out.print("claim a Magma Forge");
break;
case job_type::StrangeMoodCarpenter:
- out.print("become a Carpenter");
+ out.print("claim a Carpenter's Workshop");
break;
case job_type::StrangeMoodMason:
- out.print("become a Mason (or Miner)");
+ out.print("claim a Mason's Workshop");
break;
case job_type::StrangeMoodBowyer:
- out.print("become a Bowyer");
+ out.print("claim a Boywer's Workshop");
break;
case job_type::StrangeMoodTanner:
- out.print("become a Leatherworker (or Tanner)");
+ out.print("claim a Leather Works");
break;
case job_type::StrangeMoodWeaver:
- out.print("become a Clothier (or Weaver)");
+ out.print("claim a Clothier's Shop");
break;
case job_type::StrangeMoodGlassmaker:
- out.print("become a Glassmaker");
+ out.print("claim a Glass Furnace");
break;
case job_type::StrangeMoodMechanics:
- out.print("become a Mechanic");
+ out.print("claim a Mechanic's Workshop");
break;
case job_type::StrangeMoodBrooding:
out.print("enter a macabre mood?");
@@ -142,20 +142,28 @@ command_result df_showmood (color_ostream &out, vector <string> & parameters)
out.print("do something else...");
break;
}
+ out.print(" and become a legendary %s", ENUM_ATTR_STR(job_skill, caption_noun, unit->job.mood_skill));
+ if (unit->mood == mood_type::Possessed)
+ out.print(" (but not really)");
break;
-
default:
out.print("insane?");
break;
}
+ out.print(".\n");
+ if (unit->sex)
+ out.print("He has ");
+ else
+ out.print("She has ");
if (building)
{
string name;
building->getName(&name);
- out.print(" and has claimed a %s\n", name.c_str());
+ out.print("claimed a %s and wants", name.c_str());
}
else
- out.print(" and has not yet claimed a workshop\n");
+ out.print("not yet claimed a workshop but will want");
+ out.print(" the following items:\n");
for (size_t i = 0; i < job->job_items.size(); i++)
{
diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp
index 1092c86a..dbf54607 100644
--- a/plugins/workflow.cpp
+++ b/plugins/workflow.cpp
@@ -965,6 +965,9 @@ static void compute_job_outputs(color_ostream &out, ProtectedJob *pj)
mat.decode(mat.plant->material_defs.type_##tag, \
mat.plant->material_defs.idx_##tag); \
else mat.decode(-1);
+ case BrewDrink:
+ PLANT_PROCESS_MAT(DRINK, drink);
+ break;
case MillPlants:
PLANT_PROCESS_MAT(MILL, mill);
break;
diff --git a/scripts/devel/find-offsets.lua b/scripts/devel/find-offsets.lua
new file mode 100644
index 00000000..6b3c3157
--- /dev/null
+++ b/scripts/devel/find-offsets.lua
@@ -0,0 +1,601 @@
+-- Find some offsets for linux.
+
+local utils = require 'utils'
+local ms = require 'memscan'
+
+local is_known = dfhack.internal.getAddress
+
+local force_scan = {}
+for _,v in ipairs({...}) do
+ force_scan[v] = true
+end
+
+collectgarbage()
+
+print[[
+WARNING: THIS SCRIPT IS STRICTLY FOR DFHACK DEVELOPERS.
+
+Running this script on a new DF version will NOT
+MAKE IT RUN CORRECTLY if any data structures
+changed, thus possibly leading to CRASHES AND/OR
+PERMANENT SAVE CORRUPTION.
+
+This script should be initially started immediately
+after loading the game, WITHOUT first loading a world.
+It expects vanilla game configuration, without any
+custom tilesets or init file changes.
+]]
+
+if not utils.prompt_yes_no('Proceed?') then
+ return
+end
+
+-- Data segment location
+
+local data = ms.get_data_segment()
+if not data then
+ error('Could not find data segment')
+end
+
+print('\nData section: '..tostring(data))
+if data.size < 5000000 then
+ error('Data segment too short.')
+end
+
+local searcher = ms.DiffSearcher.new(data)
+
+local function validate_offset(name,validator,addr,tname,...)
+ local obj = data:object_by_field(addr,tname,...)
+ if obj and not validator(obj) then
+ obj = nil
+ end
+ ms.found_offset(name,obj)
+end
+
+local function exec_finder(finder, names)
+ if type(names) ~= 'table' then
+ names = { names }
+ end
+ local search = force_scan['all']
+ for _,v in ipairs(names) do
+ if force_scan[v] or not is_known(v) then
+ search = true
+ end
+ end
+ if search then
+ if not dfhack.safecall(finder) then
+ if not utils.prompt_yes_no('Proceed with the rest of the script?') then
+ searcher:reset()
+ error('Quit')
+ end
+ end
+ else
+ print('Already known: '..table.concat(names,', '))
+ end
+end
+
+local ordinal_names = {
+ [0] = '1st entry',
+ [1] = '2nd entry'
+}
+setmetatable(ordinal_names, {
+ __index = function(self,idx) return (idx+1)..'th entry' end
+})
+
+local function list_index_choices(length_func)
+ return function(id)
+ if id > 0 then
+ local ok, len = pcall(length_func)
+ if not ok then
+ len = 5
+ elseif len > 10 then
+ len = 10
+ end
+ return id % len
+ else
+ return 0
+ end
+ end
+end
+
+--
+-- Cursor group
+--
+
+local function find_cursor()
+ print('\nPlease navigate to the title screen to find cursor.')
+ if not utils.prompt_yes_no('Proceed?', true) then
+ return false
+ end
+
+ -- Unpadded version
+ local idx, addr = data.int32_t:find_one{
+ -30000, -30000, -30000,
+ -30000, -30000, -30000, -30000, -30000, -30000,
+ df.game_mode.NONE, df.game_type.NONE
+ }
+ if idx then
+ ms.found_offset('cursor', addr)
+ ms.found_offset('selection_rect', addr + 12)
+ ms.found_offset('gamemode', addr + 12 + 24)
+ ms.found_offset('gametype', addr + 12 + 24 + 4)
+ return true
+ end
+
+ -- Padded version
+ idx, addr = data.int32_t:find_one{
+ -30000, -30000, -30000, 0,
+ -30000, -30000, -30000, -30000, -30000, -30000, 0, 0,
+ df.game_mode.NONE, 0, 0, 0, df.game_type.NONE
+ }
+ if idx then
+ ms.found_offset('cursor', addr)
+ ms.found_offset('selection_rect', addr + 0x10)
+ ms.found_offset('gamemode', addr + 0x30)
+ ms.found_offset('gametype', addr + 0x40)
+ return true
+ end
+
+ dfhack.printerr('Could not find cursor.')
+ return false
+end
+
+exec_finder(find_cursor, { 'cursor', 'selection_rect', 'gamemode', 'gametype' })
+
+--
+-- Announcements
+--
+
+local function find_announcements()
+ local idx, addr = data.int32_t:find_one{
+ 25, 25, 31, 31, 24, 24, 40, 40, 40, 40, 40, 40, 40
+ }
+ if idx then
+ ms.found_offset('announcements', addr)
+ return
+ end
+
+ dfhack.printerr('Could not find announcements.')
+end
+
+exec_finder(find_announcements, 'announcements')
+
+--
+-- d_init
+--
+
+local function is_valid_d_init(di)
+ if di.sky_tile ~= 178 then
+ print('Sky tile expected 178, found: '..di.sky_tile)
+ if not utils.prompt_yes_no('Ignore?') then
+ return false
+ end
+ end
+
+ local ann = is_known 'announcements'
+ local size,ptr = di:sizeof()
+ if ann and ptr+size ~= ann then
+ print('Announcements not immediately after d_init.')
+ if not utils.prompt_yes_no('Ignore?') then
+ return false
+ end
+ end
+
+ return true
+end
+
+local function find_d_init()
+ local idx, addr = data.int16_t:find_one{
+ 1,0, 2,0, 5,0, 25,0, -- path_cost
+ 4,4, -- embark_rect
+ 20,1000,1000,1000,1000 -- store_dist
+ }
+ if idx then
+ validate_offset('d_init', is_valid_d_init, addr, df.d_init, 'path_cost')
+ return
+ end
+
+ dfhack.printerr('Could not find d_init')
+end
+
+exec_finder(find_d_init, 'd_init')
+
+--
+-- gview
+--
+
+local function find_gview()
+ local vs_vtable = dfhack.internal.getVTable('viewscreenst')
+ if not vs_vtable then
+ dfhack.printerr('Cannot search for gview - no viewscreenst vtable.')
+ return
+ end
+
+ local idx, addr = data.uint32_t:find_one{0, vs_vtable}
+ if idx then
+ ms.found_offset('gview', addr)
+ return
+ end
+
+ dfhack.printerr('Could not find gview')
+end
+
+exec_finder(find_gview, 'gview')
+
+--
+-- World
+--
+
+local function is_valid_world(world)
+ if not ms.is_valid_vector(world.units.all, 4)
+ or not ms.is_valid_vector(world.units.bad, 4)
+ or not ms.is_valid_vector(world.history.figures, 4)
+ or not ms.is_valid_vector(world.cur_savegame.map_features, 4)
+ then
+ dfhack.printerr('Vector layout check failed.')
+ return false
+ end
+
+ if #world.units.all == 0 or #world.units.all ~= #world.units.bad then
+ print('Different or zero size of units.all and units.bad:'..#world.units.all..' vs '..#world.units.bad)
+ if not utils.prompt_yes_no('Ignore?') then
+ return false
+ end
+ end
+
+ return true
+end
+
+local function find_world()
+ local addr = searcher:find_menu_cursor([[
+Searching for world. Please open the stockpile creation
+menu, and select different types as instructed below:]],
+ 'int32_t',
+ { 'Corpses', 'Refuse', 'Stone', 'Wood', 'Gems', 'Bars', 'Cloth', 'Leather', 'Ammo', 'Coins' },
+ df.stockpile_category
+ )
+ validate_offset('world', is_valid_world, addr, df.world, 'selected_stockpile_type')
+end
+
+exec_finder(find_world, 'world')
+
+--
+-- UI
+--
+
+local function is_valid_ui(ui)
+ if not ms.is_valid_vector(ui.economic_stone, 1)
+ or not ms.is_valid_vector(ui.dipscripts, 4)
+ then
+ dfhack.printerr('Vector layout check failed.')
+ return false
+ end
+
+ if ui.follow_item ~= -1 or ui.follow_unit ~= -1 then
+ print('Invalid follow state: '..ui.follow_item..', '..ui.follow_unit)
+ return false
+ end
+
+ return true
+end
+
+local function find_ui()
+ local addr = searcher:find_menu_cursor([[
+Searching for ui. Please open the designation
+menu, and switch modes as instructed below:]],
+ 'int16_t',
+ { 'DesignateMine', 'DesignateChannel', 'DesignateRemoveRamps', 'DesignateUpStair',
+ 'DesignateDownStair', 'DesignateUpDownStair', 'DesignateUpRamp', 'DesignateChopTrees' },
+ df.ui_sidebar_mode
+ )
+ validate_offset('ui', is_valid_ui, addr, df.ui, 'main', 'mode')
+end
+
+exec_finder(find_ui, 'ui')
+
+--
+-- ui_sidebar_menus
+--
+
+local function is_valid_ui_sidebar_menus(usm)
+ if not ms.is_valid_vector(usm.workshop_job.choices_all, 4)
+ or not ms.is_valid_vector(usm.workshop_job.choices_visible, 4)
+ then
+ dfhack.printerr('Vector layout check failed.')
+ return false
+ end
+
+ if #usm.workshop_job.choices_all == 0
+ or #usm.workshop_job.choices_all ~= #usm.workshop_job.choices_visible then
+ print('Different or zero size of visible and all choices:'..
+ #usm.workshop_job.choices_all..' vs '..#usm.workshop_job.choices_visible)
+ if not utils.prompt_yes_no('Ignore?') then
+ return false
+ end
+ end
+
+ return true
+end
+
+local function find_ui_sidebar_menus()
+ local addr = searcher:find_menu_cursor([[
+Searching for ui_sidebar_menus. Please open the add job
+ui of Mason, Craftsdwarfs, or Carpenters workshop, and
+select entries in the list:]],
+ 'int32_t',
+ { 0, 1, 2, 3, 4, 5, 6 },
+ ordinal_names
+ )
+ validate_offset('ui_sidebar_menus', is_valid_ui_sidebar_menus,
+ addr, df.ui_sidebar_menus, 'workshop_job', 'cursor')
+end
+
+exec_finder(find_ui_sidebar_menus, 'ui_sidebar_menus')
+
+--
+-- ui_build_selector
+--
+
+local function is_valid_ui_build_selector(ubs)
+ if not ms.is_valid_vector(ubs.requirements, 4)
+ or not ms.is_valid_vector(ubs.choices, 4)
+ then
+ dfhack.printerr('Vector layout check failed.')
+ return false
+ end
+
+ if ubs.building_type ~= df.building_type.Trap
+ or ubs.building_subtype ~= df.trap_type.PressurePlate then
+ print('Invalid building type and subtype:'..ubs.building_type..','..ubs.building_subtype)
+ return false
+ end
+
+ return true
+end
+
+local function find_ui_build_selector()
+ local addr = searcher:find_menu_cursor([[
+Searching for ui_build_selector. Please start constructing
+a pressure plate, and enable creatures. Then change the min
+weight as requested, remembering that the ui truncates the
+number, so when it shows "Min (5000df", it means 50000:]],
+ 'int32_t',
+ { 50000, 49000, 48000, 47000, 46000, 45000, 44000 }
+ )
+ validate_offset('ui_build_selector', is_valid_ui_build_selector,
+ addr, df.ui_build_selector, 'plate_info', 'unit_min')
+end
+
+exec_finder(find_ui_build_selector, 'ui_build_selector')
+
+--
+-- ui_selected_unit
+--
+
+local function find_ui_selected_unit()
+ if not is_known 'world' then
+ dfhack.printerr('Cannot search for ui_selected_unit: no world')
+ return
+ end
+
+ for i,unit in ipairs(df.global.world.units.active) do
+ dfhack.units.setNickname(unit, i)
+ end
+
+ local addr = searcher:find_menu_cursor([[
+Searching for ui_selected_unit. Please activate the 'v'
+mode, point it at units, and enter their numeric nickname
+into the prompts below:]],
+ 'int32_t',
+ function()
+ return utils.prompt_input(' Enter index: ', utils.check_number)
+ end,
+ 'noprompt'
+ )
+ ms.found_offset('ui_selected_unit', addr)
+end
+
+exec_finder(find_ui_selected_unit, 'ui_selected_unit')
+
+--
+-- ui_unit_view_mode
+--
+
+local function find_ui_unit_view_mode()
+ local addr = searcher:find_menu_cursor([[
+Searching for ui_unit_view_mode. Having selected a unit
+with 'v', switch the pages as requested:]],
+ 'int32_t',
+ { 'General', 'Inventory', 'Preferences', 'Wounds' },
+ df.ui_unit_view_mode.T_value
+ )
+ ms.found_offset('ui_unit_view_mode', addr)
+end
+
+exec_finder(find_ui_unit_view_mode, 'ui_unit_view_mode')
+
+--
+-- ui_look_cursor
+--
+
+local function look_item_list_count()
+ return #df.global.ui_look_list.items
+end
+
+local function find_ui_look_cursor()
+ local addr = searcher:find_menu_cursor([[
+Searching for ui_look_cursor. Please activate the 'k'
+mode, find a tile with many items or units on the ground,
+and select list entries as instructed:]],
+ 'int32_t',
+ list_index_choices(look_item_list_count),
+ ordinal_names
+ )
+ ms.found_offset('ui_look_cursor', addr)
+end
+
+exec_finder(find_ui_look_cursor, 'ui_look_cursor')
+
+--
+-- ui_building_item_cursor
+--
+
+local function building_item_list_count()
+ return #df.global.world.selected_building.contained_items
+end
+
+local function find_ui_building_item_cursor()
+ local addr = searcher:find_menu_cursor([[
+Searching for ui_building_item_cursor. Please activate the 't'
+mode, find a cluttered workshop, trade depot, or other building
+with many contained items, and select as instructed:]],
+ 'int32_t',
+ list_index_choices(building_item_list_count),
+ ordinal_names
+ )
+ ms.found_offset('ui_building_item_cursor', addr)
+end
+
+exec_finder(find_ui_building_item_cursor, 'ui_building_item_cursor')
+
+--
+-- ui_workshop_in_add
+--
+
+local function find_ui_workshop_in_add()
+ local addr = searcher:find_menu_cursor([[
+Searching for ui_workshop_in_add. Please activate the 'q'
+mode, find a workshop without jobs (or delete jobs),
+and do as instructed below.
+
+NOTE: After first 3 steps resize the game window.]],
+ 'int8_t',
+ { 1, 0 },
+ { [1] = 'enter the add job menu',
+ [0] = 'add job, thus exiting the menu' }
+ )
+ ms.found_offset('ui_workshop_in_add', addr)
+end
+
+exec_finder(find_ui_workshop_in_add, 'ui_workshop_in_add')
+
+--
+-- ui_workshop_job_cursor
+--
+
+local function workshop_job_list_count()
+ return #df.global.world.selected_building.jobs
+end
+
+local function find_ui_workshop_job_cursor()
+ local addr = searcher:find_menu_cursor([[
+Searching for ui_workshop_job_cursor. Please activate the 'q'
+mode, find a workshop with many jobs, and select as instructed:]],
+ 'int32_t',
+ list_index_choices(workshop_job_list_count),
+ ordinal_names
+ )
+ ms.found_offset('ui_workshop_job_cursor', addr)
+end
+
+exec_finder(find_ui_workshop_job_cursor, 'ui_workshop_job_cursor')
+
+--
+-- ui_building_in_assign
+--
+
+local function find_ui_building_in_assign()
+ local addr = searcher:find_menu_cursor([[
+Searching for ui_building_in_assign. Please activate
+the 'q' mode, select a room building (e.g. a bedroom)
+and do as instructed below.
+
+NOTE: After first 3 steps resize the game window.]],
+ 'int8_t',
+ { 1, 0 },
+ { [1] = 'enter the Assign owner menu',
+ [0] = 'press Esc to exit assign' }
+ )
+ ms.found_offset('ui_building_in_assign', addr)
+end
+
+exec_finder(find_ui_building_in_assign, 'ui_building_in_assign')
+
+--
+-- ui_building_in_resize
+--
+
+local function find_ui_building_in_resize()
+ local addr = searcher:find_menu_cursor([[
+Searching for ui_building_in_resize. Please activate
+the 'q' mode, select a room building (e.g. a bedroom)
+and do as instructed below.
+
+NOTE: After first 3 steps resize the game window.]],
+ 'int8_t',
+ { 1, 0 },
+ { [1] = 'enter the Resize room mode',
+ [0] = 'press Esc to exit resize' }
+ )
+ ms.found_offset('ui_building_in_resize', addr)
+end
+
+exec_finder(find_ui_building_in_resize, 'ui_building_in_resize')
+
+
+--
+-- window_x
+--
+
+local function find_window_x()
+ local addr = searcher:find_counter([[
+Searching for window_x. Please exit to main dwarfmode menu,
+scroll to the LEFT edge, then do as instructed:]],
+ 'int32_t', 10,
+ 'Please press Right to scroll right one step.'
+ )
+ ms.found_offset('window_x', addr)
+end
+
+exec_finder(find_window_x, 'window_x')
+
+--
+-- window_y
+--
+
+local function find_window_y()
+ local addr = searcher:find_counter([[
+Searching for window_y. Please exit to main dwarfmode menu,
+scroll to the TOP edge, then do as instructed:]],
+ 'int32_t', 10,
+ 'Please press Down to scroll down one step.'
+ )
+ ms.found_offset('window_y', addr)
+end
+
+exec_finder(find_window_y, 'window_y')
+
+--
+-- window_z
+--
+
+local function find_window_z()
+ local addr = searcher:find_counter([[
+Searching for window_z. Please exit to main dwarfmode menu,
+scroll to ground level, then do as instructed below.
+
+NOTE: After first 3 steps resize the game window.]],
+ 'int32_t', -1,
+ "Please press '>' to scroll one Z level down."
+ )
+ ms.found_offset('window_z', addr)
+end
+
+exec_finder(find_window_z, 'window_z')
+
+--
+-- THE END
+--
+
+print('Done.')
+searcher:reset()
diff --git a/scripts/devel/nuke-items.lua b/scripts/devel/nuke-items.lua
new file mode 100644
index 00000000..aa3f5b84
--- /dev/null
+++ b/scripts/devel/nuke-items.lua
@@ -0,0 +1,16 @@
+-- Deletes ALL items not held by units, buildings or jobs.
+--
+-- Intended solely for lag investigation.
+
+local count = 0
+
+for _,v in ipairs(df.global.world.items.all) do
+ if not (v.flags.in_building or v.flags.construction or v.flags.in_job
+ or dfhack.items.getGeneralRef(v,df.general_ref_type.UNIT_HOLDER)) then
+ count = count + 1
+ v.flags.forbid = true
+ v.flags.garbage_collect = true
+ end
+end
+
+print('Deletion requested: '..count)
diff --git a/scripts/fix/dead-units.lua b/scripts/fix/dead-units.lua
new file mode 100644
index 00000000..2d555817
--- /dev/null
+++ b/scripts/fix/dead-units.lua
@@ -0,0 +1,29 @@
+-- Remove uninteresting dead units from the unit list.
+
+local units = df.global.world.units.active
+local dwarf_race = df.global.ui.race_id
+local dwarf_civ = df.global.ui.civ_id
+local count = 0
+
+for i=#units-1,0,-1 do
+ local unit = units[i]
+ local flags1 = unit.flags1
+ local flags2 = unit.flags2
+ if flags1.dead and unit.race ~= dwarf_race then
+ local remove = false
+ if flags2.slaughter then
+ remove = true
+ elseif not unit.name.has_name then
+ remove = true
+ elseif unit.civ_id ~= dwarf_civ and
+ not (flags1.merchant or flags1.diplomat) then
+ remove = true
+ end
+ if remove then
+ count = count + 1
+ units:erase(i)
+ end
+ end
+end
+
+print('Units removed from active: '..count)
diff --git a/scripts/fix/fat-dwarves.lua b/scripts/fix/fat-dwarves.lua
new file mode 100644
index 00000000..5394f677
--- /dev/null
+++ b/scripts/fix/fat-dwarves.lua
@@ -0,0 +1,24 @@
+-- Makes fat dwarves non-fat.
+--
+-- See for more info:
+-- http://www.bay12games.com/dwarves/mantisbt/view.php?id=5971
+
+local num_fat = 0
+local num_lagging = 0
+
+for _,v in ipairs(df.global.world.units.all) do
+ local fat = v.counters2.stored_fat
+ if fat > 850000 then
+ v.counters2.stored_fat = 500000
+ if v.race == df.global.ui.race_id then
+ print(fat,dfhack.TranslateName(dfhack.units.getVisibleName(v)))
+ num_fat = num_fat + 1
+ if fat > 999990 then
+ num_lagging = num_lagging + 1
+ end
+ end
+ end
+end
+
+print("Fat dwarves cured: "..num_fat)
+print("Lag sources: "..num_lagging)
diff --git a/scripts/fix/stable-temp.lua b/scripts/fix/stable-temp.lua
new file mode 100644
index 00000000..d06d0fcc
--- /dev/null
+++ b/scripts/fix/stable-temp.lua
@@ -0,0 +1,49 @@
+-- Reset item temperature to the value of their tile.
+
+local count = 0
+local types = {}
+
+local function update_temp(item,btemp)
+ if item.temperature ~= btemp then
+ count = count + 1
+ local tid = item:getType()
+ types[tid] = (types[tid] or 0) + 1
+ end
+ item.temperature = btemp
+ item.temperature_fraction = 0
+
+ if item.contaminants then
+ for _,c in ipairs(item.contaminants) do
+ c.temperature = btemp
+ c.temperature_fraction = 0
+ end
+ end
+
+ for _,sub in ipairs(dfhack.items.getContainedItems(item)) do
+ update_temp(sub,btemp)
+ end
+
+ item:checkTemperatureDamage()
+end
+
+local last_frame = df.global.world.frame_counter-1
+
+for _,item in ipairs(df.global.world.items.all) do
+ if item.flags.on_ground and df.item_actual:is_instance(item) and
+ item.temp_updated_frame == last_frame then
+ local pos = item.pos
+ local block = dfhack.maps.getTileBlock(pos)
+ if block then
+ update_temp(item, block.temperature_1[pos.x%16][pos.y%16])
+ end
+ end
+end
+
+print('Items updated: '..count)
+
+local tlist = {}
+for k,_ in pairs(types) do tlist[#tlist+1] = k end
+table.sort(tlist, function(a,b) return types[a] > types[b] end)
+for _,k in ipairs(tlist) do
+ print(' '..df.item_type[k]..':', types[k])
+end
diff --git a/scripts/putontable.lua b/scripts/putontable.lua
index 8156a54b..5002c8e6 100644
--- a/scripts/putontable.lua
+++ b/scripts/putontable.lua
@@ -23,5 +23,5 @@ if #items==0 then
error("No items found!")
end
for k,v in pairs(items) do
- dfhack.items.moveToBuilding(v,build,1)
+ dfhack.items.moveToBuilding(v,build,0)
end