Merge remote branch 'upstream/master'

develop
doomchild 2011-03-01 10:53:39 -06:00
commit 5d44146408
60 changed files with 5569 additions and 5076 deletions

2
.gitignore vendored

@ -29,5 +29,5 @@ dfhack/python/pydfhack/_pydfhack.so
dfhack/python/PyDFHack.egg-info dfhack/python/PyDFHack.egg-info
dfhack/python/build dfhack/python/build
dfhack/python/dist dfhack/python/dist
/cmakeall.bat /cmakeall.bat

@ -2,7 +2,7 @@
PROJECT (dfhack) PROJECT (dfhack)
cmake_minimum_required(VERSION 2.6) cmake_minimum_required(VERSION 2.6)
SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMake/Modules) SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMake/Modules)
SET ( DFHACK_VERSION "0.4.1.0-dev" ) SET ( DFHACK_VERSION "0.5.2" )
# Set this to project source dir. When dfhack is used # Set this to project source dir. When dfhack is used
# as a submodule, CMAKE_SOURCE_DIR is not pointing to # as a submodule, CMAKE_SOURCE_DIR is not pointing to
@ -36,9 +36,6 @@ OPTION(BUILD_DFHACK_SHM "Build the SHM." OFF)
include_directories (${CMAKE_SOURCE_DIR}/library/include/) include_directories (${CMAKE_SOURCE_DIR}/library/include/)
include_directories (${CMAKE_SOURCE_DIR}/library/shm/) include_directories (${CMAKE_SOURCE_DIR}/library/shm/)
include_directories (${CMAKE_SOURCE_DIR}/library/depends/md5/)
include_directories (${CMAKE_SOURCE_DIR}/library/depends/tinyxml/)
include_directories (${CMAKE_SOURCE_DIR}/library/depends/argstream/)
add_subdirectory (library) add_subdirectory (library)

@ -1,3 +1,5 @@
----------------------------------------------------------------------
License of dfhack
github.com/peterix/dfhack github.com/peterix/dfhack
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
@ -19,3 +21,120 @@ must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source 3. This notice may not be removed or altered from any source
distribution. distribution.
----------------------------------------------------------------------
License of library/include/dfhack/DFstdint_win.h
ISO C9x compliant stdint.h for Microsoft Visual Studio
Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
Copyright (c) 2006-2008 Alexander Chemeris
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------
License of argstream (used by some utilities for parsing command line params)
Copyright (C) 2004 Xavier Decoret <Xavier.Decoret@imag.fr>
argsteam is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Foobar 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
This library is used by:
tools/playground/catsplosion.cpp
tools/playground/digger.cpp
tools/supported/vdig.cpp
----------------------------------------------------------------------------
License of "RSA Data Security, Inc. MD5 Message-Digest Algorithm"
Used to identify DF binaries.
Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.
License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.
License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this
documentation and/or software.
-----------------------------------------------------------------
License of the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" C++ wrapper:
This is my wrapper-class to create
a MD5 Hash from a string and a file.
This code is completly free, you
can copy it, modify it, or do
what ever you want with it.
Feb. 2005
Benjamin Grüdelbach
------------------------------------------------------------------
License of the used XML reader library
www.sourceforge.net/projects/tinyxml
Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.

@ -1,5 +1,5 @@
mkdir build-real mkdir build-real
cd build-real cd build-real
cmake ..\.. -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE:string=Debug cmake ..\.. -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE:string=Debug
mingw32-make mingw32-make
pause pause

@ -1,4 +1,4 @@
mkdir build-real mkdir build-real
cd build-real cd build-real
cmake ..\.. -G"Visual Studio 8 2005" cmake ..\.. -G"Visual Studio 8 2005"
pause pause

@ -1,4 +1,4 @@
mkdir build-real mkdir build-real
cd build-real cd build-real
cmake ..\.. -G"Visual Studio 9 2008" cmake ..\.. -G"Visual Studio 9 2008"
pause pause

@ -665,6 +665,9 @@
<class name="building_actualst"/> <class name="building_actualst"/>
<class name="buildingst"/> <class name="buildingst"/>
<class name="building_slabst" /> <class name="building_slabst" />
<class name="building_nestst" />
<class name="building_nest_boxst" />
<class name="building_hivest" />
</VTable> </VTable>
<Offsets> <Offsets>
<Address name="WORLD" description="A huge object that many things in DF are part of. Contains the whole game and can be used as a base <Address name="WORLD" description="A huge object that many things in DF are part of. Contains the whole game and can be used as a base
@ -727,6 +730,7 @@
<Address name="world_size_y"/> <Address name="world_size_y"/>
<Group name="block" description="The map block structure."> <Group name="block" description="The map block structure.">
<Offset name="vein_vector" description="Mineral veins, objects holding tile types under ice, etc..."/> <Offset name="vein_vector" description="Mineral veins, objects holding tile types under ice, etc..."/>
<Offset name="vegetation_vector" description="All the plants in this block, including farm plats."/>
<Offset name="type" description="16x16 x 2B tile type values"/> <Offset name="type" description="16x16 x 2B tile type values"/>
<Offset name="designation" description="16x16 * 4B designation fields"/> <Offset name="designation" description="16x16 * 4B designation fields"/>
<Offset name="occupancy" description="16x16 * 4B occupancy fields" /> <Offset name="occupancy" description="16x16 * 4B occupancy fields" />
@ -903,6 +907,7 @@
<Address name="current_tick" description="Current time of the year" /> <Address name="current_tick" description="Current time of the year" />
<Address name="current_year" description="Current year" /> <Address name="current_year" description="Current year" />
<Address name="current_weather" description="5x5 array of bytes for surrounding biomes. For each: 0=clear, 1=raining, 2=snowing." /> <Address name="current_weather" description="5x5 array of bytes for surrounding biomes. For each: 0=clear, 1=raining, 2=snowing." />
<Address name="game_mode" description="Current game mode" />
</Group> </Group>
</Offsets> </Offsets>
</Base> </Base>
@ -994,6 +999,7 @@
<Address name="world_size_y" value="0x016AEDD6" /> <Address name="world_size_y" value="0x016AEDD6" />
<Group name="block"> <Group name="block">
<Offset name="vein_vector" value="0x08" /> <Offset name="vein_vector" value="0x08" />
<Offset name="vegetation_vector" value="0x0078" />
<Offset name="type" value="0x009A" /> <Offset name="type" value="0x009A" />
<Offset name="designation" value="0x029C" /> <Offset name="designation" value="0x029C" />
<Offset name="occupancy" value="0x069c" /> <Offset name="occupancy" value="0x069c" />
@ -1551,6 +1557,7 @@
<Offset name="vein_vector" value="0x08" /> <Offset name="vein_vector" value="0x08" />
<Offset name="feature_local" value="0x24" /> <Offset name="feature_local" value="0x24" />
<Offset name="feature_global" value="0x28" /> <Offset name="feature_global" value="0x28" />
<Offset name="vegetation_vector" value="0x60" />
<Offset name="type" value="0x7A" /> <Offset name="type" value="0x7A" />
<Offset name="designation" value="0x27C" /> <Offset name="designation" value="0x27C" />
<Offset name="occupancy" value="0x67C" /> <Offset name="occupancy" value="0x67C" />
@ -1871,6 +1878,189 @@
</Group> </Group>
</Offsets> </Offsets>
</Version>
<Version name="v0.31.19 SDL" os="windows" base="v0.31.18 SDL">
<PETimeStamp value="0x4D5BC345" />
<MD5 value="b7d1ff74835e5e8220a7e81e31f5a28d" />
<Offsets valid="false">
<Group name="string" valid="true" />
<Group name="vector" valid="true" />
<Group name="name" valid="true" />
<Group name="Position" valid="false">
<Address name="cursor_xyz" value="0xb4a7f0" valid="true" />
<Address name="window_dims" value="0xb4b07c" valid="true" />
<Address name="window_x" value="0xec6984" valid="true" />
<Address name="window_y" value="0xef4ba0" valid="true" />
<Address name="window_z" value="0xef4b7c" valid="true" />
</Group>
<Group name="Creatures" valid="true">
<Address name="current_civ" value="0x01506c1C" /> maybe
<Address name="current_race" value="0x01506c28" />
<Address name="vector" value="0x016da47C" />
<Group name="creature">
<Offset name="name" value="0x0" />
<Offset name="custom_profession" value="0x6c" /> OK
<Offset name="profession" value="0x88" />
<Offset name="race" value="0x8c" />
<Offset name="position" value="0x90" />
<Offset name="flags1" value="0xe0" />
<Offset name="flags2" value="0xe4" />
<Offset name="caste" value="0xf8" /> maybe
<Offset name="sex" value="0xfa" /> maybe
<Offset name="id" value="0xfc" />
<Offset name="civ" value="0x108" />
<Group name="advanced">
<Offset name="pickup_equipment_bit" value="0x1bc" />
<Offset name="mood" value="0x210" />OK
<Offset name="pregnancy" value="0x214" /> maybe?
<Offset name="pregnancy_ptr" value="0x218" /> maybe?
<Offset name="birth_year" value="0x224" />
<Offset name="birth_time" value="0x228" />
<Offset name="inventory_vector" value="0x288" />
<Offset name="current_job" value="0x2f4" />
<Offset name="current_job_skill" value="0x2f8" /> THIS IS TOTAL BS
<Offset name="physical" value="0x3cc" /> DT: 0x3C4, I don't believe that's OK'
<Offset name="appearance_vector" value="0x52C" /> DT calls it 'states' it seems
<Offset name="artifact_name" value="0x5E0" /> maybe
<Offset name="soul_vector" value="0x64c" />
<Offset name="current_soul" value="0x65c" />
<Offset name="labors" value="0x670" />
<Offset name="happiness" value="0x710" />
</Group>
</Group>
<Group name="job" valid="false">
<Offset name="id" value="0x8" valid="true" />
<Offset name="materials_vector" /> MISSING!
<Offset name="type" /> MISSING!
<Group name="material">
<Offset name="flags" /> MISSING!
<Offset name="maintype" /> MISSING!
<Offset name="sectype1" /> MISSING!
<Offset name="sectype2" /> MISSING!
<Offset name="sectype3" /> MISSING!
</Group>
</Group>
<Group name="soul" valid="true">
<Offset name="mental" value="0x88" />
<Offset name="name" value="0x4" />
<Offset name="skills_vector" value="0x1f4" /> DT: 0x1F0
<Offset name="traits" value="0x214" /> DT: 0x214
</Group>
</Group>
<Group name="Maps" valid="true">
<Address name="map_data" value="0x171C128" />
<Address name="x_count_block" value="0x171c140" />
<Address name="y_count_block" value="0x171c144" />
<Address name="z_count_block" value="0x171c148" />
<Address name="x_count" value="0x171c14C" />
<Address name="y_count" value="0x171c150" />
<Address name="z_count" value="0x171c154" />
<Address name="region_x" value="0x171c158" />
<Address name="region_y" value="0x171c15C" />
<Address name="region_z" value="0x171c160" /> TODO: it's signed!
<Address name="world_size_x" value="0x171d7e4" />
<Address name="world_size_y" value="0x171d7e6" />
<Group name="block">
<Offset name="vein_vector" value="0x8" />
<Offset name="feature_global" value="0x28" />
<Offset name="feature_local" value="0x24" />
<Offset name="type" value="0x7a" />
<Offset name="designation" value="0x27c" />
<Offset name="occupancy" value="0x67c" />
<Offset name="pathfinding" value="0xd7c" />
<Offset name="temperature1" value="0x157c" />
<Offset name="temperature2" value="0x177c" /><!-- looks strange, possibly invalid -->
<Offset name="biome_stuffs" value="0x1d7c" /><!-- possibly invalid -->
</Group>
<Group name="features">
<Group name="global">
<Address name="vector" value="0x171de84" />
<Offset name="funcptr" value="0xd8" />
<Offset name="material" value="0x2c" />
<Offset name="submaterial" value="0x30" />
</Group>
<Group name="local">
<Address name="start_ptr" value="0x171df18" />
<Offset name="material" value="0x28" />
<Offset name="submaterial" value="0x2c" />
</Group>
</Group>
<Group name="geology"> LOOKS FINE?
<Address name="geoblock_vector" value="0x171dea4" />
<Address name="ptr2_region_array" value="0x171ded4" /> VERIFIED
<Offset name="geolayer_geoblock_offset" value="0x4" />
<Offset name="region_geo_index_off" value="0x58" /> VERIFIED
<Offset name="type_inside_geolayer" value="0x4" />
<HexValue name="region_size" value="0x5c" /> VERIFIED
</Group>
</Group>
<Group name="Materials" valid="false" >
<Address name="creature_type_vector" value="0x171e880" valid="true" />
<Address name="inorganics" value="0x171e790" valid="true" />
<Address name="organics_all" value="0x171e7B0" valid="true" />
<Address name="organics_plants" value="0x171e7C0" valid="true" />
<Address name="organics_trees" value="0x171e7E0" valid="true" />
<Group name="creature" valid="true">
<Offset name="extract_vector" value="0x2054" /> maybe: 0x2064 before:0x1a08
<Group name="caste" valid="true">
<Offset name="attributes" value="0xd40 0x5fc" /> looks kinda funny, same as in .16
<Offset name="bodypart_vector" value="0x530 0x51c" />OK
<Offset name="color_modifiers" value="0x1168 0xa24" /> OK
</Group>
</Group>
<Group name="descriptors" valid="true">
<Address name="all_colors_vector" value="0x17241e4" />
<Address name="colors_vector" value="0x17241c4" />
</Group>
</Group>
<Group name="GUI" valid="false">
<Address name="pause_state" value="0x14c9be1" valid="true" />
</Group>
<Group name="Vegetation" valid="true">
<Address name="vector" value="0x16db478" />
</Group>
<Group name="Constructions" valid="true">
<Address name="vector" value="0x16c6ae8" />
</Group>
<Group name="Buildings" valid="true">
<Address name="buildings_vector" value="0x16dad78" valid="true" />
One of those really. Which one is the right one?
0x16dad78
0x16dad88
<Address name="custom_workshop_vector" value="0x1724204" valid="true" />
</Group>
<Group name="World" valid="true">
<Address name="current_tick" value="0xe2e180" /> maybe
<Address name="current_year" value="0xf0d268" />
<Address name="current_weather" value="0x15027A0" />
<Address name="game_mode" value="0xb4a814" />
fortress = 0, adventure = 1, arena = 0, menu and legends = 3 0xb4a814
Game mode: 0xb4a818 . fortress = 0, adventure = 1, arena = 4
0xe2e2a2 seems to be a copy of the first one
</Group>
<Group name="Translations" valid="true">
<Address name="language_vector" value="0x0171ea94 0x016e553c"/>
<Address name="translation_vector" value="0x0171eab4 0x016e551c"/>
</Group>
<!--
<Group name="Items" valid="false">
<Address name="items_vector" />
List of offsets in the VTable :
<Offset name="item_type_accessor" />
<Offset name="item_subtype_accessor" />
<Offset name="item_subindex_accessor" />
<Offset name="item_index_accessor" />
<Offset name="item_quality_accessor" />
<Offset name="item_improvement_vector" />
<Offset name="item_improvement_subindex" />
<Offset name="item_improvement_index" />
<Offset name="item_improvement_quality" />
<Offset name="item_type_accessor" /> (in the vtable)
</Group>
-->
</Offsets>
</Version> </Version>
.-"""-. .-"""-.
' \ ' \
@ -1943,6 +2133,7 @@
<Offset name="vein_vector" value="0x08" /> <Offset name="vein_vector" value="0x08" />
<Offset name="feature_local" value="0x20" /> <Offset name="feature_local" value="0x20" />
<Offset name="feature_global" value="0x24" /> <Offset name="feature_global" value="0x24" />
<Offset name="vegetation_vector" value="0x54" />
<Offset name="type" value="0x006A" /> <Offset name="type" value="0x006A" />
<Offset name="designation" value="0x026C" /> <Offset name="designation" value="0x026C" />
<Offset name="occupancy" value="0x066c" /> <Offset name="occupancy" value="0x066c" />
@ -2329,6 +2520,115 @@
</Group> </Group>
</Offsets> </Offsets>
</Version> </Version>
<Version name="v0.31.18 linux" os="linux" base="faek">
<MD5 value="884f794d8e89e7d764057c15617c20b1" />
<Offsets valid="false">
<Group name="Maps" valid="true">
<Address name="map_data" value="0x93c0430" />
<Address name="region_x" value="0x93c045c" />
<Address name="region_y" value="0x93c0460" />
<Address name="region_z" value="0x93c0464" />
<Address name="x_count_block" value="0x93c0444" />
<Address name="y_count_block" value="0x93c0448" />
<Address name="z_count_block" value="0x93c044c" />
<Address name="x_count" value="0x93c0450" />
<Address name="y_count" value="0x93c0454" />
<Address name="z_count" value="0x93c0458" />
<Address name="world_size_x" value="0x93c1ab0" />
<Address name="world_size_y" value="0x93c1ab2" />
<Group name="block" valid="true" />
<Group name="features" valid="false" />
<Group name="geology" valid="false" />
</Group>
<Group name="GUI">
<Address name="pause_state" value="0x9366cb0"/>
</Group>
<Group name="string" valid="true" />
<Group name="vector" valid="true" />
<Group name="name" valid="true" />
</Offsets>
</Version>
<Version name="v0.31.19 linux" os="linux" base="v0.31.18 linux" rebase="0x77738">
<MD5 value="8fcb1f10af9cc2bda47d2acf42e1db54" />
<Offsets>
<Group name="Position">
<Address name="cursor_xyz" value="0x8c2b560" />
<Address name="window_dims" value="0x8ba9680" />
<Address name="window_x" value="0x8d65c70" />
<Address name="window_y" value="0x8d65c74" />
<Address name="window_z" value="0x8d65c78" />
</Group>
<Group name="Maps">
<Address name="world_size_x" value="0x94391f0" />
<Address name="world_size_y" value="0x94391f2" />
<Group name="features" valid="true">
<Group name="global">
<Address name="vector" value="0x94397cc" />
<!--
<Offset name="funcptr" value="0x94" />
<Offset name="material" value="0x28" />
<Offset name="submaterial" value="0x2c" />
-->
</Group>
<Group name="local">
<Address name="start_ptr" value="0x9439844" />
<!--
<Offset name="material" value="0x24" />
<Offset name="submaterial" value="0x28" />
-->
</Group>
</Group>
<Group name="geology" valid="true">
<Address name="geoblock_vector" value="0x94397e4" />
<Address name="ptr2_region_array" value="0x9439808" />
</Group>
</Group>
<Group name="GUI">
<Address name="pause_state" value="0x93dddf0" />
</Group>
<Group name="Materials">
<Address name="creature_type_vector" value="0x943a074" />
<Address name="inorganics" value="0x9439fc0" />
<Address name="organics_all" value="0x9439fd8" />
<Address name="organics_plants" value="0x9439fe4" />
<Address name="organics_trees" value="0x9439ffc" />
-------------------
!!LANGUAGE TABLES!!
-------------------
translation vector: 0x943a21c
lang vector: 0x943a204
word table offset: 0x1c
-------------
!!MATERIALS!!
-------------
inorganics:
0x9439fc0
organics:
0x9439fd8
organics 31.19:
trees:
0x9439ffc
plants:
0x9439fe4
color descriptors:
0x943e368
Amber color:0xa380190
all descriptors:
0x943e380
toad-first creature types:
0x943a074
all creature types:
0x943a068
0x943a074
Toad: 0xa441f10
Toad: rawname = 0x0
Toad: character (not reliable) = 0x20
Toad: caste vector = 0x60
Toad: extract? vector = 0x1f74
Toad: colors = 0x38
</Group>
</Offsets>
</Version>
</DFHack> </DFHack>

@ -29,6 +29,10 @@ include_directories (${CMAKE_SOURCE_DIR}/library/private/)
SET(PROJECT_HDRS_INTERNAL SET(PROJECT_HDRS_INTERNAL
private/ContextShared.h private/ContextShared.h
private/Internal.h private/Internal.h
private/SHMProcess.h
private/LinuxProcess.h
private/ProcessFactory.h
private/MicrosoftSTL.h
) )
SET(PROJECT_HDRS SET(PROJECT_HDRS
@ -89,6 +93,8 @@ DFContext.cpp
DFTileTypes.cpp DFTileTypes.cpp
DFProcessEnumerator.cpp DFProcessEnumerator.cpp
ContextShared.cpp ContextShared.cpp
DFProcess-SHM.cpp
MicrosoftSTL.cpp
depends/md5/md5.cpp depends/md5/md5.cpp
depends/md5/md5wrapper.cpp depends/md5/md5wrapper.cpp
@ -138,6 +144,7 @@ include/dfhack/DFstdint_win.h
SET(PROJECT_SRCS_LINUX SET(PROJECT_SRCS_LINUX
DFProcess-linux.cpp DFProcess-linux.cpp
DFProcess-linux-base.cpp
DFProcess-linux-SHM.cpp DFProcess-linux-SHM.cpp
DFProcess-linux-wine.cpp DFProcess-linux-wine.cpp
modules/WindowIO-linux.cpp modules/WindowIO-linux.cpp
@ -180,7 +187,7 @@ ENDIF( CMAKE_SIZEOF_VOID_P MATCHES 4 )
CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/library/config.h.cmake ${CMAKE_SOURCE_DIR}/library/private/config.h ) CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/library/config.h.cmake ${CMAKE_SOURCE_DIR}/library/private/config.h )
ADD_DEFINITIONS(-DBUILD_DFHACK_LIB) ADD_DEFINITIONS(-DBUILD_DFHACK_LIB -DTIXML_USE_STL)
IF(UNIX) IF(UNIX)
add_definitions(-DLINUX_BUILD) add_definitions(-DLINUX_BUILD)
@ -191,7 +198,12 @@ IF(UNIX)
SET(PROJECT_LIBS ${X11_LIBRARY} rt ) #dfhack-md5 dfhack-tixml SET(PROJECT_LIBS ${X11_LIBRARY} rt ) #dfhack-md5 dfhack-tixml
ELSE(UNIX) ELSE(UNIX)
SET(PROJECT_LIBS psapi) IF(MSVC)
SET(PROJECT_LIBS psapi ${CMAKE_SOURCE_DIR}/library/depends/ntdll/ntdll.lib)
ELSE(MSVC)
SET(PROJECT_LIBS psapi ntdll)
ENDIF(MSVC)
ENDIF(UNIX) ENDIF(UNIX)
ADD_LIBRARY(dfhack SHARED ${PROJECT_SRCS}) ADD_LIBRARY(dfhack SHARED ${PROJECT_SRCS})

@ -99,62 +99,6 @@ bool Context::Detach()
} }
d->allModules.clear(); d->allModules.clear();
memset(&(d->s_mods), 0, sizeof(d->s_mods)); memset(&(d->s_mods), 0, sizeof(d->s_mods));
/*
if(d->creatures)
{
delete d->creatures;
d->creatures = 0;
}
if(d->maps)
{
delete d->maps;
d->maps = 0;
}
if(d->gui)
{
delete d->gui;
d->gui = 0;
}
if(d->world)
{
delete d->world;
d->world = 0;
}
if(d->position)
{
delete d->position;
d->position = 0;
}
if(d->materials)
{
delete d->materials;
d->materials = 0;
}
if(d->items)
{
delete d->items;
d->items = 0;
}
if(d->translation)
{
delete d->translation;
d->translation = 0;
}
if(d->vegetation)
{
delete d->vegetation;
d->vegetation = 0;
}
if(d->constructions)
{
delete d->constructions;
d->constructions = 0;
}
if(d->translation)
{
delete d->translation;
d->translation = 0;
}*/
return true; return true;
} }
@ -236,386 +180,3 @@ MODULE_GETTER(Translation);
MODULE_GETTER(Vegetation); MODULE_GETTER(Vegetation);
MODULE_GETTER(Buildings); MODULE_GETTER(Buildings);
MODULE_GETTER(Constructions); MODULE_GETTER(Constructions);
/*
Creatures * Context::getCreatures()
{
if(!d->creatures)
d->creatures = new Creatures(d);
return d->creatures;
}
*/
/*
Maps * Context::getMaps()
{
if(!d->maps)
d->maps = new Maps(d);
return d->maps;
}
Gui * Context::getGui()
{
if(!d->gui)
d->gui = new Gui(d);
return d->gui;
}
WindowIO * Context::getWindow()
{
if(!d->windowio)
d->windowio = new WindowIO(d);
return d->windowio;
}
World * Context::getWorld()
{
if(!d->world)
d->world = new World(d);
return d->world;
}
Position * Context::getPosition()
{
if(!d->position)
d->position = new Position(d);
return d->position;
}
Materials * Context::getMaterials()
{
if(!d->materials)
d->materials = new Materials(d);
return d->materials;
}
Items * Context::getItems()
{
if(!d->items)
d->items = new Items(d);
return d->items;
}
Translation * Context::getTranslation()
{
if(!d->translation)
d->translation = new Translation(d);
return d->translation;
}
Vegetation * Context::getVegetation()
{
if(!d->vegetation)
d->vegetation = new Vegetation(d);
return d->vegetation;
}
Buildings * Context::getBuildings()
{
if(!d->buildings)
d->buildings = new Buildings(d);
return d->buildings;
}
Constructions * Context::getConstructions()
{
if(!d->constructions)
d->constructions = new Constructions(d);
return d->constructions;
}
*/
/*
// returns number of buildings, expects v_buildingtypes that will later map t_building.type to its name
bool API::InitReadEffects ( uint32_t & numeffects )
{
if(d->effectsInited)
FinishReadEffects();
int effects = 0;
try
{
effects = d->offset_descriptor->getAddress ("effects_vector");
}
catch(Error::AllMemdef)
{
return false;
}
d->effectsInited = true;
d->p_effect = new DfVector (d->p, effects);
numeffects = d->p_effect->getSize();
return true;
}
bool API::ReadEffect(const uint32_t index, t_effect_df40d & effect)
{
if(!d->effectsInited)
return false;
if(index >= d->p_effect->getSize())
return false;
// read pointer from vector at position
uint32_t temp = d->p_effect->at (index);
//read effect from memory
d->p->read (temp, sizeof (t_effect_df40d), (uint8_t *) &effect);
return true;
}
// use with care!
bool API::WriteEffect(const uint32_t index, const t_effect_df40d & effect)
{
if(!d->effectsInited)
return false;
if(index >= d->p_effect->getSize())
return false;
// read pointer from vector at position
uint32_t temp = d->p_effect->at (index);
// write effect to memory
d->p->write(temp,sizeof(t_effect_df40d), (uint8_t *) &effect);
return true;
}
void API::FinishReadEffects()
{
if(d->p_effect)
{
delete d->p_effect;
d->p_effect = NULL;
}
d->effectsInited = false;
}
*/
/*
bool API::InitReadNotes( uint32_t &numnotes )
{
try
{
memory_info * minfo = d->offset_descriptor;
int notes = minfo->getAddress ("notes");
d->note_foreground_offset = minfo->getOffset ("note_foreground");
d->note_background_offset = minfo->getOffset ("note_background");
d->note_name_offset = minfo->getOffset ("note_name");
d->note_xyz_offset = minfo->getOffset ("note_xyz");
d->p_notes = new DfVector (d->p, notes);
d->notesInited = true;
numnotes = d->p_notes->getSize();
return true;
}
catch (Error::AllMemdef&)
{
d->notesInited = false;
numnotes = 0;
throw;
}
}
bool API::ReadNote (const int32_t index, t_note & note)
{
if(!d->notesInited) return false;
// read pointer from vector at position
uint32_t temp = d->p_notes->at (index);
note.symbol = d->p->readByte(temp);
note.foreground = d->p->readWord(temp + d->note_foreground_offset);
note.background = d->p->readWord(temp + d->note_background_offset);
d->p->readSTLString (temp + d->note_name_offset, note.name, 128);
d->p->read (temp + d->note_xyz_offset, 3*sizeof (uint16_t), (uint8_t *) &note.x);
return true;
}
bool API::InitReadSettlements( uint32_t & numsettlements )
{
if(!d->InitReadNames()) return false;
try
{
memory_info * minfo = d->offset_descriptor;
int allSettlements = minfo->getAddress ("settlements");
int currentSettlement = minfo->getAddress("settlement_current");
d->settlement_name_offset = minfo->getOffset ("settlement_name");
d->settlement_world_xy_offset = minfo->getOffset ("settlement_world_xy");
d->settlement_local_xy_offset = minfo->getOffset ("settlement_local_xy");
d->p_settlements = new DfVector (d->p, allSettlements);
d->p_current_settlement = new DfVector(d->p, currentSettlement);
d->settlementsInited = true;
numsettlements = d->p_settlements->getSize();
return true;
}
catch (Error::AllMemdef&)
{
d->settlementsInited = false;
numsettlements = 0;
throw;
}
}
bool API::ReadSettlement(const int32_t index, t_settlement & settlement)
{
if(!d->settlementsInited) return false;
if(!d->p_settlements->getSize()) return false;
// read pointer from vector at position
uint32_t temp = d->p_settlements->at (index);
settlement.origin = temp;
d->readName(settlement.name, temp + d->settlement_name_offset);
d->p->read(temp + d->settlement_world_xy_offset, 2 * sizeof(int16_t), (uint8_t *) &settlement.world_x);
d->p->read(temp + d->settlement_local_xy_offset, 4 * sizeof(int16_t), (uint8_t *) &settlement.local_x1);
return true;
}
bool API::ReadCurrentSettlement(t_settlement & settlement)
{
if(!d->settlementsInited) return false;
if(!d->p_current_settlement->getSize()) return false;
uint32_t temp = d->p_current_settlement->at(0);
settlement.origin = temp;
d->readName(settlement.name, temp + d->settlement_name_offset);
d->p->read(temp + d->settlement_world_xy_offset, 2 * sizeof(int16_t), (uint8_t *) &settlement.world_x);
d->p->read(temp + d->settlement_local_xy_offset, 4 * sizeof(int16_t), (uint8_t *) &settlement.local_x1);
return true;
}
void API::FinishReadSettlements()
{
if(d->p_settlements)
{
delete d->p_settlements;
d->p_settlements = NULL;
}
if(d->p_current_settlement)
{
delete d->p_current_settlement;
d->p_current_settlement = NULL;
}
d->settlementsInited = false;
}
bool API::getItemIndexesInBox(vector<uint32_t> &indexes,
const uint16_t x1, const uint16_t y1, const uint16_t z1,
const uint16_t x2, const uint16_t y2, const uint16_t z2)
{
if(!d->itemsInited) return false;
indexes.clear();
uint32_t size = d->p_itm->getSize();
struct temp2{
uint16_t coords[3];
uint32_t flags;
};
temp2 temp2;
for(uint32_t i =0;i<size;i++){
uint32_t temp = d->p_itm->at(i);
d->p->read(temp+sizeof(uint32_t),5 * sizeof(uint16_t), (uint8_t *) &temp2);
if(temp2.flags & (1 << 0)){
if (temp2.coords[0] >= x1 && temp2.coords[0] < x2)
{
if (temp2.coords[1] >= y1 && temp2.coords[1] < y2)
{
if (temp2.coords[2] >= z1 && temp2.coords[2] < z2)
{
indexes.push_back(i);
}
}
}
}
}
return true;
}
*/
/*
void API::FinishReadNotes()
{
if(d->p_notes)
{
delete d->p_notes;
d->p_notes = 0;
}
d->notesInited = false;
}
*/
/*
bool API::InitReadItems(uint32_t & numitems)
{
try
{
int items = d->offset_descriptor->getAddress ("items");
d->item_material_offset = d->offset_descriptor->getOffset ("item_materials");
d->p_itm = new DfVector (d->p, items);
d->itemsInited = true;
numitems = d->p_itm->getSize();
return true;
}
catch (Error::AllMemdef&)
{
d->itemsInited = false;
numitems = 0;
throw;
}
}
bool API::ReadItem (const uint32_t index, t_item & item)
{
if (!d->itemsInited) return false;
t_item_df40d item_40d;
// read pointer from vector at position
uint32_t temp = d->p_itm->at (index);
//read building from memory
d->p->read (temp, sizeof (t_item_df40d), (uint8_t *) &item_40d);
// transform
int32_t type = -1;
d->offset_descriptor->resolveObjectToClassID (temp, type);
item.origin = temp;
item.vtable = item_40d.vtable;
item.x = item_40d.x;
item.y = item_40d.y;
item.z = item_40d.z;
item.type = type;
item.ID = item_40d.ID;
item.flags.whole = item_40d.flags;
//TODO certain item types (creature based, threads, seeds, bags do not have the first matType byte, instead they have the material index only located at 0x68
d->p->read (temp + d->item_material_offset, sizeof (t_matglossPair), (uint8_t *) &item.material);
//for(int i = 0; i < 0xCC; i++){ // used for item research
// uint8_t byte = MreadByte(temp+i);
// item.bytes.push_back(byte);
//}
return true;
}
void API::FinishReadItems()
{
if(d->p_itm)
{
delete d->p_itm;
d->p_itm = NULL;
}
d->itemsInited = false;
}
*/
/*
bool API::ReadItemTypes(vector< vector< t_itemType > > & itemTypes)
{
memory_info * minfo = d->offset_descriptor;
int matgloss_address = minfo->getAddress("matgloss");
int matgloss_skip = minfo->getHexValue("matgloss_skip");
int item_type_name_offset = minfo->getOffset("item_type_name");
for(int i = 8;i<20;i++)
{
DfVector p_temp (d->p, matgloss_address + i*matgloss_skip);
vector< t_itemType > typesForVec;
for(uint32_t j =0; j<p_temp.getSize();j++)
{
t_itemType currType;
uint32_t temp = *(uint32_t *) p_temp[j];
// Mread(temp+40,sizeof(name),(uint8_t *) name);
d->p->readSTLString(temp+4,currType.id,128);
d->p->readSTLString(temp+item_type_name_offset,currType.name,128);
//stringsForVec.push_back(string(name));
typesForVec.push_back(currType);
}
itemTypes.push_back(typesForVec);
}
return true;
}
*/

