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/build
dfhack/python/dist
/cmakeall.bat

@ -2,7 +2,7 @@
PROJECT (dfhack)
cmake_minimum_required(VERSION 2.6)
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
# 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/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)

@ -1,3 +1,5 @@
----------------------------------------------------------------------
License of dfhack
github.com/peterix/dfhack
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
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
cd build-real
cmake ..\.. -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE:string=Debug
mingw32-make
mkdir build-real
cd build-real
cmake ..\.. -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE:string=Debug
mingw32-make
pause

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

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

@ -665,6 +665,9 @@
<class name="building_actualst"/>
<class name="buildingst"/>
<class name="building_slabst" />
<class name="building_nestst" />
<class name="building_nest_boxst" />
<class name="building_hivest" />
</VTable>
<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
@ -727,6 +730,7 @@
<Address name="world_size_y"/>
<Group name="block" description="The map block structure.">
<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="designation" description="16x16 * 4B designation 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_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="game_mode" description="Current game mode" />
</Group>
</Offsets>
</Base>
@ -994,6 +999,7 @@
<Address name="world_size_y" value="0x016AEDD6" />
<Group name="block">
<Offset name="vein_vector" value="0x08" />
<Offset name="vegetation_vector" value="0x0078" />
<Offset name="type" value="0x009A" />
<Offset name="designation" value="0x029C" />
<Offset name="occupancy" value="0x069c" />
@ -1551,6 +1557,7 @@
<Offset name="vein_vector" value="0x08" />
<Offset name="feature_local" value="0x24" />
<Offset name="feature_global" value="0x28" />
<Offset name="vegetation_vector" value="0x60" />
<Offset name="type" value="0x7A" />
<Offset name="designation" value="0x27C" />
<Offset name="occupancy" value="0x67C" />
@ -1871,6 +1878,189 @@
</Group>
</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>
.-"""-.
' \
@ -1943,6 +2133,7 @@
<Offset name="vein_vector" value="0x08" />
<Offset name="feature_local" value="0x20" />
<Offset name="feature_global" value="0x24" />
<Offset name="vegetation_vector" value="0x54" />
<Offset name="type" value="0x006A" />
<Offset name="designation" value="0x026C" />
<Offset name="occupancy" value="0x066c" />
@ -2329,6 +2520,115 @@
</Group>
</Offsets>
</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>

@ -29,6 +29,10 @@ include_directories (${CMAKE_SOURCE_DIR}/library/private/)
SET(PROJECT_HDRS_INTERNAL
private/ContextShared.h
private/Internal.h
private/SHMProcess.h
private/LinuxProcess.h
private/ProcessFactory.h
private/MicrosoftSTL.h
)
SET(PROJECT_HDRS
@ -89,6 +93,8 @@ DFContext.cpp
DFTileTypes.cpp
DFProcessEnumerator.cpp
ContextShared.cpp
DFProcess-SHM.cpp
MicrosoftSTL.cpp
depends/md5/md5.cpp
depends/md5/md5wrapper.cpp
@ -138,6 +144,7 @@ include/dfhack/DFstdint_win.h
SET(PROJECT_SRCS_LINUX
DFProcess-linux.cpp
DFProcess-linux-base.cpp
DFProcess-linux-SHM.cpp
DFProcess-linux-wine.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 )
ADD_DEFINITIONS(-DBUILD_DFHACK_LIB)
ADD_DEFINITIONS(-DBUILD_DFHACK_LIB -DTIXML_USE_STL)
IF(UNIX)
add_definitions(-DLINUX_BUILD)
@ -191,7 +198,12 @@ IF(UNIX)
SET(PROJECT_LIBS ${X11_LIBRARY} rt ) #dfhack-md5 dfhack-tixml
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)
ADD_LIBRARY(dfhack SHARED ${PROJECT_SRCS})

@ -99,62 +99,6 @@ bool Context::Detach()
}
d->allModules.clear();
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;
}
@ -236,386 +180,3 @@ MODULE_GETTER(Translation);
MODULE_GETTER(Vegetation);
MODULE_GETTER(Buildings);
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.
*/
#include "Internal.h"
#include "dfhack/DFProcess.h"
#include "SHMProcess.h"
#include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h"
@ -39,76 +39,37 @@ distribution.
using namespace DFHack;
// a full memory barrier! better be safe than sorry.
#define gcc_barrier asm volatile("" ::: "memory"); __sync_synchronize();
class SHMProcess::Private
SHMProcess::Private::Private(SHMProcess * self_)
{
public:
Private(Process * self_)
{
memdescriptor = NULL;
process_ID = 0;
shm_addr = 0;
//shm_addr_with_cl_idx = 0;
shm_ID = -1;
attached = false;
identified = false;
useYield = false;
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))
memdescriptor = NULL;
process_ID = 0;
shm_ID = -1;
attached = false;
identified = false;
useYield = false;
server_lock = -1;
client_lock = -1;
suspend_lock = -1;
locked = false;
self = self_;
}
bool SHMProcess::Private::SetAndWait (uint32_t state)
{
uint32_t cnt = 0;
if(!attached) return false;
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(!AreLocksOk())
if(!AreLocksOk())// DF not there anymore?
{
//detach the shared memory
shmdt(shm_addr);
@ -122,10 +83,6 @@ bool SHMProcess::Private::SetAndWait (uint32_t state)
cnt = 0;
}
}
if(useYield)
{
SCHED_YIELD
}
cnt++;
}
// server returned a generic error
@ -155,21 +112,6 @@ uint32_t OS_getAffinity()
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()
{
attachmentIdx = -1;
@ -258,52 +200,23 @@ bool SHMProcess::Private::GetLocks()
return false;
}
SHMProcess::SHMProcess(uint32_t PID, vector< VersionInfo* >& known_versions)
: d(new Private(this))
// test if we have client and server locks and the server is present
bool SHMProcess::Private::AreLocksOk()
{
d->process_ID = PID;
d->memdescriptor = 0;
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)
// both locks are inited (we hold our lock)
if(client_lock != -1 && server_lock != -1)
{
detach();
throw Error::SHMVersionMismatch();
if(lockf(server_lock,F_TEST,0) == -1) // and server holds its lock
{
return true; // OK, locks are good
}
}
// try to identify the DF version (md5 the binary, compare with known versions)
d->validate(known_versions);
// detach
detach();
}
bool SHMProcess::isSuspended()
{
return d->locked;
}
bool SHMProcess::isAttached()
{
return d->attached;
// locks are bad
return false;
}
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 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
target_name[target_result] = 0;
md5wrapper md5;
// get hash of the running DF process
md5wrapper md5;
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()
{
if(d->attached)
// create linux process, add it to the vector
VersionInfo * vinfo = factory->getVersionInfoByMD5(hash);
if(vinfo)
{
detach();
memdescriptor = vinfo;
// FIXME: BIG BAD BUG RIGHT HERE!!!!
memdescriptor->setParentProcess(self);
identified = true;
return true;
}
if(d->memdescriptor)
delete d->memdescriptor;
delete d;
}
VersionInfo * SHMProcess::getDescriptor()
{
return d->memdescriptor;
}
int SHMProcess::getPID()
{
return d->process_ID;
return false;
}
// there is only one we care about.
@ -403,123 +285,19 @@ void SHMProcess::getMemRanges( vector<t_memrange> & ranges )
temp.read = permissions[0] == 'r';
temp.write = permissions[1] == 'w';
temp.execute = permissions[2] == 'x';
temp.valid = true;
ranges.push_back(temp);
}
}
bool SHMProcess::suspend()
bool SHMProcess::acquireSuspendLock()
{
if(!d->attached)
{
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;
return (lockf(d->suspend_lock,F_LOCK,0) == 0);
}
// FIXME: needs a good think-through
bool SHMProcess::asyncSuspend()
bool SHMProcess::releaseSuspendLock()
{
if(!d->attached)
{
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;
return (lockf(d->suspend_lock,F_ULOCK,0) == 0);
}
@ -531,6 +309,7 @@ bool SHMProcess::attach()
return suspend();
return true;
}
//cerr << "attach" << endl;// FIXME: throw
if(!d->GetLocks())
{
//cerr << "server is full or not really there!" << endl;
@ -589,337 +368,18 @@ bool SHMProcess::detach()
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)
{
if(!d->locked) throw Error::MemoryAccessDenied();
int typeinfo = readDWord(vptr - 0x4);
int typestring = readDWord(typeinfo + 0x4);
int typeinfo = Process::readDWord(vptr - 0x4);
int typestring = Process::readDWord(typeinfo + 0x4);
string raw = readCString(typestring);
size_t start = raw.find_first_of("abcdefghijklmnopqrstuvwxyz");// trim numbers
size_t end = raw.length();
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()
{
char cwd_name[256];
@ -932,3 +392,10 @@ string SHMProcess::getPath()
target_name[target_result] = '\0';
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.
*/
#include "Internal.h"
#include "dfhack/DFProcess.h"
#include "LinuxProcess.h"
#include "ProcessFactory.h"
#include "MicrosoftSTL.h"
#include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h"
#include <errno.h>
@ -30,37 +32,28 @@ distribution.
#include <stdio.h>
using namespace DFHack;
class WineProcess::Private
{
public:
Private(Process * self_)
namespace {
class WineProcess : public LinuxProcessBase
{
my_descriptor = NULL;
my_handle = NULL;
my_pid = 0;
attached = false;
suspended = false;
memFileHandle = 0;
self = self_;
private:
MicrosoftSTL stl;
public:
WineProcess(uint32_t pid, VersionInfoFactory * factory);
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);
};
~Private(){};
VersionInfo * my_descriptor;
Process * self;
pid_t my_handle;
uint32_t my_pid;
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);
};
}
Process* DFHack::createWineProcess(uint32_t pid, VersionInfoFactory * factory)
{
return new WineProcess(pid, factory);
}
WineProcess::WineProcess(uint32_t pid, vector <VersionInfo *> & known_versions)
: d(new Private(this))
WineProcess::WineProcess(uint32_t pid, VersionInfoFactory * factory) : LinuxProcessBase(pid)
{
char dir_name [256];
char exe_link_name [256];
@ -70,12 +63,13 @@ WineProcess::WineProcess(uint32_t pid, vector <VersionInfo *> & known_versions)
char target_name[1024];
int target_result;
d->identified = false;
d->my_descriptor = 0;
identified = false;
my_descriptor = 0;
sprintf(dir_name,"/proc/%d/", pid);
sprintf(exe_link_name,"/proc/%d/exe", pid);
sprintf(mem_name,"/proc/%d/mem", pid);
memFile = mem_name;
sprintf(cwd_name,"/proc/%d/cwd", 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
sprintf(exe_link,"%s/%s",target_name,cmdline.c_str());
// create wine process, add it to the vector
d->identified = d->validate(exe_link,pid,mem_name,known_versions);
return;
}
}
}
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)
md5wrapper md5;
// get hash of the running DF process
string hash = md5.getHashFromFile(exe_link);
// create linux process, add it to the vector
VersionInfo * vinfo = factory->getVersionInfoByMD5(hash);
if(vinfo)
{
writeQuad(offset, *(uint64_t *) (source + indexptr));
offset +=8;
indexptr +=8;
size -=8;
my_descriptor = new VersionInfo(*vinfo);
my_descriptor->setParentProcess(this);
stl.init(this);
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;
}
}
}
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)
{
uint32_t start_offset = offset + d->STLSTR_buf_off;
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;
return stl.readSTLString(offset, buffer, bufcapacity);
}
const string WineProcess::readSTLString (uint32_t offset)
{
uint32_t start_offset = offset + d->STLSTR_buf_off;
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;
return stl.readSTLString(offset);
}
string WineProcess::readClassName (uint32_t vptr)
{
int rtti = readDWord(vptr - 0x4);
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));
return stl.readClassName(vptr);
}

@ -22,42 +22,34 @@ must not be misrepresented as being the original software.
distribution.
*/
#include "Internal.h"
#include "dfhack/DFProcess.h"
#include "LinuxProcess.h"
#include "ProcessFactory.h"
#include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h"
#include <errno.h>
#include <sys/ptrace.h>
using namespace DFHack;
class NormalProcess::Private
{
public:
Private(Process * self_)
namespace {
class NormalProcess : public LinuxProcessBase
{
my_descriptor = NULL;
my_handle = NULL;
my_pid = 0;
attached = false;
suspended = false;
memFileHandle = 0;
self = self_;
public:
NormalProcess(uint32_t pid, VersionInfoFactory * known_versions);
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);
};
~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)
: d(new Private(this))
Process* DFHack::createNormalProcess(uint32_t pid, VersionInfoFactory * known_versions)
{
return new NormalProcess(pid, known_versions);
}
NormalProcess::NormalProcess(uint32_t pid, VersionInfoFactory * known_versions) : LinuxProcessBase(pid)
{
char dir_name [256];
char exe_link_name [256];
@ -67,12 +59,13 @@ NormalProcess::NormalProcess(uint32_t pid, vector< VersionInfo* >& known_version
char target_name[1024];
int target_result;
d->identified = false;
d->my_descriptor = 0;
identified = false;
my_descriptor = 0;
sprintf(dir_name,"/proc/%d/", pid);
sprintf(exe_link_name,"/proc/%d/exe", pid);
sprintf(mem_name,"/proc/%d/mem", pid);
memFile = mem_name;
sprintf(cwd_name,"/proc/%d/cwd", 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?
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
d->identified = d->validate(target_name,pid,mem_name,known_versions );
return;
}
}
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)
VersionInfo * vinfo = known_versions->getVersionInfoByMD5(hash);
if(vinfo)
{
cerr << "couldn't detach from process pid" << d->my_handle << endl;
perror("ptrace detach");
return false;
}
else
{
d->attached = false;
return true;
my_descriptor = new VersionInfo(*vinfo);
my_descriptor->setParentProcess(this);
identified = 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
{
uint32_t _M_length;
@ -554,7 +105,7 @@ struct _Rep_base
size_t NormalProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity)
{
_Rep_base header;
offset = readDWord(offset);
offset = Process::readDWord(offset);
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
read(offset,read_real,(uint8_t * )buffer);
@ -566,7 +117,7 @@ const string NormalProcess::readSTLString (uint32_t offset)
{
_Rep_base header;
offset = readDWord(offset);
offset = Process::readDWord(offset);
read(offset - sizeof(_Rep_base),sizeof(_Rep_base),(uint8_t *)&header);
// FIXME: use char* everywhere, avoid string
@ -579,22 +130,10 @@ const string NormalProcess::readSTLString (uint32_t offset)
string NormalProcess::readClassName (uint32_t vptr)
{
int typeinfo = readDWord(vptr - 0x4);
int typestring = readDWord(typeinfo + 0x4);
int typeinfo = Process::readDWord(vptr - 0x4);
int typestring = Process::readDWord(typeinfo + 0x4);
string raw = readCString(typestring);
size_t start = raw.find_first_of("abcdefghijklmnopqrstuvwxyz");// trim numbers
size_t end = raw.length();
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.
*/
#include "Internal.h"
#include "dfhack/DFProcess.h"
#include "SHMProcess.h"
#include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h"
#include "shms.h"
#include "mod-core.h"
using namespace DFHack;
// a full memory barrier! better be safe than sorry.
class SHMProcess::Private
SHMProcess::Private::Private(SHMProcess * self_)
{
public:
Private()
{
memdescriptor = NULL;
process_ID = 0;
shm_addr = 0;
attached = false;
locked = false;
identified = false;
useYield = 0;
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);
memdescriptor = NULL;
process_ID = 0;
attached = false;
locked = false;
identified = false;
useYield = 0;
DFSVMutex = 0;
DFCLMutex = 0;
DFCLSuspendMutex = 0;
self = self_;
}
bool SHMProcess::Private::SetAndWait (uint32_t state)
@ -116,6 +73,7 @@ bool SHMProcess::Private::SetAndWait (uint32_t state)
}
cnt++;
}
// server returned a generic error
if(SHMCMD == CORE_ERROR)
{
return false;
@ -123,6 +81,11 @@ bool SHMProcess::Private::SetAndWait (uint32_t state)
return true;
}
bool SHMProcess::SetAndWait (uint32_t state)
{
return d->SetAndWait(state);
}
uint32_t OS_getAffinity()
{
HANDLE hProcess = GetCurrentProcess();
@ -253,71 +216,10 @@ bool SHMProcess::Private::AreLocksOk()
return false;
}
/*
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)
bool SHMProcess::Private::validate(VersionInfoFactory * factory)
{
// try to identify the DF version
IMAGE_NT_HEADERS32 pe_header;
IMAGE_NT_HEADERS pe_header;
IMAGE_SECTION_HEADER sections[16];
HMODULE hmod = NULL;
DWORD junk;
@ -340,60 +242,23 @@ bool SHMProcess::Private::validate(vector <VersionInfo *> & known_versions)
uint32_t base = (uint32_t)hmod;
// 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), sizeof(sections) , (uint8_t *)&sections );
// iterate over the list of memory locations
vector<VersionInfo *>::iterator it;
for ( it=known_versions.begin() ; it < known_versions.end(); it++ )
VersionInfo* vinfo = factory->getVersionInfoByPETimestamp(pe_header.FileHeader.TimeDateStamp);
if(vinfo)
{
uint32_t pe_timestamp;
try
{
pe_timestamp = (*it)->getPE();
}
catch(Error::AllMemdef&)
{
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;
}
VersionInfo *m = new VersionInfo(*vinfo);
m->RebaseAll(base);
memdescriptor = m;
m->setParentProcess(self);
identified = true;
CloseHandle(hProcess);
return true;
}
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 )
{
@ -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
void SHMProcess::getMemRanges( vector<t_memrange> & ranges )
{
// code here is taken from hexsearch by Silas Dunmore.
// As this IMHO isn't a 'sunstantial portion' of anything, I'm not including the MIT license here
// 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);
// BLAH
ranges.clear();
}
bool SHMProcess::suspend()
bool SHMProcess::acquireSuspendLock()
{
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",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();
return ( WaitForSingleObject(d->DFCLSuspendMutex,INFINITE) == 0 );
}
// FIXME: wait for the server to advance a step!
bool SHMProcess::resume()
bool SHMProcess::releaseSuspendLock()
{
if(!d->attached)
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;
return ( ReleaseMutex(d->DFCLSuspendMutex) != 0);
}
@ -576,21 +322,6 @@ bool SHMProcess::attach()
//cerr << "server is full or not really there!" << endl;
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.
@ -633,12 +364,10 @@ bool SHMProcess::attach()
bool SHMProcess::detach()
{
if(!d->attached) return true;
//cerr << "detach" << endl;// FIXME: throw
if(d->locked)
{
resume();
}
//cerr << "detach after resume" << endl;// FIXME: throw
// detach segment
UnmapViewOfFile(d->shm_addr);
// release it for some other client
@ -650,280 +379,10 @@ bool SHMProcess::detach()
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)
{
int rtti = readDWord(vptr - 0x4);
int typeinfo = readDWord(rtti + 0xC);
int rtti = Process::readDWord(vptr - 0x4);
int typeinfo = Process::readDWord(rtti + 0xC);
string raw = readCString(typeinfo + 0xC); // skips the .?AV
raw.resize(raw.length() - 2);// trim @@ from end
return raw;
@ -940,51 +399,10 @@ string SHMProcess::getPath()
string out(String);
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)
{
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();
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;
return d->shm_addr;
}

@ -22,179 +22,205 @@ must not be misrepresented as being the original software.
distribution.
*/
#include "Internal.h"
#include "dfhack/DFProcess.h"
#include "ProcessFactory.h"
#include "MicrosoftSTL.h"
#include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h"
#include <string.h>
using namespace DFHack;
class NormalProcess::Private
namespace
{
public:
Private()
{
my_descriptor = NULL;
my_handle = NULL;
my_main_thread = NULL;
my_pid = 0;
attached = false;
suspended = false;
};
~Private(){};
VersionInfo * my_descriptor;
HANDLE my_handle;
HANDLE my_main_thread;
uint32_t my_pid;
string memFile;
bool attached;
bool suspended;
bool identified;
uint32_t STLSTR_buf_off;
uint32_t STLSTR_size_off;
uint32_t STLSTR_cap_off;
};
class NormalProcess : public Process
{
private:
VersionInfo * my_descriptor;
HANDLE my_handle;
HANDLE my_main_thread;
uint32_t my_pid;
string memFile;
bool attached;
bool suspended;
bool identified;
IMAGE_NT_HEADERS pe_header;
IMAGE_SECTION_HEADER * sections;
uint32_t base;
MicrosoftSTL stl;
public:
NormalProcess(uint32_t pid, VersionInfoFactory * factory);
~NormalProcess();
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) { 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;
DWORD junk;
HANDLE hProcess;
DWORD needed;
bool found = false;
IMAGE_NT_HEADERS32 pe_header;
IMAGE_SECTION_HEADER sections[16];
d->identified = false;
identified = false;
// open process
hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid );
if (NULL == hProcess)
my_handle = OpenProcess( PROCESS_ALL_ACCESS, FALSE, my_pid );
if (NULL == my_handle)
return;
// 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;
return; //if enumprocessModules fails, give up
}
// got base ;)
uint32_t base = (uint32_t)hmod;
base = (uint32_t)hmod;
// temporarily assign this to allow some checks
d->my_handle = hProcess;
d->my_main_thread = 0;
my_main_thread = 0;
// read from this process
try
{
uint32_t pe_offset = readDWord(base+0x3C);
read(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header);
read(base + pe_offset+ sizeof(pe_header), sizeof(sections) , (uint8_t *)&sections );
d->my_handle = 0;
uint32_t pe_offset = Process::readDWord(base+0x3C);
read(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header);
const size_t sectionsSize = sizeof(IMAGE_SECTION_HEADER) * pe_header.FileHeader.NumberOfSections;
sections = (IMAGE_SECTION_HEADER *) malloc(sectionsSize);
read(base + pe_offset + sizeof(pe_header), sectionsSize, (uint8_t *)sections);
}
catch (exception &)
{
CloseHandle(hProcess);
d->my_handle = 0;
CloseHandle(my_handle);
my_handle = 0;
return;
}
// see if there's a version entry that matches this process
vector<VersionInfo*>::iterator it;
for ( it=known_versions.begin() ; it < known_versions.end(); it++ )
VersionInfo* vinfo = factory->getVersionInfoByPETimestamp(pe_header.FileHeader.TimeDateStamp);
if(vinfo)
{
// filter by OS
if(VersionInfo::OS_WINDOWS != (*it)->getOS())
continue;
uint32_t pe_timestamp;
// filter by timestamp, skip entries without a timestamp
try
{
pe_timestamp = (*it)->getPE();
}
catch(Error::AllMemdef&)
{
continue;
}
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
}
identified = true;
// give the process a data model and memory layout fixed for the base of first module
my_descriptor = new VersionInfo(*vinfo);
my_descriptor->RebaseAll(base);
// keep track of created memory_info object so we can destroy it later
my_descriptor->setParentProcess(this);
// TODO: detect errors in thread enumeration
vector<uint32_t> threads;
getThreadIDs( threads );
my_main_thread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads[0]);
stl.init(this);
}
// close handle of processes that aren't DF
if(!found)
else
{
CloseHandle(hProcess);
// close handles of processes that aren't DF
my_handle = 0;
CloseHandle(my_handle);
}
}
/*
*/
NormalProcess::~NormalProcess()
{
if(d->attached)
if(attached)
{
detach();
}
// destroy our rebased copy of the memory descriptor
delete d->my_descriptor;
if(d->my_handle != NULL)
delete my_descriptor;
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()
{
return d->my_descriptor;
return my_descriptor;
}
int NormalProcess::getPID()
{
return d->my_pid;
return my_pid;
}
bool NormalProcess::isSuspended()
{
return d->suspended;
return suspended;
}
bool NormalProcess::isAttached()
{
return d->attached;
return attached;
}
bool NormalProcess::isIdentified()
{
return d->identified;
return identified;
}
bool NormalProcess::asyncSuspend()
@ -204,49 +230,49 @@ bool NormalProcess::asyncSuspend()
bool NormalProcess::suspend()
{
if(!d->attached)
if(!attached)
return false;
if(d->suspended)
if(suspended)
{
return true;
}
SuspendThread(d->my_main_thread);
d->suspended = true;
SuspendThread(my_main_thread);
suspended = true;
return true;
}
bool NormalProcess::forceresume()
{
if(!d->attached)
if(!attached)
return false;
while (ResumeThread(d->my_main_thread) > 1);
d->suspended = false;
while (ResumeThread(my_main_thread) > 1);
suspended = false;
return true;
}
bool NormalProcess::resume()
{
if(!d->attached)
if(!attached)
return false;
if(!d->suspended)
if(!suspended)
{
return true;
}
ResumeThread(d->my_main_thread);
d->suspended = false;
ResumeThread(my_main_thread);
suspended = false;
return true;
}
bool NormalProcess::attach()
{
if(d->attached)
if(attached)
{
if(!d->suspended)
if(!suspended)
return suspend();
return true;
}
d->attached = true;
attached = true;
suspend();
return true;
@ -255,9 +281,9 @@ bool NormalProcess::attach()
bool NormalProcess::detach()
{
if(!d->attached) return true;
if(!attached) return true;
resume();
d->attached = false;
attached = false;
return true;
}
@ -281,7 +307,7 @@ bool NormalProcess::getThreadIDs(vector<uint32_t> & threads )
do
{
if( te32.th32OwnerProcessID == d->my_pid )
if( te32.th32OwnerProcessID == my_pid )
{
threads.push_back(te32.th32ThreadID);
}
@ -290,143 +316,200 @@ bool NormalProcess::getThreadIDs(vector<uint32_t> & threads )
CloseHandle( AllThreads );
return true;
}
//FIXME: use VirtualQuery to probe for memory ranges, cross-reference with base-corrected PE segment entries
void NormalProcess::getMemRanges( vector<t_memrange> & ranges )
/*
typedef struct _MEMORY_BASIC_INFORMATION
{
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.
// As this IMHO isn't a 'sunstantial portion' of anything, I'm not including the MIT license here
// I'm faking this, because there's no way I'm using VirtualQuery
t_memrange temp;
uint32_t base = d->my_descriptor->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");// that's right. I'm calling it pants. Windows can go to HELL
ranges.push_back(temp);
PVOID dwAddress;
DWORD dwSize;
DWORD dwFlags;
ULONG reserved;
};
*/
void HeapNodes(DWORD pid, map<uint64_t, unsigned int> & heaps)
{
// Create debug buffer
PDEBUG_BUFFER db = RtlCreateQueryDebugBuffer(0, FALSE);
// Get process heap data
RtlQueryProcessDebugInformation( pid, PDI_HEAPS/* | PDI_HEAP_BLOCKS*/, db);
ULONG heapNodeCount = db->HeapInformation ? *PULONG(db->HeapInformation):0;
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;
if(!ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint8_t), NULL))
throw Error::MemoryAccessDenied();
return result;
MEMORY_BASIC_INFORMATION MBI;
map<uint64_t, unsigned int> heaps;
uint64_t movingStart = 0;
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)
{
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();
}
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)
{
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();
}
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)
{
if(!ReadProcessMemory(d->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))
if(!ReadProcessMemory(my_handle, (int*) offset, &result, sizeof(uint32_t), NULL))
throw Error::MemoryAccessDenied();
return result;
}
void NormalProcess::readQuad (const uint32_t offset, uint64_t &result)
{
if(!ReadProcessMemory(d->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))
if(!ReadProcessMemory(my_handle, (int*) offset, &result, sizeof(uint64_t), NULL))
throw Error::MemoryAccessDenied();
return 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();
}
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();
}
// WRITING
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();
}
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();
}
// using these is expensive.
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();
}
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();
}
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();
}
///FIXME: reduce use of temporary objects
const string NormalProcess::readCString (const uint32_t offset)
{
string temp;
char temp_c[256];
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();
// needs to be 254+1 byte for the null term
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)
{
uint32_t start_offset = offset + d->STLSTR_buf_off;
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;
return stl.readSTLString(offset, buffer, bufcapacity);
}
const string NormalProcess::readSTLString (uint32_t offset)
{
uint32_t start_offset = offset + d->STLSTR_buf_off;
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;
return stl.readSTLString(offset);
}
string NormalProcess::readClassName (uint32_t vptr)
{
int rtti = readDWord(vptr - 0x4);
int typeinfo = readDWord(rtti + 0xC);
string raw = readCString(typeinfo + 0xC); // skips the .?AV
raw.resize(raw.length() - 2);// trim @@ from end
return raw;
return stl.readClassName(vptr);
}
string NormalProcess::getPath()
{
HMODULE hmod;
DWORD junk;
char String[255];
EnumProcessModules(d->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
EnumProcessModules(my_handle, &hmod, 1 * sizeof(HMODULE), &junk); //get the module from the handle
GetModuleFileNameEx(my_handle,hmod,String,sizeof(String)); //get the filename from the module
string out(String);
return(out.substr(0,out.find_last_of("\\")));
}
}

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

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

Binary file not shown.

@ -31,6 +31,7 @@ distribution.
#include "tinyxml.h"
FILE* TiXmlFOpen( const char* filename, const char* mode );
bool TiXmlBase::condenseWhiteSpace = true;
@ -161,6 +162,7 @@ void TiXmlNode::CopyTo( TiXmlNode* target ) const
{
target->SetValue (value.c_str() );
target->userData = userData;
target->location = location;
}
@ -186,7 +188,7 @@ TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )
assert( node->parent == 0 || node->parent == this );
assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() );
if ( node->Type() == TiXmlNode::DOCUMENT )
if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT )
{
delete node;
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 )
{
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 );
return 0;
@ -228,7 +230,7 @@ TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode&
if ( !beforeThis || beforeThis->parent != this ) {
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 );
return 0;
@ -260,7 +262,7 @@ TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& a
if ( !afterThis || afterThis->parent != this ) {
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 );
return 0;
@ -289,9 +291,20 @@ TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& a
TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )
{
if ( !replaceThis )
return 0;
if ( replaceThis->parent != this )
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();
if ( !node )
return 0;
@ -317,6 +330,10 @@ TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& wit
bool TiXmlNode::RemoveChild( TiXmlNode* removeThis )
{
if ( !removeThis ) {
return false;
}
if ( removeThis->parent != this )
{
assert( 0 );
@ -502,7 +519,7 @@ const TiXmlDocument* TiXmlNode::GetDocument() const
TiXmlElement::TiXmlElement (const char * _value)
: TiXmlNode( TiXmlNode::ELEMENT )
: TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
{
firstChild = lastChild = 0;
value = _value;
@ -511,7 +528,7 @@ TiXmlElement::TiXmlElement (const char * _value)
#ifdef TIXML_USE_STL
TiXmlElement::TiXmlElement( const std::string& _value )
: TiXmlNode( TiXmlNode::ELEMENT )
: TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
{
firstChild = lastChild = 0;
value = _value;
@ -520,7 +537,7 @@ TiXmlElement::TiXmlElement( const std::string& _value )
TiXmlElement::TiXmlElement( const TiXmlElement& copy)
: TiXmlNode( TiXmlNode::ELEMENT )
: TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
{
firstChild = lastChild = 0;
copy.CopyTo( this );
@ -564,9 +581,9 @@ const char* TiXmlElement::Attribute( const char* name ) const
#ifdef TIXML_USE_STL
const std::string* TiXmlElement::Attribute( const std::string& name ) const
{
const TiXmlAttribute* node = attributeSet.Find( name );
if ( node )
return &node->ValueStr();
const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( attrib )
return &attrib->ValueStr();
return 0;
}
#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* s = Attribute( name );
if ( i )
{
if ( s ) {
*i = atoi( s );
}
else {
*i = 0;
const TiXmlAttribute* attrib = attributeSet.Find( name );
const char* result = 0;
if ( attrib ) {
result = attrib->Value();
if ( i ) {
attrib->QueryIntValue( i );
}
}
return s;
return result;
}
#ifdef TIXML_USE_STL
const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const
{
const std::string* s = Attribute( name );
if ( i )
{
if ( s ) {
*i = atoi( s->c_str() );
}
else {
*i = 0;
const TiXmlAttribute* attrib = attributeSet.Find( name );
const std::string* result = 0;
if ( attrib ) {
result = &attrib->ValueStr();
if ( i ) {
attrib->QueryIntValue( i );
}
}
return s;
return result;
}
#endif
const char* TiXmlElement::Attribute( const char* name, double* d ) const
{
const char* s = Attribute( name );
if ( d )
{
if ( s ) {
*d = atof( s );
}
else {
*d = 0;
const TiXmlAttribute* attrib = attributeSet.Find( name );
const char* result = 0;
if ( attrib ) {
result = attrib->Value();
if ( d ) {
attrib->QueryDoubleValue( d );
}
}
return s;
return result;
}
#ifdef TIXML_USE_STL
const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const
{
const std::string* s = Attribute( name );
if ( d )
{
if ( s ) {
*d = atof( s->c_str() );
}
else {
*d = 0;
const TiXmlAttribute* attrib = attributeSet.Find( name );
const std::string* result = 0;
if ( attrib ) {
result = &attrib->ValueStr();
if ( d ) {
attrib->QueryDoubleValue( d );
}
}
return s;
return result;
}
#endif
int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const
{
const TiXmlAttribute* node = attributeSet.Find( name );
if ( !node )
const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( !attrib )
return TIXML_NO_ATTRIBUTE;
return node->QueryIntValue( ival );
return attrib->QueryIntValue( ival );
}
#ifdef TIXML_USE_STL
int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const
{
const TiXmlAttribute* node = attributeSet.Find( name );
if ( !node )
const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( !attrib )
return TIXML_NO_ATTRIBUTE;
return node->QueryIntValue( ival );
return attrib->QueryIntValue( ival );
}
#endif
int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const
{
const TiXmlAttribute* node = attributeSet.Find( name );
if ( !node )
const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( !attrib )
return TIXML_NO_ATTRIBUTE;
return node->QueryDoubleValue( dval );
return attrib->QueryDoubleValue( dval );
}
#ifdef TIXML_USE_STL
int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const
{
const TiXmlAttribute* node = attributeSet.Find( name );
if ( !node )
const TiXmlAttribute* attrib = attributeSet.Find( name );
if ( !attrib )
return TIXML_NO_ATTRIBUTE;
return node->QueryDoubleValue( dval );
return attrib->QueryDoubleValue( dval );
}
#endif
void TiXmlElement::SetAttribute( const char * name, int val )
{
char buf[64];
#if defined(TIXML_SNPRINTF)
TIXML_SNPRINTF( buf, sizeof(buf), "%d", val );
#else
sprintf( buf, "%d", val );
#endif
SetAttribute( name, buf );
TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
if ( attrib ) {
attrib->SetIntValue( val );
}
}
#ifdef TIXML_USE_STL
void TiXmlElement::SetAttribute( const std::string& name, int val )
{
std::ostringstream oss;
oss << val;
SetAttribute( name, oss.str() );
TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
if ( attrib ) {
attrib->SetIntValue( val );
}
}
#endif
void TiXmlElement::SetDoubleAttribute( const char * name, double val )
{
char buf[256];
#if defined(TIXML_SNPRINTF)
TIXML_SNPRINTF( buf, sizeof(buf), "%f", val );
#else
sprintf( buf, "%f", val );
#endif
SetAttribute( name, buf );
TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
if ( attrib ) {
attrib->SetDoubleValue( val );
}
}
void TiXmlElement::SetAttribute( const char * cname, const char * cvalue )
{
#ifdef TIXML_USE_STL
TIXML_STRING _name( cname );
TIXML_STRING _value( cvalue );
#else
const char* _name = cname;
const char* _value = cvalue;
#endif
TiXmlAttribute* node = attributeSet.Find( _name );
if ( node )
{
node->SetValue( _value );
return;
#ifdef TIXML_USE_STL
void TiXmlElement::SetDoubleAttribute( const std::string& name, double val )
{
TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
if ( attrib ) {
attrib->SetDoubleValue( val );
}
}
#endif
TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue );
if ( attrib )
{
attributeSet.Add( attrib );
}
else
{
TiXmlDocument* document = GetDocument();
if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
void TiXmlElement::SetAttribute( const char * cname, const char * cvalue )
{
TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname );
if ( attrib ) {
attrib->SetValue( cvalue );
}
}
#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 );
if ( node )
{
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 );
TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name );
if ( attrib ) {
attrib->SetValue( _value );
}
}
#endif
@ -881,14 +866,14 @@ const char* TiXmlElement::GetText() const
}
TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT )
TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
{
tabsize = 4;
useMicrosoftBOM = false;
ClearError();
}
TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT )
TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
{
tabsize = 4;
useMicrosoftBOM = false;
@ -898,7 +883,7 @@ TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode
#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;
useMicrosoftBOM = false;
@ -908,7 +893,7 @@ TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiX
#endif
TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT )
TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
{
copy.CopyTo( this );
}
@ -923,34 +908,17 @@ void TiXmlDocument::operator=( const TiXmlDocument& copy )
bool TiXmlDocument::LoadFile( TiXmlEncoding encoding )
{
// See STL_STRING_BUG below.
//StringToBuffer buf( value );
return LoadFile( Value(), encoding );
}
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() );
}
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 );
value = filename;
@ -995,11 +963,6 @@ bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )
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:
// 2.11 End-of-Line Handling
// <snip>
@ -1030,58 +993,46 @@ bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )
return false;
}
const char* lastPos = buf;
const char* p = buf;
// Process the buffer in place to normalize new lines. (See comment above.)
// 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;
while( *p ) {
assert( p < (buf+length) );
if ( *p == 0xa ) {
// Newline character. No special rules for this. Append all the characters
// since the last string, and include the newline.
data.append( lastPos, (p-lastPos+1) ); // append, include the newline
++p; // move past the newline
lastPos = p; // and point to the new buffer (may be 0)
assert( p <= (buf+length) );
}
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) );
assert( q <= (buf+length) );
assert( q <= p );
if ( *p == CR ) {
*q++ = LF;
p++;
if ( *p == LF ) { // check for CR+LF (and skip LF)
p++;
}
}
else {
++p;
*q++ = *p++;
}
}
// Handle any left over characters.
if ( p-lastPos ) {
data.append( lastPos, p-lastPos );
}
delete [] buf;
buf = 0;
assert( q <= (buf+length) );
*q = 0;
Parse( data.c_str(), 0, encoding );
Parse( buf, 0, encoding );
if ( Error() )
return false;
else
return true;
delete [] buf;
return !Error();
}
@ -1266,9 +1217,9 @@ void TiXmlAttribute::SetDoubleValue( double _value )
{
char buf [256];
#if defined(TIXML_SNPRINTF)
TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value);
TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value);
#else
sprintf (buf, "%lf", _value);
sprintf (buf, "%g", _value);
#endif
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 );
}
@ -1382,7 +1333,7 @@ TiXmlNode* TiXmlText::Clone() const
TiXmlDeclaration::TiXmlDeclaration( const char * _version,
const char * _encoding,
const char * _standalone )
: TiXmlNode( TiXmlNode::DECLARATION )
: TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
{
version = _version;
encoding = _encoding;
@ -1394,7 +1345,7 @@ TiXmlDeclaration::TiXmlDeclaration( const char * _version,
TiXmlDeclaration::TiXmlDeclaration( const std::string& _version,
const std::string& _encoding,
const std::string& _standalone )
: TiXmlNode( TiXmlNode::DECLARATION )
: TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
{
version = _version;
encoding = _encoding;
@ -1404,7 +1355,7 @@ TiXmlDeclaration::TiXmlDeclaration( const std::string& _version,
TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy )
: TiXmlNode( TiXmlNode::DECLARATION )
: TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
{
copy.CopyTo( this );
}
@ -1548,9 +1499,9 @@ void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )
#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 )
return node;
@ -1558,23 +1509,22 @@ const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const
return 0;
}
/*
TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name )
TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name )
{
for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
{
if ( node->name == name )
return node;
TiXmlAttribute* attrib = Find( _name );
if ( !attrib ) {
attrib = new TiXmlAttribute();
Add( attrib );
attrib->SetName( _name );
}
return 0;
return attrib;
}
*/
#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 )
return node;
@ -1582,17 +1532,18 @@ const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const
return 0;
}
/*
TiXmlAttribute* TiXmlAttributeSet::Find( const char* name )
TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name )
{
for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
{
if ( strcmp( node->name.c_str(), name ) == 0 )
return node;
TiXmlAttribute* attrib = Find( _name );
if ( !attrib ) {
attrib = new TiXmlAttribute();
Add( attrib );
attrib->SetName( _name );
}
return 0;
return attrib;
}
*/
#ifdef TIXML_USE_STL
std::istream& operator>> (std::istream & in, TiXmlNode & base)

@ -63,21 +63,19 @@ distribution.
#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
// Microsoft visual studio, version 2005 and higher.
#define TIXML_SNPRINTF _snprintf_s
#define TIXML_SNSCANF _snscanf_s
#define TIXML_SSCANF sscanf_s
#elif defined(_MSC_VER) && (_MSC_VER >= 1200 )
// Microsoft visual studio, version 6 and higher.
//#pragma message( "Using _sn* functions." )
#define TIXML_SNPRINTF _snprintf
#define TIXML_SNSCANF _snscanf
#define TIXML_SSCANF sscanf
#elif defined(__GNUC__) && (__GNUC__ >= 3 )
// GCC version 3 and higher.s
//#warning( "Using sn* functions." )
#define TIXML_SNPRINTF snprintf
#define TIXML_SNSCANF snscanf
#define TIXML_SSCANF sscanf
#else
#define TIXML_SNPRINTF snprintf
#define TIXML_SSCANF sscanf
#endif
#endif
@ -92,8 +90,8 @@ class TiXmlDeclaration;
class TiXmlParsingData;
const int TIXML_MAJOR_VERSION = 2;
const int TIXML_MINOR_VERSION = 5;
const int TIXML_PATCH_VERSION = 3;
const int TIXML_MINOR_VERSION = 6;
const int TIXML_PATCH_VERSION = 1;
/* Internal structure for tracking location of items
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
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
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
false, <b>no children of this node or its sibilings</b> will be Visited.
@ -267,7 +266,6 @@ public:
TIXML_NO_ERROR = 0,
TIXML_ERROR,
TIXML_ERROR_OPENING_FILE,
TIXML_ERROR_OUT_OF_MEMORY,
TIXML_ERROR_PARSING_ELEMENT,
TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
TIXML_ERROR_READING_ELEMENT_VALUE,
@ -288,6 +286,7 @@ public:
protected:
static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding );
inline static bool IsWhiteSpace( char c )
{
return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' );
@ -462,13 +461,13 @@ public:
*/
enum NodeType
{
DOCUMENT,
ELEMENT,
COMMENT,
UNKNOWN,
TEXT,
DECLARATION,
TYPECOUNT
TINYXML_DOCUMENT,
TINYXML_ELEMENT,
TINYXML_COMMENT,
TINYXML_UNKNOWN,
TINYXML_TEXT,
TINYXML_DECLARATION,
TINYXML_TYPECOUNT
};
virtual ~TiXmlNode();
@ -915,17 +914,14 @@ public:
const TiXmlAttribute* Last() const { 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 ) {
return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) );
}
#ifdef TIXML_USE_STL
const TiXmlAttribute* Find( const std::string& _name ) const;
TiXmlAttribute* Find( const std::string& _name ) {
return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) );
}
TiXmlAttribute* Find( const char* _name ) const;
TiXmlAttribute* FindOrCreate( const char* _name );
# ifdef TIXML_USE_STL
TiXmlAttribute* Find( const std::string& _name ) const;
TiXmlAttribute* FindOrCreate( const std::string& _name );
# endif
#endif
private:
//*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element),
@ -1000,11 +996,21 @@ public:
}
#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
attribute into the specified type. Very easy, very powerful, but
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
*/
@ -1020,13 +1026,8 @@ public:
return TIXML_SUCCESS;
return TIXML_WRONG_TYPE;
}
/*
This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string"
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
int QueryValueAttribute( const std::string& name, std::string* outValue ) const
{
const TiXmlAttribute* node = attributeSet.Find( name );
if ( !node )
@ -1034,7 +1035,6 @@ public:
*outValue = node->ValueStr();
return TIXML_SUCCESS;
}
*/
#endif
/** 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 );
///< STL std::string form.
void SetAttribute( const std::string& name, int _value );
///< STL std::string form.
void SetDoubleAttribute( const std::string& name, double value );
#endif
/** 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 );
private:
TiXmlAttributeSet attributeSet;
};
@ -1155,9 +1156,9 @@ class TiXmlComment : public TiXmlNode
{
public:
/// Constructs an empty comment.
TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {}
TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {}
/// Construct a comment from text.
TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) {
TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {
SetValue( _value );
}
TiXmlComment( const TiXmlComment& );
@ -1209,7 +1210,7 @@ public:
normal, encoded text. If you want it be output as a CDATA text
element, set the parameter _cdata to 'true'
*/
TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT)
TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT)
{
SetValue( initValue );
cdata = false;
@ -1218,14 +1219,14 @@ public:
#ifdef TIXML_USE_STL
/// Constructor.
TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT)
TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT)
{
SetValue( initValue );
cdata = false;
}
#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 ); }
// Write this text object to a FILE stream.
@ -1278,7 +1279,7 @@ class TiXmlDeclaration : public TiXmlNode
{
public:
/// Construct an empty declaration.
TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {}
TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {}
#ifdef TIXML_USE_STL
/// Constructor.
@ -1346,10 +1347,10 @@ private:
class TiXmlUnknown : public TiXmlNode
{
public:
TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {}
TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {}
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 ); }
/// Creates a copy of this Unknown and returns it.
@ -1423,14 +1424,10 @@ public:
#ifdef TIXML_USE_STL
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 );
}
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() );
}
#endif

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

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

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

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

@ -7,8 +7,18 @@
#ifdef _MSC_VER
// don't spew nonsense
#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 )
// Let me demonstrate:
/**
* [peterix@peterix dfhack]$ man wcscpy_s
* No manual entry for wcscpy_s
*
* Proprietary extensions.
*/
// disable stupid
#pragma warning( disable: 4800 )
// disable more stupid

@ -62,16 +62,17 @@ namespace DFHack
// memory range name (if any)
char name[1024];
// permission to read
bool read;
bool read : 1;
// permission to write
bool write;
bool write : 1;
// permission to execute
bool execute;
bool execute : 1;
inline bool isInRange( uint64_t address)
{
if (address >= start && address <= end) return true;
if (address >= start && address < end) return true;
return false;
}
bool valid;
uint8_t * buffer;
};
@ -98,33 +99,33 @@ namespace DFHack
virtual bool forceresume() = 0;
/// 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
virtual void readQuad(const uint32_t address, uint64_t & value) = 0;
/// write a 8-byte integer
virtual void writeQuad(const uint32_t address, const uint64_t value) = 0;
/// 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
virtual void readDWord(const uint32_t address, uint32_t & value) = 0;
/// write a 4-byte integer
virtual void writeDWord(const uint32_t address, const uint32_t value) = 0;
/// 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
virtual void readFloat(const uint32_t address, float & value) = 0;
/// 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
virtual void readWord(const uint32_t address, uint16_t & value) = 0;
/// write a 2-byte integer
virtual void writeWord(const uint32_t address, const uint16_t value) = 0;
/// 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
virtual void readByte(const uint32_t address, uint8_t & value) = 0;
/// write a byte
@ -153,6 +154,8 @@ namespace DFHack
virtual bool isAttached() = 0;
/// @return true if the process is identified -- has a Memory.xml entry
virtual bool isIdentified() = 0;
/// @return true if this is a Process snapshot
virtual bool isSnapshot() { return false; };
/// find the thread IDs of the process
virtual bool getThreadIDs(std::vector<uint32_t> & threads ) = 0;
@ -173,203 +176,5 @@ namespace DFHack
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

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

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

@ -7,6 +7,8 @@
#include "dfhack/DFExport.h"
#include "dfhack/DFModule.h"
#include "Vegetation.h"
namespace DFHack
{
/***************************************************************************
@ -66,7 +68,7 @@ namespace DFHack
int32_t type;
/// bit mask describing how the vein maps to the map block
/// assignment[y] & (1 << x) describes the tile (x, y) of the block
int16_t assignment[16];
int16_t assignment[16];
uint32_t flags;
/// this is NOT part of the DF vein, but an address of the vein as seen by DFhack.
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.
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
{
eNorthWest,
@ -148,15 +172,7 @@ namespace DFHack
liquid_water,
liquid_magma
};
/*
enum e_liquidcharacter
{
liquid_fresh,
liquid_unk1,
liquid_salt,
liquid_unk2,
};
*/
struct naked_designation
{
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,
std::vector<t_vein>* veins,
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:
struct Private;
Private *d;

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

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

@ -72,24 +72,36 @@ Buildings::Buildings(DFContextShared * d_)
d = new Private;
d->d = d_;
d->owner = d_->p;
d->p_bld = NULL;
d->Inited = d->Started = d->hasCustomWorkshops = false;
VersionInfo * mem = d->d->offset_descriptor;
OffsetGroup * OG_build = mem->getGroup("Buildings");
d->buildings_vector = OG_build->getAddress ("buildings_vector");
d->Inited = true;
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;
d->buildings_vector = OG_build->getAddress ("buildings_vector");
}
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()
@ -101,6 +113,8 @@ Buildings::~Buildings()
bool Buildings::Start(uint32_t & numbuildings)
{
if(!d->Inited)
return false;
d->p_bld = new DfVector <uint32_t> (d->owner, d->buildings_vector);
numbuildings = d->p_bld->size();
d->Started = true;
@ -148,7 +162,7 @@ bool Buildings::Finish()
bool Buildings::ReadCustomWorkshopTypes(map <uint32_t, string> & btypes)
{
if(!d->Started)
if(!d->Inited)
return false;
if(!d->hasCustomWorkshops)
return false;

@ -72,6 +72,7 @@ Creatures::Creatures(DFContextShared* _d)
d->owner = _d->p;
d->Inited = false;
d->Started = false;
d->p_cre = NULL;
d->d->InitReadNames(); // throws on error
VersionInfo * minfo = d->d->offset_descriptor;
OffsetGroup *OG_Creatures = minfo->getGroup("Creatures");
@ -149,10 +150,14 @@ Creatures::~Creatures()
bool Creatures::Start( uint32_t &numcreatures )
{
d->p_cre = new DfVector <uint32_t> (d->owner, d->creatures.vector);
d->Started = true;
numcreatures = d->p_cre->size();
return true;
if(d->Ft_basic)
{
d->p_cre = new DfVector <uint32_t> (d->owner, d->creatures.vector);
d->Started = true;
numcreatures = d->p_cre->size();
return true;
}
return false;
}
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));
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;
bool Inited;
bool Started;
bool hasGeology;
bool hasFeatures;
bool hasVeggies;
// map between feature address and the read object
map <uint32_t, t_feature> local_feature_store;
@ -74,6 +77,7 @@ Maps::Maps(DFContextShared* _d)
DFHack::VersionInfo * mem = p->getDescriptor();
Server::Maps::maps_offsets &off = d->offsets;
d->hasFeatures = d->hasGeology = d->hasVeggies = true;
// get the offsets once here
OffsetGroup *OG_Maps = mem->getGroup("Maps");
@ -87,7 +91,6 @@ Maps::Maps(DFContextShared* _d)
off.region_z_offset = OG_Maps->getAddress ("region_z");
off.world_size_x = OG_Maps->getAddress ("world_size_x");
off.world_size_y = OG_Maps->getAddress ("world_size_y");
OffsetGroup *OG_MapBlock = OG_Maps->getGroup("block");
{
off.tile_type_offset = OG_MapBlock->getOffset ("type");
@ -100,18 +103,41 @@ Maps::Maps(DFContextShared* _d)
off.temperature1_offset = OG_MapBlock->getOffset ("temperature1");
off.temperature2_offset = OG_MapBlock->getOffset ("temperature2");
}
OffsetGroup *OG_Geology = OG_Maps->getGroup("geology");
try
{
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");
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");
d->hasFeatures = false;
}
try
{
OffsetGroup * OG_Veg = d->d->offset_descriptor->getGroup("Vegetation");
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");
@ -121,6 +147,12 @@ Maps::Maps(DFContextShared* _d)
mem->resolveClassnameToVPtr("block_square_event_frozen_liquid", off.vein_ice_vptr);
off.vein_mineral_vptr = 0;
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
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
*/
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
t_vein v;
t_frozenliquidvein fv;
t_spattervein sv;
t_grassvein gv;
t_worldconstruction wcv;
Process* p = d->owner;
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(ices) ices->clear();
if(splatter) splatter->clear();
if(grass) splatter->clear();
if(constructions) constructions->clear();
Server::Maps::maps_offsets &off = d->offsets;
if (addr)
@ -568,6 +604,22 @@ try_again:
// store it in the vector
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
{
string cname = p->readClassName(type);
@ -586,12 +638,20 @@ try_again:
off.vein_spatter_vptr = type;
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
{
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
}
}
@ -661,6 +721,7 @@ __int16 __userpurge GetGeologicalRegion<ax>(__int16 block_X<cx>, int X<ebx>, __i
bool Maps::ReadGeology (vector < vector <uint16_t> >& assign)
{
MAPS_GUARD
if(!d->hasGeology) return false;
Process *p = d->owner;
// get needed addresses and offsets. Now this is what I call crazy.
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 )
{
MAPS_GUARD
if(!d->hasFeatures) return false;
// can't be used without a map!
if(!d->block)
return false;
@ -822,6 +884,7 @@ bool Maps::ReadLocalFeatures( std::map <planecoord, std::vector<t_feature *> > &
bool Maps::ReadGlobalFeatures( std::vector <t_feature> & features)
{
MAPS_GUARD
if(!d->hasFeatures) return false;
// can't be used without a map!
if(!d->block)
return false;
@ -864,3 +927,25 @@ bool Maps::ReadGlobalFeatures( std::vector <t_feature> & features)
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.
*/
#include "dfhack/DFPragma.h"
#include <vector>
#include <algorithm>

@ -49,9 +49,11 @@ struct World::Private
bool Inited;
bool StartedTime;
bool StartedWeather;
bool StartedMode;
uint32_t year_offset;
uint32_t tick_offset;
uint32_t weather_offset;
uint32_t gamemode_offset;
DFContextShared *d;
Process * owner;
};
@ -62,7 +64,7 @@ World::World(DFContextShared * _d)
d = new Private;
d->d = _d;
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");
try
@ -78,6 +80,12 @@ World::World(DFContextShared * _d)
d->StartedWeather = true;
}
catch(Error::All &){};
try
{
d->gamemode_offset = OG_World->getAddress( "game_mode" );
d->StartedMode = true;
}
catch(Error::All &){};
d->Inited = true;
}
@ -110,6 +118,14 @@ uint32_t World::ReadCurrentTick()
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:
/*
World::ReadCurrentMonth and World::ReadCurrentDay

@ -81,6 +81,57 @@ using namespace std;
#include <winnt.h>
#include <psapi.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
// 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
{
// increment on every change
#define MAPS_VERSION 4
#define MAPS_VERSION 5
typedef struct
{
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 biome_stuffs;// = d->offset_descriptor->getOffset ("biome_stuffs");
uint32_t veinvector;// = d->offset_descriptor->getOffset ("v_vein");
uint32_t vegvector;
uint32_t temperature1_offset;
uint32_t temperature2_offset;
uint32_t global_feature_offset;
@ -57,6 +58,8 @@ typedef struct
uint32_t vein_mineral_vptr;
uint32_t vein_ice_vptr;
uint32_t vein_spatter_vptr;
uint32_t vein_grass_vptr;
uint32_t vein_worldconstruction_vptr;
/*
GEOLOGY
*/
@ -72,6 +75,11 @@ typedef struct
uint32_t world_size_y;// = minfo->getOffset ("world_size_y");
uint32_t geolayer_geoblock_offset;// = minfo->getOffset ("geolayer_geoblock_offset");
uint32_t type_inside_geolayer;// = mem->getOffset ("type_inside_geolayer");
/*
* Vegetation
*/
uint32_t tree_desc_offset;
} maps_offsets;
typedef struct

@ -12,6 +12,29 @@ using namespace std;
#define DFHACK_WANT_MISCUTILS
#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)
{
uint32_t addr;
@ -40,6 +63,7 @@ int main (int numargs, const char ** args)
DFHack::VersionInfo* mem = DF->getMemoryInfo();
DFHack::Position * pos = DF->getPosition();
DFHack::Vegetation * v = DF->getVegetation();
DFHack::Maps * mps = DF->getMaps();
DFHack::Materials * mat = DF->getMaterials();
mat->ReadOrganicMaterials();
@ -60,30 +84,34 @@ int main (int numargs, const char ** args)
}
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++)
{
DFHack::t_tree tree;
v->Read(i,tree);
if(tree.x == x && tree.y == y && tree.z == z)
{
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);
cout << "----==== Tree at "<< x << "/" << y << "/" << z << " ====----" << endl;
print_tree(DF, tree);
break;
}
}

@ -24,19 +24,14 @@ TARGET_LINK_LIBRARIES(dfdigger dfhack)
ADD_EXECUTABLE(dfdigger2 digger2.cpp)
TARGET_LINK_LIBRARIES(dfdigger2 dfhack)
ADD_EXECUTABLE(primitives primitives.cpp)
# itemdesignator - change some item designations (dump, forbid, on-fire) for all
# items of a given type and material
# Author: belal
#ADD_EXECUTABLE(dfitemdesignator itemdesignator.cpp)
#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
# Author: Zhentar
ADD_EXECUTABLE(dfcatsplosion catsplosion.cpp)
@ -113,9 +108,3 @@ dfprinttiletypes
dfhellhole
RUNTIME DESTINATION bin
)
IF(UNIX)
install(TARGETS
dfincremental
RUNTIME DESTINATION bin
)
ENDIF(UNIX)

@ -21,7 +21,6 @@
#include <assert.h>
using namespace std;
#include <argstream.h>
#include <DFHack.h>
#include <dfhack/DFTileTypes.h>
#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)
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)
SET(VEINLOOK_BUILT "NO")
@ -126,5 +132,7 @@ dfsuspend
dfflows
dfliquids
dfweather
dfautosearch
dfincremental
RUNTIME DESTINATION bin
)

@ -12,18 +12,37 @@ class SegmentFinder
{
_DF = DF;
mr_ = mr;
mr_.buffer = (uint8_t *)malloc (mr_.end - mr_.start);
DF->ReadRaw(mr_.start,(mr_.end - mr_.start),mr_.buffer);
_SF = SF;
if(mr.valid)
{
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()
{
delete mr_.buffer;
if(valid)
free(mr_.buffer);
}
bool isValid()
{
return valid;
}
template <class needleType, class hayType, typename comparator >
bool Find (needleType needle, const uint8_t increment , vector <uint64_t> &newfound, comparator oper)
{
if(!valid) return !newfound.empty();
//loop
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 >
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);
//loop
for(uint64_t offset = start - mr_.start; offset < stopper; offset +=1)
@ -49,6 +69,7 @@ class SegmentFinder
template <class needleType, class hayType, typename comparator >
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++)
{
if(mr_.isInRange(found[i]))
@ -65,6 +86,7 @@ class SegmentFinder
SegmentedFinder * _SF;
DFHack::Context * _DF;
DFHack::t_memrange mr_;
bool valid;
};
class SegmentedFinder
@ -364,14 +386,16 @@ public:
return false;
}
}
bool insert( char what )
template < class T >
bool insert( T what )
{
if(constant)
return false;
if(d->length+1 == d->allocated)
Allocate(d->allocated * 2);
((char *) d->object)[d->length] = what;
d->length ++;
if(d->length+sizeof(T) >= d->allocated)
Allocate((d->length+sizeof(T)) * 2);
(*(T *)( (uint64_t)d->object + d->length)) = what;
d->length += sizeof(T);
return true;
}
Bytestreamdata * d;
bool constant;
@ -431,7 +455,7 @@ std::istream& operator>> ( std::istream& out, Bytestream& bs )
{
small = reads - '0';
state = 0;
bs.insert(big*16 + small);
bs.insert<char>(big*16 + small);
}
}
if((reads >= 'a' && reads <= 'f'))
@ -445,7 +469,7 @@ std::istream& operator>> ( std::istream& out, Bytestream& bs )
{
small = reads - 'a' + 10;
state = 0;
bs.insert(big*16 + small);
bs.insert<char>(big*16 + small);
}
}
it++;
@ -455,7 +479,7 @@ std::istream& operator>> ( std::istream& out, Bytestream& bs )
if (state == 1)
{
small = 0;
bs.insert(big*16 + small);
bs.insert<char>(big*16 + small);
}
}
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);
uint8_t zeroes [16][16] = {0};
DFHack::occupancies40d occ;
// walk the map
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))
{
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++)
{
DFHack::t_spattervein & vein = splatter[i];

@ -21,146 +21,12 @@ using namespace std;
#include <DFHack.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)
{
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)
{
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
// 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());
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());
end = min(4, (int)ranges.size());
@ -226,6 +92,7 @@ bool getRanges(DFHack::Process * p, vector <DFHack::t_memrange>& selected_ranges
}
it++;
}
return 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;
}
else if(select.empty())
else if(select == "q")
{
return false;
}
else if(select.empty())
{
goto incremental_more;
}
else
{
if(numberz)
@ -532,29 +403,91 @@ void FindData(DFHack::ContextManager & DFMgr, vector <DFHack::t_memrange>& range
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();
DFHack::Context * DF = DFMgr.getSingleContext();
DF->Attach();
SegmentedFinder sf(ranges,DF);
switch(size)
DFHack::Position * pos = DF->getPosition();
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:
sf.Incremental<uint8_t,uint8_t>(test1,alignment,found, equalityP<uint8_t>);
break;
case 2:
sf.Incremental<uint16_t,uint16_t>(test1,alignment,found, equalityP<uint16_t>);
break;
case 4:
sf.Incremental<uint32_t,uint32_t>(test1,alignment,found, equalityP<uint32_t>);
break;
select.insert<uint16_t>(x);
select.insert<uint16_t>(y);
select.insert<uint16_t>(z);
}
else
{
select.insert<uint32_t>(x);
select.insert<uint32_t>(y);
select.insert<uint32_t>(z);
}
cout << select << endl;
SegmentedFinder sf(ranges,DF);
sf.Incremental< Bytestream ,uint32_t>(select,1,found, findBytestream);
DF->Detach();
}
}
*/
void PtrTrace(DFHack::ContextManager & DFMgr, vector <DFHack::t_memrange>& ranges)
{
int element_size;
@ -736,7 +669,7 @@ struct tilecolors
};
#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> 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);
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
to_filter = filtVectors;
sf.Filter<uint32_t,vecTriplet>(31 * 4,to_filter,vectorLength<uint32_t>);
@ -968,70 +910,80 @@ int main (void)
getRanges(p,selected_ranges);
DFHack::VersionInfo *minfo = DF->getMemoryInfo();
DFHack::VersionInfo::OSType os = minfo->getOS();
DFHack::OSType os = minfo->getOS();
string prompt =
"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"
" 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;
bool finish = 0;
do
{
getNumber(prompt,mode, 1, false);
} while (mode < 1 || mode > 12 );
switch (mode)
{
case 1:
DF->Detach();
FindIntegers(DFMgr, selected_ranges);
break;
case 2:
DF->Detach();
FindVectorByLength(DFMgr, selected_ranges);
break;
case 3:
DF->Detach();
FindVectorByObjectRawname(DFMgr, selected_ranges);
break;
case 4:
DF->Detach();
FindStrings(DFMgr, selected_ranges);
break;
case 5:
automatedLangtables(DF,selected_ranges);
break;
case 6:
DF->Detach();
FindVectorByBounds(DFMgr,selected_ranges);
break;
case 7:
DF->Detach();
FindPtrVectorsByObjectAddress(DFMgr,selected_ranges);
break;
case 8:
DF->Detach();
FindVectorByFirstObjectRawname(DFMgr, selected_ranges);
break;
case 9:
DF->Detach();
FindStrBufs(DFMgr, selected_ranges);
break;
case 10:
DF->Detach();
FindData(DFMgr, selected_ranges);
break;
case 11:
DF->Detach();
PtrTrace(DFMgr, selected_ranges);
break;
case 12:
DF->Detach();
DataPtrTrace(DFMgr, selected_ranges);
break;
default:
cout << "not implemented :(" << endl;
}
switch (mode)
{
case 0:
finish = 1;
break;
case 1:
DF->Detach();
FindIntegers(DFMgr, selected_ranges);
break;
case 2:
DF->Detach();
FindVectorByLength(DFMgr, selected_ranges);
break;
case 3:
DF->Detach();
FindVectorByObjectRawname(DFMgr, selected_ranges);
break;
case 4:
DF->Detach();
FindStrings(DFMgr, selected_ranges);
break;
case 5:
autoSearch(DF,selected_ranges);
break;
case 6:
DF->Detach();
FindVectorByBounds(DFMgr,selected_ranges);
break;
case 7:
DF->Detach();
FindPtrVectorsByObjectAddress(DFMgr,selected_ranges);
break;
case 8:
DF->Detach();
FindVectorByFirstObjectRawname(DFMgr, selected_ranges);
break;
case 9:
DF->Detach();
FindStrBufs(DFMgr, selected_ranges);
break;
case 10:
DF->Detach();
FindData(DFMgr, selected_ranges);
break;
case 11:
DF->Detach();
PtrTrace(DFMgr, selected_ranges);
break;
case 12:
DF->Detach();
DataPtrTrace(DFMgr, selected_ranges);
break;
case 13:
DF->Detach();
FindCoords(DFMgr, selected_ranges);
break;
default:
cout << "Unknown function, try again." << endl;
}
} while ( !finish );
#ifndef LINUX_BUILD
cout << "Done. Press any key to continue" << endl;
cin.ignore();

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

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

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