@ -0,0 +1,485 @@
/*
www.sourceforge.net/projects/dfhack
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "Internal.h"
#include "SHMProcess.h"
#include "ProcessFactory.h"
#include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h"
#include "shms.h"
#include "mod-core.h"
using namespace DFHack;
Process* DFHack::createSHMProcess(uint32_t pid, VersionInfoFactory * factory)
{
return new SHMProcess(pid, factory);
}
SHMProcess::SHMProcess(uint32_t PID, VersionInfoFactory * factory)
: d(new Private(this))
{
d->process_ID = PID;
// attach the SHM
if(!attach())
{
return;
}
// Test bridge version, get PID, sync Yield
bool bridgeOK;
if(!d->Aux_Core_Attach(bridgeOK,d->process_ID))
{
detach();
throw Error::SHMAttachFailure();
}
else if(!bridgeOK)
{
detach();
throw Error::SHMVersionMismatch();
}
// try to identify the DF version (md5 the binary, compare with known versions)
d->validate(factory);
// at this point, DF is attached and suspended, make it run
detach();
}
SHMProcess::~SHMProcess()
{
if(d->attached)
{
detach();
}
// destroy data model. this is assigned by processmanager
if(d->memdescriptor)
delete d->memdescriptor;
delete d;
}
VersionInfo * SHMProcess::getDescriptor()
{
return d->memdescriptor;
}
int SHMProcess::getPID()
{
return d->process_ID;
}
bool SHMProcess::isSuspended()
{
return d->locked;
}
bool SHMProcess::isAttached()
{
return d->attached;
}
bool SHMProcess::isIdentified()
{
return d->identified;
}
bool SHMProcess::suspend()
{
if(!d->attached)
{
return false;
}
if(d->locked)
{
return true;
}
//cerr << "suspend" << endl;// FIXME: throw
// FIXME: this should be controlled on the server side
// FIXME: IF server got CORE_RUN in this frame, interpret CORE_SUSPEND as CORE_STEP
// did we just resume a moment ago?
if(D_SHMCMD == CORE_RUN)
{
//fprintf(stderr,"%d invokes step\n",attachmentIdx);
// wait for the next window
/*
if(!d->SetAndWait(CORE_STEP))
{
throw Error::SHMLockingError("if(!d->SetAndWait(CORE_STEP))");
}
*/
D_SHMCMD = CORE_STEP;
}
else
{
//fprintf(stderr,"%d invokes suspend\n",attachmentIdx);
// lock now
/*
if(!d->SetAndWait(CORE_SUSPEND))
{
throw Error::SHMLockingError("if(!d->SetAndWait(CORE_SUSPEND))");
}
*/
D_SHMCMD = CORE_SUSPEND;
}
//fprintf(stderr,"waiting for lock\n");
// we wait for the server to give up our suspend lock (held by default)
if(acquireSuspendLock())
{
d->locked = true;
return true;
}
return false;
}
// FIXME: needs a good think-through
bool SHMProcess::asyncSuspend()
{
if(!d->attached)
{
return false;
}
if(d->locked)
{
return true;
}
//cerr << "async suspend" << endl;// FIXME: throw
uint32_t cmd = D_SHMCMD;
if(cmd == CORE_SUSPENDED)
{
// we have to hold the lock to be really suspended
if(acquireSuspendLock())
{
d->locked = true;
return true;
}
return false;
}
else
{
// did we just resume a moment ago?
if(cmd == CORE_STEP)
{
return false;
}
else if(cmd == CORE_RUN)
{
D_SHMCMD = CORE_STEP;
}
else
{
D_SHMCMD = CORE_SUSPEND;
}
return false;
}
}
bool SHMProcess::forceresume()
{
return resume();
}
// FIXME: wait for the server to advance a step!
bool SHMProcess::resume()
{
if(!d->attached)
return false;
if(!d->locked)
return true;
//cerr << "resume" << endl;// FIXME: throw
// unlock the suspend lock
if(releaseSuspendLock())
{
d->locked = false;
if(d->SetAndWait(CORE_RUN)) // we have to make sure the server responds!
{
return true;
}
throw Error::SHMLockingError("if(d->SetAndWait(CORE_RUN))");
}
throw Error::SHMLockingError("if(releaseSuspendLock())");
return false;
}
// get module index by name and version. bool 0 = error
bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT)
{
if(!d->locked) throw Error::MemoryAccessDenied();
modulelookup * payload = D_SHMDATA(modulelookup);
payload->version = version;
strncpy(payload->name,name,255);
payload->name[255] = 0;
if(!SetAndWait(CORE_ACQUIRE_MODULE))
{
return false; // FIXME: throw a fatal exception instead
}
if(D_SHMHDR->error)
{
return false;
}
//fprintf(stderr,"%s v%d : %d\n", name, version, D_SHMHDR->value);
OUTPUT = D_SHMHDR->value;
return true;
}
bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID)
{
if(!locked) throw Error::MemoryAccessDenied();
SHMDATA(coreattach)->cl_affinity = OS_getAffinity();
if(!SetAndWait(CORE_ATTACH)) return false;
/*
cerr <<"CORE_VERSION" << CORE_VERSION << endl;
cerr <<"server CORE_VERSION" << SHMDATA(coreattach)->sv_version << endl;
*/
versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION );
PID = SHMDATA(coreattach)->sv_PID;
useYield = SHMDATA(coreattach)->sv_useYield;
#ifdef DEBUG
if(useYield) cerr << "Using Yield!" << endl;
#endif
return true;
}
void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer)
{
if(!d->locked) throw Error::MemoryAccessDenied();
// normal read under 1MB
if(size <= SHM_BODY)
{
D_SHMHDR->address = src_address;
D_SHMHDR->length = size;
full_barrier
d->SetAndWait(CORE_READ);
memcpy (target_buffer, D_SHMDATA(void),size);
}
// a big read, we pull data over the shm in iterations
else
{
// first read equals the size of the SHM window
uint32_t to_read = SHM_BODY;
while (size)
{
// read to_read bytes from src_cursor
D_SHMHDR->address = src_address;
D_SHMHDR->length = to_read;
full_barrier
d->SetAndWait(CORE_READ);
memcpy (target_buffer, D_SHMDATA(void) ,to_read);
// decrease size by bytes read
size -= to_read;
// move the cursors
src_address += to_read;
target_buffer += to_read;
// check how much to write in the next iteration
to_read = min(size, (uint32_t) SHM_BODY);
}
}
}
void SHMProcess::readByte (const uint32_t offset, uint8_t &val )
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_BYTE);
val = D_SHMHDR->value;
}
void SHMProcess::readWord (const uint32_t offset, uint16_t &val)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_WORD);
val = D_SHMHDR->value;
}
void SHMProcess::readDWord (const uint32_t offset, uint32_t &val)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_DWORD);
val = D_SHMHDR->value;
}
void SHMProcess::readQuad (const uint32_t offset, uint64_t &val)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_QUAD);
val = D_SHMHDR->Qvalue;
}
void SHMProcess::readFloat (const uint32_t offset, float &val)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_DWORD);
val = reinterpret_cast<float&> (D_SHMHDR->value);
}
/*
* WRITING
*/
void SHMProcess::writeQuad (uint32_t offset, uint64_t data)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->Qvalue = data;
full_barrier
d->SetAndWait(CORE_WRITE_QUAD);
}
void SHMProcess::writeDWord (uint32_t offset, uint32_t data)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->value = data;
full_barrier
d->SetAndWait(CORE_WRITE_DWORD);
}
// using these is expensive.
void SHMProcess::writeWord (uint32_t offset, uint16_t data)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->value = data;
full_barrier
d->SetAndWait(CORE_WRITE_WORD);
}
void SHMProcess::writeByte (uint32_t offset, uint8_t data)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->value = data;
full_barrier
d->SetAndWait(CORE_WRITE_BYTE);
}
void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer)
{
if(!d->locked) throw Error::MemoryAccessDenied();
// normal write under 1MB
if(size <= SHM_BODY)
{
D_SHMHDR->address = dst_address;
D_SHMHDR->length = size;
memcpy(D_SHMDATA(void),source_buffer, size);
full_barrier
d->SetAndWait(CORE_WRITE);
}
// a big write, we push this over the shm in iterations
else
{
// first write equals the size of the SHM window
uint32_t to_write = SHM_BODY;
while (size)
{
// write to_write bytes to dst_cursor
D_SHMHDR->address = dst_address;
D_SHMHDR->length = to_write;
memcpy(D_SHMDATA(void),source_buffer, to_write);
full_barrier
d->SetAndWait(CORE_WRITE);
// decrease size by bytes written
size -= to_write;
// move the cursors
source_buffer += to_write;
dst_address += to_write;
// check how much to write in the next iteration
to_write = min(size, (uint32_t) SHM_BODY);
}
}
}
// FIXME: butt-fugly
const std::string SHMProcess::readCString (uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
std::string temp;
char temp_c[256];
int counter = 0;
char r;
do
{
r = Process::readByte(offset+counter);
temp_c[counter] = r;
counter++;
} while (r && counter < 255);
temp_c[counter] = 0;
temp = temp_c;
return temp;
}
const std::string SHMProcess::readSTLString(uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_STL_STRING);
return(string( D_SHMDATA(char) ));
}
size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_STL_STRING);
size_t length = D_SHMHDR->value;
size_t fit = min(bufcapacity - 1, length);
strncpy(buffer,D_SHMDATA(char),fit);
buffer[fit] = 0;
return fit;
}
void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = address;
strncpy(D_SHMDATA(char),writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator
full_barrier
d->SetAndWait(CORE_WRITE_STL_STRING);
}

@ -22,7 +22,7 @@ must not be misrepresented as being the original software.
distribution. distribution.
*/ */
#include "Internal.h" #include "Internal.h"
#include "dfhack/DFProcess.h" #include "SHMProcess.h"
#include "dfhack/VersionInfo.h" #include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h" #include "dfhack/DFError.h"
@ -39,76 +39,37 @@ distribution.
using namespace DFHack; using namespace DFHack;
// a full memory barrier! better be safe than sorry. SHMProcess::Private::Private(SHMProcess * self_)
#define gcc_barrier asm volatile("" ::: "memory"); __sync_synchronize();
class SHMProcess::Private
{ {
public: memdescriptor = NULL;
Private(Process * self_) process_ID = 0;
{ shm_ID = -1;
memdescriptor = NULL; attached = false;
process_ID = 0; identified = false;
shm_addr = 0; useYield = false;
//shm_addr_with_cl_idx = 0; server_lock = -1;
shm_ID = -1; client_lock = -1;
attached = false; suspend_lock = -1;
identified = false; locked = false;
useYield = false; self = self_;
server_lock = -1; }
client_lock = -1;
suspend_lock = -1;
attachmentIdx = 0;
locked = false;
self = self_;
};
~Private(){};
VersionInfo * memdescriptor;
Process * self;
pid_t process_ID;
char *shm_addr;
int shm_ID;
Process* q;
int server_lock;
int client_lock;
int suspend_lock;
int attachmentIdx;
bool attached;
bool locked;
bool identified;
bool useYield;
bool validate(std::vector< VersionInfo* >& known_versions);
bool Aux_Core_Attach(bool & versionOK, pid_t & PID);
//bool waitWhile (uint32_t state);
bool SetAndWait (uint32_t state);
bool GetLocks();
bool AreLocksOk();
void FreeLocks();
};
#define SHMCMD ( (uint32_t *) shm_addr)[attachmentIdx]
#define D_SHMCMD ( (uint32_t *) (d->shm_addr))[d->attachmentIdx]
#define SHMHDR ((shm_core_hdr *)shm_addr)
#define D_SHMHDR ((shm_core_hdr *)(d->shm_addr))
#define SHMDATA(type) ((type *)(shm_addr + SHM_HEADER))
#define D_SHMDATA(type) ((type *)(d->shm_addr + SHM_HEADER))
bool SHMProcess::Private::SetAndWait (uint32_t state) bool SHMProcess::Private::SetAndWait (uint32_t state)
{ {
uint32_t cnt = 0; uint32_t cnt = 0;
if(!attached) return false; if(!attached) return false;
SHMCMD = state; SHMCMD = state;
while (SHMCMD == state) while (SHMCMD == state)
{ {
// check if the other process is still there // yield the CPU, only on single-core CPUs
if(useYield)
{
SCHED_YIELD
}
if(cnt == 10000) if(cnt == 10000)
{ {
if(!AreLocksOk()) if(!AreLocksOk())// DF not there anymore?
{ {
//detach the shared memory //detach the shared memory
shmdt(shm_addr); shmdt(shm_addr);
@ -122,10 +83,6 @@ bool SHMProcess::Private::SetAndWait (uint32_t state)
cnt = 0; cnt = 0;
} }
} }
if(useYield)
{
SCHED_YIELD
}
cnt++; cnt++;
} }
// server returned a generic error // server returned a generic error
@ -155,21 +112,6 @@ uint32_t OS_getAffinity()
return affinity; return affinity;
} }
// test if we have client and server locks and the server is present
bool SHMProcess::Private::AreLocksOk()
{
// both locks are inited (we hold our lock)
if(client_lock != -1 && server_lock != -1)
{
if(lockf(server_lock,F_TEST,0) == -1) // and server holds its lock
{
return true; // OK, locks are good
}
}
// locks are bad
return false;
}
void SHMProcess::Private::FreeLocks() void SHMProcess::Private::FreeLocks()
{ {
attachmentIdx = -1; attachmentIdx = -1;
@ -258,52 +200,23 @@ bool SHMProcess::Private::GetLocks()
return false; return false;
} }
SHMProcess::SHMProcess(uint32_t PID, vector< VersionInfo* >& known_versions) // test if we have client and server locks and the server is present
: d(new Private(this)) bool SHMProcess::Private::AreLocksOk()
{ {
d->process_ID = PID; // both locks are inited (we hold our lock)
d->memdescriptor = 0; if(client_lock != -1 && server_lock != -1)
if(!attach())
{
// couldn't attach to process
return;
}
/*
* Test bridge version, get PID, sync Yield
*/
bool bridgeOK;
if(!d->Aux_Core_Attach(bridgeOK,d->process_ID))
{
detach();
throw Error::SHMAttachFailure();
}
if(!bridgeOK)
{ {
detach(); if(lockf(server_lock,F_TEST,0) == -1) // and server holds its lock
throw Error::SHMVersionMismatch(); {
return true; // OK, locks are good
}
} }
// locks are bad
// try to identify the DF version (md5 the binary, compare with known versions) return false;
d->validate(known_versions);
// detach
detach();
}
bool SHMProcess::isSuspended()
{
return d->locked;
}
bool SHMProcess::isAttached()
{
return d->attached;
} }
bool SHMProcess::isIdentified()
{
return d->identified;
}
bool SHMProcess::Private::validate(vector <VersionInfo *> & known_versions) bool SHMProcess::Private::validate(VersionInfoFactory * factory)
{ {
char exe_link_name [256]; char exe_link_name [256];
char target_name[1024]; char target_name[1024];
@ -320,52 +233,21 @@ bool SHMProcess::Private::validate(vector <VersionInfo *> & known_versions)
// see http://www.opengroup.org/onlinepubs/000095399/functions/readlink.html // see http://www.opengroup.org/onlinepubs/000095399/functions/readlink.html
target_name[target_result] = 0; target_name[target_result] = 0;
md5wrapper md5;
// get hash of the running DF process // get hash of the running DF process
md5wrapper md5;
string hash = md5.getHashFromFile(target_name); string hash = md5.getHashFromFile(target_name);
vector<VersionInfo *>::iterator it;
// cerr << exe_file << " " << hash << endl;
// iterate over the list of memory locations
for ( it=known_versions.begin() ; it < known_versions.end(); it++ )
{
try{
if(hash == (*it)->getMD5()) // are the md5 hashes the same?
{
VersionInfo *m = new VersionInfo(**it);
memdescriptor = m;
m->setParentProcess(dynamic_cast<Process *>( self ));
identified = true;
// cerr << "identified " << m->getVersion() << endl;
return true;
}
}
catch (Error::AllMemdef&)
{
continue;
}
}
return false;
}
SHMProcess::~SHMProcess() // create linux process, add it to the vector
{ VersionInfo * vinfo = factory->getVersionInfoByMD5(hash);
if(d->attached) if(vinfo)
{ {
detach(); memdescriptor = vinfo;
// FIXME: BIG BAD BUG RIGHT HERE!!!!
memdescriptor->setParentProcess(self);
identified = true;
return true;
} }
if(d->memdescriptor) return false;
delete d->memdescriptor;
delete d;
}
VersionInfo * SHMProcess::getDescriptor()
{
return d->memdescriptor;
}
int SHMProcess::getPID()
{
return d->process_ID;
} }
// there is only one we care about. // there is only one we care about.
@ -403,123 +285,19 @@ void SHMProcess::getMemRanges( vector<t_memrange> & ranges )
temp.read = permissions[0] == 'r'; temp.read = permissions[0] == 'r';
temp.write = permissions[1] == 'w'; temp.write = permissions[1] == 'w';
temp.execute = permissions[2] == 'x'; temp.execute = permissions[2] == 'x';
temp.valid = true;
ranges.push_back(temp); ranges.push_back(temp);
} }
} }
bool SHMProcess::suspend() bool SHMProcess::acquireSuspendLock()
{ {
if(!d->attached) return (lockf(d->suspend_lock,F_LOCK,0) == 0);
{
return false;
}
if(d->locked)
{
return true;
}
// FIXME: this should be controlled on the server side
// FIXME: IF server got CORE_RUN in this frame, interpret CORE_SUSPEND as CORE_STEP
// did we just resume a moment ago?
if(D_SHMCMD == CORE_RUN)
{
//fprintf(stderr,"%d invokes step\n",d->attachmentIdx);
/*
// wait for the next window
if(!d->SetAndWait(CORE_STEP))
{
throw Error::SHMLockingError("if(!d->SetAndWait(CORE_STEP))");
}
*/
D_SHMCMD = CORE_STEP;
}
else
{
//fprintf(stderr,"%d invokes suspend\n",d->attachmentIdx);
// lock now
/*
if(!d->SetAndWait(CORE_SUSPEND))
{
throw Error::SHMLockingError("if(!d->SetAndWait(CORE_SUSPEND))");
}
*/
D_SHMCMD = CORE_SUSPEND;
}
//fprintf(stderr,"waiting for lock\n");
// we wait for the server to give up our suspend lock (held by default)
if(lockf(d->suspend_lock,F_LOCK,0) == 0)
{
d->locked = true;
return true;
}
return false;
} }
// FIXME: needs a good think-through bool SHMProcess::releaseSuspendLock()
bool SHMProcess::asyncSuspend()
{ {
if(!d->attached) return (lockf(d->suspend_lock,F_ULOCK,0) == 0);
{
return false;
}
if(d->locked)
{
return true;
}
uint32_t cmd = D_SHMCMD;
if(cmd == CORE_SUSPENDED)
{
// we have to hold the lock to be really suspended
if(lockf(d->suspend_lock,F_LOCK,0) == 0)
{
d->locked = true;
return true;
}
return false;
}
else
{
// did we just resume a moment ago?
if(cmd == CORE_STEP)
{
return false;
}
else if(cmd == CORE_RUN)
{
D_SHMCMD = CORE_STEP;
}
else
{
D_SHMCMD = CORE_SUSPEND;
}
return false;
}
}
bool SHMProcess::forceresume()
{
return resume();
}
// FIXME: wait for the server to advance a step!
bool SHMProcess::resume()
{
if(!d->attached)
return false;
if(!d->locked)
return true;
// unlock the suspend lock
if(lockf(d->suspend_lock,F_ULOCK,0) == 0)
{
d->locked = false;
if(d->SetAndWait(CORE_RUN)) // we have to make sure the server responds!
{
return true;
}
throw Error::SHMLockingError("if(d->SetAndWait(CORE_RUN))");
}
throw Error::SHMLockingError("if(lockf(d->suspend_lock,F_ULOCK,0) == 0)");
return false;
} }
@ -531,6 +309,7 @@ bool SHMProcess::attach()
return suspend(); return suspend();
return true; return true;
} }
//cerr << "attach" << endl;// FIXME: throw
if(!d->GetLocks()) if(!d->GetLocks())
{ {
//cerr << "server is full or not really there!" << endl; //cerr << "server is full or not really there!" << endl;
@ -589,337 +368,18 @@ bool SHMProcess::detach()
return false; return false;
} }
void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer)
{
if(!d->locked) throw Error::MemoryAccessDenied();
// normal read under 1MB
if(size <= SHM_BODY)
{
D_SHMHDR->address = src_address;
D_SHMHDR->length = size;
gcc_barrier
d->SetAndWait(CORE_READ);
memcpy (target_buffer, D_SHMDATA(void),size);
}
// a big read, we pull data over the shm in iterations
else
{
// first read equals the size of the SHM window
uint32_t to_read = SHM_BODY;
while (size)
{
// read to_read bytes from src_cursor
D_SHMHDR->address = src_address;
D_SHMHDR->length = to_read;
gcc_barrier
d->SetAndWait(CORE_READ);
memcpy (target_buffer, D_SHMDATA(void) ,to_read);
// decrease size by bytes read
size -= to_read;
// move the cursors
src_address += to_read;
target_buffer += to_read;
// check how much to write in the next iteration
to_read = min(size, (uint32_t) SHM_BODY);
}
}
}
uint8_t SHMProcess::readByte (const uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
d->SetAndWait(CORE_READ_BYTE);
return D_SHMHDR->value;
}
void SHMProcess::readByte (const uint32_t offset, uint8_t &val )
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
d->SetAndWait(CORE_READ_BYTE);
val = D_SHMHDR->value;
}
uint16_t SHMProcess::readWord (const uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
d->SetAndWait(CORE_READ_WORD);
return D_SHMHDR->value;
}
void SHMProcess::readWord (const uint32_t offset, uint16_t &val)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
d->SetAndWait(CORE_READ_WORD);
val = D_SHMHDR->value;
}
uint32_t SHMProcess::readDWord (const uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
d->SetAndWait(CORE_READ_DWORD);
return D_SHMHDR->value;
}
void SHMProcess::readDWord (const uint32_t offset, uint32_t &val)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
d->SetAndWait(CORE_READ_DWORD);
val = D_SHMHDR->value;
}
uint64_t SHMProcess::readQuad (const uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
d->SetAndWait(CORE_READ_QUAD);
return D_SHMHDR->Qvalue;
}
void SHMProcess::readQuad (const uint32_t offset, uint64_t &val)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
d->SetAndWait(CORE_READ_QUAD);
val = D_SHMHDR->Qvalue;
}
float SHMProcess::readFloat (const uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
d->SetAndWait(CORE_READ_DWORD);
return reinterpret_cast<float&> (D_SHMHDR->value);
}
void SHMProcess::readFloat (const uint32_t offset, float &val)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
d->SetAndWait(CORE_READ_DWORD);
val = reinterpret_cast<float&> (D_SHMHDR->value);
}
/*
* WRITING
*/
void SHMProcess::writeQuad (const uint32_t offset, const uint64_t data)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->Qvalue = data;
gcc_barrier
d->SetAndWait(CORE_WRITE_QUAD);
}
void SHMProcess::writeDWord (uint32_t offset, uint32_t data)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->value = data;
gcc_barrier
d->SetAndWait(CORE_WRITE_DWORD);
}
// using these is expensive.
void SHMProcess::writeWord (uint32_t offset, uint16_t data)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->value = data;
gcc_barrier
d->SetAndWait(CORE_WRITE_WORD);
}
void SHMProcess::writeByte (uint32_t offset, uint8_t data)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->value = data;
gcc_barrier
d->SetAndWait(CORE_WRITE_BYTE);
}
void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer)
{
if(!d->locked) throw Error::MemoryAccessDenied();
// normal write under 1MB
if(size <= SHM_BODY)
{
D_SHMHDR->address = dst_address;
D_SHMHDR->length = size;
memcpy(D_SHMDATA(void),source_buffer, size);
gcc_barrier
d->SetAndWait(CORE_WRITE);
}
// a big write, we push this over the shm in iterations
else
{
// first write equals the size of the SHM window
uint32_t to_write = SHM_BODY;
while (size)
{
// write to_write bytes to dst_cursor
D_SHMHDR->address = dst_address;
D_SHMHDR->length = to_write;
memcpy(D_SHMDATA(void),source_buffer, to_write);
gcc_barrier
d->SetAndWait(CORE_WRITE);
// decrease size by bytes written
size -= to_write;
// move the cursors
source_buffer += to_write;
dst_address += to_write;
// check how much to write in the next iteration
to_write = min(size, (uint32_t) SHM_BODY);
}
}
}
// FIXME: butt-fugly
const std::string SHMProcess::readCString (uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
std::string temp;
char temp_c[256];
int counter = 0;
char r;
do
{
r = readByte(offset+counter);
temp_c[counter] = r;
counter++;
} while (r && counter < 255);
temp_c[counter] = 0;
temp = temp_c;
return temp;
}
const std::string SHMProcess::readSTLString(uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_STL_STRING);
return(string( D_SHMDATA(char) ));
}
size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_STL_STRING);
size_t length = D_SHMHDR->value;
size_t fit = min(bufcapacity - 1, length);
strncpy(buffer,D_SHMDATA(char),fit);
buffer[fit] = 0;
return fit;
}
void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = address;
strncpy(D_SHMDATA(char),writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator
full_barrier
d->SetAndWait(CORE_WRITE_STL_STRING);
}
string SHMProcess::readClassName (uint32_t vptr) string SHMProcess::readClassName (uint32_t vptr)
{ {
if(!d->locked) throw Error::MemoryAccessDenied(); if(!d->locked) throw Error::MemoryAccessDenied();
int typeinfo = readDWord(vptr - 0x4); int typeinfo = Process::readDWord(vptr - 0x4);
int typestring = readDWord(typeinfo + 0x4); int typestring = Process::readDWord(typeinfo + 0x4);
string raw = readCString(typestring); string raw = readCString(typestring);
size_t start = raw.find_first_of("abcdefghijklmnopqrstuvwxyz");// trim numbers size_t start = raw.find_first_of("abcdefghijklmnopqrstuvwxyz");// trim numbers
size_t end = raw.length(); size_t end = raw.length();
return raw.substr(start,end-start); return raw.substr(start,end-start);
} }
// get module index by name and version. bool 0 = error
bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT)
{
if(!d->locked) throw Error::MemoryAccessDenied();
modulelookup * payload = D_SHMDATA(modulelookup);
payload->version = version;
strncpy(payload->name,name,255);
payload->name[255] = 0;
if(!SetAndWait(CORE_ACQUIRE_MODULE))
{
return false; // FIXME: throw a fatal exception instead
}
if(D_SHMHDR->error)
{
return false;
}
//fprintf(stderr,"%s v%d : %d\n", name, version, D_SHMHDR->value);
OUTPUT = D_SHMHDR->value;
return true;
}
char * SHMProcess::getSHMStart (void)
{
if(!d->locked) return 0; //THROW HERE!
return /*d->shm_addr_with_cl_idx*/ d->shm_addr;
}
bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID)
{
if(!locked) throw Error::MemoryAccessDenied();
SHMDATA(coreattach)->cl_affinity = OS_getAffinity();
if(!SetAndWait(CORE_ATTACH)) return false;
/*
cerr <<"CORE_VERSION" << CORE_VERSION << endl;
cerr <<"server CORE_VERSION" << SHMDATA(coreattach)->sv_version << endl;
*/
versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION );
PID = SHMDATA(coreattach)->sv_PID;
useYield = SHMDATA(coreattach)->sv_useYield;
#ifdef DEBUG
if(useYield) cerr << "Using Yield!" << endl;
#endif
return true;
}
string SHMProcess::getPath() string SHMProcess::getPath()
{ {
char cwd_name[256]; char cwd_name[256];
@ -932,3 +392,10 @@ string SHMProcess::getPath()
target_name[target_result] = '\0'; target_name[target_result] = '\0';
return(string(target_name)); return(string(target_name));
} }
char * SHMProcess::getSHMStart (void)
{
if(!d->locked) return 0; //THROW HERE!
return d->shm_addr;
}

@ -0,0 +1,432 @@
/*
www.sourceforge.net/projects/dfhack
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "Internal.h"
#include "LinuxProcess.h"
#include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h"
#include <errno.h>
#include <sys/ptrace.h>
using namespace DFHack;
LinuxProcessBase::LinuxProcessBase(uint32_t pid)
: my_pid(pid)
{
my_descriptor = NULL;
attached = false;
suspended = false;
memFileHandle = 0;
}
bool LinuxProcessBase::isSuspended()
{
return suspended;
}
bool LinuxProcessBase::isAttached()
{
return attached;
}
bool LinuxProcessBase::isIdentified()
{
return identified;
}
LinuxProcessBase::~LinuxProcessBase()
{
if(attached)
{
detach();
}
// destroy our copy of the memory descriptor
if(my_descriptor)
delete my_descriptor;
}
VersionInfo * LinuxProcessBase::getDescriptor()
{
return my_descriptor;
}
int LinuxProcessBase::getPID()
{
return my_pid;
}
//FIXME: implement
bool LinuxProcessBase::getThreadIDs(vector<uint32_t> & threads )
{
return false;
}
//FIXME: cross-reference with ELF segment entries?
void LinuxProcessBase::getMemRanges( vector<t_memrange> & ranges )
{
char buffer[1024];
char permissions[5]; // r/-, w/-, x/-, p/s, 0
sprintf(buffer, "/proc/%lu/maps", my_pid);
FILE *mapFile = ::fopen(buffer, "r");
uint64_t offset, device1, device2, node;
while (fgets(buffer, 1024, mapFile))
{
t_memrange temp;
temp.name[0] = 0;
sscanf(buffer, "%llx-%llx %s %llx %2llu:%2llu %llu %s",
&temp.start,
&temp.end,
(char*)&permissions,
&offset, &device1, &device2, &node,
(char*)&temp.name);
temp.read = permissions[0] == 'r';
temp.write = permissions[1] == 'w';
temp.execute = permissions[2] == 'x';
temp.valid = true;
ranges.push_back(temp);
}
}
bool LinuxProcessBase::asyncSuspend()
{
return suspend();
}
bool LinuxProcessBase::suspend()
{
int status;
if(!attached)
return false;
if(suspended)
return true;
if (kill(my_pid, SIGSTOP) == -1)
{
// no, we got an error
perror("kill SIGSTOP error");
return false;
}
while(true)
{
// we wait on the pid
pid_t w = waitpid(my_pid, &status, 0);
if (w == -1)
{
// child died
perror("DF exited during suspend call");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{
break;
}
}
suspended = true;
return true;
}
bool LinuxProcessBase::forceresume()
{
return resume();
}
bool LinuxProcessBase::resume()
{
if(!attached)
return false;
if(!suspended)
return true;
if (ptrace(PTRACE_CONT, my_pid, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace resume error");
return false;
}
suspended = false;
return true;
}
bool LinuxProcessBase::attach()
{
int status;
if(attached)
{
if(!suspended)
return suspend();
return true;
}
// can we attach?
if (ptrace(PTRACE_ATTACH , my_pid, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace attach error");
cerr << "attach failed on pid " << my_pid << endl;
return false;
}
while(true)
{
// we wait on the pid
pid_t w = waitpid(my_pid, &status, 0);
if (w == -1)
{
// child died
perror("wait inside attach()");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{
break;
}
}
suspended = true;
int proc_pid_mem = open(memFile.c_str(),O_RDONLY);
if(proc_pid_mem == -1)
{
ptrace(PTRACE_DETACH, my_pid, NULL, NULL);
cerr << memFile << endl;
cerr << "couldn't open /proc/" << my_pid << "/mem" << endl;
perror("open(memFile.c_str(),O_RDONLY)");
return false;
}
else
{
attached = true;
memFileHandle = proc_pid_mem;
return true; // we are attached
}
}
bool LinuxProcessBase::detach()
{
if(!attached) return true;
if(!suspended) suspend();
int result = 0;
// close /proc/PID/mem
result = close(memFileHandle);
if(result == -1)
{
cerr << "couldn't close /proc/"<< my_pid <<"/mem" << endl;
perror("mem file close");
return false;
}
else
{
// detach
result = ptrace(PTRACE_DETACH, my_pid, NULL, NULL);
if(result == -1)
{
cerr << "couldn't detach from process pid" << my_pid << endl;
perror("ptrace detach");
return false;
}
else
{
attached = false;
return true;
}
}
}
void LinuxProcessBase::read (const uint32_t offset, const uint32_t size, uint8_t *target)
{
if(size == 0) return;
ssize_t result;
ssize_t total = 0;
ssize_t remaining = size;
while (total != size)
{
result = pread(memFileHandle, target + total ,remaining,offset + total);
if(result == -1)
{
cerr << "pread failed: can't read " << size << " bytes at addres " << offset << endl;
cerr << "errno: " << errno << endl;
errno = 0;
throw Error::MemoryAccessDenied();
}
else
{
total += result;
remaining -= result;
}
}
}
void LinuxProcessBase::readByte (const uint32_t offset, uint8_t &val )
{
read(offset, 1, &val);
}
void LinuxProcessBase::readWord (const uint32_t offset, uint16_t &val)
{
read(offset, 2, (uint8_t *) &val);
}
void LinuxProcessBase::readDWord (const uint32_t offset, uint32_t &val)
{
read(offset, 4, (uint8_t *) &val);
}
void LinuxProcessBase::readFloat (const uint32_t offset, float &val)
{
read(offset, 4, (uint8_t *) &val);
}
void LinuxProcessBase::readQuad (const uint32_t offset, uint64_t &val)
{
read(offset, 8, (uint8_t *) &val);
}
/*
* WRITING
*/
void LinuxProcessBase::writeQuad (uint32_t offset, const uint64_t data)
{
#ifdef HAVE_64_BIT
ptrace(PTRACE_POKEDATA,my_pid, offset, data);
#else
ptrace(PTRACE_POKEDATA,my_pid, offset, (uint32_t) data);
ptrace(PTRACE_POKEDATA,my_pid, offset+4, (uint32_t) (data >> 32));
#endif
}
void LinuxProcessBase::writeDWord (uint32_t offset, uint32_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = Process::readQuad(offset);
orig &= 0xFFFFFFFF00000000;
orig |= data;
ptrace(PTRACE_POKEDATA,my_pid, offset, orig);
#else
ptrace(PTRACE_POKEDATA,my_pid, offset, data);
#endif
}
// using these is expensive.
void LinuxProcessBase::writeWord (uint32_t offset, uint16_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = Process::readQuad(offset);
orig &= 0xFFFFFFFFFFFF0000;
orig |= data;
ptrace(PTRACE_POKEDATA,my_pid, offset, orig);
#else
uint32_t orig = Process::readDWord(offset);
orig &= 0xFFFF0000;
orig |= data;
ptrace(PTRACE_POKEDATA,my_pid, offset, orig);
#endif
}
void LinuxProcessBase::writeByte (uint32_t offset, uint8_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = Process::readQuad(offset);
orig &= 0xFFFFFFFFFFFFFF00;
orig |= data;
ptrace(PTRACE_POKEDATA,my_pid, offset, orig);
#else
uint32_t orig = Process::readDWord(offset);
orig &= 0xFFFFFF00;
orig |= data;
ptrace(PTRACE_POKEDATA,my_pid, offset, orig);
#endif
}
// blah. I hate the kernel devs for crippling /proc/PID/mem. THIS IS RIDICULOUS
void LinuxProcessBase::write (uint32_t offset, uint32_t size, uint8_t *source)
{
uint32_t indexptr = 0;
while (size > 0)
{
#ifdef HAVE_64_BIT
// quad!
if(size >= 8)
{
writeQuad(offset, *(uint64_t *) (source + indexptr));
offset +=8;
indexptr +=8;
size -=8;
}
else
#endif
// default: we push 4 bytes
if(size >= 4)
{
writeDWord(offset, *(uint32_t *) (source + indexptr));
offset +=4;
indexptr +=4;
size -=4;
}
// last is either three or 2 bytes
else if(size >= 2)
{
writeWord(offset, *(uint16_t *) (source + indexptr));
offset +=2;
indexptr +=2;
size -=2;
}
// finishing move
else if(size == 1)
{
writeByte(offset, *(uint8_t *) (source + indexptr));
return;
}
}
}
const std::string LinuxProcessBase::readCString (uint32_t offset)
{
std::string temp;
char temp_c[256];
int counter = 0;
char r;
do
{
r = Process::readByte(offset+counter);
temp_c[counter] = r;
counter++;
} while (r && counter < 255);
temp_c[counter] = 0;
temp = temp_c;
return temp;
}
string LinuxProcessBase::getPath()
{
char cwd_name[256];
char target_name[1024];
int target_result;
sprintf(cwd_name,"/proc/%d/cwd", getPID());
// resolve /proc/PID/exe link
target_result = readlink(cwd_name, target_name, sizeof(target_name));
target_name[target_result] = '\0';
return(string(target_name));
}

@ -22,7 +22,9 @@ must not be misrepresented as being the original software.
distribution. distribution.
*/ */
#include "Internal.h" #include "Internal.h"
#include "dfhack/DFProcess.h" #include "LinuxProcess.h"
#include "ProcessFactory.h"
#include "MicrosoftSTL.h"
#include "dfhack/VersionInfo.h" #include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h" #include "dfhack/DFError.h"
#include <errno.h> #include <errno.h>
@ -30,37 +32,28 @@ distribution.
#include <stdio.h> #include <stdio.h>
using namespace DFHack; using namespace DFHack;
class WineProcess::Private namespace {
{ class WineProcess : public LinuxProcessBase
public:
Private(Process * self_)
{ {
my_descriptor = NULL; private:
my_handle = NULL; MicrosoftSTL stl;
my_pid = 0; public:
attached = false; WineProcess(uint32_t pid, VersionInfoFactory * factory);
suspended = false;
memFileHandle = 0; const std::string readSTLString (uint32_t offset);
self = self_; size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
void writeSTLString(const uint32_t address, const std::string writeString){};
// get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr);
}; };
~Private(){}; }
VersionInfo * my_descriptor;
Process * self; Process* DFHack::createWineProcess(uint32_t pid, VersionInfoFactory * factory)
pid_t my_handle; {
uint32_t my_pid; return new WineProcess(pid, factory);
string memFile; }
int memFileHandle;
bool attached;
bool suspended;
bool identified;
uint32_t STLSTR_buf_off;
uint32_t STLSTR_size_off;
uint32_t STLSTR_cap_off;
bool validate(char * exe_file, uint32_t pid, char * mem_file, vector <VersionInfo *> & known_versions);
};
WineProcess::WineProcess(uint32_t pid, vector <VersionInfo *> & known_versions) WineProcess::WineProcess(uint32_t pid, VersionInfoFactory * factory) : LinuxProcessBase(pid)
: d(new Private(this))
{ {
char dir_name [256]; char dir_name [256];
char exe_link_name [256]; char exe_link_name [256];
@ -70,12 +63,13 @@ WineProcess::WineProcess(uint32_t pid, vector <VersionInfo *> & known_versions)
char target_name[1024]; char target_name[1024];
int target_result; int target_result;
d->identified = false; identified = false;
d->my_descriptor = 0; my_descriptor = 0;
sprintf(dir_name,"/proc/%d/", pid); sprintf(dir_name,"/proc/%d/", pid);
sprintf(exe_link_name,"/proc/%d/exe", pid); sprintf(exe_link_name,"/proc/%d/exe", pid);
sprintf(mem_name,"/proc/%d/mem", pid); sprintf(mem_name,"/proc/%d/mem", pid);
memFile = mem_name;
sprintf(cwd_name,"/proc/%d/cwd", pid); sprintf(cwd_name,"/proc/%d/cwd", pid);
sprintf(cmdline_name,"/proc/%d/cmdline", pid); sprintf(cmdline_name,"/proc/%d/cmdline", pid);
@ -106,533 +100,34 @@ WineProcess::WineProcess(uint32_t pid, vector <VersionInfo *> & known_versions)
// put executable name and path together // put executable name and path together
sprintf(exe_link,"%s/%s",target_name,cmdline.c_str()); sprintf(exe_link,"%s/%s",target_name,cmdline.c_str());
// create wine process, add it to the vector md5wrapper md5;
d->identified = d->validate(exe_link,pid,mem_name,known_versions); // get hash of the running DF process
return; string hash = md5.getHashFromFile(exe_link);
} // create linux process, add it to the vector
} VersionInfo * vinfo = factory->getVersionInfoByMD5(hash);
} if(vinfo)
bool WineProcess::isSuspended()
{
return d->suspended;
}
bool WineProcess::isAttached()
{
return d->attached;
}
bool WineProcess::isIdentified()
{
return d->identified;
}
bool WineProcess::Private::validate(char* exe_file, uint32_t pid, char* mem_file, std::vector< VersionInfo* >& known_versions)
{
md5wrapper md5;
// get hash of the running DF process
string hash = md5.getHashFromFile(exe_file);
vector<VersionInfo *>::iterator it;
// iterate over the list of memory locations
for ( it=known_versions.begin() ; it < known_versions.end(); it++ )
{
string thishash;
try
{
thishash = (*it)->getMD5();
}
catch (Error::AllMemdef& e)
{
continue;
}
// are the md5 hashes the same?
if(VersionInfo::OS_WINDOWS == (*it)->getOS() && hash == thishash)
{
// keep track of created memory_info object so we can destroy it later
VersionInfo *m = new VersionInfo(**it);
my_descriptor = m;
m->setParentProcess(dynamic_cast<Process *>( self ));
my_handle = my_pid = pid;
// tell WineProcess about the /proc/PID/mem file
memFile = mem_file;
identified = true;
OffsetGroup * strGrp = m->getGroup("string")->getGroup("MSVC");
STLSTR_buf_off = strGrp->getOffset("buffer");
STLSTR_size_off = strGrp->getOffset("size");
STLSTR_cap_off = strGrp->getOffset("capacity");
return true;
}
}
return false;
}
WineProcess::~WineProcess()
{
if(d->attached)
{
detach();
}
// destroy our copy of the memory descriptor
if(d->my_descriptor)
delete d->my_descriptor;
delete d;
}
VersionInfo * WineProcess::getDescriptor()
{
return d->my_descriptor;
}
int WineProcess::getPID()
{
return d->my_pid;
}
//FIXME: implement
bool WineProcess::getThreadIDs(vector<uint32_t> & threads )
{
return false;
}
//FIXME: cross-reference with ELF segment entries?
void WineProcess::getMemRanges( vector<t_memrange> & ranges )
{
char buffer[1024];
char permissions[5]; // r/-, w/-, x/-, p/s, 0
sprintf(buffer, "/proc/%lu/maps", d->my_pid);
FILE *mapFile = ::fopen(buffer, "r");
uint64_t offset, device1, device2, node;
while (fgets(buffer, 1024, mapFile))
{
t_memrange temp;
temp.name[0] = 0;
sscanf(buffer, "%llx-%llx %s %llx %2llu:%2llu %llu %s",
&temp.start,
&temp.end,
(char*)&permissions,
&offset, &device1, &device2, &node,
(char*)&temp.name);
temp.read = permissions[0] == 'r';
temp.write = permissions[1] == 'w';
temp.execute = permissions[2] == 'x';
ranges.push_back(temp);
}
}
bool WineProcess::asyncSuspend()
{
return suspend();
}
bool WineProcess::suspend()
{
int status;
if(!d->attached)
return false;
if(d->suspended)
return true;
if (kill(d->my_handle, SIGSTOP) == -1)
{
// no, we got an error
perror("kill SIGSTOP error");
return false;
}
while(true)
{
// we wait on the pid
pid_t w = waitpid(d->my_handle, &status, 0);
if (w == -1)
{
// child died
perror("DF exited during suspend call");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{
break;
}
}
d->suspended = true;
return true;
}
bool WineProcess::forceresume()
{
return resume();
}
bool WineProcess::resume()
{
if(!d->attached)
return false;
if(!d->suspended)
return true;
if (ptrace(PTRACE_CONT, d->my_handle, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace resume error");
return false;
}
d->suspended = false;
return true;
}
bool WineProcess::attach()
{
int status;
if(d->attached)
{
if(!d->suspended)
return suspend();
return true;
}
// can we attach?
if (ptrace(PTRACE_ATTACH , d->my_handle, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace attach error");
cerr << "attach failed on pid " << d->my_handle << endl;
return false;
}
while(true)
{
// we wait on the pid
pid_t w = waitpid(d->my_handle, &status, 0);
if (w == -1)
{
// child died
perror("wait inside attach()");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{
break;
}
}
d->suspended = true;
int proc_pid_mem = open(d->memFile.c_str(),O_RDONLY);
if(proc_pid_mem == -1)
{
ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL);
cerr << d->memFile << endl;
cerr << "couldn't open /proc/" << d->my_handle << "/mem" << endl;
perror("open(memFile.c_str(),O_RDONLY)");
return false;
}
else
{
d->attached = true;
d->memFileHandle = proc_pid_mem;
return true; // we are attached
}
}
bool WineProcess::detach()
{
if(!d->attached) return true;
if(!d->suspended) suspend();
int result = 0;
// close /proc/PID/mem
result = close(d->memFileHandle);
if(result == -1)
{
cerr << "couldn't close /proc/"<< d->my_handle <<"/mem" << endl;
perror("mem file close");
return false;
}
else
{
// detach
result = ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL);
if(result == -1)
{
cerr << "couldn't detach from process pid" << d->my_handle << endl;
perror("ptrace detach");
return false;
}
else
{
d->attached = false;
return true;
}
}
}
// danger: uses recursion!
void WineProcess::read (const uint32_t offset, const uint32_t size, uint8_t *target)
{
if(size == 0) return;
ssize_t result;
ssize_t total = 0;
ssize_t remaining = size;
while (total != size)
{
result = pread(d->memFileHandle, target + total ,remaining,offset + total);
if(result == -1)
{
cerr << "pread failed: can't read " << size << " bytes at addres " << offset << endl;
cerr << "errno: " << errno << endl;
errno = 0;
throw Error::MemoryAccessDenied();
}
else
{
total += result;
remaining -= result;
}
}
}
uint8_t WineProcess::readByte (const uint32_t offset)
{
uint8_t val;
read(offset, 1, &val);
return val;
}
void WineProcess::readByte (const uint32_t offset, uint8_t &val )
{
read(offset, 1, &val);
}
uint16_t WineProcess::readWord (const uint32_t offset)
{
uint16_t val;
read(offset, 2, (uint8_t *) &val);
return val;
}
void WineProcess::readWord (const uint32_t offset, uint16_t &val)
{
read(offset, 2, (uint8_t *) &val);
}
uint32_t WineProcess::readDWord (const uint32_t offset)
{
uint32_t val;
read(offset, 4, (uint8_t *) &val);
return val;
}
void WineProcess::readDWord (const uint32_t offset, uint32_t &val)
{
read(offset, 4, (uint8_t *) &val);
}
float WineProcess::readFloat (const uint32_t offset)
{
float val;
read(offset, 4, (uint8_t *) &val);
return val;
}
void WineProcess::readFloat (const uint32_t offset, float &val)
{
read(offset, 4, (uint8_t *) &val);
}
void WineProcess::readQuad (const uint32_t offset, uint64_t &val)
{
read(offset, 8, (uint8_t *) &val);
}
uint64_t WineProcess::readQuad (const uint32_t offset)
{
uint64_t val;
read(offset, 8, (uint8_t *) &val);
return val;
}
/*
* WRITING
*/
void WineProcess::writeQuad (uint32_t offset, const uint64_t data)
{
#ifdef HAVE_64_BIT
ptrace(PTRACE_POKEDATA,d->my_handle, offset, data);
#else
ptrace(PTRACE_POKEDATA,d->my_handle, offset, (uint32_t) data);
ptrace(PTRACE_POKEDATA,d->my_handle, offset+4, (uint32_t) (data >> 32));
#endif
}
void WineProcess::writeDWord (uint32_t offset, uint32_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = readQuad(offset);
orig &= 0xFFFFFFFF00000000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#else
ptrace(PTRACE_POKEDATA,d->my_handle, offset, data);
#endif
}
// using these is expensive.
void WineProcess::writeWord (uint32_t offset, uint16_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = readQuad(offset);
orig &= 0xFFFFFFFFFFFF0000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#else
uint32_t orig = readDWord(offset);
orig &= 0xFFFF0000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#endif
}
void WineProcess::writeByte (uint32_t offset, uint8_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = readQuad(offset);
orig &= 0xFFFFFFFFFFFFFF00;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#else
uint32_t orig = readDWord(offset);
orig &= 0xFFFFFF00;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#endif
}
// blah. I hate the kernel devs for crippling /proc/PID/mem. THIS IS RIDICULOUS
void WineProcess::write (uint32_t offset, uint32_t size, uint8_t *source)
{
uint32_t indexptr = 0;
while (size > 0)
{
#ifdef HAVE_64_BIT
// quad!
if(size >= 8)
{ {
writeQuad(offset, *(uint64_t *) (source + indexptr)); my_descriptor = new VersionInfo(*vinfo);
offset +=8; my_descriptor->setParentProcess(this);
indexptr +=8; stl.init(this);
size -=8; identified = true;
} }
else
#endif
// default: we push 4 bytes
if(size >= 4)
{
writeDWord(offset, *(uint32_t *) (source + indexptr));
offset +=4;
indexptr +=4;
size -=4;
}
// last is either three or 2 bytes
else if(size >= 2)
{
writeWord(offset, *(uint16_t *) (source + indexptr));
offset +=2;
indexptr +=2;
size -=2;
}
// finishing move
else if(size == 1)
{
writeByte(offset, *(uint8_t *) (source + indexptr));
return; return;
} }
} }
} }
const std::string WineProcess::readCString (uint32_t offset)
{
std::string temp;
char temp_c[256];
int counter = 0;
char r;
do
{
r = readByte(offset+counter);
temp_c[counter] = r;
counter++;
} while (r && counter < 255);
temp_c[counter] = 0;
temp = temp_c;
return temp;
}
size_t WineProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) size_t WineProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity)
{ {
uint32_t start_offset = offset + d->STLSTR_buf_off; return stl.readSTLString(offset, buffer, bufcapacity);
size_t length = readDWord(offset + d->STLSTR_size_off);
size_t capacity = readDWord(offset + d->STLSTR_cap_off);
size_t read_real = min(length, bufcapacity-1);// keep space for null termination
// read data from inside the string structure
if(capacity < 16)
{
read(start_offset, read_real , (uint8_t *)buffer);
}
else // read data from what the offset + 4 dword points to
{
start_offset = readDWord(start_offset);// dereference the start offset
read(start_offset, read_real, (uint8_t *)buffer);
}
buffer[read_real] = 0;
return read_real;
} }
const string WineProcess::readSTLString (uint32_t offset) const string WineProcess::readSTLString (uint32_t offset)
{ {
uint32_t start_offset = offset + d->STLSTR_buf_off; return stl.readSTLString(offset);
size_t length = readDWord(offset + d->STLSTR_size_off);
size_t capacity = readDWord(offset + d->STLSTR_cap_off);
char * temp = new char[capacity+1];
// read data from inside the string structure
if(capacity < 16)
{
read(start_offset, capacity, (uint8_t *)temp);
}
else // read data from what the offset + 4 dword points to
{
start_offset = readDWord(start_offset);// dereference the start offset
read(start_offset, capacity, (uint8_t *)temp);
}
temp[length] = 0;
string ret = temp;
delete temp;
return ret;
} }
string WineProcess::readClassName (uint32_t vptr) string WineProcess::readClassName (uint32_t vptr)
{ {
int rtti = readDWord(vptr - 0x4); return stl.readClassName(vptr);
int typeinfo = readDWord(rtti + 0xC);
string raw = readCString(typeinfo + 0xC); // skips the .?AV
raw.resize(raw.length() - 2);// trim @@ from end
return raw;
}
string WineProcess::getPath()
{
char cwd_name[256];
char target_name[1024];
int target_result;
sprintf(cwd_name,"/proc/%d/cwd", getPID());
// resolve /proc/PID/exe link
target_result = readlink(cwd_name, target_name, sizeof(target_name));
target_name[target_result] = '\0';
return(string(target_name));
} }

@ -22,42 +22,34 @@ must not be misrepresented as being the original software.
distribution. distribution.
*/ */
#include "Internal.h" #include "Internal.h"
#include "dfhack/DFProcess.h" #include "LinuxProcess.h"
#include "ProcessFactory.h"
#include "dfhack/VersionInfo.h" #include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h" #include "dfhack/DFError.h"
#include <errno.h> #include <errno.h>
#include <sys/ptrace.h> #include <sys/ptrace.h>
using namespace DFHack; using namespace DFHack;
class NormalProcess::Private namespace {
{ class NormalProcess : public LinuxProcessBase
public:
Private(Process * self_)
{ {
my_descriptor = NULL; public:
my_handle = NULL; NormalProcess(uint32_t pid, VersionInfoFactory * known_versions);
my_pid = 0;
attached = false; const std::string readSTLString (uint32_t offset);
suspended = false; size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
memFileHandle = 0; void writeSTLString(const uint32_t address, const std::string writeString){};
self = self_; // get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr);
}; };
~Private(){}; }
Window* my_window;
VersionInfo * my_descriptor;
pid_t my_handle;
uint32_t my_pid;
string memFile;
int memFileHandle;
bool attached;
bool suspended;
bool identified;
Process * self;
bool validate(char * exe_file, uint32_t pid, char * mem_file, vector <VersionInfo *> & known_versions);
};
NormalProcess::NormalProcess(uint32_t pid, vector< VersionInfo* >& known_versions) Process* DFHack::createNormalProcess(uint32_t pid, VersionInfoFactory * known_versions)
: d(new Private(this)) {
return new NormalProcess(pid, known_versions);
}
NormalProcess::NormalProcess(uint32_t pid, VersionInfoFactory * known_versions) : LinuxProcessBase(pid)
{ {
char dir_name [256]; char dir_name [256];
char exe_link_name [256]; char exe_link_name [256];
@ -67,12 +59,13 @@ NormalProcess::NormalProcess(uint32_t pid, vector< VersionInfo* >& known_version
char target_name[1024]; char target_name[1024];
int target_result; int target_result;
d->identified = false; identified = false;
d->my_descriptor = 0; my_descriptor = 0;
sprintf(dir_name,"/proc/%d/", pid); sprintf(dir_name,"/proc/%d/", pid);
sprintf(exe_link_name,"/proc/%d/exe", pid); sprintf(exe_link_name,"/proc/%d/exe", pid);
sprintf(mem_name,"/proc/%d/mem", pid); sprintf(mem_name,"/proc/%d/mem", pid);
memFile = mem_name;
sprintf(cwd_name,"/proc/%d/cwd", pid); sprintf(cwd_name,"/proc/%d/cwd", pid);
sprintf(cmdline_name,"/proc/%d/cmdline", pid); sprintf(cmdline_name,"/proc/%d/cmdline", pid);
@ -88,462 +81,20 @@ NormalProcess::NormalProcess(uint32_t pid, vector< VersionInfo* >& known_version
// is this the regular linux DF? // is this the regular linux DF?
if (strstr(target_name, "dwarfort.exe") != 0 || strstr(target_name,"Dwarf_Fortress") != 0) if (strstr(target_name, "dwarfort.exe") != 0 || strstr(target_name,"Dwarf_Fortress") != 0)
{ {
md5wrapper md5;
// get hash of the running DF process
string hash = md5.getHashFromFile(target_name);
// create linux process, add it to the vector // create linux process, add it to the vector
d->identified = d->validate(target_name,pid,mem_name,known_versions ); VersionInfo * vinfo = known_versions->getVersionInfoByMD5(hash);
return; if(vinfo)
}
}
bool NormalProcess::isSuspended()
{
return d->suspended;
}
bool NormalProcess::isAttached()
{
return d->attached;
}
bool NormalProcess::isIdentified()
{
return d->identified;
}
bool NormalProcess::Private::validate(char * exe_file,uint32_t pid, char * memFile, vector <VersionInfo *> & known_versions)
{
md5wrapper md5;
// get hash of the running DF process
string hash = md5.getHashFromFile(exe_file);
vector<VersionInfo *>::iterator it;
// iterate over the list of memory locations
for ( it=known_versions.begin() ; it < known_versions.end(); it++ )
{
try
{
//cout << hash << " ?= " << (*it)->getMD5() << endl;
if(hash == (*it)->getMD5()) // are the md5 hashes the same?
{
VersionInfo * m = *it;
if (VersionInfo::OS_LINUX == m->getOS())
{
VersionInfo *m2 = new VersionInfo(*m);
my_descriptor = m2;
m2->setParentProcess(dynamic_cast<Process *>( self ));
my_handle = my_pid = pid;
}
else
{
// some error happened, continue with next process
continue;
}
// tell NormalProcess about the /proc/PID/mem file
this->memFile = memFile;
identified = true;
return true;
}
}
catch (Error::AllMemdef&)
{
continue;
}
}
return false;
}
NormalProcess::~NormalProcess()
{
if(d->attached)
{
detach();
}
// destroy our copy of the memory descriptor
if(d->my_descriptor)
delete d->my_descriptor;
delete d;
}
VersionInfo * NormalProcess::getDescriptor()
{
return d->my_descriptor;
}
int NormalProcess::getPID()
{
return d->my_pid;
}
//FIXME: implement
bool NormalProcess::getThreadIDs(vector<uint32_t> & threads )
{
return false;
}
//FIXME: cross-reference with ELF segment entries?
void NormalProcess::getMemRanges( vector<t_memrange> & ranges )
{
char buffer[1024];
char permissions[5]; // r/-, w/-, x/-, p/s, 0
sprintf(buffer, "/proc/%lu/maps", d->my_pid);
FILE *mapFile = ::fopen(buffer, "r");
uint64_t offset, device1, device2, node;
while (fgets(buffer, 1024, mapFile))
{
t_memrange temp;
temp.name[0] = 0;
sscanf(buffer, "%llx-%llx %s %llx %2llu:%2llu %llu %s",
&temp.start,
&temp.end,
(char*)&permissions,
&offset, &device1, &device2, &node,
(char*)&temp.name);
temp.read = permissions[0] == 'r';
temp.write = permissions[1] == 'w';
temp.execute = permissions[2] == 'x';
ranges.push_back(temp);
}
}
bool NormalProcess::asyncSuspend()
{
return suspend();
}
bool NormalProcess::suspend()
{
int status;
if(!d->attached)
return false;
if(d->suspended)
return true;
if (kill(d->my_handle, SIGSTOP) == -1)
{
// no, we got an error
perror("kill SIGSTOP error");
return false;
}
while(true)
{
// we wait on the pid
pid_t w = waitpid(d->my_handle, &status, 0);
if (w == -1)
{
// child died
perror("DF exited during suspend call");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{
break;
}
}
d->suspended = true;
return true;
}
bool NormalProcess::forceresume()
{
return resume();
}
bool NormalProcess::resume()
{
if(!d->attached)
return false;
if(!d->suspended)
return true;
if (ptrace(PTRACE_CONT, d->my_handle, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace resume error");
return false;
}
d->suspended = false;
return true;
}
bool NormalProcess::attach()
{
int status;
if(d->attached)
{
if(!d->suspended)
return suspend();
return true;
}
// can we attach?
if (ptrace(PTRACE_ATTACH , d->my_handle, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace attach error");
cerr << "attach failed on pid " << d->my_handle << endl;
return false;
}
while(true)
{
// we wait on the pid
pid_t w = waitpid(d->my_handle, &status, 0);
if (w == -1)
{
// child died
perror("wait inside attach()");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{
break;
}
}
d->suspended = true;
int proc_pid_mem = open(d->memFile.c_str(),O_RDONLY);
if(proc_pid_mem == -1)
{
ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL);
cerr << "couldn't open /proc/" << d->my_handle << "/mem" << endl;
perror("open(memFile.c_str(),O_RDONLY)");
return false;
}
else
{
d->attached = true;
d->memFileHandle = proc_pid_mem;
return true; // we are attached
}
}
bool NormalProcess::detach()
{
if(!d->attached) return true;
if(!d->suspended) suspend();
int result = 0;
// close /proc/PID/mem
result = close(d->memFileHandle);
if(result == -1)
{
cerr << "couldn't close /proc/"<< d->my_handle <<"/mem" << endl;
perror("mem file close");
return false;
}
else
{
// detach
result = ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL);
if(result == -1)
{ {
cerr << "couldn't detach from process pid" << d->my_handle << endl; my_descriptor = new VersionInfo(*vinfo);
perror("ptrace detach"); my_descriptor->setParentProcess(this);
return false; identified = true;
}
else
{
d->attached = false;
return true;
} }
} }
} }
// danger: uses recursion!
void NormalProcess::read (const uint32_t offset, const uint32_t size, uint8_t *target)
{
if(size == 0) return;
ssize_t result;
result = pread(d->memFileHandle, target,size,offset);
if(result != size)
{
if(result == -1)
{
cerr << "pread failed: can't read 0x" << hex << size << " bytes at address 0x" << offset << endl;
cerr << "errno: " << errno << endl;
errno = 0;
throw Error::MemoryAccessDenied();
}
else
{
this->read(offset + result, size - result, target + result);
}
}
}
uint8_t NormalProcess::readByte (const uint32_t offset)
{
uint8_t val;
read(offset, 1, &val);
return val;
}
void NormalProcess::readByte (const uint32_t offset, uint8_t &val )
{
read(offset, 1, &val);
}
uint16_t NormalProcess::readWord (const uint32_t offset)
{
uint16_t val;
read(offset, 2, (uint8_t *) &val);
return val;
}
void NormalProcess::readWord (const uint32_t offset, uint16_t &val)
{
read(offset, 2, (uint8_t *) &val);
}
uint32_t NormalProcess::readDWord (const uint32_t offset)
{
uint32_t val;
read(offset, 4, (uint8_t *) &val);
return val;
}
void NormalProcess::readDWord (const uint32_t offset, uint32_t &val)
{
read(offset, 4, (uint8_t *) &val);
}
float NormalProcess::readFloat (const uint32_t offset)
{
float val;
read(offset, 4, (uint8_t *) &val);
return val;
}
void NormalProcess::readFloat (const uint32_t offset, float &val)
{
read(offset, 4, (uint8_t *) &val);
}
uint64_t NormalProcess::readQuad (const uint32_t offset)
{
uint64_t val;
read(offset, 8, (uint8_t *) &val);
return val;
}
void NormalProcess::readQuad (const uint32_t offset, uint64_t &val)
{
read(offset, 8, (uint8_t *) &val);
}
/*
* WRITING
*/
void NormalProcess::writeQuad (uint32_t offset, const uint64_t data)
{
#ifdef HAVE_64_BIT
ptrace(PTRACE_POKEDATA,d->my_handle, offset, data);
#else
ptrace(PTRACE_POKEDATA,d->my_handle, offset, (uint32_t) data);
ptrace(PTRACE_POKEDATA,d->my_handle, offset+4, (uint32_t) (data >> 32));
#endif
}
void NormalProcess::writeDWord (uint32_t offset, uint32_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = readQuad(offset);
orig &= 0xFFFFFFFF00000000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#else
ptrace(PTRACE_POKEDATA,d->my_handle, offset, data);
#endif
}
// using these is expensive.
void NormalProcess::writeWord (uint32_t offset, uint16_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = readQuad(offset);
orig &= 0xFFFFFFFFFFFF0000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#else
uint32_t orig = readDWord(offset);
orig &= 0xFFFF0000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#endif
}
void NormalProcess::writeByte (uint32_t offset, uint8_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = readQuad(offset);
orig &= 0xFFFFFFFFFFFFFF00;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#else
uint32_t orig = readDWord(offset);
orig &= 0xFFFFFF00;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#endif
}
// blah. I hate the kernel devs for crippling /proc/PID/mem. THIS IS RIDICULOUS
void NormalProcess::write (uint32_t offset, uint32_t size, uint8_t *source)
{
uint32_t indexptr = 0;
while (size > 0)
{
#ifdef HAVE_64_BIT
// quad!
if(size >= 8)
{
writeQuad(offset, *(uint64_t *) (source + indexptr));
offset +=8;
indexptr +=8;
size -=8;
}
else
#endif
// default: we push 4 bytes
if(size >= 4)
{
writeDWord(offset, *(uint32_t *) (source + indexptr));
offset +=4;
indexptr +=4;
size -=4;
}
// last is either three or 2 bytes
else if(size >= 2)
{
writeWord(offset, *(uint16_t *) (source + indexptr));
offset +=2;
indexptr +=2;
size -=2;
}
// finishing move
else if(size == 1)
{
writeByte(offset, *(uint8_t *) (source + indexptr));
return;
}
}
}
const std::string NormalProcess::readCString (uint32_t offset)
{
std::string temp;
char temp_c[256];
int counter = 0;
char r;
do
{
r = readByte(offset+counter);
temp_c[counter] = r;
counter++;
} while (r && counter < 255);
temp_c[counter] = 0;
temp = temp_c;
return temp;
}
struct _Rep_base struct _Rep_base
{ {
uint32_t _M_length; uint32_t _M_length;
@ -554,7 +105,7 @@ struct _Rep_base
size_t NormalProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) size_t NormalProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity)
{ {
_Rep_base header; _Rep_base header;
offset = readDWord(offset); offset = Process::readDWord(offset);
read(offset - sizeof(_Rep_base),sizeof(_Rep_base),(uint8_t *)&header); read(offset - sizeof(_Rep_base),sizeof(_Rep_base),(uint8_t *)&header);
size_t read_real = min((size_t)header._M_length, bufcapacity-1);// keep space for null termination size_t read_real = min((size_t)header._M_length, bufcapacity-1);// keep space for null termination
read(offset,read_real,(uint8_t * )buffer); read(offset,read_real,(uint8_t * )buffer);
@ -566,7 +117,7 @@ const string NormalProcess::readSTLString (uint32_t offset)
{ {
_Rep_base header; _Rep_base header;
offset = readDWord(offset); offset = Process::readDWord(offset);
read(offset - sizeof(_Rep_base),sizeof(_Rep_base),(uint8_t *)&header); read(offset - sizeof(_Rep_base),sizeof(_Rep_base),(uint8_t *)&header);
// FIXME: use char* everywhere, avoid string // FIXME: use char* everywhere, avoid string
@ -579,22 +130,10 @@ const string NormalProcess::readSTLString (uint32_t offset)
string NormalProcess::readClassName (uint32_t vptr) string NormalProcess::readClassName (uint32_t vptr)
{ {
int typeinfo = readDWord(vptr - 0x4); int typeinfo = Process::readDWord(vptr - 0x4);
int typestring = readDWord(typeinfo + 0x4); int typestring = Process::readDWord(typeinfo + 0x4);
string raw = readCString(typestring); string raw = readCString(typestring);
size_t start = raw.find_first_of("abcdefghijklmnopqrstuvwxyz");// trim numbers size_t start = raw.find_first_of("abcdefghijklmnopqrstuvwxyz");// trim numbers
size_t end = raw.length(); size_t end = raw.length();
return raw.substr(start,end-start); return raw.substr(start,end-start);
} }
string NormalProcess::getPath()
{
char cwd_name[256];
char target_name[1024];
int target_result;
sprintf(cwd_name,"/proc/%d/cwd", getPID());
// resolve /proc/PID/exe link
target_result = readlink(cwd_name, target_name, sizeof(target_name));
target_name[target_result] = '\0';
return(string(target_name));
}

@ -22,68 +22,25 @@ must not be misrepresented as being the original software.
distribution. distribution.
*/ */
#include "Internal.h" #include "Internal.h"
#include "dfhack/DFProcess.h" #include "SHMProcess.h"
#include "dfhack/VersionInfo.h" #include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h" #include "dfhack/DFError.h"
#include "shms.h" #include "shms.h"
#include "mod-core.h" #include "mod-core.h"
using namespace DFHack; using namespace DFHack;
// a full memory barrier! better be safe than sorry. SHMProcess::Private::Private(SHMProcess * self_)
class SHMProcess::Private
{ {
public: memdescriptor = NULL;
Private() process_ID = 0;
{ attached = false;
memdescriptor = NULL; locked = false;
process_ID = 0; identified = false;
shm_addr = 0; useYield = 0;
attached = false; DFSVMutex = 0;
locked = false; DFCLMutex = 0;
identified = false; DFCLSuspendMutex = 0;
useYield = 0; self = self_;
DFSVMutex = 0;
DFCLMutex = 0;
DFCLSuspendMutex = 0;
attachmentIdx = -1;
};
~Private(){};
VersionInfo * memdescriptor;
SHMProcess * self;
uint32_t process_ID;
char *shm_addr;
HANDLE DFSVMutex;
HANDLE DFCLMutex;
HANDLE DFCLSuspendMutex;
int attachmentIdx;
bool attached;
bool locked;
bool identified;
bool useYield;
bool validate(std::vector< VersionInfo* >& known_versions);
bool Aux_Core_Attach(bool & versionOK, uint32_t & PID);
bool SetAndWait (uint32_t state);
bool GetLocks();
bool AreLocksOk();
void FreeLocks();
};
// some helpful macros to keep the code bloat in check
#define SHMCMD ( (uint32_t *) shm_addr)[attachmentIdx]
#define D_SHMCMD ( (uint32_t *) (d->shm_addr))[d->attachmentIdx]
#define SHMHDR ((shm_core_hdr *)shm_addr)
#define D_SHMHDR ((shm_core_hdr *)(d->shm_addr))
#define SHMDATA(type) ((type *)(shm_addr + SHM_HEADER))
#define D_SHMDATA(type) ((type *)(d->shm_addr + SHM_HEADER))
bool SHMProcess::SetAndWait (uint32_t state)
{
return d->SetAndWait(state);
} }
bool SHMProcess::Private::SetAndWait (uint32_t state) bool SHMProcess::Private::SetAndWait (uint32_t state)
@ -116,6 +73,7 @@ bool SHMProcess::Private::SetAndWait (uint32_t state)
} }
cnt++; cnt++;
} }
// server returned a generic error
if(SHMCMD == CORE_ERROR) if(SHMCMD == CORE_ERROR)
{ {
return false; return false;
@ -123,6 +81,11 @@ bool SHMProcess::Private::SetAndWait (uint32_t state)
return true; return true;
} }
bool SHMProcess::SetAndWait (uint32_t state)
{
return d->SetAndWait(state);
}
uint32_t OS_getAffinity() uint32_t OS_getAffinity()
{ {
HANDLE hProcess = GetCurrentProcess(); HANDLE hProcess = GetCurrentProcess();
@ -253,71 +216,10 @@ bool SHMProcess::Private::AreLocksOk()
return false; return false;
} }
bool SHMProcess::Private::validate(VersionInfoFactory * factory)
/*
char svmutexname [256];
char clmutexname [256];
sprintf(clmutexname,"DFCLMutex-%d",PID);
// get server and client mutex
d->DFSVMutex = OpenMutex(SYNCHRONIZE,false, svmutexname);
if(d->DFSVMutex == 0)
{
return;
}
d->DFCLMutex = OpenMutex(SYNCHRONIZE,false, clmutexname);
if(d->DFCLMutex == 0)
{
return;
}
*/
SHMProcess::SHMProcess(uint32_t PID, vector <VersionInfo *> & known_versions)
: d(new Private())
{
d->process_ID = PID;
d->self = this;
// attach the SHM
if(!attach())
{
return;
}
// Test bridge version, get PID, sync Yield
bool bridgeOK;
if(!d->Aux_Core_Attach(bridgeOK,d->process_ID))
{
detach();
throw Error::SHMAttachFailure();
}
else if(!bridgeOK)
{
detach();
throw Error::SHMVersionMismatch();
}
d->validate(known_versions);
// at this point, DF is attached and suspended, make it run
detach();
}
bool SHMProcess::isSuspended()
{
return d->locked;
}
bool SHMProcess::isAttached()
{
return d->attached;
}
bool SHMProcess::isIdentified()
{
return d->identified;
}
bool SHMProcess::Private::validate(vector <VersionInfo *> & known_versions)
{ {
// try to identify the DF version // try to identify the DF version
IMAGE_NT_HEADERS32 pe_header; IMAGE_NT_HEADERS pe_header;
IMAGE_SECTION_HEADER sections[16]; IMAGE_SECTION_HEADER sections[16];
HMODULE hmod = NULL; HMODULE hmod = NULL;
DWORD junk; DWORD junk;
@ -340,60 +242,23 @@ bool SHMProcess::Private::validate(vector <VersionInfo *> & known_versions)
uint32_t base = (uint32_t)hmod; uint32_t base = (uint32_t)hmod;
// read from this process // read from this process
uint32_t pe_offset = self->readDWord(base+0x3C); uint32_t pe_offset = self->Process::readDWord(base+0x3C);
self->read(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header); self->read(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header);
self->read(base + pe_offset+ sizeof(pe_header), sizeof(sections) , (uint8_t *)&sections ); self->read(base + pe_offset+ sizeof(pe_header), sizeof(sections) , (uint8_t *)&sections );
// iterate over the list of memory locations VersionInfo* vinfo = factory->getVersionInfoByPETimestamp(pe_header.FileHeader.TimeDateStamp);
vector<VersionInfo *>::iterator it; if(vinfo)
for ( it=known_versions.begin() ; it < known_versions.end(); it++ )
{ {
uint32_t pe_timestamp; VersionInfo *m = new VersionInfo(*vinfo);
try m->RebaseAll(base);
{ memdescriptor = m;
pe_timestamp = (*it)->getPE(); m->setParentProcess(self);
} identified = true;
catch(Error::AllMemdef&) CloseHandle(hProcess);
{ return true;
continue;
}
if (pe_timestamp == pe_header.FileHeader.TimeDateStamp)
{
VersionInfo *m = new VersionInfo(**it);
m->RebaseAll(base);
memdescriptor = m;
m->setParentProcess(self);
identified = true;
cerr << "identified " << m->getVersion() << endl;
CloseHandle(hProcess);
return true;
}
} }
return false; return false;
} }
SHMProcess::~SHMProcess()
{
if(d->attached)
{
detach();
}
// destroy data model. this is assigned by processmanager
if(d->memdescriptor)
{
delete d->memdescriptor;
}
delete d;
}
VersionInfo * SHMProcess::getDescriptor()
{
return d->memdescriptor;
}
int SHMProcess::getPID()
{
return d->process_ID;
}
bool SHMProcess::getThreadIDs(vector<uint32_t> & threads ) bool SHMProcess::getThreadIDs(vector<uint32_t> & threads )
{ {
@ -428,137 +293,18 @@ bool SHMProcess::getThreadIDs(vector<uint32_t> & threads )
//FIXME: use VirtualQuery to probe for memory ranges, cross-reference with base-corrected PE segment entries //FIXME: use VirtualQuery to probe for memory ranges, cross-reference with base-corrected PE segment entries
void SHMProcess::getMemRanges( vector<t_memrange> & ranges ) void SHMProcess::getMemRanges( vector<t_memrange> & ranges )
{ {
// code here is taken from hexsearch by Silas Dunmore. // BLAH
// As this IMHO isn't a 'sunstantial portion' of anything, I'm not including the MIT license here ranges.clear();
// I'm faking this, because there's no way I'm using VirtualQuery
t_memrange temp;
uint32_t base = d->memdescriptor->getBase();
temp.start = base + 0x1000; // more fakery.
temp.end = base + readDWord(base+readDWord(base+0x3C)+0x50)-1; // yay for magic.
temp.read = 1;
temp.write = 1;
temp.execute = 0; // fake
strcpy(temp.name,"pants");
ranges.push_back(temp);
} }
bool SHMProcess::suspend() bool SHMProcess::acquireSuspendLock()
{ {
if(!d->attached) return ( WaitForSingleObject(d->DFCLSuspendMutex,INFINITE) == 0 );
{
return false;
}
if(d->locked)
{
return true;
}
//cerr << "suspend" << endl;// FIXME: throw
// FIXME: this should be controlled on the server side
// FIXME: IF server got CORE_RUN in this frame, interpret CORE_SUSPEND as CORE_STEP
// did we just resume a moment ago?
if(D_SHMCMD == CORE_RUN)
{
//fprintf(stderr,"%d invokes step\n",d->attachmentIdx);
// wait for the next window
/*
if(!d->SetAndWait(CORE_STEP))
{
throw Error::SHMLockingError("if(!d->SetAndWait(CORE_STEP))");
}
*/
D_SHMCMD = CORE_STEP;
}
else
{
//fprintf(stderr,"%d invokes suspend\n",d->attachmentIdx);
// lock now
/*
if(!d->SetAndWait(CORE_SUSPEND))
{
throw Error::SHMLockingError("if(!d->SetAndWait(CORE_SUSPEND))");
}
*/
D_SHMCMD = CORE_SUSPEND;
}
//fprintf(stderr,"waiting for lock\n");
// we wait for the server to give up our suspend lock (held by default)
if( WaitForSingleObject(d->DFCLSuspendMutex,INFINITE) == 0 )
{
d->locked = true;
return true;
}
return false;
}
// FIXME: needs a good think-through
bool SHMProcess::asyncSuspend()
{
if(!d->attached)
{
return false;
}
if(d->locked)
{
return true;
}
//cerr << "async suspend" << endl;// FIXME: throw
uint32_t cmd = D_SHMCMD;
if(cmd == CORE_SUSPENDED)
{
// we have to hold the lock to be really suspended
if( WaitForSingleObject(d->DFCLSuspendMutex,INFINITE) == 0 )
{
d->locked = true;
return true;
}
return false;
}
else
{
// did we just resume a moment ago?
if(cmd == CORE_STEP)
{
return false;
}
else if(cmd == CORE_RUN)
{
D_SHMCMD = CORE_STEP;
}
else
{
D_SHMCMD = CORE_SUSPEND;
}
return false;
}
}
bool SHMProcess::forceresume()
{
return resume();
} }
// FIXME: wait for the server to advance a step! bool SHMProcess::releaseSuspendLock()
bool SHMProcess::resume()
{ {
if(!d->attached) return ( ReleaseMutex(d->DFCLSuspendMutex) != 0);
return false;
if(!d->locked)
return true;
//cerr << "resume" << endl;// FIXME: throw
// unlock the suspend lock
if( ReleaseMutex(d->DFCLSuspendMutex) != 0)
{
d->locked = false;
if(d->SetAndWait(CORE_RUN)) // we have to make sure the server responds!
{
return true;
}
throw Error::SHMLockingError("if(d->SetAndWait(CORE_RUN))");
}
throw Error::SHMLockingError("if( ReleaseMutex(d->DFCLSuspendMutex) != 0)");
return false;
} }
@ -576,21 +322,6 @@ bool SHMProcess::attach()
//cerr << "server is full or not really there!" << endl; //cerr << "server is full or not really there!" << endl;
return false; return false;
} }
/*
// check if DF is there
if(!d->isValidSV())
{
return false; // NOT
}
*/
/*
// try locking client mutex
uint32_t result = WaitForSingleObject(d->DFCLMutex,0);
if( result != WAIT_OBJECT_0 && result != WAIT_ABANDONED)
{
return false; // we couldn't lock it
}
*/
/* /*
* Locate the segment. * Locate the segment.
@ -633,12 +364,10 @@ bool SHMProcess::attach()
bool SHMProcess::detach() bool SHMProcess::detach()
{ {
if(!d->attached) return true; if(!d->attached) return true;
//cerr << "detach" << endl;// FIXME: throw
if(d->locked) if(d->locked)
{ {
resume(); resume();
} }
//cerr << "detach after resume" << endl;// FIXME: throw
// detach segment // detach segment
UnmapViewOfFile(d->shm_addr); UnmapViewOfFile(d->shm_addr);
// release it for some other client // release it for some other client
@ -650,280 +379,10 @@ bool SHMProcess::detach()
return true; return true;
} }
void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer)
{
if(!d->locked) throw Error::MemoryAccessDenied();
// normal read under 1MB
if(size <= SHM_BODY)
{
D_SHMHDR->address = src_address;
D_SHMHDR->length = size;
full_barrier
d->SetAndWait(CORE_READ);
memcpy (target_buffer, D_SHMDATA(void),size);
}
// a big read, we pull data over the shm in iterations
else
{
// first read equals the size of the SHM window
uint32_t to_read = SHM_BODY;
while (size)
{
// read to_read bytes from src_cursor
D_SHMHDR->address = src_address;
D_SHMHDR->length = to_read;
full_barrier
d->SetAndWait(CORE_READ);
memcpy (target_buffer, D_SHMDATA(void) ,to_read);
// decrease size by bytes read
size -= to_read;
// move the cursors
src_address += to_read;
target_buffer += to_read;
// check how much to write in the next iteration
to_read = min(size, (uint32_t) SHM_BODY);
}
}
}
uint8_t SHMProcess::readByte (const uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_BYTE);
return D_SHMHDR->value;
}
void SHMProcess::readByte (const uint32_t offset, uint8_t &val )
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_BYTE);
val = D_SHMHDR->value;
}
uint16_t SHMProcess::readWord (const uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_WORD);
return D_SHMHDR->value;
}
void SHMProcess::readWord (const uint32_t offset, uint16_t &val)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_WORD);
val = D_SHMHDR->value;
}
uint32_t SHMProcess::readDWord (const uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_DWORD);
return D_SHMHDR->value;
}
void SHMProcess::readDWord (const uint32_t offset, uint32_t &val)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_DWORD);
val = D_SHMHDR->value;
}
float SHMProcess::readFloat (const uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_DWORD);
return reinterpret_cast<float&> (D_SHMHDR->value);
}
void SHMProcess::readFloat (const uint32_t offset, float &val)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_DWORD);
val = reinterpret_cast<float&> (D_SHMHDR->value);
}
uint64_t SHMProcess::readQuad (const uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_QUAD);
return D_SHMHDR->Qvalue;
}
void SHMProcess::readQuad (const uint32_t offset, uint64_t &val)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_QUAD);
val = D_SHMHDR->Qvalue;
}
/*
* WRITING
*/
void SHMProcess::writeQuad (uint32_t offset, uint64_t data)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->Qvalue = data;
full_barrier
d->SetAndWait(CORE_WRITE_QUAD);
}
void SHMProcess::writeDWord (uint32_t offset, uint32_t data)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->value = data;
full_barrier
d->SetAndWait(CORE_WRITE_DWORD);
}
// using these is expensive.
void SHMProcess::writeWord (uint32_t offset, uint16_t data)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->value = data;
full_barrier
d->SetAndWait(CORE_WRITE_WORD);
}
void SHMProcess::writeByte (uint32_t offset, uint8_t data)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->value = data;
full_barrier
d->SetAndWait(CORE_WRITE_BYTE);
}
void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer)
{
if(!d->locked) throw Error::MemoryAccessDenied();
// normal write under 1MB
if(size <= SHM_BODY)
{
D_SHMHDR->address = dst_address;
D_SHMHDR->length = size;
memcpy(D_SHMDATA(void),source_buffer, size);
full_barrier
d->SetAndWait(CORE_WRITE);
}
// a big write, we push this over the shm in iterations
else
{
// first write equals the size of the SHM window
uint32_t to_write = SHM_BODY;
while (size)
{
// write to_write bytes to dst_cursor
D_SHMHDR->address = dst_address;
D_SHMHDR->length = to_write;
memcpy(D_SHMDATA(void),source_buffer, to_write);
full_barrier
d->SetAndWait(CORE_WRITE);
// decrease size by bytes written
size -= to_write;
// move the cursors
source_buffer += to_write;
dst_address += to_write;
// check how much to write in the next iteration
to_write = min(size, (uint32_t) SHM_BODY);
}
}
}
// FIXME: butt-fugly
const std::string SHMProcess::readCString (uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
std::string temp;
char temp_c[256];
int counter = 0;
char r;
do
{
r = readByte(offset+counter);
temp_c[counter] = r;
counter++;
} while (r && counter < 255);
temp_c[counter] = 0;
temp = temp_c;
return temp;
}
const std::string SHMProcess::readSTLString(uint32_t offset)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_STL_STRING);
return(string( D_SHMDATA(char) ));
}
size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = offset;
full_barrier
d->SetAndWait(CORE_READ_STL_STRING);
size_t length = D_SHMHDR->value;
size_t fit = min(bufcapacity - 1, length);
strncpy(buffer,D_SHMDATA(char),fit);
buffer[fit] = 0;
return fit;
}
void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString)
{
if(!d->locked) throw Error::MemoryAccessDenied();
D_SHMHDR->address = address;
strncpy(D_SHMDATA(char),writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator
full_barrier
d->SetAndWait(CORE_WRITE_STL_STRING);
}
string SHMProcess::readClassName (uint32_t vptr) string SHMProcess::readClassName (uint32_t vptr)
{ {
int rtti = readDWord(vptr - 0x4); int rtti = Process::readDWord(vptr - 0x4);
int typeinfo = readDWord(rtti + 0xC); int typeinfo = Process::readDWord(rtti + 0xC);
string raw = readCString(typeinfo + 0xC); // skips the .?AV string raw = readCString(typeinfo + 0xC); // skips the .?AV
raw.resize(raw.length() - 2);// trim @@ from end raw.resize(raw.length() - 2);// trim @@ from end
return raw; return raw;
@ -940,51 +399,10 @@ string SHMProcess::getPath()
string out(String); string out(String);
return(out.substr(0,out.find_last_of("\\"))); return(out.substr(0,out.find_last_of("\\")));
} }
// get module index by name and version. bool 0 = error
bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT)
{
if(!d->locked) throw Error::MemoryAccessDenied();
modulelookup * payload = D_SHMDATA(modulelookup);
payload->version = version;
strncpy(payload->name,name,255);
payload->name[255] = 0;
if(!SetAndWait(CORE_ACQUIRE_MODULE))
{
return false; // FIXME: throw a fatal exception instead
}
if(D_SHMHDR->error)
{
return false;
}
//fprintf(stderr,"%s v%d : %d\n", name, version, D_SHMHDR->value);
OUTPUT = D_SHMHDR->value;
return true;
}
char * SHMProcess::getSHMStart (void) char * SHMProcess::getSHMStart (void)
{ {
if(!d->locked) throw Error::MemoryAccessDenied(); if(!d->locked) throw Error::MemoryAccessDenied();
return /*d->shm_addr_with_cl_idx*/ d->shm_addr;
}
bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, uint32_t & PID)
{
if(!locked) throw Error::MemoryAccessDenied();
SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); return d->shm_addr;
if(!SetAndWait(CORE_ATTACH)) return false;
/*
cerr <<"CORE_VERSION" << CORE_VERSION << endl;
cerr <<"server CORE_VERSION" << SHMDATA(coreattach)->sv_version << endl;
*/
versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION );
PID = SHMDATA(coreattach)->sv_PID;
useYield = SHMDATA(coreattach)->sv_useYield;
#ifdef DEBUG
if(useYield) cerr << "Using Yield!" << endl;
#endif
return true;
} }

@ -22,179 +22,205 @@ must not be misrepresented as being the original software.
distribution. distribution.
*/ */
#include "Internal.h" #include "Internal.h"
#include "dfhack/DFProcess.h" #include "ProcessFactory.h"
#include "MicrosoftSTL.h"
#include "dfhack/VersionInfo.h" #include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h" #include "dfhack/DFError.h"
#include <string.h>
using namespace DFHack; using namespace DFHack;
class NormalProcess::Private namespace
{ {
public: class NormalProcess : public Process
Private() {
{ private:
my_descriptor = NULL; VersionInfo * my_descriptor;
my_handle = NULL; HANDLE my_handle;
my_main_thread = NULL; HANDLE my_main_thread;
my_pid = 0; uint32_t my_pid;
attached = false; string memFile;
suspended = false; bool attached;
}; bool suspended;
~Private(){}; bool identified;
VersionInfo * my_descriptor; IMAGE_NT_HEADERS pe_header;
HANDLE my_handle; IMAGE_SECTION_HEADER * sections;
HANDLE my_main_thread; uint32_t base;
uint32_t my_pid; MicrosoftSTL stl;
string memFile; public:
bool attached; NormalProcess(uint32_t pid, VersionInfoFactory * factory);
bool suspended; ~NormalProcess();
bool identified; bool attach();
uint32_t STLSTR_buf_off; bool detach();
uint32_t STLSTR_size_off;
uint32_t STLSTR_cap_off; bool suspend();
}; bool asyncSuspend();
bool resume();
bool forceresume();
void readQuad(const uint32_t address, uint64_t & value);
void writeQuad(const uint32_t address, const uint64_t value);
void readDWord(const uint32_t address, uint32_t & value);
void writeDWord(const uint32_t address, const uint32_t value);
void readFloat(const uint32_t address, float & value);
void readWord(const uint32_t address, uint16_t & value);
void writeWord(const uint32_t address, const uint16_t value);
void readByte(const uint32_t address, uint8_t & value);
void writeByte(const uint32_t address, const uint8_t value);
void read( uint32_t address, uint32_t length, uint8_t* buffer);
void write(uint32_t address, uint32_t length, uint8_t* buffer);
const std::string readSTLString (uint32_t offset);
size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
void writeSTLString(const uint32_t address, const std::string writeString){};
// get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr);
const std::string readCString (uint32_t offset);
bool isSuspended();
bool isAttached();
bool isIdentified();
bool getThreadIDs(std::vector<uint32_t> & threads );
void getMemRanges(std::vector<t_memrange> & ranges );
VersionInfo *getDescriptor();
int getPID();
std::string getPath();
// get module index by name and version. bool 1 = error
bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { OUTPUT=0; return false;};
// get the SHM start if available
char * getSHMStart (void){return 0;};
// set a SHM command and wait for a response
bool SetAndWait (uint32_t state){return false;};
};
}
Process* DFHack::createNormalProcess(uint32_t pid, VersionInfoFactory * factory)
{
return new NormalProcess(pid, factory);
}
NormalProcess::NormalProcess(uint32_t pid, VersionInfoFactory * factory)
: my_pid(pid)
{
my_descriptor = NULL;
my_main_thread = NULL;
attached = false;
suspended = false;
base = 0;
sections = 0;
NormalProcess::NormalProcess(uint32_t pid, vector <VersionInfo *> & known_versions)
: d(new Private())
{
HMODULE hmod = NULL; HMODULE hmod = NULL;
DWORD junk; DWORD needed;
HANDLE hProcess;
bool found = false; bool found = false;
IMAGE_NT_HEADERS32 pe_header; identified = false;
IMAGE_SECTION_HEADER sections[16];
d->identified = false;
// open process // open process
hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid ); my_handle = OpenProcess( PROCESS_ALL_ACCESS, FALSE, my_pid );
if (NULL == hProcess) if (NULL == my_handle)
return; return;
// try getting the first module of the process // try getting the first module of the process
if(EnumProcessModules(hProcess, &hmod, 1 * sizeof(HMODULE), &junk) == 0) if(EnumProcessModules(my_handle, &hmod, sizeof(hmod), &needed) == 0)
{ {
CloseHandle(hProcess); CloseHandle(my_handle);
my_handle=0;
// cout << "EnumProcessModules fail'd" << endl; // cout << "EnumProcessModules fail'd" << endl;
return; //if enumprocessModules fails, give up return; //if enumprocessModules fails, give up
} }
// got base ;) // got base ;)
uint32_t base = (uint32_t)hmod; base = (uint32_t)hmod;
// temporarily assign this to allow some checks my_main_thread = 0;
d->my_handle = hProcess;
d->my_main_thread = 0;
// read from this process // read from this process
try try
{ {
uint32_t pe_offset = readDWord(base+0x3C); uint32_t pe_offset = Process::readDWord(base+0x3C);
read(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header); read(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header);
read(base + pe_offset+ sizeof(pe_header), sizeof(sections) , (uint8_t *)&sections ); const size_t sectionsSize = sizeof(IMAGE_SECTION_HEADER) * pe_header.FileHeader.NumberOfSections;
d->my_handle = 0; sections = (IMAGE_SECTION_HEADER *) malloc(sectionsSize);
read(base + pe_offset + sizeof(pe_header), sectionsSize, (uint8_t *)sections);
} }
catch (exception &) catch (exception &)
{ {
CloseHandle(hProcess); CloseHandle(my_handle);
d->my_handle = 0; my_handle = 0;
return; return;
} }
// see if there's a version entry that matches this process VersionInfo* vinfo = factory->getVersionInfoByPETimestamp(pe_header.FileHeader.TimeDateStamp);
vector<VersionInfo*>::iterator it; if(vinfo)
for ( it=known_versions.begin() ; it < known_versions.end(); it++ )
{ {
// filter by OS identified = true;
if(VersionInfo::OS_WINDOWS != (*it)->getOS()) // give the process a data model and memory layout fixed for the base of first module
continue; my_descriptor = new VersionInfo(*vinfo);
uint32_t pe_timestamp; my_descriptor->RebaseAll(base);
// filter by timestamp, skip entries without a timestamp // keep track of created memory_info object so we can destroy it later
try my_descriptor->setParentProcess(this);
{
pe_timestamp = (*it)->getPE(); // TODO: detect errors in thread enumeration
} vector<uint32_t> threads;
catch(Error::AllMemdef&) getThreadIDs( threads );
{ my_main_thread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads[0]);
continue; stl.init(this);
}
if (pe_timestamp != pe_header.FileHeader.TimeDateStamp)
continue;
// all went well
{
printf("Match found! Using version %s.\n", (*it)->getVersion().c_str());
d->identified = true;
// give the process a data model and memory layout fixed for the base of first module
VersionInfo *m = new VersionInfo(**it);
m->RebaseAll(base);
// keep track of created memory_info object so we can destroy it later
d->my_descriptor = m;
m->setParentProcess(this);
// process is responsible for destroying its data model
d->my_pid = pid;
d->my_handle = hProcess;
d->identified = true;
// TODO: detect errors in thread enumeration
vector<uint32_t> threads;
getThreadIDs( threads );
d->my_main_thread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads[0]);
OffsetGroup * strGrp = m->getGroup("string")->getGroup("MSVC");
d->STLSTR_buf_off = strGrp->getOffset("buffer");
d->STLSTR_size_off = strGrp->getOffset("size");
d->STLSTR_cap_off = strGrp->getOffset("capacity");
found = true;
break; // break the iterator loop
}
} }
// close handle of processes that aren't DF else
if(!found)
{ {
CloseHandle(hProcess); // close handles of processes that aren't DF
my_handle = 0;
CloseHandle(my_handle);
} }
} }
/*
*/
NormalProcess::~NormalProcess() NormalProcess::~NormalProcess()
{ {
if(d->attached) if(attached)
{ {
detach(); detach();
} }
// destroy our rebased copy of the memory descriptor // destroy our rebased copy of the memory descriptor
delete d->my_descriptor; delete my_descriptor;
if(d->my_handle != NULL) if(my_handle != NULL)
{ {
CloseHandle(d->my_handle); CloseHandle(my_handle);
} }
if(d->my_main_thread != NULL) if(my_main_thread != NULL)
{ {
CloseHandle(d->my_main_thread); CloseHandle(my_main_thread);
} }
delete d; if(sections != NULL)
free(sections);
} }
VersionInfo * NormalProcess::getDescriptor() VersionInfo * NormalProcess::getDescriptor()
{ {
return d->my_descriptor; return my_descriptor;
} }
int NormalProcess::getPID() int NormalProcess::getPID()
{ {
return d->my_pid; return my_pid;
} }
bool NormalProcess::isSuspended() bool NormalProcess::isSuspended()
{ {
return d->suspended; return suspended;
} }
bool NormalProcess::isAttached() bool NormalProcess::isAttached()
{ {
return d->attached; return attached;
} }
bool NormalProcess::isIdentified() bool NormalProcess::isIdentified()
{ {
return d->identified; return identified;
} }
bool NormalProcess::asyncSuspend() bool NormalProcess::asyncSuspend()
@ -204,49 +230,49 @@ bool NormalProcess::asyncSuspend()
bool NormalProcess::suspend() bool NormalProcess::suspend()
{ {
if(!d->attached) if(!attached)
return false; return false;
if(d->suspended) if(suspended)
{ {
return true; return true;
} }
SuspendThread(d->my_main_thread); SuspendThread(my_main_thread);
d->suspended = true; suspended = true;
return true; return true;
} }
bool NormalProcess::forceresume() bool NormalProcess::forceresume()
{ {
if(!d->attached) if(!attached)
return false; return false;
while (ResumeThread(d->my_main_thread) > 1); while (ResumeThread(my_main_thread) > 1);
d->suspended = false; suspended = false;
return true; return true;
} }
bool NormalProcess::resume() bool NormalProcess::resume()
{ {
if(!d->attached) if(!attached)
return false; return false;
if(!d->suspended) if(!suspended)
{ {
return true; return true;
} }
ResumeThread(d->my_main_thread); ResumeThread(my_main_thread);
d->suspended = false; suspended = false;
return true; return true;
} }
bool NormalProcess::attach() bool NormalProcess::attach()
{ {
if(d->attached) if(attached)
{ {
if(!d->suspended) if(!suspended)
return suspend(); return suspend();
return true; return true;
} }
d->attached = true; attached = true;
suspend(); suspend();
return true; return true;
@ -255,9 +281,9 @@ bool NormalProcess::attach()
bool NormalProcess::detach() bool NormalProcess::detach()
{ {
if(!d->attached) return true; if(!attached) return true;
resume(); resume();
d->attached = false; attached = false;
return true; return true;
} }
@ -281,7 +307,7 @@ bool NormalProcess::getThreadIDs(vector<uint32_t> & threads )
do do
{ {
if( te32.th32OwnerProcessID == d->my_pid ) if( te32.th32OwnerProcessID == my_pid )
{ {
threads.push_back(te32.th32ThreadID); threads.push_back(te32.th32ThreadID);
} }
@ -290,143 +316,200 @@ bool NormalProcess::getThreadIDs(vector<uint32_t> & threads )
CloseHandle( AllThreads ); CloseHandle( AllThreads );
return true; return true;
} }
/*
//FIXME: use VirtualQuery to probe for memory ranges, cross-reference with base-corrected PE segment entries typedef struct _MEMORY_BASIC_INFORMATION
void NormalProcess::getMemRanges( vector<t_memrange> & ranges ) {
void * BaseAddress;
void * AllocationBase;
uint32_t AllocationProtect;
size_t RegionSize;
uint32_t State;
uint32_t Protect;
uint32_t Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
*/
/*
//Internal structure used to store heap block information.
struct HeapBlock
{ {
// code here is taken from hexsearch by Silas Dunmore. PVOID dwAddress;
// As this IMHO isn't a 'sunstantial portion' of anything, I'm not including the MIT license here DWORD dwSize;
DWORD dwFlags;
// I'm faking this, because there's no way I'm using VirtualQuery ULONG reserved;
};
t_memrange temp; */
uint32_t base = d->my_descriptor->getBase(); void HeapNodes(DWORD pid, map<uint64_t, unsigned int> & heaps)
temp.start = base + 0x1000; // more fakery. {
temp.end = base + readDWord(base+readDWord(base+0x3C)+0x50)-1; // yay for magic. // Create debug buffer
temp.read = 1; PDEBUG_BUFFER db = RtlCreateQueryDebugBuffer(0, FALSE);
temp.write = 1; // Get process heap data
temp.execute = 0; // fake RtlQueryProcessDebugInformation( pid, PDI_HEAPS/* | PDI_HEAP_BLOCKS*/, db);
strcpy(temp.name,"pants");// that's right. I'm calling it pants. Windows can go to HELL ULONG heapNodeCount = db->HeapInformation ? *PULONG(db->HeapInformation):0;
ranges.push_back(temp); PDEBUG_HEAP_INFORMATION heapInfo = PDEBUG_HEAP_INFORMATION(PULONG(db-> HeapInformation) + 1);
// Go through each of the heap nodes and dispaly the information
for (unsigned int i = 0; i < heapNodeCount; i++)
{
heaps[heapInfo[i].Base] = i;
}
// Clean up the buffer
RtlDestroyQueryDebugBuffer( db );
} }
uint8_t NormalProcess::readByte (const uint32_t offset) // FIXME: NEEDS TESTING!
void NormalProcess::getMemRanges( vector<t_memrange> & ranges )
{ {
uint8_t result; MEMORY_BASIC_INFORMATION MBI;
if(!ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint8_t), NULL)) map<uint64_t, unsigned int> heaps;
throw Error::MemoryAccessDenied(); uint64_t movingStart = 0;
return result; map <uint64_t, string> nameMap;
// get page size
SYSTEM_INFO si;
GetSystemInfo(&si);
uint64_t PageSize = si.dwPageSize;
// enumerate heaps
HeapNodes(my_pid, heaps);
// go through all the VM regions, convert them to our internal format
while (VirtualQueryEx(this->my_handle, (const void*) (movingStart), &MBI, sizeof(MBI)) == sizeof(MBI))
{
movingStart = ((uint64_t)MBI.BaseAddress + MBI.RegionSize);
if(movingStart % PageSize != 0)
movingStart = (movingStart / PageSize + 1) * PageSize;
// skip empty regions and regions we share with other processes (DLLs)
if( !(MBI.State & MEM_COMMIT) /*|| !(MBI.Type & MEM_PRIVATE)*/ )
continue;
t_memrange temp;
temp.start = (uint64_t) MBI.BaseAddress;
temp.end = ((uint64_t)MBI.BaseAddress + (uint64_t)MBI.RegionSize);
temp.read = MBI.Protect & PAGE_EXECUTE_READ || MBI.Protect & PAGE_EXECUTE_READWRITE || MBI.Protect & PAGE_READONLY || MBI.Protect & PAGE_READWRITE;
temp.write = MBI.Protect & PAGE_EXECUTE_READWRITE || MBI.Protect & PAGE_READWRITE;
temp.execute = MBI.Protect & PAGE_EXECUTE_READ || MBI.Protect & PAGE_EXECUTE_READWRITE || MBI.Protect & PAGE_EXECUTE;
temp.valid = true;
if(!GetModuleBaseName(this->my_handle, (HMODULE) temp.start, temp.name, 1024))
{
if(nameMap.count(temp.start))
{
// potential buffer overflow...
strcpy(temp.name, nameMap[temp.start].c_str());
}
else
{
// filter away shared segments without a name.
if( !(MBI.Type & MEM_PRIVATE) )
continue;
else
{
// could be a heap?
if(heaps.count(temp.start))
{
sprintf(temp.name,"HEAP %d",heaps[temp.start]);
}
else temp.name[0]=0;
}
}
}
else
{
// this is our executable! (could be generalized to pull segments from libs, but whatever)
if(base == temp.start)
{
for(int i = 0; i < pe_header.FileHeader.NumberOfSections; i++)
{
char sectionName[9];
memcpy(sectionName,sections[i].Name,8);
sectionName[8] = 0;
string nm;
nm.append(temp.name);
nm.append(" : ");
nm.append(sectionName);
nameMap[temp.start + sections[i].VirtualAddress] = nm;
}
}
else
continue;
}
ranges.push_back(temp);
}
} }
void NormalProcess::readByte (const uint32_t offset,uint8_t &result) void NormalProcess::readByte (const uint32_t offset,uint8_t &result)
{ {
if(!ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint8_t), NULL)) if(!ReadProcessMemory(my_handle, (int*) offset, &result, sizeof(uint8_t), NULL))
throw Error::MemoryAccessDenied(); throw Error::MemoryAccessDenied();
} }
uint16_t NormalProcess::readWord (const uint32_t offset)
{
uint16_t result;
if(!ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint16_t), NULL))
throw Error::MemoryAccessDenied();
return result;
}
void NormalProcess::readWord (const uint32_t offset, uint16_t &result) void NormalProcess::readWord (const uint32_t offset, uint16_t &result)
{ {
if(!ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint16_t), NULL)) if(!ReadProcessMemory(my_handle, (int*) offset, &result, sizeof(uint16_t), NULL))
throw Error::MemoryAccessDenied(); throw Error::MemoryAccessDenied();
} }
uint32_t NormalProcess::readDWord (const uint32_t offset)
{
uint32_t result;
if(!ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint32_t), NULL))
throw Error::MemoryAccessDenied();
return result;
}
void NormalProcess::readDWord (const uint32_t offset, uint32_t &result) void NormalProcess::readDWord (const uint32_t offset, uint32_t &result)
{ {
if(!ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint32_t), NULL)) if(!ReadProcessMemory(my_handle, (int*) offset, &result, sizeof(uint32_t), NULL))
throw Error::MemoryAccessDenied();
}
uint64_t NormalProcess::readQuad (const uint32_t offset)
{
uint64_t result;
if(!ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint64_t), NULL))
throw Error::MemoryAccessDenied(); throw Error::MemoryAccessDenied();
return result;
} }
void NormalProcess::readQuad (const uint32_t offset, uint64_t &result) void NormalProcess::readQuad (const uint32_t offset, uint64_t &result)
{ {
if(!ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint64_t), NULL)) if(!ReadProcessMemory(my_handle, (int*) offset, &result, sizeof(uint64_t), NULL))
throw Error::MemoryAccessDenied();
}
float NormalProcess::readFloat (const uint32_t offset)
{
float result;
if(!ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(float), NULL))
throw Error::MemoryAccessDenied(); throw Error::MemoryAccessDenied();
return result;
} }
void NormalProcess::readFloat (const uint32_t offset, float &result) void NormalProcess::readFloat (const uint32_t offset, float &result)
{ {
if(!ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(float), NULL)) if(!ReadProcessMemory(my_handle, (int*) offset, &result, sizeof(float), NULL))
throw Error::MemoryAccessDenied(); throw Error::MemoryAccessDenied();
} }
void NormalProcess::read (const uint32_t offset, uint32_t size, uint8_t *target) void NormalProcess::read (const uint32_t offset, uint32_t size, uint8_t *target)
{ {
if(!ReadProcessMemory(d->my_handle, (int*) offset, target, size, NULL)) if(!ReadProcessMemory(my_handle, (int*) offset, target, size, NULL))
throw Error::MemoryAccessDenied(); throw Error::MemoryAccessDenied();
} }
// WRITING // WRITING
void NormalProcess::writeQuad (const uint32_t offset, uint64_t data) void NormalProcess::writeQuad (const uint32_t offset, uint64_t data)
{ {
if(!WriteProcessMemory(d->my_handle, (int*) offset, &data, sizeof(data), NULL)) if(!WriteProcessMemory(my_handle, (int*) offset, &data, sizeof(data), NULL))
throw Error::MemoryAccessDenied(); throw Error::MemoryAccessDenied();
} }
void NormalProcess::writeDWord (const uint32_t offset, uint32_t data) void NormalProcess::writeDWord (const uint32_t offset, uint32_t data)
{ {
if(!WriteProcessMemory(d->my_handle, (int*) offset, &data, sizeof(data), NULL)) if(!WriteProcessMemory(my_handle, (int*) offset, &data, sizeof(data), NULL))
throw Error::MemoryAccessDenied(); throw Error::MemoryAccessDenied();
} }
// using these is expensive. // using these is expensive.
void NormalProcess::writeWord (uint32_t offset, uint16_t data) void NormalProcess::writeWord (uint32_t offset, uint16_t data)
{ {
if(!WriteProcessMemory(d->my_handle, (int*) offset, &data, sizeof(data), NULL)) if(!WriteProcessMemory(my_handle, (int*) offset, &data, sizeof(data), NULL))
throw Error::MemoryAccessDenied(); throw Error::MemoryAccessDenied();
} }
void NormalProcess::writeByte (uint32_t offset, uint8_t data) void NormalProcess::writeByte (uint32_t offset, uint8_t data)
{ {
if(!WriteProcessMemory(d->my_handle, (int*) offset, &data, sizeof(data), NULL)) if(!WriteProcessMemory(my_handle, (int*) offset, &data, sizeof(data), NULL))
throw Error::MemoryAccessDenied(); throw Error::MemoryAccessDenied();
} }
void NormalProcess::write (uint32_t offset, uint32_t size, uint8_t *source) void NormalProcess::write (uint32_t offset, uint32_t size, uint8_t *source)
{ {
if(!WriteProcessMemory(d->my_handle, (int*) offset, source, size, NULL)) if(!WriteProcessMemory(my_handle, (int*) offset, source, size, NULL))
throw Error::MemoryAccessDenied(); throw Error::MemoryAccessDenied();
} }
///FIXME: reduce use of temporary objects ///FIXME: reduce use of temporary objects
const string NormalProcess::readCString (const uint32_t offset) const string NormalProcess::readCString (const uint32_t offset)
{ {
string temp; string temp;
char temp_c[256]; char temp_c[256];
SIZE_T read; SIZE_T read;
if(!ReadProcessMemory(d->my_handle, (int *) offset, temp_c, 254, &read)) if(!ReadProcessMemory(my_handle, (int *) offset, temp_c, 254, &read))
throw Error::MemoryAccessDenied(); throw Error::MemoryAccessDenied();
// needs to be 254+1 byte for the null term // needs to be 254+1 byte for the null term
temp_c[read+1] = 0; temp_c[read+1] = 0;
@ -436,65 +519,26 @@ const string NormalProcess::readCString (const uint32_t offset)
size_t NormalProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) size_t NormalProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity)
{ {
uint32_t start_offset = offset + d->STLSTR_buf_off; return stl.readSTLString(offset, buffer, bufcapacity);
size_t length = readDWord(offset + d->STLSTR_size_off);
size_t capacity = readDWord(offset + d->STLSTR_cap_off);
size_t read_real = min(length, bufcapacity-1);// keep space for null termination
// read data from inside the string structure
if(capacity < 16)
{
read(start_offset, read_real , (uint8_t *)buffer);
}
else // read data from what the offset + 4 dword points to
{
start_offset = readDWord(start_offset);// dereference the start offset
read(start_offset, read_real, (uint8_t *)buffer);
}
buffer[read_real] = 0;
return read_real;
} }
const string NormalProcess::readSTLString (uint32_t offset) const string NormalProcess::readSTLString (uint32_t offset)
{ {
uint32_t start_offset = offset + d->STLSTR_buf_off; return stl.readSTLString(offset);
size_t length = readDWord(offset + d->STLSTR_size_off);
size_t capacity = readDWord(offset + d->STLSTR_cap_off);
char * temp = new char[capacity+1];
// read data from inside the string structure
if(capacity < 16)
{
read(start_offset, capacity, (uint8_t *)temp);
}
else // read data from what the offset + 4 dword points to
{
start_offset = readDWord(start_offset);// dereference the start offset
read(start_offset, capacity, (uint8_t *)temp);
}
temp[length] = 0;
string ret = temp;
delete temp;
return ret;
} }
string NormalProcess::readClassName (uint32_t vptr) string NormalProcess::readClassName (uint32_t vptr)
{ {
int rtti = readDWord(vptr - 0x4); return stl.readClassName(vptr);
int typeinfo = readDWord(rtti + 0xC);
string raw = readCString(typeinfo + 0xC); // skips the .?AV
raw.resize(raw.length() - 2);// trim @@ from end
return raw;
} }
string NormalProcess::getPath() string NormalProcess::getPath()
{ {
HMODULE hmod; HMODULE hmod;
DWORD junk; DWORD junk;
char String[255]; char String[255];
EnumProcessModules(d->my_handle, &hmod, 1 * sizeof(HMODULE), &junk); //get the module from the handle EnumProcessModules(my_handle, &hmod, 1 * sizeof(HMODULE), &junk); //get the module from the handle
GetModuleFileNameEx(d->my_handle,hmod,String,sizeof(String)); //get the filename from the module GetModuleFileNameEx(my_handle,hmod,String,sizeof(String)); //get the filename from the module
string out(String); string out(String);
return(out.substr(0,out.find_last_of("\\"))); return(out.substr(0,out.find_last_of("\\")));
} }

@ -23,10 +23,10 @@ distribution.
*/ */
#include "Internal.h" #include "Internal.h"
#include "ProcessFactory.h"
#include "dfhack/VersionInfoFactory.h" #include "dfhack/VersionInfoFactory.h"
#include "dfhack/DFProcessEnumerator.h" #include "dfhack/DFProcessEnumerator.h"
#include "dfhack/DFProcess.h"
#include "dfhack/VersionInfo.h" #include "dfhack/VersionInfo.h"
@ -120,19 +120,19 @@ Process * BadProcesses::operator[](uint32_t index)
Process *ProcessEnumerator::Private::GetProcessObject(ProcessID ID) Process *ProcessEnumerator::Private::GetProcessObject(ProcessID ID)
{ {
Process *p1 = new SHMProcess(ID.pid,meminfo->versions); Process *p1 = createSHMProcess(ID.pid, meminfo);
if(p1->isIdentified()) if(p1->isIdentified())
return p1; return p1;
else else
delete p1; delete p1;
Process *p2 = new NormalProcess(ID.pid,meminfo->versions); Process *p2 = createNormalProcess(ID.pid, meminfo);
if(p2->isIdentified()) if(p2->isIdentified())
return p2; return p2;
else else
delete p2; delete p2;
#ifdef LINUX_BUILD #ifdef LINUX_BUILD
Process *p3 = new WineProcess(ID.pid,meminfo->versions); Process *p3 = createWineProcess(ID.pid, meminfo);
if(p3->isIdentified()) if(p3->isIdentified())
return p3; return p3;
else else

@ -0,0 +1,96 @@
/*
www.sourceforge.net/projects/dfhack
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "Internal.h"
#include "MicrosoftSTL.h"
#include "dfhack/DFProcess.h"
#include "dfhack/VersionInfo.h"
#include <string>
using namespace DFHack;
void MicrosoftSTL::init(Process* self)
{
p = self;
OffsetGroup * strGrp = p->getDescriptor()->getGroup("string")->getGroup("MSVC");
STLSTR_buf_off = strGrp->getOffset("buffer");
STLSTR_size_off = strGrp->getOffset("size");
STLSTR_cap_off = strGrp->getOffset("capacity");
}
size_t MicrosoftSTL::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity)
{
uint32_t start_offset = offset + STLSTR_buf_off;
size_t length = p->readDWord(offset + STLSTR_size_off);
size_t capacity = p->readDWord(offset + STLSTR_cap_off);
size_t read_real = min(length, bufcapacity-1);// keep space for null termination
// read data from inside the string structure
if(capacity < 16)
{
p->read(start_offset, read_real , (uint8_t *)buffer);
}
else // read data from what the offset + 4 dword points to
{
start_offset = p->readDWord(start_offset);// dereference the start offset
p->read(start_offset, read_real, (uint8_t *)buffer);
}
buffer[read_real] = 0;
return read_real;
}
const string MicrosoftSTL::readSTLString (uint32_t offset)
{
uint32_t start_offset = offset + STLSTR_buf_off;
size_t length = p->readDWord(offset + STLSTR_size_off);
size_t capacity = p->readDWord(offset + STLSTR_cap_off);
char * temp = new char[capacity+1];
// read data from inside the string structure
if(capacity < 16)
{
p->read(start_offset, capacity, (uint8_t *)temp);
}
else // read data from what the offset + 4 dword points to
{
start_offset = p->readDWord(start_offset);// dereference the start offset
p->read(start_offset, capacity, (uint8_t *)temp);
}
temp[length] = 0;
string ret = temp;
delete temp;
return ret;
}
string MicrosoftSTL::readClassName (uint32_t vptr)
{
int rtti = p->readDWord(vptr - 0x4);
int typeinfo = p->readDWord(rtti + 0xC);
string raw = p->readCString(typeinfo + 0xC); // skips the .?AV
raw.resize(raw.length() - 2);// trim @@ from end
return raw;
}

@ -545,7 +545,7 @@ namespace DFHack
Process * p; // the process this belongs to Process * p; // the process this belongs to
string version; string version;
VersionInfo::OSType OS; OSType OS;
std::string md5; std::string md5;
uint32_t PE_timestamp; uint32_t PE_timestamp;
}; };
@ -707,7 +707,7 @@ void VersionInfo::setOS(OSType os)
} }
VersionInfo::OSType VersionInfo::getOS() const OSType VersionInfo::getOS() const
{ {
return d->OS; return d->OS;
} }

@ -90,6 +90,12 @@ inline bool operator>=(const triple<_T1, _T2, _T3>& __x, const triple<_T1, _T2,
return !(__x < __y); return !(__x < __y);
} }
VersionInfoFactory::VersionInfoFactory(string path_to_xml)
{
error = false;
loadFile(path_to_xml);
}
VersionInfoFactory::~VersionInfoFactory() VersionInfoFactory::~VersionInfoFactory()
{ {
// for each stored version, delete // for each stored version, delete
@ -100,6 +106,35 @@ VersionInfoFactory::~VersionInfoFactory()
versions.clear(); versions.clear();
} }
VersionInfo * VersionInfoFactory::getVersionInfoByMD5(string hash)
{
VersionInfo * vinfo;
for(uint32_t i = 0; i < versions.size();i++)
{
vinfo = versions[i];
if(vinfo->getMD5() == hash)
{
return vinfo;
}
}
return NULL;
}
VersionInfo * VersionInfoFactory::getVersionInfoByPETimestamp(uint32_t timestamp)
{
VersionInfo * vinfo;
for(uint32_t i = 0; i < versions.size();i++)
{
vinfo = versions[i];
if(vinfo->getPE() == timestamp)
{
return vinfo;
}
}
return NULL;
}
void VersionInfoFactory::ParseVTable(TiXmlElement* vtable, VersionInfo* mem) void VersionInfoFactory::ParseVTable(TiXmlElement* vtable, VersionInfo* mem)
{ {
TiXmlElement* pClassEntry; TiXmlElement* pClassEntry;
@ -172,8 +207,6 @@ void VersionInfoFactory::ParseOffsets(TiXmlElement * parent, VersionInfo* target
TiXmlElement* pEntry; TiXmlElement* pEntry;
// we get the <Offsets>, look at the children // we get the <Offsets>, look at the children
pEntry = parent->FirstChildElement(); pEntry = parent->FirstChildElement();
if(!pEntry)
return;
const char *cstr_invalid = parent->Attribute("valid"); const char *cstr_invalid = parent->Attribute("valid");
INVAL_TYPE parent_inval = NOT_SET; INVAL_TYPE parent_inval = NOT_SET;
if(cstr_invalid) if(cstr_invalid)
@ -185,6 +218,11 @@ void VersionInfoFactory::ParseOffsets(TiXmlElement * parent, VersionInfo* target
} }
OffsetGroup * currentGroup = reinterpret_cast<OffsetGroup *> (target); OffsetGroup * currentGroup = reinterpret_cast<OffsetGroup *> (target);
currentGroup->setInvalid(parent_inval); currentGroup->setInvalid(parent_inval);
// we end here if there are no child tags.
if(!pEntry)
return;
breadcrumbs.push_back(groupTriple(pEntry,currentGroup, parent_inval)); breadcrumbs.push_back(groupTriple(pEntry,currentGroup, parent_inval));
} }
@ -225,7 +263,7 @@ void VersionInfoFactory::ParseOffsets(TiXmlElement * parent, VersionInfo* target
} }
// skip non-elements // skip non-elements
if (currentElem->Type() != TiXmlNode::ELEMENT) if (currentElem->Type() != TiXmlNode::TINYXML_ELEMENT)
{ {
groupTriple & gp = breadcrumbs.back(); groupTriple & gp = breadcrumbs.back();
gp.first = gp.first->NextSiblingElement(); gp.first = gp.first->NextSiblingElement();
@ -365,7 +403,7 @@ void VersionInfoFactory::ParseBase (TiXmlElement* entry, VersionInfo* mem)
throw Error::MemoryXmlBadAttribute("name"); throw Error::MemoryXmlBadAttribute("name");
mem->setVersion(cstr_version); mem->setVersion(cstr_version);
mem->setOS(VersionInfo::OS_BAD); mem->setOS(OS_BAD);
// process additional entries // process additional entries
pElement = entry->FirstChildElement()->ToElement(); pElement = entry->FirstChildElement()->ToElement();
@ -625,12 +663,6 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem)
} // for } // for
} // method } // method
VersionInfoFactory::VersionInfoFactory(string path_to_xml)
{
error = false;
loadFile(path_to_xml);
}
// load the XML file with offsets // load the XML file with offsets
bool VersionInfoFactory::loadFile(string path_to_xml) bool VersionInfoFactory::loadFile(string path_to_xml)
{ {

Binary file not shown.

@ -31,6 +31,7 @@ distribution.
#include "tinyxml.h" #include "tinyxml.h"
FILE* TiXmlFOpen( const char* filename, const char* mode );
bool TiXmlBase::condenseWhiteSpace = true; bool TiXmlBase::condenseWhiteSpace = true;
@ -161,6 +162,7 @@ void TiXmlNode::CopyTo( TiXmlNode* target ) const
{ {
target->SetValue (value.c_str() ); target->SetValue (value.c_str() );
target->userData = userData; target->userData = userData;
target->location = location;
} }
@ -186,7 +188,7 @@ TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )
assert( node->parent == 0 || node->parent == this ); assert( node->parent == 0 || node->parent == this );
assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() );
if ( node->Type() == TiXmlNode::DOCUMENT ) if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT )
{ {
delete node; delete node;
if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
@ -210,7 +212,7 @@ TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )
TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis )
{ {
if ( addThis.Type() == TiXmlNode::DOCUMENT ) if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
{ {
if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
return 0; return 0;
@ -228,7 +230,7 @@ TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode&
if ( !beforeThis || beforeThis->parent != this ) { if ( !beforeThis || beforeThis->parent != this ) {
return 0; return 0;
} }
if ( addThis.Type() == TiXmlNode::DOCUMENT ) if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
{ {
if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
return 0; return 0;
@ -260,7 +262,7 @@ TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& a
if ( !afterThis || afterThis->parent != this ) { if ( !afterThis || afterThis->parent != this ) {
return 0; return 0;
} }
if ( addThis.Type() == TiXmlNode::DOCUMENT ) if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
{ {
if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
return 0; return 0;
@ -289,9 +291,20 @@ TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& a
TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )
{ {
if ( !replaceThis )
return 0;
if ( replaceThis->parent != this ) if ( replaceThis->parent != this )
return 0; return 0;
if ( withThis.ToDocument() ) {
// A document can never be a child. Thanks to Noam.
TiXmlDocument* document = GetDocument();
if ( document )
document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
return 0;
}
TiXmlNode* node = withThis.Clone(); TiXmlNode* node = withThis.Clone();
if ( !node ) if ( !node )
return 0; return 0;
@ -317,6 +330,10 @@ TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& wit
bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) bool TiXmlNode::RemoveChild( TiXmlNode* removeThis )
{ {
if ( !removeThis ) {
return false;
}
if ( removeThis->parent != this ) if ( removeThis->parent != this )
{ {
assert( 0 ); assert( 0 );
@ -502,7 +519,7 @@ const TiXmlDocument* TiXmlNode::GetDocument() const
TiXmlElement::TiXmlElement (const char * _value) TiXmlElement::TiXmlElement (const char * _value)
: TiXmlNode( TiXmlNode::ELEMENT ) : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
{ {
firstChild = lastChild = 0; firstChild = lastChild = 0;
value = _value; value = _value;
@ -511,7 +528,7 @@ TiXmlElement::TiXmlElement (const char * _value)
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
TiXmlElement::TiXmlElement( const std::string& _value ) TiXmlElement::TiXmlElement( const std::string& _value )
: TiXmlNode( TiXmlNode::ELEMENT ) : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
{ {
firstChild = lastChild = 0; firstChild = lastChild = 0;
value = _value; value = _value;
@ -520,7 +537,7 @@ TiXmlElement::TiXmlElement( const std::string& _value )
TiXmlElement::TiXmlElement( const TiXmlElement& copy) TiXmlElement::TiXmlElement( const TiXmlElement& copy)
: TiXmlNode( TiXmlNode::ELEMENT ) : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
{ {
firstChild = lastChild = 0; firstChild = lastChild = 0;
copy.CopyTo( this ); copy.CopyTo( this );
@ -564,9 +581,9 @@ const char* TiXmlElement::Attribute( const char* name ) const
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
const std::string* TiXmlElement::Attribute( const std::string& name ) const const std::string* TiXmlElement::Attribute( const std::string& name ) const
{ {
const TiXmlAttribute* node = attributeSet.Find( name ); const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( node ) if ( attrib )
return &node->ValueStr(); return &attrib->ValueStr();
return 0; return 0;
} }
#endif #endif
@ -574,195 +591,163 @@ const std::string* TiXmlElement::Attribute( const std::string& name ) const
const char* TiXmlElement::Attribute( const char* name, int* i ) const const char* TiXmlElement::Attribute( const char* name, int* i ) const
{ {
const char* s = Attribute( name ); const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( i ) const char* result = 0;
{
if ( s ) { if ( attrib ) {
*i = atoi( s ); result = attrib->Value();
} if ( i ) {
else { attrib->QueryIntValue( i );
*i = 0;
} }
} }
return s; return result;
} }
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const
{ {
const std::string* s = Attribute( name ); const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( i ) const std::string* result = 0;
{
if ( s ) { if ( attrib ) {
*i = atoi( s->c_str() ); result = &attrib->ValueStr();
} if ( i ) {
else { attrib->QueryIntValue( i );
*i = 0;
} }
} }
return s; return result;
} }
#endif #endif
const char* TiXmlElement::Attribute( const char* name, double* d ) const const char* TiXmlElement::Attribute( const char* name, double* d ) const
{ {
const char* s = Attribute( name ); const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( d ) const char* result = 0;
{
if ( s ) { if ( attrib ) {
*d = atof( s ); result = attrib->Value();
} if ( d ) {
else { attrib->QueryDoubleValue( d );
*d = 0;
} }
} }
return s; return result;
} }
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const
{ {
const std::string* s = Attribute( name ); const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( d ) const std::string* result = 0;
{
if ( s ) { if ( attrib ) {
*d = atof( s->c_str() ); result = &attrib->ValueStr();
} if ( d ) {
else { attrib->QueryDoubleValue( d );
*d = 0;
} }
} }
return s; return result;
} }
#endif #endif
int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const
{ {
const TiXmlAttribute* node = attributeSet.Find( name ); const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( !node ) if ( !attrib )
return TIXML_NO_ATTRIBUTE; return TIXML_NO_ATTRIBUTE;
return node->QueryIntValue( ival ); return attrib->QueryIntValue( ival );
} }
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const
{ {
const TiXmlAttribute* node = attributeSet.Find( name ); const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( !node ) if ( !attrib )
return TIXML_NO_ATTRIBUTE; return TIXML_NO_ATTRIBUTE;
return node->QueryIntValue( ival ); return attrib->QueryIntValue( ival );
} }
#endif #endif
int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const
{ {
const TiXmlAttribute* node = attributeSet.Find( name ); const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( !node ) if ( !attrib )
return TIXML_NO_ATTRIBUTE; return TIXML_NO_ATTRIBUTE;
return node->QueryDoubleValue( dval ); return attrib->QueryDoubleValue( dval );
} }
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const
{ {
const TiXmlAttribute* node = attributeSet.Find( name ); const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( !node ) if ( !attrib )
return TIXML_NO_ATTRIBUTE; return TIXML_NO_ATTRIBUTE;
return node->QueryDoubleValue( dval ); return attrib->QueryDoubleValue( dval );
} }
#endif #endif
void TiXmlElement::SetAttribute( const char * name, int val ) void TiXmlElement::SetAttribute( const char * name, int val )
{ {
char buf[64]; TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
#if defined(TIXML_SNPRINTF) if ( attrib ) {
TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); attrib->SetIntValue( val );
#else }
sprintf( buf, "%d", val );
#endif
SetAttribute( name, buf );
} }
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
void TiXmlElement::SetAttribute( const std::string& name, int val ) void TiXmlElement::SetAttribute( const std::string& name, int val )
{ {
std::ostringstream oss; TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
oss << val; if ( attrib ) {
SetAttribute( name, oss.str() ); attrib->SetIntValue( val );
}
} }
#endif #endif
void TiXmlElement::SetDoubleAttribute( const char * name, double val ) void TiXmlElement::SetDoubleAttribute( const char * name, double val )
{ {
char buf[256]; TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
#if defined(TIXML_SNPRINTF) if ( attrib ) {
TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); attrib->SetDoubleValue( val );
#else }
sprintf( buf, "%f", val );
#endif
SetAttribute( name, buf );
} }
void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) #ifdef TIXML_USE_STL
{ void TiXmlElement::SetDoubleAttribute( const std::string& name, double val )
#ifdef TIXML_USE_STL {
TIXML_STRING _name( cname ); TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
TIXML_STRING _value( cvalue ); if ( attrib ) {
#else attrib->SetDoubleValue( val );
const char* _name = cname;
const char* _value = cvalue;
#endif
TiXmlAttribute* node = attributeSet.Find( _name );
if ( node )
{
node->SetValue( _value );
return;
} }
}
#endif
TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue );
if ( attrib ) void TiXmlElement::SetAttribute( const char * cname, const char * cvalue )
{ {
attributeSet.Add( attrib ); TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname );
} if ( attrib ) {
else attrib->SetValue( cvalue );
{
TiXmlDocument* document = GetDocument();
if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
} }
} }
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
void TiXmlElement::SetAttribute( const std::string& name, const std::string& _value ) void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value )
{ {
TiXmlAttribute* node = attributeSet.Find( name ); TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name );
if ( node ) if ( attrib ) {
{ attrib->SetValue( _value );
node->SetValue( _value );
return;
}
TiXmlAttribute* attrib = new TiXmlAttribute( name, _value );
if ( attrib )
{
attributeSet.Add( attrib );
}
else
{
TiXmlDocument* document = GetDocument();
if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
} }
} }
#endif #endif
@ -881,14 +866,14 @@ const char* TiXmlElement::GetText() const
} }
TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
{ {
tabsize = 4; tabsize = 4;
useMicrosoftBOM = false; useMicrosoftBOM = false;
ClearError(); ClearError();
} }
TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
{ {
tabsize = 4; tabsize = 4;
useMicrosoftBOM = false; useMicrosoftBOM = false;
@ -898,7 +883,7 @@ TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
{ {
tabsize = 4; tabsize = 4;
useMicrosoftBOM = false; useMicrosoftBOM = false;
@ -908,7 +893,7 @@ TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiX
#endif #endif
TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
{ {
copy.CopyTo( this ); copy.CopyTo( this );
} }
@ -923,34 +908,17 @@ void TiXmlDocument::operator=( const TiXmlDocument& copy )
bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) bool TiXmlDocument::LoadFile( TiXmlEncoding encoding )
{ {
// See STL_STRING_BUG below.
//StringToBuffer buf( value );
return LoadFile( Value(), encoding ); return LoadFile( Value(), encoding );
} }
bool TiXmlDocument::SaveFile() const bool TiXmlDocument::SaveFile() const
{ {
// See STL_STRING_BUG below.
// StringToBuffer buf( value );
//
// if ( buf.buffer && SaveFile( buf.buffer ) )
// return true;
//
// return false;
return SaveFile( Value() ); return SaveFile( Value() );
} }
bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding )
{ {
// There was a really terrifying little bug here. The code:
// value = filename
// in the STL case, cause the assignment method of the std::string to
// be called. What is strange, is that the std::string had the same
// address as it's c_str() method, and so bad things happen. Looks
// like a bug in the Microsoft STL implementation.
// Add an extra string to avoid the crash.
TIXML_STRING filename( _filename ); TIXML_STRING filename( _filename );
value = filename; value = filename;
@ -995,11 +963,6 @@ bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )
return false; return false;
} }
// If we have a file, assume it is all one big XML file, and read it in.
// The document parser may decide the document ends sooner than the entire file, however.
TIXML_STRING data;
data.reserve( length );
// Subtle bug here. TinyXml did use fgets. But from the XML spec: // Subtle bug here. TinyXml did use fgets. But from the XML spec:
// 2.11 End-of-Line Handling // 2.11 End-of-Line Handling
// <snip> // <snip>
@ -1030,58 +993,46 @@ bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )
return false; return false;
} }
const char* lastPos = buf; // Process the buffer in place to normalize new lines. (See comment above.)
const char* p = buf; // Copies from the 'p' to 'q' pointer, where p can advance faster if
// a newline-carriage return is hit.
//
// Wikipedia:
// Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or
// CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)...
// * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others
// * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS
// * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9
const char* p = buf; // the read head
char* q = buf; // the write head
const char CR = 0x0d;
const char LF = 0x0a;
buf[length] = 0; buf[length] = 0;
while( *p ) { while( *p ) {
assert( p < (buf+length) ); assert( p < (buf+length) );
if ( *p == 0xa ) { assert( q <= (buf+length) );
// Newline character. No special rules for this. Append all the characters assert( q <= p );
// since the last string, and include the newline.
data.append( lastPos, (p-lastPos+1) ); // append, include the newline if ( *p == CR ) {
++p; // move past the newline *q++ = LF;
lastPos = p; // and point to the new buffer (may be 0) p++;
assert( p <= (buf+length) ); if ( *p == LF ) { // check for CR+LF (and skip LF)
} p++;
else if ( *p == 0xd ) {
// Carriage return. Append what we have so far, then
// handle moving forward in the buffer.
if ( (p-lastPos) > 0 ) {
data.append( lastPos, p-lastPos ); // do not add the CR
}
data += (char)0xa; // a proper newline
if ( *(p+1) == 0xa ) {
// Carriage return - new line sequence
p += 2;
lastPos = p;
assert( p <= (buf+length) );
}
else {
// it was followed by something else...that is presumably characters again.
++p;
lastPos = p;
assert( p <= (buf+length) );
} }
} }
else { else {
++p; *q++ = *p++;
} }
} }
// Handle any left over characters. assert( q <= (buf+length) );
if ( p-lastPos ) { *q = 0;
data.append( lastPos, p-lastPos );
}
delete [] buf;
buf = 0;
Parse( data.c_str(), 0, encoding ); Parse( buf, 0, encoding );
if ( Error() ) delete [] buf;
return false; return !Error();
else
return true;
} }
@ -1266,9 +1217,9 @@ void TiXmlAttribute::SetDoubleValue( double _value )
{ {
char buf [256]; char buf [256];
#if defined(TIXML_SNPRINTF) #if defined(TIXML_SNPRINTF)
TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value);
#else #else
sprintf (buf, "%lf", _value); sprintf (buf, "%g", _value);
#endif #endif
SetValue (buf); SetValue (buf);
} }
@ -1284,7 +1235,7 @@ double TiXmlAttribute::DoubleValue() const
} }
TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT )
{ {
copy.CopyTo( this ); copy.CopyTo( this );
} }
@ -1382,7 +1333,7 @@ TiXmlNode* TiXmlText::Clone() const
TiXmlDeclaration::TiXmlDeclaration( const char * _version, TiXmlDeclaration::TiXmlDeclaration( const char * _version,
const char * _encoding, const char * _encoding,
const char * _standalone ) const char * _standalone )
: TiXmlNode( TiXmlNode::DECLARATION ) : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
{ {
version = _version; version = _version;
encoding = _encoding; encoding = _encoding;
@ -1394,7 +1345,7 @@ TiXmlDeclaration::TiXmlDeclaration( const char * _version,
TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, TiXmlDeclaration::TiXmlDeclaration( const std::string& _version,
const std::string& _encoding, const std::string& _encoding,
const std::string& _standalone ) const std::string& _standalone )
: TiXmlNode( TiXmlNode::DECLARATION ) : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
{ {
version = _version; version = _version;
encoding = _encoding; encoding = _encoding;
@ -1404,7 +1355,7 @@ TiXmlDeclaration::TiXmlDeclaration( const std::string& _version,
TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy )
: TiXmlNode( TiXmlNode::DECLARATION ) : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
{ {
copy.CopyTo( this ); copy.CopyTo( this );
} }
@ -1548,9 +1499,9 @@ void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const
{ {
for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
{ {
if ( node->name == name ) if ( node->name == name )
return node; return node;
@ -1558,23 +1509,22 @@ const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const
return 0; return 0;
} }
/* TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name )
TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name )
{ {
for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) TiXmlAttribute* attrib = Find( _name );
{ if ( !attrib ) {
if ( node->name == name ) attrib = new TiXmlAttribute();
return node; Add( attrib );
attrib->SetName( _name );
} }
return 0; return attrib;
} }
*/
#endif #endif
const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const
{ {
for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
{ {
if ( strcmp( node->name.c_str(), name ) == 0 ) if ( strcmp( node->name.c_str(), name ) == 0 )
return node; return node;
@ -1582,17 +1532,18 @@ const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const
return 0; return 0;
} }
/*
TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name )
{ {
for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) TiXmlAttribute* attrib = Find( _name );
{ if ( !attrib ) {
if ( strcmp( node->name.c_str(), name ) == 0 ) attrib = new TiXmlAttribute();
return node; Add( attrib );
attrib->SetName( _name );
} }
return 0; return attrib;
} }
*/
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
std::istream& operator>> (std::istream & in, TiXmlNode & base) std::istream& operator>> (std::istream & in, TiXmlNode & base)

@ -63,21 +63,19 @@ distribution.
#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
// Microsoft visual studio, version 2005 and higher. // Microsoft visual studio, version 2005 and higher.
#define TIXML_SNPRINTF _snprintf_s #define TIXML_SNPRINTF _snprintf_s
#define TIXML_SNSCANF _snscanf_s
#define TIXML_SSCANF sscanf_s #define TIXML_SSCANF sscanf_s
#elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) #elif defined(_MSC_VER) && (_MSC_VER >= 1200 )
// Microsoft visual studio, version 6 and higher. // Microsoft visual studio, version 6 and higher.
//#pragma message( "Using _sn* functions." ) //#pragma message( "Using _sn* functions." )
#define TIXML_SNPRINTF _snprintf #define TIXML_SNPRINTF _snprintf
#define TIXML_SNSCANF _snscanf
#define TIXML_SSCANF sscanf #define TIXML_SSCANF sscanf
#elif defined(__GNUC__) && (__GNUC__ >= 3 ) #elif defined(__GNUC__) && (__GNUC__ >= 3 )
// GCC version 3 and higher.s // GCC version 3 and higher.s
//#warning( "Using sn* functions." ) //#warning( "Using sn* functions." )
#define TIXML_SNPRINTF snprintf #define TIXML_SNPRINTF snprintf
#define TIXML_SNSCANF snscanf
#define TIXML_SSCANF sscanf #define TIXML_SSCANF sscanf
#else #else
#define TIXML_SNPRINTF snprintf
#define TIXML_SSCANF sscanf #define TIXML_SSCANF sscanf
#endif #endif
#endif #endif
@ -92,8 +90,8 @@ class TiXmlDeclaration;
class TiXmlParsingData; class TiXmlParsingData;
const int TIXML_MAJOR_VERSION = 2; const int TIXML_MAJOR_VERSION = 2;
const int TIXML_MINOR_VERSION = 5; const int TIXML_MINOR_VERSION = 6;
const int TIXML_PATCH_VERSION = 3; const int TIXML_PATCH_VERSION = 1;
/* Internal structure for tracking location of items /* Internal structure for tracking location of items
in the XML file. in the XML file.
@ -109,10 +107,11 @@ struct TiXmlCursor
/** /**
Implements the interface to the "Visitor pattern" (see the Accept() method.)
If you call the Accept() method, it requires being passed a TiXmlVisitor If you call the Accept() method, it requires being passed a TiXmlVisitor
class to handle callbacks. For nodes that contain other nodes (Document, Element) class to handle callbacks. For nodes that contain other nodes (Document, Element)
you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves
are simple called with Visit(). are simply called with Visit().
If you return 'true' from a Visit method, recursive parsing will continue. If you return If you return 'true' from a Visit method, recursive parsing will continue. If you return
false, <b>no children of this node or its sibilings</b> will be Visited. false, <b>no children of this node or its sibilings</b> will be Visited.
@ -267,7 +266,6 @@ public:
TIXML_NO_ERROR = 0, TIXML_NO_ERROR = 0,
TIXML_ERROR, TIXML_ERROR,
TIXML_ERROR_OPENING_FILE, TIXML_ERROR_OPENING_FILE,
TIXML_ERROR_OUT_OF_MEMORY,
TIXML_ERROR_PARSING_ELEMENT, TIXML_ERROR_PARSING_ELEMENT,
TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
TIXML_ERROR_READING_ELEMENT_VALUE, TIXML_ERROR_READING_ELEMENT_VALUE,
@ -288,6 +286,7 @@ public:
protected: protected:
static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding );
inline static bool IsWhiteSpace( char c ) inline static bool IsWhiteSpace( char c )
{ {
return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' );
@ -462,13 +461,13 @@ public:
*/ */
enum NodeType enum NodeType
{ {
DOCUMENT, TINYXML_DOCUMENT,
ELEMENT, TINYXML_ELEMENT,
COMMENT, TINYXML_COMMENT,
UNKNOWN, TINYXML_UNKNOWN,
TEXT, TINYXML_TEXT,
DECLARATION, TINYXML_DECLARATION,
TYPECOUNT TINYXML_TYPECOUNT
}; };
virtual ~TiXmlNode(); virtual ~TiXmlNode();
@ -915,17 +914,14 @@ public:
const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
const TiXmlAttribute* Find( const char* _name ) const; TiXmlAttribute* Find( const char* _name ) const;
TiXmlAttribute* Find( const char* _name ) { TiXmlAttribute* FindOrCreate( const char* _name );
return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) );
} # ifdef TIXML_USE_STL
#ifdef TIXML_USE_STL TiXmlAttribute* Find( const std::string& _name ) const;
const TiXmlAttribute* Find( const std::string& _name ) const; TiXmlAttribute* FindOrCreate( const std::string& _name );
TiXmlAttribute* Find( const std::string& _name ) { # endif
return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) );
}
#endif
private: private:
//*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element),
@ -1000,11 +996,21 @@ public:
} }
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
/// QueryStringAttribute examines the attribute - see QueryIntAttribute().
int QueryStringAttribute( const char* name, std::string* _value ) const {
const char* cstr = Attribute( name );
if ( cstr ) {
*_value = std::string( cstr );
return TIXML_SUCCESS;
}
return TIXML_NO_ATTRIBUTE;
}
/** Template form of the attribute query which will try to read the /** Template form of the attribute query which will try to read the
attribute into the specified type. Very easy, very powerful, but attribute into the specified type. Very easy, very powerful, but
be careful to make sure to call this with the correct type. be careful to make sure to call this with the correct type.
NOTE: This method doesn't work correctly for 'string' types. NOTE: This method doesn't work correctly for 'string' types that contain spaces.
@return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE
*/ */
@ -1020,13 +1026,8 @@ public:
return TIXML_SUCCESS; return TIXML_SUCCESS;
return TIXML_WRONG_TYPE; return TIXML_WRONG_TYPE;
} }
/*
This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string" int QueryValueAttribute( const std::string& name, std::string* outValue ) const
but template specialization is hard to get working cross-compiler. Leaving the bug for now.
// The above will fail for std::string because the space character is used as a seperator.
// Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string
template<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const
{ {
const TiXmlAttribute* node = attributeSet.Find( name ); const TiXmlAttribute* node = attributeSet.Find( name );
if ( !node ) if ( !node )
@ -1034,7 +1035,6 @@ public:
*outValue = node->ValueStr(); *outValue = node->ValueStr();
return TIXML_SUCCESS; return TIXML_SUCCESS;
} }
*/
#endif #endif
/** Sets an attribute of name to a given value. The attribute /** Sets an attribute of name to a given value. The attribute
@ -1053,6 +1053,8 @@ public:
void SetAttribute( const std::string& name, const std::string& _value ); void SetAttribute( const std::string& name, const std::string& _value );
///< STL std::string form. ///< STL std::string form.
void SetAttribute( const std::string& name, int _value ); void SetAttribute( const std::string& name, int _value );
///< STL std::string form.
void SetDoubleAttribute( const std::string& name, double value );
#endif #endif
/** Sets an attribute of name to a given value. The attribute /** Sets an attribute of name to a given value. The attribute
@ -1144,7 +1146,6 @@ protected:
const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding );
private: private:
TiXmlAttributeSet attributeSet; TiXmlAttributeSet attributeSet;
}; };
@ -1155,9 +1156,9 @@ class TiXmlComment : public TiXmlNode
{ {
public: public:
/// Constructs an empty comment. /// Constructs an empty comment.
TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {}
/// Construct a comment from text. /// Construct a comment from text.
TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) { TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {
SetValue( _value ); SetValue( _value );
} }
TiXmlComment( const TiXmlComment& ); TiXmlComment( const TiXmlComment& );
@ -1209,7 +1210,7 @@ public:
normal, encoded text. If you want it be output as a CDATA text normal, encoded text. If you want it be output as a CDATA text
element, set the parameter _cdata to 'true' element, set the parameter _cdata to 'true'
*/ */
TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT) TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT)
{ {
SetValue( initValue ); SetValue( initValue );
cdata = false; cdata = false;
@ -1218,14 +1219,14 @@ public:
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
/// Constructor. /// Constructor.
TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT)
{ {
SetValue( initValue ); SetValue( initValue );
cdata = false; cdata = false;
} }
#endif #endif
TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); }
void operator=( const TiXmlText& base ) { base.CopyTo( this ); } void operator=( const TiXmlText& base ) { base.CopyTo( this ); }
// Write this text object to a FILE stream. // Write this text object to a FILE stream.
@ -1278,7 +1279,7 @@ class TiXmlDeclaration : public TiXmlNode
{ {
public: public:
/// Construct an empty declaration. /// Construct an empty declaration.
TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {}
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
/// Constructor. /// Constructor.
@ -1346,10 +1347,10 @@ private:
class TiXmlUnknown : public TiXmlNode class TiXmlUnknown : public TiXmlNode
{ {
public: public:
TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {}
virtual ~TiXmlUnknown() {} virtual ~TiXmlUnknown() {}
TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); }
void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); }
/// Creates a copy of this Unknown and returns it. /// Creates a copy of this Unknown and returns it.
@ -1423,14 +1424,10 @@ public:
#ifdef TIXML_USE_STL #ifdef TIXML_USE_STL
bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version.
{ {
// StringToBuffer f( filename );
// return ( f.buffer && LoadFile( f.buffer, encoding ));
return LoadFile( filename.c_str(), encoding ); return LoadFile( filename.c_str(), encoding );
} }
bool SaveFile( const std::string& filename ) const ///< STL std::string version. bool SaveFile( const std::string& filename ) const ///< STL std::string version.
{ {
// StringToBuffer f( filename );
// return ( f.buffer && SaveFile( f.buffer ));
return SaveFile( filename.c_str() ); return SaveFile( filename.c_str() );
} }
#endif #endif

@ -36,7 +36,6 @@ const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] =
"No error", "No error",
"Error", "Error",
"Failed to open file", "Failed to open file",
"Memory allocation failed.",
"Error parsing Element.", "Error parsing Element.",
"Failed to read Element name", "Failed to read Element name",
"Error reading Element value.", "Error reading Element value.",

@ -346,7 +346,7 @@ const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding )
continue; continue;
} }
if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. if ( IsWhiteSpace( *p ) ) // Still using old rules for white space.
++p; ++p;
else else
break; break;
@ -354,7 +354,7 @@ const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding )
} }
else else
{ {
while ( (*p && IsWhiteSpace( *p )) || *p == '\n' || *p =='\r' ) while ( *p && IsWhiteSpace( *p ) )
++p; ++p;
} }
@ -631,7 +631,7 @@ const char* TiXmlBase::ReadText( const char* p,
} }
} }
} }
if ( p ) if ( p && *p )
p += strlen( endTag ); p += strlen( endTag );
return p; return p;
} }
@ -825,7 +825,6 @@ TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding )
return 0; return 0;
} }
TiXmlDocument* doc = GetDocument();
p = SkipWhiteSpace( p, encoding ); p = SkipWhiteSpace( p, encoding );
if ( !p || !*p ) if ( !p || !*p )
@ -896,11 +895,6 @@ TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding )
// Set the parent, so it can report errors // Set the parent, so it can report errors
returnNode->parent = this; returnNode->parent = this;
} }
else
{
if ( doc )
doc->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
}
return returnNode; return returnNode;
} }
@ -1083,7 +1077,6 @@ const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEnc
TIXML_STRING endTag ("</"); TIXML_STRING endTag ("</");
endTag += value; endTag += value;
endTag += ">";
// Check for and read attributes. Also look for an empty // Check for and read attributes. Also look for an empty
// tag or an end tag. // tag or an end tag.
@ -1122,10 +1115,20 @@ const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEnc
} }
// We should find the end tag now // We should find the end tag now
// note that:
// </foo > and
// </foo>
// are both valid end tags.
if ( StringEqual( p, endTag.c_str(), false, encoding ) ) if ( StringEqual( p, endTag.c_str(), false, encoding ) )
{ {
p += endTag.length(); p += endTag.length();
return p; p = SkipWhiteSpace( p, encoding );
if ( p && *p && *p == '>' ) {
++p;
return p;
}
if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
return 0;
} }
else else
{ {
@ -1139,7 +1142,6 @@ const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEnc
TiXmlAttribute* attrib = new TiXmlAttribute(); TiXmlAttribute* attrib = new TiXmlAttribute();
if ( !attrib ) if ( !attrib )
{ {
if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, pErr, data, encoding );
return 0; return 0;
} }
@ -1162,7 +1164,7 @@ const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEnc
#endif #endif
if ( node ) if ( node )
{ {
node->SetValue( attrib->Value() ); if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
delete attrib; delete attrib;
return 0; return 0;
} }
@ -1191,8 +1193,7 @@ const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXm
if ( !textNode ) if ( !textNode )
{ {
if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, encoding ); return 0;
return 0;
} }
if ( TiXmlBase::IsWhiteSpaceCondensed() ) if ( TiXmlBase::IsWhiteSpaceCondensed() )
@ -1379,7 +1380,7 @@ const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEnc
value.append( p, 1 ); value.append( p, 1 );
++p; ++p;
} }
if ( p ) if ( p && *p )
p += strlen( endTag ); p += strlen( endTag );
return p; return p;
@ -1391,10 +1392,6 @@ const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlE
p = SkipWhiteSpace( p, encoding ); p = SkipWhiteSpace( p, encoding );
if ( !p || !*p ) return 0; if ( !p || !*p ) return 0;
// int tabsize = 4;
// if ( document )
// tabsize = document->TabSize();
if ( data ) if ( data )
{ {
data->Stamp( p, encoding ); data->Stamp( p, encoding );
@ -1446,7 +1443,7 @@ const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlE
// its best, even without them. // its best, even without them.
value = ""; value = "";
while ( p && *p // existence while ( p && *p // existence
&& !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace && !IsWhiteSpace( *p ) // whitespace
&& *p != '/' && *p != '>' ) // tag end && *p != '/' && *p != '>' ) // tag end
{ {
if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) {

@ -25,6 +25,8 @@ distribution.
#ifndef DFHACK_C_API #ifndef DFHACK_C_API
#define DFHACK_C_API #define DFHACK_C_API
#include "dfhack/DFPragma.h"
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include <vector> #include <vector>

@ -26,6 +26,7 @@ distribution.
#define ERROR_H_INCLUDED #define ERROR_H_INCLUDED
#include "DFExport.h" #include "DFExport.h"
#include "DFPragma.h"
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <exception> #include <exception>

@ -7,8 +7,18 @@
#ifdef _MSC_VER #ifdef _MSC_VER
// don't spew nonsense // don't spew nonsense
#pragma warning( disable: 4251 ) #pragma warning( disable: 4251 )
// don't display bogus 'deprecation' and 'unsafe' warnings // don't display bogus 'deprecation' and 'unsafe' warnings.
// See the idiocy: http://msdn.microsoft.com/en-us/magazine/cc163794.aspx
#define _CRT_SECURE_NO_DEPRECATE
#define _SCL_SECURE_NO_DEPRECATE
#pragma warning( disable: 4996 ) #pragma warning( disable: 4996 )
// Let me demonstrate:
/**
* [peterix@peterix dfhack]$ man wcscpy_s
* No manual entry for wcscpy_s
*
* Proprietary extensions.
*/
// disable stupid // disable stupid
#pragma warning( disable: 4800 ) #pragma warning( disable: 4800 )
// disable more stupid // disable more stupid

@ -62,16 +62,17 @@ namespace DFHack
// memory range name (if any) // memory range name (if any)
char name[1024]; char name[1024];
// permission to read // permission to read
bool read; bool read : 1;
// permission to write // permission to write
bool write; bool write : 1;
// permission to execute // permission to execute
bool execute; bool execute : 1;
inline bool isInRange( uint64_t address) inline bool isInRange( uint64_t address)
{ {
if (address >= start && address <= end) return true; if (address >= start && address < end) return true;
return false; return false;
} }
bool valid;
uint8_t * buffer; uint8_t * buffer;
}; };
@ -98,33 +99,33 @@ namespace DFHack
virtual bool forceresume() = 0; virtual bool forceresume() = 0;
/// read a 8-byte integer /// read a 8-byte integer
virtual uint64_t readQuad(const uint32_t address) = 0; uint64_t readQuad(const uint32_t address) { uint64_t result; readQuad(address, result); return result; }
/// read a 8-byte integer /// read a 8-byte integer
virtual void readQuad(const uint32_t address, uint64_t & value) = 0; virtual void readQuad(const uint32_t address, uint64_t & value) = 0;
/// write a 8-byte integer /// write a 8-byte integer
virtual void writeQuad(const uint32_t address, const uint64_t value) = 0; virtual void writeQuad(const uint32_t address, const uint64_t value) = 0;
/// read a 4-byte integer /// read a 4-byte integer
virtual uint32_t readDWord(const uint32_t address) = 0; uint32_t readDWord(const uint32_t address) { uint32_t result; readDWord(address, result); return result; }
/// read a 4-byte integer /// read a 4-byte integer
virtual void readDWord(const uint32_t address, uint32_t & value) = 0; virtual void readDWord(const uint32_t address, uint32_t & value) = 0;
/// write a 4-byte integer /// write a 4-byte integer
virtual void writeDWord(const uint32_t address, const uint32_t value) = 0; virtual void writeDWord(const uint32_t address, const uint32_t value) = 0;
/// read a float /// read a float
virtual float readFloat(const uint32_t address) = 0; float readFloat(const uint32_t address) { float result; readFloat(address, result); return result; }
/// write a float /// write a float
virtual void readFloat(const uint32_t address, float & value) = 0; virtual void readFloat(const uint32_t address, float & value) = 0;
/// read a 2-byte integer /// read a 2-byte integer
virtual uint16_t readWord(const uint32_t address) = 0; uint16_t readWord(const uint32_t address) { uint16_t result; readWord(address, result); return result; }
/// read a 2-byte integer /// read a 2-byte integer
virtual void readWord(const uint32_t address, uint16_t & value) = 0; virtual void readWord(const uint32_t address, uint16_t & value) = 0;
/// write a 2-byte integer /// write a 2-byte integer
virtual void writeWord(const uint32_t address, const uint16_t value) = 0; virtual void writeWord(const uint32_t address, const uint16_t value) = 0;
/// read a byte /// read a byte
virtual uint8_t readByte(const uint32_t address) = 0; uint8_t readByte(const uint32_t address) { uint8_t result; readByte(address, result); return result; }
/// read a byte /// read a byte
virtual void readByte(const uint32_t address, uint8_t & value) = 0; virtual void readByte(const uint32_t address, uint8_t & value) = 0;
/// write a byte /// write a byte
@ -153,6 +154,8 @@ namespace DFHack
virtual bool isAttached() = 0; virtual bool isAttached() = 0;
/// @return true if the process is identified -- has a Memory.xml entry /// @return true if the process is identified -- has a Memory.xml entry
virtual bool isIdentified() = 0; virtual bool isIdentified() = 0;
/// @return true if this is a Process snapshot
virtual bool isSnapshot() { return false; };
/// find the thread IDs of the process /// find the thread IDs of the process
virtual bool getThreadIDs(std::vector<uint32_t> & threads ) = 0; virtual bool getThreadIDs(std::vector<uint32_t> & threads ) = 0;
@ -173,203 +176,5 @@ namespace DFHack
virtual bool SetAndWait (uint32_t state) = 0; virtual bool SetAndWait (uint32_t state) = 0;
}; };
////////////////////////////////////////////////////////////////////////////
// Compiler appeasement area. Not worth a look really... //
////////////////////////////////////////////////////////////////////////////
class DFHACK_EXPORT NormalProcess : virtual public Process
{
friend class ProcessEnumerator;
class Private;
private:
Private * const d;
public:
NormalProcess(uint32_t pid, std::vector <VersionInfo *> & known_versions);
~NormalProcess();
bool attach();
bool detach();
bool suspend();
bool asyncSuspend();
bool resume();
bool forceresume();
uint64_t readQuad(const uint32_t address);
void readQuad(const uint32_t address, uint64_t & value);
void writeQuad(const uint32_t address, const uint64_t value);
uint32_t readDWord(const uint32_t address);
void readDWord(const uint32_t address, uint32_t & value);
void writeDWord(const uint32_t address, const uint32_t value);
float readFloat(const uint32_t address);
void readFloat(const uint32_t address, float & value);
uint16_t readWord(const uint32_t address);
void readWord(const uint32_t address, uint16_t & value);
void writeWord(const uint32_t address, const uint16_t value);
uint8_t readByte(const uint32_t address);
void readByte(const uint32_t address, uint8_t & value);
void writeByte(const uint32_t address, const uint8_t value);
void read( uint32_t address, uint32_t length, uint8_t* buffer);
void write(uint32_t address, uint32_t length, uint8_t* buffer);
const std::string readSTLString (uint32_t offset);
size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
void writeSTLString(const uint32_t address, const std::string writeString){};
// get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr);
const std::string readCString (uint32_t offset);
bool isSuspended();
bool isAttached();
bool isIdentified();
bool getThreadIDs(std::vector<uint32_t> & threads );
void getMemRanges(std::vector<t_memrange> & ranges );
VersionInfo *getDescriptor();
int getPID();
std::string getPath();
// get module index by name and version. bool 1 = error
bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { OUTPUT=0; return false;};
// get the SHM start if available
char * getSHMStart (void){return 0;};
// set a SHM command and wait for a response
bool SetAndWait (uint32_t state){return false;};
};
class DFHACK_EXPORT SHMProcess : virtual public Process
{
friend class ProcessEnumerator;
class Private;
private:
Private * const d;
public:
SHMProcess(uint32_t PID, std::vector <VersionInfo *> & known_versions);
~SHMProcess();
// Set up stuff so we can read memory
bool attach();
bool detach();
bool suspend();
bool asyncSuspend();
bool resume();
bool forceresume();
uint64_t readQuad(const uint32_t address);
void readQuad(const uint32_t address, uint64_t & value);
void writeQuad(const uint32_t address, const uint64_t value);
uint32_t readDWord(const uint32_t address);
void readDWord(const uint32_t address, uint32_t & value);
void writeDWord(const uint32_t address, const uint32_t value);
float readFloat(const uint32_t address);
void readFloat(const uint32_t address, float & value);
uint16_t readWord(const uint32_t address);
void readWord(const uint32_t address, uint16_t & value);
void writeWord(const uint32_t address, const uint16_t value);
uint8_t readByte(const uint32_t address);
void readByte(const uint32_t address, uint8_t & value);
void writeByte(const uint32_t address, const uint8_t value);
void read( uint32_t address, uint32_t length, uint8_t* buffer);
void write(uint32_t address, uint32_t length, uint8_t* buffer);
const std::string readSTLString (uint32_t offset);
size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
void writeSTLString(const uint32_t address, const std::string writeString);
// get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr);
const std::string readCString (uint32_t offset);
bool isSuspended();
bool isAttached();
bool isIdentified();
bool getThreadIDs(std::vector<uint32_t> & threads );
void getMemRanges(std::vector<t_memrange> & ranges );
VersionInfo *getDescriptor();
int getPID();
std::string getPath();
// get module index by name and version. bool 1 = error
bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT);
// get the SHM start if available
char * getSHMStart (void);
bool SetAndWait (uint32_t state);
};
#ifdef LINUX_BUILD
class DFHACK_EXPORT WineProcess : virtual public Process
{
friend class ProcessEnumerator;
class Private;
private:
Private * const d;
public:
WineProcess(uint32_t pid, std::vector <VersionInfo *> & known_versions);
~WineProcess();
bool attach();
bool detach();
bool suspend();
bool asyncSuspend();
bool resume();
bool forceresume();
uint64_t readQuad(const uint32_t address);
void readQuad(const uint32_t address, uint64_t & value);
void writeQuad(const uint32_t address, const uint64_t value);
uint32_t readDWord(const uint32_t address);
void readDWord(const uint32_t address, uint32_t & value);
void writeDWord(const uint32_t address, const uint32_t value);
float readFloat(const uint32_t address);
void readFloat(const uint32_t address, float & value);
uint16_t readWord(const uint32_t address);
void readWord(const uint32_t address, uint16_t & value);
void writeWord(const uint32_t address, const uint16_t value);
uint8_t readByte(const uint32_t address);
void readByte(const uint32_t address, uint8_t & value);
void writeByte(const uint32_t address, const uint8_t value);
void read( uint32_t address, uint32_t length, uint8_t* buffer);
void write(uint32_t address, uint32_t length, uint8_t* buffer);
const std::string readSTLString (uint32_t offset);
size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
void writeSTLString(const uint32_t address, const std::string writeString){};
// get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr);
const std::string readCString (uint32_t offset);
bool isSuspended();
bool isAttached();
bool isIdentified();
bool getThreadIDs(std::vector<uint32_t> & threads );
void getMemRanges(std::vector<t_memrange> & ranges );
VersionInfo *getDescriptor();
int getPID();
// get module index by name and version. bool 1 = error
bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) {OUTPUT=0; return false;};
// get the SHM start if available
char * getSHMStart (void){return 0;};
bool SetAndWait (uint32_t state){return false;};
std::string getPath();
};
#endif
} }
#endif #endif

@ -93,18 +93,18 @@ namespace DFHack
/* /*
* Version Info * Version Info
*/ */
enum OSType
{
OS_WINDOWS,
OS_LINUX,
OS_APPLE,
OS_BAD
};
class DFHACK_EXPORT VersionInfo : public OffsetGroup class DFHACK_EXPORT VersionInfo : public OffsetGroup
{ {
private: private:
VersionInfoPrivate * d; VersionInfoPrivate * d;
public: public:
enum OSType
{
OS_WINDOWS,
OS_LINUX,
OS_APPLE,
OS_BAD
};
VersionInfo(); VersionInfo();
VersionInfo(const VersionInfo&); VersionInfo(const VersionInfo&);
void copy(const DFHack::VersionInfo* old); void copy(const DFHack::VersionInfo* old);

@ -41,6 +41,8 @@ namespace DFHack
// memory info entries loaded from a file // memory info entries loaded from a file
bool loadFile( std::string path_to_xml); bool loadFile( std::string path_to_xml);
bool isInErrorState() const {return error;}; bool isInErrorState() const {return error;};
VersionInfo * getVersionInfoByMD5(std::string md5string);
VersionInfo * getVersionInfoByPETimestamp(uint32_t timestamp);
std::vector<VersionInfo*> versions; std::vector<VersionInfo*> versions;
private: private:
void ParseVTable(TiXmlElement* vtable, VersionInfo* mem); void ParseVTable(TiXmlElement* vtable, VersionInfo* mem);

@ -7,6 +7,8 @@
#include "dfhack/DFExport.h" #include "dfhack/DFExport.h"
#include "dfhack/DFModule.h" #include "dfhack/DFModule.h"
#include "Vegetation.h"
namespace DFHack namespace DFHack
{ {
/*************************************************************************** /***************************************************************************
@ -66,7 +68,7 @@ namespace DFHack
int32_t type; int32_t type;
/// bit mask describing how the vein maps to the map block /// bit mask describing how the vein maps to the map block
/// assignment[y] & (1 << x) describes the tile (x, y) of the block /// assignment[y] & (1 << x) describes the tile (x, y) of the block
int16_t assignment[16]; int16_t assignment[16];
uint32_t flags; uint32_t flags;
/// this is NOT part of the DF vein, but an address of the vein as seen by DFhack. /// this is NOT part of the DF vein, but an address of the vein as seen by DFhack.
uint32_t address_of; uint32_t address_of;
@ -99,7 +101,29 @@ namespace DFHack
/// this is NOT part of the DF vein, but an address of the vein as seen by DFhack. /// this is NOT part of the DF vein, but an address of the vein as seen by DFhack.
uint32_t address_of; uint32_t address_of;
}; };
struct t_grassvein
{
uint32_t vtable;
/// material vector index
uint32_t material;
/// 16x16 array of covering 'intensity'
uint8_t intensity[16][16];
/// this is NOT part of the DF vein, but an address of the vein as seen by DFhack.
uint32_t address_of;
};
struct t_worldconstruction
{
uint32_t vtable;
/// material vector index
uint32_t material;
/// 16x16 array of bits
uint16_t assignment[16];
/// this is NOT part of the structure, but an address of it as seen by DFhack.
uint32_t address_of;
};
enum BiomeOffset enum BiomeOffset
{ {
eNorthWest, eNorthWest,
@ -148,15 +172,7 @@ namespace DFHack
liquid_water, liquid_water,
liquid_magma liquid_magma
}; };
/*
enum e_liquidcharacter
{
liquid_fresh,
liquid_unk1,
liquid_salt,
liquid_unk2,
};
*/
struct naked_designation struct naked_designation
{ {
unsigned int flow_size : 3; // how much liquid is here? unsigned int flow_size : 3; // how much liquid is here?
@ -426,8 +442,12 @@ namespace DFHack
bool ReadVeins(uint32_t x, uint32_t y, uint32_t z, bool ReadVeins(uint32_t x, uint32_t y, uint32_t z,
std::vector<t_vein>* veins, std::vector<t_vein>* veins,
std::vector<t_frozenliquidvein>* ices = 0, std::vector<t_frozenliquidvein>* ices = 0,
std::vector<t_spattervein>* splatter = 0); std::vector<t_spattervein>* splatter = 0,
std::vector<t_grassvein>* grass = 0,
std::vector<t_worldconstruction>* constructions = 0
);
/// read all plants in this block
bool ReadVegetation(uint32_t x, uint32_t y, uint32_t z, std::vector<t_tree>* plants);
private: private:
struct Private; struct Private;
Private *d; Private *d;

@ -145,7 +145,7 @@ namespace DFHack
void ReadAllMaterials(void); void ReadAllMaterials(void);
std::string getType(t_material & mat); std::string getType(t_material & mat);
std::string getDescription(t_material & mat); std::string getDescription(t_material & mat);
private: private:
class Private; class Private;

@ -15,6 +15,12 @@ namespace DFHack
RAINING, RAINING,
SNOWING SNOWING
}; };
enum GameMode
{
GM_Fort = 0,
GM_Adventurer = 1,
GM_Menu = 3,
};
class DFContextShared; class DFContextShared;
class DFHACK_EXPORT World : public Module class DFHACK_EXPORT World : public Module
{ {
@ -31,7 +37,7 @@ namespace DFHack
uint32_t ReadCurrentDay(); uint32_t ReadCurrentDay();
uint8_t ReadCurrentWeather(); uint8_t ReadCurrentWeather();
void SetCurrentWeather(uint8_t weather); void SetCurrentWeather(uint8_t weather);
int32_t ReadGameMode();
private: private:
struct Private; struct Private;
Private *d; Private *d;

@ -72,24 +72,36 @@ Buildings::Buildings(DFContextShared * d_)
d = new Private; d = new Private;
d->d = d_; d->d = d_;
d->owner = d_->p; d->owner = d_->p;
d->p_bld = NULL;
d->Inited = d->Started = d->hasCustomWorkshops = false; d->Inited = d->Started = d->hasCustomWorkshops = false;
VersionInfo * mem = d->d->offset_descriptor; VersionInfo * mem = d->d->offset_descriptor;
OffsetGroup * OG_build = mem->getGroup("Buildings"); OffsetGroup * OG_build = mem->getGroup("Buildings");
d->buildings_vector = OG_build->getAddress ("buildings_vector"); d->Inited = true;
try try
{ {
d->custom_workshop_vector = OG_build->getAddress("custom_workshop_vector"); d->buildings_vector = OG_build->getAddress ("buildings_vector");
d->building_custom_workshop_type = OG_build->getOffset("building_custom_workshop_type");
d->custom_workshop_type = OG_build->getOffset("custom_workshop_type");
d->custom_workshop_name = OG_build->getOffset("custom_workshop_name");
mem->resolveClassnameToClassID("building_custom_workshop", d->custom_workshop_id);
d->hasCustomWorkshops = true;
} }
catch(DFHack::Error::AllMemdef &e) catch(DFHack::Error::AllMemdef &e)
{ {
cerr << "Custom workshops not available. Memory Definition: " << e.what() << endl; cerr << "Buildings not available... " << e.what() << endl;
d->Inited = false;
}
if(d->Inited)
{
try
{
d->custom_workshop_vector = OG_build->getAddress("custom_workshop_vector");
d->building_custom_workshop_type = OG_build->getOffset("building_custom_workshop_type");
d->custom_workshop_type = OG_build->getOffset("custom_workshop_type");
d->custom_workshop_name = OG_build->getOffset("custom_workshop_name");
mem->resolveClassnameToClassID("building_custom_workshop", d->custom_workshop_id);
d->hasCustomWorkshops = true;
}
catch(DFHack::Error::AllMemdef &e)
{
cerr << "Custom workshops not available. Memory Definition: " << e.what() << endl;
}
} }
d->Inited = true;
} }
Buildings::~Buildings() Buildings::~Buildings()
@ -101,6 +113,8 @@ Buildings::~Buildings()
bool Buildings::Start(uint32_t & numbuildings) bool Buildings::Start(uint32_t & numbuildings)
{ {
if(!d->Inited)
return false;
d->p_bld = new DfVector <uint32_t> (d->owner, d->buildings_vector); d->p_bld = new DfVector <uint32_t> (d->owner, d->buildings_vector);
numbuildings = d->p_bld->size(); numbuildings = d->p_bld->size();
d->Started = true; d->Started = true;
@ -148,7 +162,7 @@ bool Buildings::Finish()
bool Buildings::ReadCustomWorkshopTypes(map <uint32_t, string> & btypes) bool Buildings::ReadCustomWorkshopTypes(map <uint32_t, string> & btypes)
{ {
if(!d->Started) if(!d->Inited)
return false; return false;
if(!d->hasCustomWorkshops) if(!d->hasCustomWorkshops)
return false; return false;

@ -72,6 +72,7 @@ Creatures::Creatures(DFContextShared* _d)
d->owner = _d->p; d->owner = _d->p;
d->Inited = false; d->Inited = false;
d->Started = false; d->Started = false;
d->p_cre = NULL;
d->d->InitReadNames(); // throws on error d->d->InitReadNames(); // throws on error
VersionInfo * minfo = d->d->offset_descriptor; VersionInfo * minfo = d->d->offset_descriptor;
OffsetGroup *OG_Creatures = minfo->getGroup("Creatures"); OffsetGroup *OG_Creatures = minfo->getGroup("Creatures");
@ -149,10 +150,14 @@ Creatures::~Creatures()
bool Creatures::Start( uint32_t &numcreatures ) bool Creatures::Start( uint32_t &numcreatures )
{ {
d->p_cre = new DfVector <uint32_t> (d->owner, d->creatures.vector); if(d->Ft_basic)
d->Started = true; {
numcreatures = d->p_cre->size(); d->p_cre = new DfVector <uint32_t> (d->owner, d->creatures.vector);
return true; d->Started = true;
numcreatures = d->p_cre->size();
return true;
}
return false;
} }
bool Creatures::Finish() bool Creatures::Finish()

@ -0,0 +1,170 @@
// This is just a graveyard of old 40d code. Things in here COULD be turned into modules, but it requires research.
bool API::InitReadEffects ( uint32_t & numeffects )
{
if(d->effectsInited)
FinishReadEffects();
int effects = 0;
try
{
effects = d->offset_descriptor->getAddress ("effects_vector");
}
catch(Error::AllMemdef)
{
return false;
}
d->effectsInited = true;
d->p_effect = new DfVector (d->p, effects);
numeffects = d->p_effect->getSize();
return true;
}
bool API::ReadEffect(const uint32_t index, t_effect_df40d & effect)
{
if(!d->effectsInited)
return false;
if(index >= d->p_effect->getSize())
return false;
// read pointer from vector at position
uint32_t temp = d->p_effect->at (index);
//read effect from memory
d->p->read (temp, sizeof (t_effect_df40d), (uint8_t *) &effect);
return true;
}
// use with care!
bool API::WriteEffect(const uint32_t index, const t_effect_df40d & effect)
{
if(!d->effectsInited)
return false;
if(index >= d->p_effect->getSize())
return false;
// read pointer from vector at position
uint32_t temp = d->p_effect->at (index);
// write effect to memory
d->p->write(temp,sizeof(t_effect_df40d), (uint8_t *) &effect);
return true;
}
void API::FinishReadEffects()
{
if(d->p_effect)
{
delete d->p_effect;
d->p_effect = NULL;
}
d->effectsInited = false;
}
bool API::InitReadNotes( uint32_t &numnotes )
{
try
{
memory_info * minfo = d->offset_descriptor;
int notes = minfo->getAddress ("notes");
d->note_foreground_offset = minfo->getOffset ("note_foreground");
d->note_background_offset = minfo->getOffset ("note_background");
d->note_name_offset = minfo->getOffset ("note_name");
d->note_xyz_offset = minfo->getOffset ("note_xyz");
d->p_notes = new DfVector (d->p, notes);
d->notesInited = true;
numnotes = d->p_notes->getSize();
return true;
}
catch (Error::AllMemdef&)
{
d->notesInited = false;
numnotes = 0;
throw;
}
}
bool API::ReadNote (const int32_t index, t_note & note)
{
if(!d->notesInited) return false;
// read pointer from vector at position
uint32_t temp = d->p_notes->at (index);
note.symbol = d->p->readByte(temp);
note.foreground = d->p->readWord(temp + d->note_foreground_offset);
note.background = d->p->readWord(temp + d->note_background_offset);
d->p->readSTLString (temp + d->note_name_offset, note.name, 128);
d->p->read (temp + d->note_xyz_offset, 3*sizeof (uint16_t), (uint8_t *) &note.x);
return true;
}
bool API::InitReadSettlements( uint32_t & numsettlements )
{
if(!d->InitReadNames()) return false;
try
{
memory_info * minfo = d->offset_descriptor;
int allSettlements = minfo->getAddress ("settlements");
int currentSettlement = minfo->getAddress("settlement_current");
d->settlement_name_offset = minfo->getOffset ("settlement_name");
d->settlement_world_xy_offset = minfo->getOffset ("settlement_world_xy");
d->settlement_local_xy_offset = minfo->getOffset ("settlement_local_xy");
d->p_settlements = new DfVector (d->p, allSettlements);
d->p_current_settlement = new DfVector(d->p, currentSettlement);
d->settlementsInited = true;
numsettlements = d->p_settlements->getSize();
return true;
}
catch (Error::AllMemdef&)
{
d->settlementsInited = false;
numsettlements = 0;
throw;
}
}
bool API::ReadSettlement(const int32_t index, t_settlement & settlement)
{
if(!d->settlementsInited) return false;
if(!d->p_settlements->getSize()) return false;
// read pointer from vector at position
uint32_t temp = d->p_settlements->at (index);
settlement.origin = temp;
d->readName(settlement.name, temp + d->settlement_name_offset);
d->p->read(temp + d->settlement_world_xy_offset, 2 * sizeof(int16_t), (uint8_t *) &settlement.world_x);
d->p->read(temp + d->settlement_local_xy_offset, 4 * sizeof(int16_t), (uint8_t *) &settlement.local_x1);
return true;
}
bool API::ReadCurrentSettlement(t_settlement & settlement)
{
if(!d->settlementsInited) return false;
if(!d->p_current_settlement->getSize()) return false;
uint32_t temp = d->p_current_settlement->at(0);
settlement.origin = temp;
d->readName(settlement.name, temp + d->settlement_name_offset);
d->p->read(temp + d->settlement_world_xy_offset, 2 * sizeof(int16_t), (uint8_t *) &settlement.world_x);
d->p->read(temp + d->settlement_local_xy_offset, 4 * sizeof(int16_t), (uint8_t *) &settlement.local_x1);
return true;
}
void API::FinishReadSettlements()
{
if(d->p_settlements)
{
delete d->p_settlements;
d->p_settlements = NULL;
}
if(d->p_current_settlement)
{
delete d->p_current_settlement;
d->p_current_settlement = NULL;
}
d->settlementsInited = false;
}
void API::FinishReadNotes()
{
if(d->p_notes)
{
delete d->p_notes;
d->p_notes = 0;
}
d->notesInited = false;
}

@ -357,3 +357,126 @@ std::string Items::getItemDescription(uint32_t itemptr, Materials * Materials)
out.append(this->getItemClass(item.matdesc.itemType)); out.append(this->getItemClass(item.matdesc.itemType));
return out; return out;
} }
// The OLD items code follows (40d era)
// TODO: merge with the current Items module
/*
bool API::InitReadItems(uint32_t & numitems)
{
try
{
int items = d->offset_descriptor->getAddress ("items");
d->item_material_offset = d->offset_descriptor->getOffset ("item_materials");
d->p_itm = new DfVector (d->p, items);
d->itemsInited = true;
numitems = d->p_itm->getSize();
return true;
}
catch (Error::AllMemdef&)
{
d->itemsInited = false;
numitems = 0;
throw;
}
}
bool API::getItemIndexesInBox(vector<uint32_t> &indexes,
const uint16_t x1, const uint16_t y1, const uint16_t z1,
const uint16_t x2, const uint16_t y2, const uint16_t z2)
{
if(!d->itemsInited) return false;
indexes.clear();
uint32_t size = d->p_itm->getSize();
struct temp2{
uint16_t coords[3];
uint32_t flags;
};
temp2 temp2;
for(uint32_t i =0;i<size;i++){
uint32_t temp = d->p_itm->at(i);
d->p->read(temp+sizeof(uint32_t),5 * sizeof(uint16_t), (uint8_t *) &temp2);
if(temp2.flags & (1 << 0)){
if (temp2.coords[0] >= x1 && temp2.coords[0] < x2)
{
if (temp2.coords[1] >= y1 && temp2.coords[1] < y2)
{
if (temp2.coords[2] >= z1 && temp2.coords[2] < z2)
{
indexes.push_back(i);
}
}
}
}
}
return true;
}
bool API::ReadItem (const uint32_t index, t_item & item)
{
if (!d->itemsInited) return false;
t_item_df40d item_40d;
// read pointer from vector at position
uint32_t temp = d->p_itm->at (index);
//read building from memory
d->p->read (temp, sizeof (t_item_df40d), (uint8_t *) &item_40d);
// transform
int32_t type = -1;
d->offset_descriptor->resolveObjectToClassID (temp, type);
item.origin = temp;
item.vtable = item_40d.vtable;
item.x = item_40d.x;
item.y = item_40d.y;
item.z = item_40d.z;
item.type = type;
item.ID = item_40d.ID;
item.flags.whole = item_40d.flags;
//TODO certain item types (creature based, threads, seeds, bags do not have the first matType byte, instead they have the material index only located at 0x68
d->p->read (temp + d->item_material_offset, sizeof (t_matglossPair), (uint8_t *) &item.material);
//for(int i = 0; i < 0xCC; i++){ // used for item research
// uint8_t byte = MreadByte(temp+i);
// item.bytes.push_back(byte);
//}
return true;
}
void API::FinishReadItems()
{
if(d->p_itm)
{
delete d->p_itm;
d->p_itm = NULL;
}
d->itemsInited = false;
}
*/
/*
bool API::ReadItemTypes(vector< vector< t_itemType > > & itemTypes)
{
memory_info * minfo = d->offset_descriptor;
int matgloss_address = minfo->getAddress("matgloss");
int matgloss_skip = minfo->getHexValue("matgloss_skip");
int item_type_name_offset = minfo->getOffset("item_type_name");
for(int i = 8;i<20;i++)
{
DfVector p_temp (d->p, matgloss_address + i*matgloss_skip);
vector< t_itemType > typesForVec;
for(uint32_t j =0; j<p_temp.getSize();j++)
{
t_itemType currType;
uint32_t temp = *(uint32_t *) p_temp[j];
// Mread(temp+40,sizeof(name),(uint8_t *) name);
d->p->readSTLString(temp+4,currType.id,128);
d->p->readSTLString(temp+item_type_name_offset,currType.name,128);
//stringsForVec.push_back(string(name));
typesForVec.push_back(currType);
}
itemTypes.push_back(typesForVec);
}
return true;
}
*/

@ -57,6 +57,9 @@ struct Maps::Private
OffsetGroup *OG_vector; OffsetGroup *OG_vector;
bool Inited; bool Inited;
bool Started; bool Started;
bool hasGeology;
bool hasFeatures;
bool hasVeggies;
// map between feature address and the read object // map between feature address and the read object
map <uint32_t, t_feature> local_feature_store; map <uint32_t, t_feature> local_feature_store;
@ -74,6 +77,7 @@ Maps::Maps(DFContextShared* _d)
DFHack::VersionInfo * mem = p->getDescriptor(); DFHack::VersionInfo * mem = p->getDescriptor();
Server::Maps::maps_offsets &off = d->offsets; Server::Maps::maps_offsets &off = d->offsets;
d->hasFeatures = d->hasGeology = d->hasVeggies = true;
// get the offsets once here // get the offsets once here
OffsetGroup *OG_Maps = mem->getGroup("Maps"); OffsetGroup *OG_Maps = mem->getGroup("Maps");
@ -87,7 +91,6 @@ Maps::Maps(DFContextShared* _d)
off.region_z_offset = OG_Maps->getAddress ("region_z"); off.region_z_offset = OG_Maps->getAddress ("region_z");
off.world_size_x = OG_Maps->getAddress ("world_size_x"); off.world_size_x = OG_Maps->getAddress ("world_size_x");
off.world_size_y = OG_Maps->getAddress ("world_size_y"); off.world_size_y = OG_Maps->getAddress ("world_size_y");
OffsetGroup *OG_MapBlock = OG_Maps->getGroup("block"); OffsetGroup *OG_MapBlock = OG_Maps->getGroup("block");
{ {
off.tile_type_offset = OG_MapBlock->getOffset ("type"); off.tile_type_offset = OG_MapBlock->getOffset ("type");
@ -100,18 +103,41 @@ Maps::Maps(DFContextShared* _d)
off.temperature1_offset = OG_MapBlock->getOffset ("temperature1"); off.temperature1_offset = OG_MapBlock->getOffset ("temperature1");
off.temperature2_offset = OG_MapBlock->getOffset ("temperature2"); off.temperature2_offset = OG_MapBlock->getOffset ("temperature2");
} }
try
OffsetGroup *OG_Geology = OG_Maps->getGroup("geology"); {
OffsetGroup *OG_Geology = OG_Maps->getGroup("geology");
{
off.world_regions = OG_Geology->getAddress ("ptr2_region_array");
off.region_size = OG_Geology->getHexValue ("region_size");
off.region_geo_index_offset = OG_Geology->getOffset ("region_geo_index_off");
off.geolayer_geoblock_offset = OG_Geology->getOffset ("geolayer_geoblock_offset");
off.world_geoblocks_vector = OG_Geology->getAddress ("geoblock_vector");
off.type_inside_geolayer = OG_Geology->getOffset ("type_inside_geolayer");
}
}
catch(Error::AllMemdef &)
{
d->hasGeology = false;
}
try
{
d->OG_global_features = OG_Maps->getGroup("features")->getGroup("global");
d->OG_local_features = OG_Maps->getGroup("features")->getGroup("local");
}
catch(Error::AllMemdef &)
{ {
off.world_regions = OG_Geology->getAddress ("ptr2_region_array"); d->hasFeatures = false;
off.region_size = OG_Geology->getHexValue ("region_size"); }
off.region_geo_index_offset = OG_Geology->getOffset ("region_geo_index_off"); try
off.geolayer_geoblock_offset = OG_Geology->getOffset ("geolayer_geoblock_offset"); {
off.world_geoblocks_vector = OG_Geology->getAddress ("geoblock_vector"); OffsetGroup * OG_Veg = d->d->offset_descriptor->getGroup("Vegetation");
off.type_inside_geolayer = OG_Geology->getOffset ("type_inside_geolayer"); off.vegvector = OG_MapBlock->getOffset ("vegetation_vector");
off.tree_desc_offset = OG_Veg->getOffset ("tree_desc_offset");
}
catch(Error::AllMemdef &)
{
d->hasVeggies = false;
} }
d->OG_global_features = OG_Maps->getGroup("features")->getGroup("global");
d->OG_local_features = OG_Maps->getGroup("features")->getGroup("local");
} }
d->OG_vector = mem->getGroup("vector"); d->OG_vector = mem->getGroup("vector");
@ -121,6 +147,12 @@ Maps::Maps(DFContextShared* _d)
mem->resolveClassnameToVPtr("block_square_event_frozen_liquid", off.vein_ice_vptr); mem->resolveClassnameToVPtr("block_square_event_frozen_liquid", off.vein_ice_vptr);
off.vein_mineral_vptr = 0; off.vein_mineral_vptr = 0;
mem->resolveClassnameToVPtr("block_square_event_mineral",off.vein_mineral_vptr); mem->resolveClassnameToVPtr("block_square_event_mineral",off.vein_mineral_vptr);
off.vein_spatter_vptr = 0;
mem->resolveClassnameToVPtr("block_square_event_material_spatterst",off.vein_spatter_vptr);
off.vein_grass_vptr = 0;
mem->resolveClassnameToVPtr("block_square_event_grassst",off.vein_grass_vptr);
off.vein_worldconstruction_vptr = 0;
mem->resolveClassnameToVPtr("block_square_event_world_constructionst",off.vein_worldconstruction_vptr);
// upload offsets to SHM server if possible // upload offsets to SHM server if possible
d->maps_module = 0; d->maps_module = 0;
@ -517,18 +549,22 @@ bool Maps::WriteGlobalFeature(uint32_t x, uint32_t y, uint32_t z, int16_t global
/* /*
* Block events * Block events
*/ */
bool Maps::ReadVeins(uint32_t x, uint32_t y, uint32_t z, vector <t_vein>* veins, vector <t_frozenliquidvein>* ices, vector <t_spattervein> *splatter) bool Maps::ReadVeins(uint32_t x, uint32_t y, uint32_t z, vector <t_vein>* veins, vector <t_frozenliquidvein>* ices, vector <t_spattervein> *splatter, vector <t_grassvein> *grass, vector <t_worldconstruction> *constructions)
{ {
MAPS_GUARD MAPS_GUARD
t_vein v; t_vein v;
t_frozenliquidvein fv; t_frozenliquidvein fv;
t_spattervein sv; t_spattervein sv;
t_grassvein gv;
t_worldconstruction wcv;
Process* p = d->owner; Process* p = d->owner;
uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z];
if(veins) veins->clear(); if(veins) veins->clear();
if(ices) ices->clear(); if(ices) ices->clear();
if(splatter) splatter->clear(); if(splatter) splatter->clear();
if(grass) splatter->clear();
if(constructions) constructions->clear();
Server::Maps::maps_offsets &off = d->offsets; Server::Maps::maps_offsets &off = d->offsets;
if (addr) if (addr)
@ -568,6 +604,22 @@ try_again:
// store it in the vector // store it in the vector
splatter->push_back (sv); splatter->push_back (sv);
} }
else if(grass && type == off.vein_grass_vptr)
{
// read the splatter vein data (dereference pointer)
p->read (temp, sizeof(t_grassvein), (uint8_t *) &gv);
gv.address_of = temp;
// store it in the vector
grass->push_back (gv);
}
else if(constructions && type == off.vein_worldconstruction_vptr)
{
// read the splatter vein data (dereference pointer)
p->read (temp, sizeof(t_worldconstruction), (uint8_t *) &wcv);
wcv.address_of = temp;
// store it in the vector
constructions->push_back (wcv);
}
else else
{ {
string cname = p->readClassName(type); string cname = p->readClassName(type);
@ -586,12 +638,20 @@ try_again:
off.vein_spatter_vptr = type; off.vein_spatter_vptr = type;
goto try_again; goto try_again;
} }
#ifdef DEBUG else if(grass && cname=="block_square_event_grassst")
{
off.vein_grass_vptr = type;
goto try_again;
}
else if(constructions && cname=="block_square_event_world_constructionst")
{
off.vein_worldconstruction_vptr = type;
goto try_again;
}
else else
{ {
cerr << "unknown vein " << cname << endl; cerr << "unknown vein " << cname << hex << " 0x" << temp << " block: 0x" << addr << dec << endl;
} }
#endif
// or it was something we don't care about // or it was something we don't care about
} }
} }
@ -661,6 +721,7 @@ __int16 __userpurge GetGeologicalRegion<ax>(__int16 block_X<cx>, int X<ebx>, __i
bool Maps::ReadGeology (vector < vector <uint16_t> >& assign) bool Maps::ReadGeology (vector < vector <uint16_t> >& assign)
{ {
MAPS_GUARD MAPS_GUARD
if(!d->hasGeology) return false;
Process *p = d->owner; Process *p = d->owner;
// get needed addresses and offsets. Now this is what I call crazy. // get needed addresses and offsets. Now this is what I call crazy.
uint16_t worldSizeX, worldSizeY; uint16_t worldSizeX, worldSizeY;
@ -730,6 +791,7 @@ bool Maps::ReadGeology (vector < vector <uint16_t> >& assign)
bool Maps::ReadLocalFeatures( std::map <planecoord, std::vector<t_feature *> > & local_features ) bool Maps::ReadLocalFeatures( std::map <planecoord, std::vector<t_feature *> > & local_features )
{ {
MAPS_GUARD MAPS_GUARD
if(!d->hasFeatures) return false;
// can't be used without a map! // can't be used without a map!
if(!d->block) if(!d->block)
return false; return false;
@ -822,6 +884,7 @@ bool Maps::ReadLocalFeatures( std::map <planecoord, std::vector<t_feature *> > &
bool Maps::ReadGlobalFeatures( std::vector <t_feature> & features) bool Maps::ReadGlobalFeatures( std::vector <t_feature> & features)
{ {
MAPS_GUARD MAPS_GUARD
if(!d->hasFeatures) return false;
// can't be used without a map! // can't be used without a map!
if(!d->block) if(!d->block)
return false; return false;
@ -864,3 +927,25 @@ bool Maps::ReadGlobalFeatures( std::vector <t_feature> & features)
return true; return true;
} }
bool Maps::ReadVegetation(uint32_t x, uint32_t y, uint32_t z, std::vector<t_tree>* plants)
{
if(!d->hasVeggies || !d->Started)
return false;
uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z];
if(!addr)
return false;
t_tree shrubbery;
plants->clear();
Server::Maps::maps_offsets & off = d->offsets;
DfVector<uint32_t> vegptrs(d->owner, addr + off.vegvector);
for(int i = 0; i < vegptrs.size(); i++)
{
d->owner->read (vegptrs[i] + off.tree_desc_offset, sizeof (t_tree), (uint8_t *) &shrubbery);
shrubbery.address = vegptrs[i];
plants->push_back(shrubbery);
}
if(plants->empty()) return false;
return true;
}

@ -22,6 +22,7 @@ must not be misrepresented as being the original software.
distribution. distribution.
*/ */
#include "dfhack/DFPragma.h"
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>

@ -49,9 +49,11 @@ struct World::Private
bool Inited; bool Inited;
bool StartedTime; bool StartedTime;
bool StartedWeather; bool StartedWeather;
bool StartedMode;
uint32_t year_offset; uint32_t year_offset;
uint32_t tick_offset; uint32_t tick_offset;
uint32_t weather_offset; uint32_t weather_offset;
uint32_t gamemode_offset;
DFContextShared *d; DFContextShared *d;
Process * owner; Process * owner;
}; };
@ -62,7 +64,7 @@ World::World(DFContextShared * _d)
d = new Private; d = new Private;
d->d = _d; d->d = _d;
d->owner = _d->p; d->owner = _d->p;
d->Inited = d->StartedTime = d->StartedWeather = false; d->Inited = d->StartedTime = d->StartedWeather = d->StartedMode = false;
OffsetGroup * OG_World = d->d->offset_descriptor->getGroup("World"); OffsetGroup * OG_World = d->d->offset_descriptor->getGroup("World");
try try
@ -78,6 +80,12 @@ World::World(DFContextShared * _d)
d->StartedWeather = true; d->StartedWeather = true;
} }
catch(Error::All &){}; catch(Error::All &){};
try
{
d->gamemode_offset = OG_World->getAddress( "game_mode" );
d->StartedMode = true;
}
catch(Error::All &){};
d->Inited = true; d->Inited = true;
} }
@ -110,6 +118,14 @@ uint32_t World::ReadCurrentTick()
return 0; return 0;
} }
int32_t World::ReadGameMode()
{
if(d->Inited && d->StartedMode)
return d->owner->readDWord(d->gamemode_offset);
return -1;
}
// FIX'D according to this: // FIX'D according to this:
/* /*
World::ReadCurrentMonth and World::ReadCurrentDay World::ReadCurrentMonth and World::ReadCurrentDay

@ -81,6 +81,57 @@ using namespace std;
#include <winnt.h> #include <winnt.h>
#include <psapi.h> #include <psapi.h>
#include <tlhelp32.h> #include <tlhelp32.h>
#include <Dbghelp.h>
typedef LONG NTSTATUS;
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
// FIXME: it is uncertain how these map to 64bit
typedef struct _DEBUG_BUFFER
{
HANDLE SectionHandle;
PVOID SectionBase;
PVOID RemoteSectionBase;
ULONG SectionBaseDelta;
HANDLE EventPairHandle;
ULONG Unknown[2];
HANDLE RemoteThreadHandle;
ULONG InfoClassMask;
ULONG SizeOfInfo;
ULONG AllocatedSize;
ULONG SectionSize;
PVOID ModuleInformation;
PVOID BackTraceInformation;
PVOID HeapInformation;
PVOID LockInformation;
PVOID Reserved[8];
} DEBUG_BUFFER, *PDEBUG_BUFFER;
typedef struct _DEBUG_HEAP_INFORMATION
{
ULONG Base; // 0×00
ULONG Flags; // 0×04
USHORT Granularity; // 0×08
USHORT Unknown; // 0x0A
ULONG Allocated; // 0x0C
ULONG Committed; // 0×10
ULONG TagCount; // 0×14
ULONG BlockCount; // 0×18
ULONG Reserved[7]; // 0x1C
PVOID Tags; // 0×38
PVOID Blocks; // 0x3C
} DEBUG_HEAP_INFORMATION, *PDEBUG_HEAP_INFORMATION;
// RtlQueryProcessDebugInformation.DebugInfoClassMask constants
#define PDI_MODULES 0x01
#define PDI_BACKTRACE 0x02
#define PDI_HEAPS 0x04
#define PDI_HEAP_TAGS 0x08
#define PDI_HEAP_BLOCKS 0x10
#define PDI_LOCKS 0x20
extern "C" __declspec(dllimport) NTSTATUS __stdcall RtlQueryProcessDebugInformation( IN ULONG ProcessId, IN ULONG DebugInfoClassMask, IN OUT PDEBUG_BUFFER DebugBuffer);
extern "C" __declspec(dllimport) PDEBUG_BUFFER __stdcall RtlCreateQueryDebugBuffer( IN ULONG Size, IN BOOLEAN EventPair);
extern "C" __declspec(dllimport) NTSTATUS __stdcall RtlDestroyQueryDebugBuffer( IN PDEBUG_BUFFER DebugBuffer);
#endif #endif
// dfhack dependencies // dfhack dependencies

@ -0,0 +1,91 @@
/*
www.sourceforge.net/projects/dfhack
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef LINUX_PROCESS_H_INCLUDED
#define LINUX_PROCESS_H_INCLUDED
#include "dfhack/DFProcess.h"
namespace DFHack
{
class LinuxProcessBase : public Process
{
protected:
VersionInfo * my_descriptor;
pid_t my_pid;
string memFile;
int memFileHandle;
bool attached;
bool suspended;
bool identified;
public:
LinuxProcessBase(uint32_t pid);
~LinuxProcessBase();
bool attach();
bool detach();
bool suspend();
bool asyncSuspend();
bool resume();
bool forceresume();
void readQuad(const uint32_t address, uint64_t & value);
void writeQuad(const uint32_t address, const uint64_t value);
void readDWord(const uint32_t address, uint32_t & value);
void writeDWord(const uint32_t address, const uint32_t value);
void readFloat(const uint32_t address, float & value);
void readWord(const uint32_t address, uint16_t & value);
void writeWord(const uint32_t address, const uint16_t value);
void readByte(const uint32_t address, uint8_t & value);
void writeByte(const uint32_t address, const uint8_t value);
void read( uint32_t address, uint32_t length, uint8_t* buffer);
void write(uint32_t address, uint32_t length, uint8_t* buffer);
const std::string readCString (uint32_t offset);
bool isSuspended();
bool isAttached();
bool isIdentified();
VersionInfo *getDescriptor();
int getPID();
std::string getPath();
bool getThreadIDs(std::vector<uint32_t> & threads );
void getMemRanges(std::vector<t_memrange> & ranges );
// get module index by name and version. bool 1 = error
bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { OUTPUT=0; return false;};
// get the SHM start if available
char * getSHMStart (void){return 0;};
// set a SHM command and wait for a response
bool SetAndWait (uint32_t state){return false;};
};
}
#endif

@ -0,0 +1,46 @@
/*
www.sourceforge.net/projects/dfhack
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include <string>
namespace DFHack {
class Process;
class MicrosoftSTL
{
private:
uint32_t STLSTR_buf_off;
uint32_t STLSTR_size_off;
uint32_t STLSTR_cap_off;
Process* p;
public:
void init(Process* p);
const std::string readSTLString (uint32_t offset);
size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
void writeSTLString(const uint32_t address, const std::string writeString){};
// get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr);
};
}

@ -0,0 +1,39 @@
/*
www.sourceforge.net/projects/dfhack
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef PROCESS_FACTORY_H_INCLUDED
#define PROCESS_FACTORY_H_INCLUDED
#include "dfhack/DFProcess.h"
#include "dfhack/VersionInfoFactory.h"
namespace DFHack
{
Process* createNormalProcess(uint32_t pid, VersionInfoFactory * factory);
Process* createSHMProcess(uint32_t pid, VersionInfoFactory * factory);
#ifdef LINUX_BUILD
Process* createWineProcess(uint32_t pid, VersionInfoFactory * factory);
#endif
}
#endif

@ -0,0 +1,145 @@
/*
www.sourceforge.net/projects/dfhack
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef SHM_PROCESS_H_INCLUDED
#define SHM_PROCESS_H_INCLUDED
#include "dfhack/DFProcess.h"
#include "dfhack/DFIntegers.h"
#include "dfhack/VersionInfoFactory.h"
namespace DFHack
{
class DFHACK_EXPORT SHMProcess : public Process
{
private:
class Private;
Private * const d;
public:
SHMProcess(uint32_t PID, VersionInfoFactory * factory);
~SHMProcess();
// Set up stuff so we can read memory
bool attach();
bool detach();
bool suspend();
bool asyncSuspend();
bool resume();
bool forceresume();
void readQuad(const uint32_t address, uint64_t & value);
void writeQuad(const uint32_t address, const uint64_t value);
void readDWord(const uint32_t address, uint32_t & value);
void writeDWord(const uint32_t address, const uint32_t value);
void readFloat(const uint32_t address, float & value);
void readWord(const uint32_t address, uint16_t & value);
void writeWord(const uint32_t address, const uint16_t value);
void readByte(const uint32_t address, uint8_t & value);
void writeByte(const uint32_t address, const uint8_t value);
void read( uint32_t address, uint32_t length, uint8_t* buffer);
void write(uint32_t address, uint32_t length, uint8_t* buffer);
const std::string readSTLString (uint32_t offset);
size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
void writeSTLString(const uint32_t address, const std::string writeString);
// get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr);
const std::string readCString (uint32_t offset);
bool isSuspended();
bool isAttached();
bool isIdentified();
bool getThreadIDs(std::vector<uint32_t> & threads );
void getMemRanges(std::vector<t_memrange> & ranges );
VersionInfo *getDescriptor();
int getPID();
std::string getPath();
// get module index by name and version. bool 1 = error
bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT);
// get the SHM start if available
char * getSHMStart (void);
bool SetAndWait (uint32_t state);
private:
bool acquireSuspendLock();
bool releaseSuspendLock();
};
class SHMProcess::Private
{
public:
Private(SHMProcess * self_);
~Private(){}
VersionInfo * memdescriptor;
SHMProcess * self;
char *shm_addr;
int attachmentIdx;
bool attached;
bool locked;
bool identified;
bool useYield;
#ifdef LINUX_BUILD
pid_t process_ID;
int shm_ID;
int server_lock;
int client_lock;
int suspend_lock;
#else
typedef uint32_t pid_t;
uint32_t process_ID;
HANDLE DFSVMutex;
HANDLE DFCLMutex;
HANDLE DFCLSuspendMutex;
#endif
bool validate(VersionInfoFactory * factory);
bool Aux_Core_Attach(bool & versionOK, pid_t& PID);
bool SetAndWait (uint32_t state);
bool GetLocks();
bool AreLocksOk();
void FreeLocks();
};
}
// some helpful macros to keep the code bloat in check
#define SHMCMD ( (uint32_t *) shm_addr)[attachmentIdx]
#define D_SHMCMD ( (uint32_t *) (d->shm_addr))[d->attachmentIdx]
#define SHMHDR ((shm_core_hdr *)shm_addr)
#define D_SHMHDR ((shm_core_hdr *)(d->shm_addr))
#define SHMDATA(type) ((type *)(shm_addr + SHM_HEADER))
#define D_SHMDATA(type) ((type *)(d->shm_addr + SHM_HEADER))
#endif

@ -34,7 +34,7 @@ namespace DFHack
namespace Maps namespace Maps
{ {
// increment on every change // increment on every change
#define MAPS_VERSION 4 #define MAPS_VERSION 5
typedef struct typedef struct
{ {
uint32_t map_offset;// = d->offset_descriptor->getAddress ("map_data"); uint32_t map_offset;// = d->offset_descriptor->getAddress ("map_data");
@ -49,6 +49,7 @@ typedef struct
uint32_t occupancy_offset;// = d->offset_descriptor->getOffset ("occupancy"); uint32_t occupancy_offset;// = d->offset_descriptor->getOffset ("occupancy");
uint32_t biome_stuffs;// = d->offset_descriptor->getOffset ("biome_stuffs"); uint32_t biome_stuffs;// = d->offset_descriptor->getOffset ("biome_stuffs");
uint32_t veinvector;// = d->offset_descriptor->getOffset ("v_vein"); uint32_t veinvector;// = d->offset_descriptor->getOffset ("v_vein");
uint32_t vegvector;
uint32_t temperature1_offset; uint32_t temperature1_offset;
uint32_t temperature2_offset; uint32_t temperature2_offset;
uint32_t global_feature_offset; uint32_t global_feature_offset;
@ -57,6 +58,8 @@ typedef struct
uint32_t vein_mineral_vptr; uint32_t vein_mineral_vptr;
uint32_t vein_ice_vptr; uint32_t vein_ice_vptr;
uint32_t vein_spatter_vptr; uint32_t vein_spatter_vptr;
uint32_t vein_grass_vptr;
uint32_t vein_worldconstruction_vptr;
/* /*
GEOLOGY GEOLOGY
*/ */
@ -72,6 +75,11 @@ typedef struct
uint32_t world_size_y;// = minfo->getOffset ("world_size_y"); uint32_t world_size_y;// = minfo->getOffset ("world_size_y");
uint32_t geolayer_geoblock_offset;// = minfo->getOffset ("geolayer_geoblock_offset"); uint32_t geolayer_geoblock_offset;// = minfo->getOffset ("geolayer_geoblock_offset");
uint32_t type_inside_geolayer;// = mem->getOffset ("type_inside_geolayer"); uint32_t type_inside_geolayer;// = mem->getOffset ("type_inside_geolayer");
/*
* Vegetation
*/
uint32_t tree_desc_offset;
} maps_offsets; } maps_offsets;
typedef struct typedef struct

@ -12,6 +12,29 @@ using namespace std;
#define DFHACK_WANT_MISCUTILS #define DFHACK_WANT_MISCUTILS
#include <DFHack.h> #include <DFHack.h>
void print_tree( DFHack::Context * DF , DFHack::t_tree & tree)
{
DFHack::Materials * mat = DF->getMaterials();
printf("%d:%d = ",tree.type,tree.material);
if(tree.type == 1 || tree.type == 3)
{
cout << "near-water ";
}
cout << mat->organic[tree.material].id << " ";
if(tree.type == 0 || tree.type == 1)
{
cout << "tree";
}
if(tree.type == 2 || tree.type == 3)
{
cout << "shrub";
}
cout << endl;
printf("Address: 0x%x\n", tree.address);
hexdump(DF,tree.address,13);
}
int main (int numargs, const char ** args) int main (int numargs, const char ** args)
{ {
uint32_t addr; uint32_t addr;
@ -40,6 +63,7 @@ int main (int numargs, const char ** args)
DFHack::VersionInfo* mem = DF->getMemoryInfo(); DFHack::VersionInfo* mem = DF->getMemoryInfo();
DFHack::Position * pos = DF->getPosition(); DFHack::Position * pos = DF->getPosition();
DFHack::Vegetation * v = DF->getVegetation(); DFHack::Vegetation * v = DF->getVegetation();
DFHack::Maps * mps = DF->getMaps();
DFHack::Materials * mat = DF->getMaterials(); DFHack::Materials * mat = DF->getMaterials();
mat->ReadOrganicMaterials(); mat->ReadOrganicMaterials();
@ -60,30 +84,34 @@ int main (int numargs, const char ** args)
} }
else else
{ {
cout << "----==== Tree at "<< x << "/" << y << "/" << z << " ====----" << endl; // new method, gets the vector of trees in a block. can show farm plants
if(mps->Start())
{
vector<DFHack::t_tree> alltrees;
if(mps->ReadVegetation(x/16,y/16,z,&alltrees))
{
for(int i = 0 ; i < alltrees.size(); i++)
{
DFHack::t_tree & tree = alltrees[i];
// you could take the tree coords from the struct and % them with 16 for use in loops over the whole block
if(tree.x == x && tree.y == y && tree.z == z)
{
cout << "----==== Tree at "<< x << "/" << y << "/" << z << " ====----" << endl;
print_tree(DF, tree);
break;
}
}
}
}
// old method, gets the tree from the global vegetation vector. can't show farm plants
for(uint32_t i =0; i < numVegs; i++) for(uint32_t i =0; i < numVegs; i++)
{ {
DFHack::t_tree tree; DFHack::t_tree tree;
v->Read(i,tree); v->Read(i,tree);
if(tree.x == x && tree.y == y && tree.z == z) if(tree.x == x && tree.y == y && tree.z == z)
{ {
printf("%d:%d = ",tree.type,tree.material); cout << "----==== Tree at "<< x << "/" << y << "/" << z << " ====----" << endl;
if(tree.type == 1 || tree.type == 3) print_tree(DF, tree);
{
cout << "near-water ";
}
cout << mat->organic[tree.material].id << " ";
if(tree.type == 0 || tree.type == 1)
{
cout << "tree";
}
if(tree.type == 2 || tree.type == 3)
{
cout << "shrub";
}
cout << endl;
printf("Address: 0x%x\n", tree.address);
hexdump(DF,tree.address,13);
break; break;
} }
} }

@ -24,19 +24,14 @@ TARGET_LINK_LIBRARIES(dfdigger dfhack)
ADD_EXECUTABLE(dfdigger2 digger2.cpp) ADD_EXECUTABLE(dfdigger2 digger2.cpp)
TARGET_LINK_LIBRARIES(dfdigger2 dfhack) TARGET_LINK_LIBRARIES(dfdigger2 dfhack)
ADD_EXECUTABLE(primitives primitives.cpp)
# itemdesignator - change some item designations (dump, forbid, on-fire) for all # itemdesignator - change some item designations (dump, forbid, on-fire) for all
# items of a given type and material # items of a given type and material
# Author: belal # Author: belal
#ADD_EXECUTABLE(dfitemdesignator itemdesignator.cpp) #ADD_EXECUTABLE(dfitemdesignator itemdesignator.cpp)
#TARGET_LINK_LIBRARIES(dfitemdesignator dfhack) #TARGET_LINK_LIBRARIES(dfitemdesignator dfhack)
# incrementalsearch - a bit like cheat engine, only DF-specific, very basic
# and Linux-only
IF(UNIX)
ADD_EXECUTABLE(dfincremental incrementalsearch.cpp)
TARGET_LINK_LIBRARIES(dfincremental dfhack)
ENDIF(UNIX)
# catsplosion - Accelerates pregnancy # catsplosion - Accelerates pregnancy
# Author: Zhentar # Author: Zhentar
ADD_EXECUTABLE(dfcatsplosion catsplosion.cpp) ADD_EXECUTABLE(dfcatsplosion catsplosion.cpp)
@ -113,9 +108,3 @@ dfprinttiletypes
dfhellhole dfhellhole
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
) )
IF(UNIX)
install(TARGETS
dfincremental
RUNTIME DESTINATION bin
)
ENDIF(UNIX)

@ -21,7 +21,6 @@
#include <assert.h> #include <assert.h>
using namespace std; using namespace std;
#include <argstream.h>
#include <DFHack.h> #include <DFHack.h>
#include <dfhack/DFTileTypes.h> #include <dfhack/DFTileTypes.h>
#define BLOCK_SIZE 16 #define BLOCK_SIZE 16

File diff suppressed because it is too large Load Diff

@ -0,0 +1,27 @@
#include <iostream>
#include <iomanip>
#include <climits>
#include <vector>
#include <sstream>
#include <ctime>
#include <cstdio>
using namespace std;
std::string teststr1;
std::string * teststr2;
std::string teststr3("test");
int main (int numargs, const char ** args)
{
printf("std::string E : 0x%x\n", &teststr1);
teststr1 = "This is a fairly long string, much longer than the one made by default constructor.";
cin.ignore();
printf("std::string L : 0x%x\n", &teststr1);
teststr1 = "This one is shorter";
cin.ignore();
printf("std::string S : 0x%x\n", &teststr1);
cin.ignore();
teststr2 = new string();
printf("std::string * : 0x%x\n", &teststr2);
printf("std::string(\"test\") : 0x%x\n", &teststr3);
cin.ignore();
return 0;
}

@ -72,6 +72,12 @@ TARGET_LINK_LIBRARIES(dfdoffsets dfhack)
ADD_EXECUTABLE(dfweather weather.cpp) ADD_EXECUTABLE(dfweather weather.cpp)
TARGET_LINK_LIBRARIES(dfweather dfhack) TARGET_LINK_LIBRARIES(dfweather dfhack)
# incrementalsearch - a bit like cheat engine, only DF-specific, very basic
ADD_EXECUTABLE(dfautosearch autosearch.cpp)
TARGET_LINK_LIBRARIES(dfautosearch dfhack)
ADD_EXECUTABLE(dfincremental incrementalsearch.cpp)
TARGET_LINK_LIBRARIES(dfincremental dfhack)
IF(UNIX) IF(UNIX)
SET(VEINLOOK_BUILT "NO") SET(VEINLOOK_BUILT "NO")
@ -126,5 +132,7 @@ dfsuspend
dfflows dfflows
dfliquids dfliquids
dfweather dfweather
dfautosearch
dfincremental
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
) )

@ -12,18 +12,37 @@ class SegmentFinder
{ {
_DF = DF; _DF = DF;
mr_ = mr; mr_ = mr;
mr_.buffer = (uint8_t *)malloc (mr_.end - mr_.start); if(mr.valid)
DF->ReadRaw(mr_.start,(mr_.end - mr_.start),mr_.buffer); {
_SF = SF; mr_.buffer = (uint8_t *)malloc (mr_.end - mr_.start);
_SF = SF;
try
{
DF->ReadRaw(mr_.start,(mr_.end - mr_.start),mr_.buffer);
valid = true;
}
catch (DFHack::Error::MemoryAccessDenied &)
{
free(mr_.buffer);
valid = false;
mr.valid = false; // mark the range passed in as bad
cout << "Range 0x" << hex << mr_.start << " - 0x" << mr_.end << dec << " not readable." << endl;
}
}
} }
~SegmentFinder() ~SegmentFinder()
{ {
delete mr_.buffer; if(valid)
free(mr_.buffer);
}
bool isValid()
{
return valid;
} }
template <class needleType, class hayType, typename comparator > template <class needleType, class hayType, typename comparator >
bool Find (needleType needle, const uint8_t increment , vector <uint64_t> &newfound, comparator oper) bool Find (needleType needle, const uint8_t increment , vector <uint64_t> &newfound, comparator oper)
{ {
if(!valid) return !newfound.empty();
//loop //loop
for(uint64_t offset = 0; offset < (mr_.end - mr_.start) - sizeof(hayType); offset += increment) for(uint64_t offset = 0; offset < (mr_.end - mr_.start) - sizeof(hayType); offset += increment)
{ {
@ -36,6 +55,7 @@ class SegmentFinder
template < class needleType, class hayType, typename comparator > template < class needleType, class hayType, typename comparator >
uint64_t FindInRange (needleType needle, comparator oper, uint64_t start, uint64_t length) uint64_t FindInRange (needleType needle, comparator oper, uint64_t start, uint64_t length)
{ {
if(!valid) return 0;
uint64_t stopper = min((mr_.end - mr_.start) - sizeof(hayType), (start - mr_.start) - sizeof(hayType) + length); uint64_t stopper = min((mr_.end - mr_.start) - sizeof(hayType), (start - mr_.start) - sizeof(hayType) + length);
//loop //loop
for(uint64_t offset = start - mr_.start; offset < stopper; offset +=1) for(uint64_t offset = start - mr_.start; offset < stopper; offset +=1)
@ -49,6 +69,7 @@ class SegmentFinder
template <class needleType, class hayType, typename comparator > template <class needleType, class hayType, typename comparator >
bool Filter (needleType needle, vector <uint64_t> &found, vector <uint64_t> &newfound, comparator oper) bool Filter (needleType needle, vector <uint64_t> &found, vector <uint64_t> &newfound, comparator oper)
{ {
if(!valid) return !newfound.empty();
for( uint64_t i = 0; i < found.size(); i++) for( uint64_t i = 0; i < found.size(); i++)
{ {
if(mr_.isInRange(found[i])) if(mr_.isInRange(found[i]))
@ -65,6 +86,7 @@ class SegmentFinder
SegmentedFinder * _SF; SegmentedFinder * _SF;
DFHack::Context * _DF; DFHack::Context * _DF;
DFHack::t_memrange mr_; DFHack::t_memrange mr_;
bool valid;
}; };
class SegmentedFinder class SegmentedFinder
@ -364,14 +386,16 @@ public:
return false; return false;
} }
} }
bool insert( char what ) template < class T >
bool insert( T what )
{ {
if(constant) if(constant)
return false; return false;
if(d->length+1 == d->allocated) if(d->length+sizeof(T) >= d->allocated)
Allocate(d->allocated * 2); Allocate((d->length+sizeof(T)) * 2);
((char *) d->object)[d->length] = what; (*(T *)( (uint64_t)d->object + d->length)) = what;
d->length ++; d->length += sizeof(T);
return true;
} }
Bytestreamdata * d; Bytestreamdata * d;
bool constant; bool constant;
@ -431,7 +455,7 @@ std::istream& operator>> ( std::istream& out, Bytestream& bs )
{ {
small = reads - '0'; small = reads - '0';
state = 0; state = 0;
bs.insert(big*16 + small); bs.insert<char>(big*16 + small);
} }
} }
if((reads >= 'a' && reads <= 'f')) if((reads >= 'a' && reads <= 'f'))
@ -445,7 +469,7 @@ std::istream& operator>> ( std::istream& out, Bytestream& bs )
{ {
small = reads - 'a' + 10; small = reads - 'a' + 10;
state = 0; state = 0;
bs.insert(big*16 + small); bs.insert<char>(big*16 + small);
} }
} }
it++; it++;
@ -455,7 +479,7 @@ std::istream& operator>> ( std::istream& out, Bytestream& bs )
if (state == 1) if (state == 1)
{ {
small = 0; small = 0;
bs.insert(big*16 + small); bs.insert<char>(big*16 + small);
} }
} }
else else

@ -0,0 +1,782 @@
// this is an incremental search tool. It only works on Linux.
// here be dragons... and ugly code :P
#include <iostream>
#include <climits>
#include <vector>
#include <map>
#include <set>
#include <list>
#include <ctime>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
#ifndef LINUX_BUILD
#define WINVER 0x0500
// this one prevents windows from infecting the global namespace with filth
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <DFHack.h>
#include "SegmentedFinder.h"
class Token
{
public:
Token(uint64_t _offset)
{
offset = _offset;
offset_valid = 1;
value_valid = 0;
parent = 0;
}
Token(const std::string & offsetn)
{
full_offset_name = offsetn;
offset_valid = 0;
value_valid = 0;
parent = 0;
}
Token()
{
offset_valid = 0;
value_valid = 0;
parent = 0;
}
virtual ~Token(){};
virtual bool LoadData(SegmentedFinder * s) = 0;
virtual void EmptyData()
{
value_valid = false;
};
virtual bool Match(SegmentedFinder * s, uint64_t offset) = 0;
virtual void EmptyOffset()
{
offset_valid = false;
};
virtual bool AcquireOffset(DFHack::VersionInfo * vinfo)
{
vinfo->getOffset(full_offset_name);
return true;
}
virtual uint32_t Length() = 0;
virtual uint64_t getAbsolute(){if(parent) return parent->getAbsolute() + offset; else return offset;};
void setParent( Token *par )
{
par = parent;
}
protected:
uint64_t offset;// offset from the start of the parent token
std::string full_offset_name;
Token * parent;
bool offset_valid :1;
bool value_valid :1;
};
class Byte: virtual public Token
{
public:
Byte(uint64_t _offset):Token(_offset){};
Byte():Token(){};
~Byte();
virtual bool LoadData(SegmentedFinder * s)
{
if(offset_valid)
{
char * ptr = s->Translate<char>(getAbsolute());
if(ptr)
{
value = *ptr;
value_valid = true;
return true;
}
}
return false;
};
// is the loaded data same as data at offset? yes -> set our offset to that.
virtual bool Match(SegmentedFinder * s, uint64_t offs)
{
if(value_valid && (*s->Translate<char>(parent->getAbsolute() + offset)) == value )
{
if(parent)
offset = offs - parent->getAbsolute();
else
offset = offs;
return true;
}
return false;
};
virtual uint32_t Length()
{
return 1;
};
private:
char value;
};
class Short: virtual public Token
{
public:
Short(uint64_t _offset):Token(_offset){};
Short():Token(){};
~Short();
virtual bool LoadData(SegmentedFinder * s)
{
if(offset_valid)
{
uint16_t * ptr = s->Translate<uint16_t>(getAbsolute());
if(ptr)
{
value = *ptr;
value_valid = true;
return true;
}
}
return false;
};
// is the loaded data same as data at offset? yes -> set our offset to that.
virtual bool Match(SegmentedFinder * s, uint64_t offs)
{
if(value_valid && (*s->Translate<uint16_t>(parent->getAbsolute() + offset)) == value )
{
if(parent)
offset = offs - parent->getAbsolute();
else
offset = offs;
return true;
}
return false;
};
virtual uint32_t Length()
{
return 2;
};
private:
uint16_t value;
};
class Long: virtual public Token
{
public:
Long(uint64_t _offset):Token(_offset){};
Long():Token(){};
~Long();
virtual bool LoadData(SegmentedFinder * s)
{
if(offset_valid)
{
uint32_t * ptr = s->Translate<uint32_t>(getAbsolute());
if(ptr)
{
value = *ptr;
value_valid = true;
return true;
}
}
return false;
};
// is the loaded data same as data at offset? yes -> set our offset to that.
virtual bool Match(SegmentedFinder * s, uint64_t offs)
{
if(value_valid && (*s->Translate<uint32_t>(offs)) == value )
{
if(parent)
offset = offs - parent->getAbsolute();
else
offset = offs;
return true;
}
return false;
};
virtual uint32_t Length(){return 4;};
private:
uint32_t value;
};
class PtrVector : virtual public Token
{
public:
PtrVector(uint64_t _offset):Token(_offset){};
PtrVector():Token(){};
~PtrVector();
virtual uint32_t Length(){return 12;};
private:
vector <uint64_t> value;
};
class Pointer: virtual public Token
{
public:
Pointer(uint64_t _offset):Token(_offset){};
Pointer():Token(){};
~Pointer();
virtual uint32_t Length(){return 4;};
private:
uint64_t value;
};
class String: virtual public Token
{
protected:
string value;
};
class Struct: virtual public Token
{
public:
Struct(uint64_t _offset):Token(_offset){};
Struct():Token(){};
~Struct(){};
void Add( Token * t ){members.push_back(t);};
virtual uint32_t Length(){return 0;}; // FIXME: temporary solution, should be the minimal length of all the contents combined
virtual bool LoadData(SegmentedFinder* s)
{
bool OK = true;
for(int i = 0; i < members.size() && OK; i++)
OK &= members[i]->LoadData(s);
return OK;
};
// TODO: IMPLEMENT!
virtual bool Match(SegmentedFinder* s, uint64_t offset)
{
return false;
}
private:
vector<Token*> members;
};
class LinuxString: virtual public String
{
public:
LinuxString(uint64_t _offset):Token(_offset){};
LinuxString():Token(){};
~LinuxString(){};
virtual uint32_t Length(){return 4;};
virtual bool LoadData(SegmentedFinder* s)
{
return false;
}
virtual bool Match(SegmentedFinder* s, uint64_t offset)
{
return false;
}
/*
// read string pointer, translate to local scheme
char *str = sf->Translate<char>(*offset);
// verify
if(!str)
return false;
uint32_t length = *(uint32_t *)(offset - 12);
uint32_t capacity = *(uint32_t *)(offset - 8);
if(length > capacity)
return false;
//char * temp = new char[length+1];
// read data from inside the string structure
//memcpy(temp, str,length + 1);
output = str;
return true;
*/
};
class WindowsString: virtual public String
{
public:
WindowsString(uint64_t _offset):Token(_offset){};
WindowsString():Token(){};
~WindowsString(){};
virtual uint32_t Length(){return 0x1C;}; // FIXME: pouzivat Memory.xml?
virtual bool LoadData(SegmentedFinder* s)
{
return false;
}
virtual bool Match(SegmentedFinder* s, uint64_t offset)
{
return false;
}
string rdWinString( char * offset, SegmentedFinder & sf )
{
char * start_offset = offset + 4; // FIXME: pouzivat Memory.xml?
uint32_t length = *(uint32_t *)(offset + 20); // FIXME: pouzivat Memory.xml?
uint32_t capacity = *(uint32_t *)(offset + 24); // FIXME: pouzivat Memory.xml?
char * temp = new char[capacity+1];
// read data from inside the string structure
if(capacity < 16)
{
memcpy(temp, start_offset,capacity);
//read(start_offset, capacity, (uint8_t *)temp);
}
else // read data from what the offset + 4 dword points to
{
start_offset = sf.Translate<char>(*(uint32_t*)start_offset);
memcpy(temp, start_offset,capacity);
}
temp[length] = 0;
string ret = temp;
delete temp;
return ret;
}
};
inline void printRange(DFHack::t_memrange * tpr)
{
std::cout << std::hex << tpr->start << " - " << tpr->end << "|" << (tpr->read ? "r" : "-") << (tpr->write ? "w" : "-") << (tpr->execute ? "x" : "-") << "|" << tpr->name << std::endl;
}
bool getRanges(DFHack::Process * p, vector <DFHack::t_memrange>& selected_ranges)
{
vector <DFHack::t_memrange> ranges;
selected_ranges.clear();
p->getMemRanges(ranges);
cout << "Which range to search? (default is 1-4)" << endl;
for(int i = 0; i< ranges.size();i++)
{
cout << dec << "(" << i << ") ";
printRange(&(ranges[i]));
}
int start, end;
while(1)
{
string select;
cout << ">>";
std::getline(cin, select);
if(select.empty())
{
// empty input, assume default. observe the length of the memory range vector
// these are hardcoded values, intended for my convenience only
if(p->getDescriptor()->getOS() == DFHack::OS_WINDOWS)
{
start = min(11, (int)ranges.size());
end = min(14, (int)ranges.size());
}
else if(p->getDescriptor()->getOS() == DFHack::OS_LINUX)
{
start = min(2, (int)ranges.size());
end = min(4, (int)ranges.size());
}
else
{
start = 1;
end = 1;
}
break;
}
// I like the C variants here. much less object clutter
else if(sscanf(select.c_str(), "%d-%d", &start, &end) == 2)
{
start = min(start, (int)ranges.size());
end = min(end, (int)ranges.size());
break;
}
else
{
continue;
}
break;
}
end++;
cout << "selected ranges:" <<endl;
vector <DFHack::t_memrange>::iterator it;
it = ranges.begin() + start;
while (it != ranges.begin() + end)
{
// check if readable
if((*it).read)
{
selected_ranges.push_back(*it);
printRange(&*it);
}
it++;
}
return true;
}
bool getNumber (string prompt, int & output, int def, bool pdef = true)
{
cout << prompt;
if(pdef)
cout << " default=" << def << endl;
while (1)
{
string select;
cout << ">>";
std::getline(cin, select);
if(select.empty())
{
output = def;
break;
}
else if( sscanf(select.c_str(), "%d", &output) == 1 )
{
break;
}
else
{
continue;
}
}
return true;
}
bool getString (string prompt, string & output)
{
cout << prompt;
cout << ">>";
string select;
std::getline(cin, select);
if(select.empty())
{
return false;
}
else
{
output = select;
return true;
}
}
// meh
#pragma pack(1)
struct tilecolors
{
uint16_t fore;
uint16_t back;
uint16_t bright;
};
#pragma pack()
void printFound(vector <uint64_t> &found, const char * what)
{
cout << what << ":" << endl;
for(int i = 0; i < found.size();i++)
{
cout << hex << "0x" << found[i] << endl;
}
}
void printFoundStrVec(vector <uint64_t> &found, const char * what, SegmentedFinder & s)
{
cout << what << ":" << endl;
for(int i = 0; i < found.size();i++)
{
cout << hex << "0x" << found[i] << endl;
cout << "--------------------------" << endl;
vecTriplet * vt = s.Translate<vecTriplet>(found[i]);
if(vt)
{
int j = 0;
for(uint32_t idx = vt->start; idx < vt->finish; idx += sizeof(uint32_t))
{
uint32_t object_ptr;
// deref ptr idx, get ptr to object
if(!s.Read(idx,object_ptr))
{
cout << "BAD!" << endl;
break;
}
// deref ptr to first object, get ptr to string
uint32_t string_ptr;
if(!s.Read(object_ptr,string_ptr))
{
cout << "BAD!" << endl;
break;
}
// get string location in our local cache
char * str = s.Translate<char>(string_ptr);
if(!str)
{
cout << "BAD!" << endl;
break;
}
cout << dec << j << ":" << hex << "0x" << object_ptr << " : " << str << endl;
j++;
}
}
else
{
cout << "BAD!" << endl;
break;
}
cout << "--------------------------" << endl;
}
}
class TokenFactory
{
DFHack::OSType platform;
public:
TokenFactory(DFHack::OSType platform_in)
{
platform = platform_in;
}
template <class T>
T * Build()
{
return new T;
}
template <class T>
T * Build(uint64_t offset)
{
return new T(offset);
}
};
template <>
String * TokenFactory::Build()
{
switch(platform)
{
case DFHack::OS_WINDOWS:
return new WindowsString();
case DFHack::OS_LINUX:
case DFHack::OS_APPLE:
return new LinuxString();
}
return 0;
};
template <>
String * TokenFactory::Build(uint64_t offset)
{
switch(platform)
{
case DFHack::OS_WINDOWS:
return new WindowsString(offset);
case DFHack::OS_LINUX:
case DFHack::OS_APPLE:
return new LinuxString(offset);
}
return 0;
};
void autoSearch(DFHack::Context * DF, vector <DFHack::t_memrange>& ranges, DFHack::OSType platform)
{
cout << "stealing memory..." << endl;
SegmentedFinder sf(ranges, DF);
TokenFactory tf(platform);
cout << "done!" << endl;
Struct maps;
maps.Add(tf.Build<String>());
maps.Add(tf.Build<String>());
/*
vector <uint64_t> allVectors;
vector <uint64_t> filtVectors;
vector <uint64_t> to_filter;
cout << "stealing memory..." << endl;
SegmentedFinder sf(ranges, DF);
cout << "looking for vectors..." << endl;
sf.Find<int ,vecTriplet>(0,4,allVectors, vectorAll);
filtVectors = allVectors;
cout << "-------------------" << endl;
cout << "!!LANGUAGE TABLES!!" << endl;
cout << "-------------------" << endl;
uint64_t kulet_vector;
uint64_t word_table_offset;
uint64_t DWARF_vector;
uint64_t DWARF_object;
// find lang vector (neutral word table)
to_filter = filtVectors;
sf.Filter<const char * ,vecTriplet>("ABBEY",to_filter, vectorStringFirst);
uint64_t lang_addr = to_filter[0];
// find dwarven language word table
to_filter = filtVectors;
sf.Filter<const char * ,vecTriplet>("kulet",to_filter, vectorStringFirst);
kulet_vector = to_filter[0];
// find vector of languages
to_filter = filtVectors;
sf.Filter<const char * ,vecTriplet>("DWARF",to_filter, vectorStringFirst);
// verify
for(int i = 0; i < to_filter.size(); i++)
{
vecTriplet * vec = sf.Translate<vecTriplet>(to_filter[i]);
if(((vec->finish - vec->start) / 4) == 4) // verified
{
DWARF_vector = to_filter[i];
DWARF_object = sf.Read<uint32_t>(vec->start);
// compute word table offset from dwarf word table and dwarf language object addresses
word_table_offset = kulet_vector - DWARF_object;
break;
}
}
cout << "translation vector: " << hex << "0x" << DWARF_vector << endl;
cout << "lang vector: " << hex << "0x" << lang_addr << endl;
cout << "word table offset: " << hex << "0x" << word_table_offset << endl;
cout << "-------------" << endl;
cout << "!!MATERIALS!!" << endl;
cout << "-------------" << endl;
// inorganics vector
to_filter = filtVectors;
//sf.Find<uint32_t,vecTriplet>(257 * 4,4,to_filter,vectorLength<uint32_t>);
sf.Filter<const char * ,vecTriplet>("IRON",to_filter, vectorString);
sf.Filter<const char * ,vecTriplet>("ONYX",to_filter, vectorString);
sf.Filter<const char * ,vecTriplet>("RAW_ADAMANTINE",to_filter, vectorString);
sf.Filter<const char * ,vecTriplet>("BLOODSTONE",to_filter, vectorString);
printFound(to_filter,"inorganics");
// organics vector
to_filter = filtVectors;
sf.Filter<uint32_t,vecTriplet>(52 * 4,to_filter,vectorLength<uint32_t>);
sf.Filter<const char * ,vecTriplet>("MUSHROOM_HELMET_PLUMP",to_filter, vectorStringFirst);
printFound(to_filter,"organics");
// tree vector
to_filter = filtVectors;
sf.Filter<uint32_t,vecTriplet>(31 * 4,to_filter,vectorLength<uint32_t>);
sf.Filter<const char * ,vecTriplet>("MANGROVE",to_filter, vectorStringFirst);
printFound(to_filter,"trees");
// plant vector
to_filter = filtVectors;
sf.Filter<uint32_t,vecTriplet>(21 * 4,to_filter,vectorLength<uint32_t>);
sf.Filter<const char * ,vecTriplet>("MUSHROOM_HELMET_PLUMP",to_filter, vectorStringFirst);
printFound(to_filter,"plants");
// color descriptors
//AMBER, 112
to_filter = filtVectors;
sf.Filter<uint32_t,vecTriplet>(112 * 4,to_filter,vectorLength<uint32_t>);
sf.Filter<const char * ,vecTriplet>("AMBER",to_filter, vectorStringFirst);
printFound(to_filter,"color descriptors");
if(!to_filter.empty())
{
uint64_t vec = to_filter[0];
vecTriplet *vtColors = sf.Translate<vecTriplet>(vec);
uint32_t colorObj = sf.Read<uint32_t>(vtColors->start);
cout << "Amber color:" << hex << "0x" << colorObj << endl;
// TODO: find string 'amber', the floats
}
// all descriptors
//AMBER, 338
to_filter = filtVectors;
sf.Filter<uint32_t,vecTriplet>(338 * 4,to_filter,vectorLength<uint32_t>);
sf.Filter<const char * ,vecTriplet>("AMBER",to_filter, vectorStringFirst);
printFound(to_filter,"all descriptors");
// creature type
//ELEPHANT, ?? (demons abound)
to_filter = filtVectors;
//sf.Find<uint32_t,vecTriplet>(338 * 4,4,to_filter,vectorLength<uint32_t>);
sf.Filter<const char * ,vecTriplet>("ELEPHANT",to_filter, vectorString);
sf.Filter<const char * ,vecTriplet>("CAT",to_filter, vectorString);
sf.Filter<const char * ,vecTriplet>("DWARF",to_filter, vectorString);
sf.Filter<const char * ,vecTriplet>("WAMBLER_FLUFFY",to_filter, vectorString);
sf.Filter<const char * ,vecTriplet>("TOAD",to_filter, vectorString);
sf.Filter<const char * ,vecTriplet>("DEMON_1",to_filter, vectorString);
vector <uint64_t> toad_first = to_filter;
vector <uint64_t> elephant_first = to_filter;
sf.Filter<const char * ,vecTriplet>("TOAD",toad_first, vectorStringFirst);
sf.Filter<const char * ,vecTriplet>("ELEPHANT",elephant_first, vectorStringFirst);
printFoundStrVec(toad_first,"toad-first creature types",sf);
printFound(elephant_first,"elephant-first creature types");
printFound(to_filter,"all creature types");
uint64_t to_use = 0;
if(!elephant_first.empty())
{
to_use = elephant_first[0];
vecTriplet *vtCretypes = sf.Translate<vecTriplet>(to_use);
uint32_t elephant = sf.Read<uint32_t>(vtCretypes->start);
uint64_t Eoffset;
cout << "Elephant: 0x" << hex << elephant << endl;
cout << "Elephant: rawname = 0x0" << endl;
uint8_t letter_E = 'E';
Eoffset = sf.FindInRange<uint8_t,uint8_t> (letter_E,equalityP<uint8_t>, elephant, 0x300 );
if(Eoffset)
{
cout << "Elephant: big E = 0x" << hex << Eoffset - elephant << endl;
}
Eoffset = sf.FindInRange<const char *,vecTriplet> ("FEMALE",vectorStringFirst, elephant, 0x300 );
if(Eoffset)
{
cout << "Elephant: caste vector = 0x" << hex << Eoffset - elephant << endl;
}
Eoffset = sf.FindInRange<const char *,vecTriplet> ("SKIN",vectorStringFirst, elephant, 0x2000 );
if(Eoffset)
{
cout << "Elephant: extract? vector = 0x" << hex << Eoffset - elephant << endl;
}
tilecolors eletc = {7,0,0};
Bytestream bs_eletc(&eletc, sizeof(tilecolors));
cout << bs_eletc;
Eoffset = sf.FindInRange<Bytestream,tilecolors> (bs_eletc, findBytestream, elephant, 0x300 );
if(Eoffset)
{
cout << "Elephant: colors = 0x" << hex << Eoffset - elephant << endl;
}
//cout << "Amber color:" << hex << "0x" << colorObj << endl;
// TODO: find string 'amber', the floats
}
if(!toad_first.empty())
{
to_use = toad_first[0];
vecTriplet *vtCretypes = sf.Translate<vecTriplet>(to_use);
uint32_t toad = sf.Read<uint32_t>(vtCretypes->start);
uint64_t Eoffset;
cout << "Toad: 0x" << hex << toad << endl;
cout << "Toad: rawname = 0x0" << endl;
Eoffset = sf.FindInRange<uint8_t,uint8_t> (0xF9,equalityP<uint8_t>, toad, 0x300 );
if(Eoffset)
{
cout << "Toad: character (not reliable) = 0x" << hex << Eoffset - toad << endl;
}
Eoffset = sf.FindInRange<const char *,vecTriplet> ("FEMALE",vectorStringFirst, toad, 0x300 );
if(Eoffset)
{
cout << "Toad: caste vector = 0x" << hex << Eoffset - toad << endl;
}
Eoffset = sf.FindInRange<const char *,vecTriplet> ("SKIN",vectorStringFirst, toad, 0x2000 );
if(Eoffset)
{
cout << "Toad: extract? vector = 0x" << hex << Eoffset - toad << endl;
}
tilecolors toadtc = {2,0,0};
Bytestream bs_toadc(&toadtc, sizeof(tilecolors));
Eoffset = sf.FindInRange<Bytestream,tilecolors> (bs_toadc, findBytestream, toad, 0x300 );
if(Eoffset)
{
cout << "Toad: colors = 0x" << hex << Eoffset - toad << endl;
}
}*/
}
int main (void)
{
string select;
DFHack::ContextManager DFMgr("Memory.xml");
DFHack::Context * DF = DFMgr.getSingleContext();
try
{
DF->Attach();
}
catch (exception& e)
{
cerr << e.what() << endl;
#ifndef LINUX_BUILD
cin.ignore();
#endif
return 1;
}
DFHack::Process * p = DF->getProcess();
vector <DFHack::t_memrange> selected_ranges;
getRanges(p,selected_ranges);
DFHack::VersionInfo *minfo = DF->getMemoryInfo();
autoSearch(DF,selected_ranges, minfo->getOS());
#ifndef LINUX_BUILD
cout << "Done. Press any key to continue" << endl;
cin.ignore();
#endif
return 0;
}

@ -55,6 +55,7 @@ int main (int argc, char** argv)
Mapz->getSize(x_max,y_max,z_max); Mapz->getSize(x_max,y_max,z_max);
uint8_t zeroes [16][16] = {0}; uint8_t zeroes [16][16] = {0};
DFHack::occupancies40d occ;
// walk the map // walk the map
for(uint32_t x = 0; x< x_max;x++) for(uint32_t x = 0; x< x_max;x++)
@ -66,6 +67,13 @@ int main (int argc, char** argv)
if(Mapz->isValidBlock(x,y,z)) if(Mapz->isValidBlock(x,y,z))
{ {
Mapz->ReadVeins(x,y,z,0,0,&splatter); Mapz->ReadVeins(x,y,z,0,0,&splatter);
Mapz->ReadOccupancy(x,y,z,&occ);
for(int i = 0; i < 16; i++)
for(int j = 0; j < 16; j++)
{
occ[i][j].unibits.splatter = 0;
}
Mapz->WriteOccupancy(x,y,z,&occ);
for(uint32_t i = 0; i < splatter.size(); i++) for(uint32_t i = 0; i < splatter.size(); i++)
{ {
DFHack::t_spattervein & vein = splatter[i]; DFHack::t_spattervein & vein = splatter[i];

@ -21,146 +21,12 @@ using namespace std;
#include <DFHack.h> #include <DFHack.h>
#include "SegmentedFinder.h" #include "SegmentedFinder.h"
template <class T>
class holder
{
public:
vector <T> values;
SegmentedFinder & sf;
holder(SegmentedFinder& sff):sf(sff){};
bool isValid(size_t idx)
{
};
};
class address
{
public:
uint64_t addr_;
unsigned int valid : 1;
virtual void print(SegmentedFinder& sff)
{
cout << hex << "0x" << addr_ << endl;
};
address(const uint64_t addr)
{
addr_ = addr;
valid = false;
}
virtual address & operator=(const uint64_t in)
{
addr_ = in;
valid = false;
return *this;
}
virtual bool isValid(SegmentedFinder& sff)
{
if(valid) return true;
if(sff.getSegmentForAddress(addr_))
{
valid = 1;
}
}
virtual bool equals (SegmentedFinder & sf, address & rhs)
{
return rhs.addr_ == addr_;
}
};
// pointer to a null-terminated byte string
class Cstr: public address
{
void print(SegmentedFinder & sf)
{
cout << hex << "0x" << addr_ << ": \"" << sf.Translate<char>(addr_) << "\"" << endl;
}
bool equals(SegmentedFinder & sf,const char * rhs)
{
uint32_t addr2 = *(sf.Translate<uint32_t>(addr_));
return strcmp(sf.Translate<const char>(addr2), rhs) == 0;
}
template <class Predicate, class inType>
bool equalsP(SegmentedFinder & sf,inType rhs)
{
return Predicate(addr_, sf, rhs);
}
bool isValid(SegmentedFinder& sf)
{
if (address::isValid(sf))
{
// read the pointer
uint32_t addr2 = *(sf.Translate<uint32_t>(addr_));
// is it a real pointer? a pretty weak test, but whatever.
if(sf.getSegmentForAddress(addr2))
return true;
}
return false;
}
};
// STL STRING
#ifdef LINUX_BUILD
class STLstr: public address
{
};
#endif
#ifndef LINUX_BUILD
class STLstr: public address
{
};
#endif
// STL VECTOR
#ifdef LINUX_BUILD
class Vector: public address
{
};
#endif
#ifndef LINUX_BUILD
class Vector: public address
{
};
#endif
class Int64: public address{};
class Int32: public address{};
class Int16: public address{};
class Int8: public address{};
inline void printRange(DFHack::t_memrange * tpr) inline void printRange(DFHack::t_memrange * tpr)
{ {
std::cout << std::hex << tpr->start << " - " << tpr->end << "|" << (tpr->read ? "r" : "-") << (tpr->write ? "w" : "-") << (tpr->execute ? "x" : "-") << "|" << tpr->name << std::endl; std::cout << std::hex << tpr->start << " - " << tpr->end << "|" << (tpr->read ? "r" : "-") << (tpr->write ? "w" : "-") << (tpr->execute ? "x" : "-") << "|" << tpr->name << std::endl;
} }
string rdWinString( char * offset, SegmentedFinder & sf )
{
char * start_offset = offset + 4;
uint32_t length = *(uint32_t *)(offset + 20);
uint32_t capacity = *(uint32_t *)(offset + 24);
char * temp = new char[capacity+1];
// read data from inside the string structure
if(capacity < 16)
{
memcpy(temp, start_offset,capacity);
//read(start_offset, capacity, (uint8_t *)temp);
}
else // read data from what the offset + 4 dword points to
{
start_offset = sf.Translate<char>(*(uint32_t*)start_offset);
memcpy(temp, start_offset,capacity);
}
temp[length] = 0;
string ret = temp;
delete temp;
return ret;
}
bool getRanges(DFHack::Process * p, vector <DFHack::t_memrange>& selected_ranges) bool getRanges(DFHack::Process * p, vector <DFHack::t_memrange>& selected_ranges)
{ {
vector <DFHack::t_memrange> ranges; vector <DFHack::t_memrange> ranges;
@ -182,12 +48,12 @@ bool getRanges(DFHack::Process * p, vector <DFHack::t_memrange>& selected_ranges
{ {
// empty input, assume default. observe the length of the memory range vector // empty input, assume default. observe the length of the memory range vector
// these are hardcoded values, intended for my convenience only // these are hardcoded values, intended for my convenience only
if(p->getDescriptor()->getOS() == DFHack::VersionInfo::OS_WINDOWS) if(p->getDescriptor()->getOS() == DFHack::OS_WINDOWS)
{ {
start = min(11, (int)ranges.size()); start = min(11, (int)ranges.size());
end = min(14, (int)ranges.size()); end = min(14, (int)ranges.size());
} }
else if(p->getDescriptor()->getOS() == DFHack::VersionInfo::OS_LINUX) else if(p->getDescriptor()->getOS() == DFHack::OS_LINUX)
{ {
start = min(2, (int)ranges.size()); start = min(2, (int)ranges.size());
end = min(4, (int)ranges.size()); end = min(4, (int)ranges.size());
@ -226,6 +92,7 @@ bool getRanges(DFHack::Process * p, vector <DFHack::t_memrange>& selected_ranges
} }
it++; it++;
} }
return true;
} }
bool getNumber (string prompt, int & output, int def, bool pdef = true) bool getNumber (string prompt, int & output, int def, bool pdef = true)
@ -312,10 +179,14 @@ bool Incremental ( vector <uint64_t> &found, const char * what, T& output,
} }
goto incremental_more; goto incremental_more;
} }
else if(select.empty()) else if(select == "q")
{ {
return false; return false;
} }
else if(select.empty())
{
goto incremental_more;
}
else else
{ {
if(numberz) if(numberz)
@ -532,29 +403,91 @@ void FindData(DFHack::ContextManager & DFMgr, vector <DFHack::t_memrange>& range
DF->Detach(); DF->Detach();
} }
} }
/*
while(Incremental(found, "integer",test1)) bool TriggerIncremental ( vector <uint64_t> &found )
{
string select;
if(found.empty())
{
cout << "search ready - position the DF cursor and hit enter when ready" << endl;
}
else if( found.size() == 1 )
{
cout << "Found single coord!" << endl;
cout << hex << "0x" << found[0] << endl;
}
else
{
cout << "Found " << dec << found.size() << " coords." << endl;
}
incremental_more:
cout << ">>";
std::getline(cin, select);
size_t num = 0;
if( sscanf(select.c_str(),"p %d", &num) && num > 0)
{
cout << "Found coords:" << endl;
for(int i = 0; i < min(found.size(), num);i++)
{
cout << hex << "0x" << found[i] << endl;
}
goto incremental_more;
}
else if(select == "p")
{
cout << "Found coords:" << endl;
for(int i = 0; i < found.size();i++)
{
cout << hex << "0x" << found[i] << endl;
}
goto incremental_more;
}
else if(select == "q")
{
return false;
}
else return true;
}
void FindCoords(DFHack::ContextManager & DFMgr, vector <DFHack::t_memrange>& ranges)
{
vector <uint64_t> found;
int size = 4;
do
{
getNumber("Select coord size (2,4 bytes)",size, 4);
} while (size != 2 && size != 4);
while (TriggerIncremental(found))
{ {
DFMgr.Refresh(); DFMgr.Refresh();
DFHack::Context * DF = DFMgr.getSingleContext(); DFHack::Context * DF = DFMgr.getSingleContext();
DF->Attach(); DF->Attach();
SegmentedFinder sf(ranges,DF); DFHack::Position * pos = DF->getPosition();
switch(size) pos->Start();
int32_t x, y, z;
pos->getCursorCoords(x,y,z);
cout << "Searching for: " << dec << x << ":" << y << ":" << z << endl;
Bytestream select;
if(size == 2)
{ {
case 1: select.insert<uint16_t>(x);
sf.Incremental<uint8_t,uint8_t>(test1,alignment,found, equalityP<uint8_t>); select.insert<uint16_t>(y);
break; select.insert<uint16_t>(z);
case 2: }
sf.Incremental<uint16_t,uint16_t>(test1,alignment,found, equalityP<uint16_t>); else
break; {
case 4: select.insert<uint32_t>(x);
sf.Incremental<uint32_t,uint32_t>(test1,alignment,found, equalityP<uint32_t>); select.insert<uint32_t>(y);
break; select.insert<uint32_t>(z);
} }
cout << select << endl;
SegmentedFinder sf(ranges,DF);
sf.Incremental< Bytestream ,uint32_t>(select,1,found, findBytestream);
DF->Detach(); DF->Detach();
} }
} }
*/
void PtrTrace(DFHack::ContextManager & DFMgr, vector <DFHack::t_memrange>& ranges) void PtrTrace(DFHack::ContextManager & DFMgr, vector <DFHack::t_memrange>& ranges)
{ {
int element_size; int element_size;
@ -736,7 +669,7 @@ struct tilecolors
}; };
#pragma pack() #pragma pack()
void automatedLangtables(DFHack::Context * DF, vector <DFHack::t_memrange>& ranges) void autoSearch(DFHack::Context * DF, vector <DFHack::t_memrange>& ranges)
{ {
vector <uint64_t> allVectors; vector <uint64_t> allVectors;
vector <uint64_t> filtVectors; vector <uint64_t> filtVectors;
@ -820,6 +753,15 @@ void automatedLangtables(DFHack::Context * DF, vector <DFHack::t_memrange>& rang
sf.Filter<const char * ,vecTriplet>("MUSHROOM_HELMET_PLUMP",to_filter, vectorStringFirst); sf.Filter<const char * ,vecTriplet>("MUSHROOM_HELMET_PLUMP",to_filter, vectorStringFirst);
printFound(to_filter,"organics"); printFound(to_filter,"organics");
// new organics vector
to_filter = filtVectors;
sf.Filter<const char * ,vecTriplet>("MUSHROOM_HELMET_PLUMP",to_filter, vectorString);
sf.Filter<const char * ,vecTriplet>("MEADOW-GRASS",to_filter, vectorString);
sf.Filter<const char * ,vecTriplet>("TUNNEL_TUBE",to_filter, vectorString);
sf.Filter<const char * ,vecTriplet>("WEED_BLADE",to_filter, vectorString);
sf.Filter<const char * ,vecTriplet>("EYEBALL",to_filter, vectorString);
printFound(to_filter,"organics 31.19");
// tree vector // tree vector
to_filter = filtVectors; to_filter = filtVectors;
sf.Filter<uint32_t,vecTriplet>(31 * 4,to_filter,vectorLength<uint32_t>); sf.Filter<uint32_t,vecTriplet>(31 * 4,to_filter,vectorLength<uint32_t>);
@ -968,70 +910,80 @@ int main (void)
getRanges(p,selected_ranges); getRanges(p,selected_ranges);
DFHack::VersionInfo *minfo = DF->getMemoryInfo(); DFHack::VersionInfo *minfo = DF->getMemoryInfo();
DFHack::VersionInfo::OSType os = minfo->getOS(); DFHack::OSType os = minfo->getOS();
string prompt = string prompt =
"Select search type: 1=number(default), 2=vector by length, 3=vector>object>string,\n" "Select search type: 1=number(default), 2=vector by length, 3=vector>object>string,\n"
" 4=string, 5=automated offset search, 6=vector by address in its array,\n" " 4=string, 5=automated offset search, 6=vector by address in its array,\n"
" 7=pointer vector by address of an object, 8=vector>first object>string\n" " 7=pointer vector by address of an object, 8=vector>first object>string\n"
" 9=string buffers, 10=known data, 11=backpointers, 12=data+backpointers\n"; " 9=string buffers, 10=known data, 11=backpointers, 12=data+backpointers\n"
" 13=coord lookup\n"
" 0= exit\n";
int mode; int mode;
bool finish = 0;
do do
{ {
getNumber(prompt,mode, 1, false); getNumber(prompt,mode, 1, false);
} while (mode < 1 || mode > 12 ); switch (mode)
switch (mode) {
{ case 0:
case 1: finish = 1;
DF->Detach(); break;
FindIntegers(DFMgr, selected_ranges); case 1:
break; DF->Detach();
case 2: FindIntegers(DFMgr, selected_ranges);
DF->Detach(); break;
FindVectorByLength(DFMgr, selected_ranges); case 2:
break; DF->Detach();
case 3: FindVectorByLength(DFMgr, selected_ranges);
DF->Detach(); break;
FindVectorByObjectRawname(DFMgr, selected_ranges); case 3:
break; DF->Detach();
case 4: FindVectorByObjectRawname(DFMgr, selected_ranges);
DF->Detach(); break;
FindStrings(DFMgr, selected_ranges); case 4:
break; DF->Detach();
case 5: FindStrings(DFMgr, selected_ranges);
automatedLangtables(DF,selected_ranges); break;
break; case 5:
case 6: autoSearch(DF,selected_ranges);
DF->Detach(); break;
FindVectorByBounds(DFMgr,selected_ranges); case 6:
break; DF->Detach();
case 7: FindVectorByBounds(DFMgr,selected_ranges);
DF->Detach(); break;
FindPtrVectorsByObjectAddress(DFMgr,selected_ranges); case 7:
break; DF->Detach();
case 8: FindPtrVectorsByObjectAddress(DFMgr,selected_ranges);
DF->Detach(); break;
FindVectorByFirstObjectRawname(DFMgr, selected_ranges); case 8:
break; DF->Detach();
case 9: FindVectorByFirstObjectRawname(DFMgr, selected_ranges);
DF->Detach(); break;
FindStrBufs(DFMgr, selected_ranges); case 9:
break; DF->Detach();
case 10: FindStrBufs(DFMgr, selected_ranges);
DF->Detach(); break;
FindData(DFMgr, selected_ranges); case 10:
break; DF->Detach();
case 11: FindData(DFMgr, selected_ranges);
DF->Detach(); break;
PtrTrace(DFMgr, selected_ranges); case 11:
break; DF->Detach();
case 12: PtrTrace(DFMgr, selected_ranges);
DF->Detach(); break;
DataPtrTrace(DFMgr, selected_ranges); case 12:
break; DF->Detach();
default: DataPtrTrace(DFMgr, selected_ranges);
cout << "not implemented :(" << endl; break;
} case 13:
DF->Detach();
FindCoords(DFMgr, selected_ranges);
break;
default:
cout << "Unknown function, try again." << endl;
}
} while ( !finish );
#ifndef LINUX_BUILD #ifndef LINUX_BUILD
cout << "Done. Press any key to continue" << endl; cout << "Done. Press any key to continue" << endl;
cin.ignore(); cin.ignore();

@ -21,6 +21,7 @@ int main (int argc, char** argv)
} }
DFHack::Position * Position = 0; DFHack::Position * Position = 0;
DFHack::World * World = 0;
DFHack::ContextManager DFMgr("Memory.xml"); DFHack::ContextManager DFMgr("Memory.xml");
DFHack::Context * DF; DFHack::Context * DF;
try try
@ -28,6 +29,7 @@ int main (int argc, char** argv)
DF = DFMgr.getSingleContext(); DF = DFMgr.getSingleContext();
DF->Attach(); DF->Attach();
Position = DF->getPosition(); Position = DF->getPosition();
World = DF->getWorld();
} }
catch (exception& e) catch (exception& e)
{ {
@ -37,17 +39,21 @@ int main (int argc, char** argv)
#endif #endif
return 1; return 1;
} }
if(World)
{
cout << "Game mode " << World->ReadGameMode() << endl;
}
if (Position) if (Position)
{ {
int32_t x,y,z; int32_t x,y,z;
int32_t width,height; int32_t width,height;
if(Position->getViewCoords(x,y,z)) if(Position->getViewCoords(x,y,z))
cout << "view coords: " << x << "/" << y << "/" << z << endl; cout << "view coords: " << x << "/" << y << "/" << z << endl;
if(Position->getCursorCoords(x,y,z)) if(Position->getCursorCoords(x,y,z))
cout << "cursor coords: " << x << "/" << y << "/" << z << endl; cout << "cursor coords: " << x << "/" << y << "/" << z << endl;
if(Position->getWindowSize(width,height)) if(Position->getWindowSize(width,height))
cout << "window size : " << width << " " << height << endl; cout << "window size : " << width << " " << height << endl;
} }
else else
{ {

@ -10,9 +10,11 @@ using namespace std;
#include <DFHack.h> #include <DFHack.h>
#include <dfhack/DFTileTypes.h> #include <dfhack/DFTileTypes.h>
#include <argstream.h> //#include <argstream.h>
#define MAX_DIM 0x300 #define MAX_DIM 0x300
//TODO: turn into the official coord class for DFHack/DF
class Vertex class Vertex
{ {
public: public:
@ -25,8 +27,10 @@ class Vertex
{ {
return (other.x == x && other.y == y && other.z == z); return (other.x == x && other.y == y && other.z == z);
} }
// FIXME: <tomprince> peterix_: you could probably get away with not defining operator< if you defined a std::less specialization for Vertex.
bool operator<(const Vertex &other) const bool operator<(const Vertex &other) const
{ {
// FIXME: could be changed to eliminate MAX_DIM and make std::map lookups faster?
return ( (z*MAX_DIM*MAX_DIM + y*MAX_DIM + x) < (other.z*MAX_DIM*MAX_DIM + other.y*MAX_DIM + other.x)); return ( (z*MAX_DIM*MAX_DIM + y*MAX_DIM + x) < (other.z*MAX_DIM*MAX_DIM + other.y*MAX_DIM + other.x));
} }
Vertex operator/(int number) const Vertex operator/(int number) const
@ -267,10 +271,12 @@ int main (int argc, char* argv[])
{ {
// Command line options // Command line options
bool updown = false; bool updown = false;
/*
argstream as(argc,argv); argstream as(argc,argv);
as >>option('x',"updown",updown,"Dig up and down stairs to reach other z-levels.") as >>option('x',"updown",updown,"Dig up and down stairs to reach other z-levels.")
>>help(); >>help();
// sane check // sane check
if (!as.isOk()) if (!as.isOk())
@ -278,6 +284,9 @@ int main (int argc, char* argv[])
cout << as.errorLog(); cout << as.errorLog();
return 1; return 1;
} }
*/
if(argc > 1 && strcmp(argv[1],"-x") == 0)
updown = true;
DFHack::ContextManager DFMgr("Memory.xml"); DFHack::ContextManager DFMgr("Memory.xml");
DFHack::Context * DF; DFHack::Context * DF;
@ -294,12 +303,12 @@ int main (int argc, char* argv[])
#endif #endif
return 1; return 1;
} }
uint32_t x_max,y_max,z_max; uint32_t x_max,y_max,z_max;
DFHack::Maps * Maps = DF->getMaps(); DFHack::Maps * Maps = DF->getMaps();
DFHack::Materials * Mats = DF->getMaterials(); DFHack::Materials * Mats = DF->getMaterials();
DFHack::Position * Pos = DF->getPosition(); DFHack::Position * Pos = DF->getPosition();
// init the map // init the map
if(!Maps->Start()) if(!Maps->Start())
{ {
@ -310,12 +319,12 @@ int main (int argc, char* argv[])
#endif #endif
return 1; return 1;
} }
int32_t cx, cy, cz; int32_t cx, cy, cz;
Maps->getSize(x_max,y_max,z_max); Maps->getSize(x_max,y_max,z_max);
uint32_t tx_max = x_max * 16; uint32_t tx_max = x_max * 16;
uint32_t ty_max = y_max * 16; uint32_t ty_max = y_max * 16;
Pos->getCursorCoords(cx,cy,cz); Pos->getCursorCoords(cx,cy,cz);
while(cx == -30000) while(cx == -30000)
{ {

@ -536,6 +536,8 @@ main(int argc, char *argv[])
vector<t_vein> veinVector; vector<t_vein> veinVector;
vector<t_frozenliquidvein> IceVeinVector; vector<t_frozenliquidvein> IceVeinVector;
vector<t_spattervein> splatter; vector<t_spattervein> splatter;
vector<t_grassvein> grass;
vector<t_worldconstruction> wconstructs;
t_temperatures b_temp1; t_temperatures b_temp1;
t_temperatures b_temp2; t_temperatures b_temp2;
@ -549,7 +551,6 @@ main(int argc, char *argv[])
{ {
pDF = DF = DFMgr.getSingleContext(); pDF = DF = DFMgr.getSingleContext();
DF->Attach(); DF->Attach();
Mats = DF->getMaterials();
Maps = DF->getMaps(); Maps = DF->getMaps();
} }
catch (exception& e) catch (exception& e)
@ -560,6 +561,15 @@ main(int argc, char *argv[])
#endif #endif
finish(0); finish(0);
} }
bool hasmats = true;
try
{
Mats = DF->getMaterials();
}
catch (exception& e)
{
hasmats = false;
}
Process* p = DF->getProcess(); Process* p = DF->getProcess();
// init the map // init the map
@ -568,27 +578,32 @@ main(int argc, char *argv[])
error = "Can't find a map to look at."; error = "Can't find a map to look at.";
finish(0); finish(0);
} }
Maps->getSize(x_max_a,y_max_a,z_max_a); Maps->getSize(x_max_a,y_max_a,z_max_a);
x_max = x_max_a; x_max = x_max_a;
y_max = y_max_a; y_max = y_max_a;
z_max = z_max_a; z_max = z_max_a;
// get stone matgloss mapping bool hasInorgMats = false;
if(!Mats->ReadInorganicMaterials()) bool hasPlantMats = false;
{ bool hasCreatureMats = false;
error = "Can't read stone types.";
pDF = 0; if(hasmats)
finish(0);
}
/*
if(!Mats->ReadCreatureTypes())
{ {
error = "Can't read stone types."; // get stone matgloss mapping
pDF = 0; if(Mats->ReadInorganicMaterials())
finish(0); {
hasInorgMats = true;
}
if(Mats->ReadCreatureTypes())
{
hasCreatureMats = true;
}
if(Mats->ReadOrganicMaterials())
{
hasPlantMats = true;
}
} }
*/
/* /*
// get region geology // get region geology
if(!DF.ReadGeology( layerassign )) if(!DF.ReadGeology( layerassign ))
@ -723,15 +738,29 @@ main(int argc, char *argv[])
IceVeinVector.clear(); IceVeinVector.clear();
effects.clear(); effects.clear();
splatter.clear(); splatter.clear();
grass.clear();
dirtybit = 0; dirtybit = 0;
// Supend, read/write data // Supend, read/write data
DF->Suspend(); DF->Suspend();
// restart cleared modules // restart cleared modules
Maps->Start(); Maps->Start();
Mats->Start(); if(hasmats)
Mats->ReadInorganicMaterials(); {
Mats->ReadCreatureTypes(); Mats->Start();
if(hasInorgMats)
{
Mats->ReadInorganicMaterials();
}
if(hasPlantMats)
{
Mats->ReadOrganicMaterials();
}
if(hasCreatureMats)
{
Mats->ReadCreatureTypes();
}
}
uint32_t effectnum; uint32_t effectnum;
/* /*
if(DF.InitReadEffects(effectnum)) if(DF.InitReadEffects(effectnum))
@ -747,22 +776,21 @@ main(int argc, char *argv[])
for(int i = -1; i <= 1; i++) for(int j = -1; j <= 1; j++) for(int i = -1; i <= 1; i++) for(int j = -1; j <= 1; j++)
{ {
mapblock40d * Block = &blocks[i+1][j+1]; mapblock40d * Block = &blocks[i+1][j+1];
if(Maps->isValidBlock(cursorX+i,cursorY+j,cursorZ)) if(Maps->isValidBlock(cursorX+i,cursorY+j,cursorZ))
{ {
Maps->ReadBlock40d(cursorX+i,cursorY+j,cursorZ, Block); Maps->ReadBlock40d(cursorX+i,cursorY+j,cursorZ, Block);
// extra processing of the block in the middle // extra processing of the block in the middle
if(i == 0 && j == 0) if(i == 0 && j == 0)
{ {
do_features(DF, Block, cursorX, cursorY, 50,10, Mats->inorganic); if(hasInorgMats)
do_features(DF, Block, cursorX, cursorY, 50,10, Mats->inorganic);
// read veins // read veins
Maps->ReadVeins(cursorX+i,cursorY+j,cursorZ,&veinVector,&IceVeinVector,&splatter); Maps->ReadVeins(cursorX+i,cursorY+j,cursorZ,&veinVector,&IceVeinVector,&splatter,&grass, &wconstructs);
// get pointer to block // get pointer to block
blockaddr = Maps->getBlockPtr(cursorX+i,cursorY+j,cursorZ); blockaddr = Maps->getBlockPtr(cursorX+i,cursorY+j,cursorZ);
blockaddr2 = Block->origin; blockaddr2 = Block->origin;
// dig all veins and trees // dig all veins and trees
if(dig) if(dig)
{ {
@ -857,11 +885,13 @@ main(int argc, char *argv[])
uint32_t mineralsize = veinVector.size(); uint32_t mineralsize = veinVector.size();
uint32_t icesize = IceVeinVector.size(); uint32_t icesize = IceVeinVector.size();
uint32_t splattersize = splatter.size(); uint32_t splattersize = splatter.size();
uint32_t totalVeinSize = mineralsize+ icesize + splattersize; uint32_t grasssize = grass.size();
uint32_t wconstrsize = wconstructs.size();
uint32_t totalVeinSize = mineralsize+ icesize + splattersize + grasssize + wconstrsize;
if(vein == totalVeinSize) vein = totalVeinSize - 1; if(vein == totalVeinSize) vein = totalVeinSize - 1;
if(vein < -1) vein = -1; if(vein < -1) vein = -1;
cprintf("X %d/%d, Y %d/%d, Z %d/%d. Vein %d of %d",cursorX+1,x_max,cursorY+1,y_max,cursorZ,z_max,vein+1,totalVeinSize); cprintf("X %d/%d, Y %d/%d, Z %d/%d. Vein %d of %d",cursorX+1,x_max,cursorY+1,y_max,cursorZ,z_max,vein+1,totalVeinSize);
if(!veinVector.empty() || !IceVeinVector.empty() || !splatter.empty()) if(!veinVector.empty() || !IceVeinVector.empty() || !splatter.empty() || !grass.empty() || !wconstructs.empty())
{ {
if(vein != -1 && vein < totalVeinSize) if(vein != -1 && vein < totalVeinSize)
{ {
@ -891,8 +921,11 @@ main(int argc, char *argv[])
} }
} }
} }
gotoxy(50,3); if(hasInorgMats)
cprintf("Mineral: %s",Mats->inorganic[veinVector[vein].type].id); {
gotoxy(50,3);
cprintf("Mineral: %s",Mats->inorganic[veinVector[vein].type].id);
}
} }
else if (vein < mineralsize + icesize) else if (vein < mineralsize + icesize)
{ {
@ -914,7 +947,7 @@ main(int argc, char *argv[])
gotoxy(50,3); gotoxy(50,3);
cprintf("ICE"); cprintf("ICE");
} }
else else if(vein < mineralsize + icesize + splattersize)
{ {
realvein = vein - mineralsize - icesize; realvein = vein - mineralsize - icesize;
t_spattervein &bloodmud = splatter[realvein]; t_spattervein &bloodmud = splatter[realvein];
@ -931,8 +964,55 @@ main(int argc, char *argv[])
} }
} }
} }
gotoxy(50,3); if(hasCreatureMats)
cprintf("Spatter: %s",PrintSplatterType(splatter[realvein].mat1,splatter[realvein].mat2,Mats->race).c_str()); {
gotoxy(50,3);
cprintf("Spatter: %s",PrintSplatterType(splatter[realvein].mat1,splatter[realvein].mat2,Mats->race).c_str());
}
}
else if(vein < mineralsize + icesize + splattersize + grasssize)
{
realvein = vein - mineralsize - icesize - splattersize;
t_grassvein & grassy =grass[realvein];
for(uint32_t yyy = 0; yyy < 16; yyy++)
{
for(uint32_t xxx = 0; xxx < 16; xxx++)
{
uint8_t intensity = grassy.intensity[xxx][yyy];
if(intensity)
{
attron(A_STANDOUT);
putch(xxx+16,yyy+16,'X', COLOR_RED);
attroff(A_STANDOUT);
}
}
}
if(hasPlantMats)
{
gotoxy(50,3);
cprintf("Grass: 0x%x, %s",grassy.address_of, Mats->organic[grassy.material].id);
}
}
else
{
realvein = vein - mineralsize - icesize - splattersize - grasssize;
t_worldconstruction & wconstr=wconstructs[realvein];
for(uint32_t j = 0; j < 16; j++)
{
for(uint32_t k = 0; k < 16; k++)
{
bool set = !!(((1 << k) & wconstr.assignment[j]) >> k);
if(set)
{
putch(k+16,j+16,'$',COLOR_RED);
}
}
}
if(hasInorgMats)
{
gotoxy(50,3);
cprintf("Road: 0x%x, %d - %s", wconstr.address_of, wconstr.material,Mats->inorganic[wconstr.material].id);
}
} }
} }
} }