diff --git a/.gitignore b/.gitignore index 3d1c650da..1a06c65bc 100644 --- a/.gitignore +++ b/.gitignore @@ -29,5 +29,5 @@ dfhack/python/pydfhack/_pydfhack.so dfhack/python/PyDFHack.egg-info dfhack/python/build dfhack/python/dist - + /cmakeall.bat \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ceffb938e..cbeda3ffa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/LICENSE b/LICENSE index 9c8259200..4cfc6a66d 100644 --- a/LICENSE +++ b/LICENSE @@ -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 + +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. \ No newline at end of file diff --git a/build/build-MinGW32-debug.bat b/build/build-MinGW32-debug.bat index e60ca7211..ee622d5ff 100644 --- a/build/build-MinGW32-debug.bat +++ b/build/build-MinGW32-debug.bat @@ -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 \ No newline at end of file diff --git a/build/generate-MSVC-2005.bat b/build/generate-MSVC-2005.bat index a187b4663..35451e362 100644 --- a/build/generate-MSVC-2005.bat +++ b/build/generate-MSVC-2005.bat @@ -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 \ No newline at end of file diff --git a/build/generate-MSVC-2008.bat b/build/generate-MSVC-2008.bat index 79e95a8c5..8d39368b5 100644 --- a/build/generate-MSVC-2008.bat +++ b/build/generate-MSVC-2008.bat @@ -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 \ No newline at end of file diff --git a/data/Memory-ng.xml b/data/Memory-ng.xml index 537e2a2da..fb4cabaaf 100644 --- a/data/Memory-ng.xml +++ b/data/Memory-ng.xml @@ -665,6 +665,9 @@ + + +
+ @@ -903,6 +907,7 @@
+
@@ -994,6 +999,7 @@
+ @@ -1551,6 +1557,7 @@ + @@ -1871,6 +1878,189 @@ + + + + + + + + + +
+
+
+
+
+ + +
maybe +
+
+ + + + OK + + + + + + maybe + maybe + + + + + OK + maybe? + maybe? + + + + + THIS IS TOTAL BS + DT: 0x3C4, I don't believe that's OK' + DT calls it 'states' it seems + maybe + + + + + + + + + MISSING! + MISSING! + + MISSING! + MISSING! + MISSING! + MISSING! + MISSING! + + + + + + DT: 0x1F0 + DT: 0x214 + + + +
+
+
+
+
+
+
+
+
+
TODO: it's signed! +
+
+ + + + + + + + + + + + + + +
+ + + + + +
+ + + + + LOOKS FINE? +
+
VERIFIED + + VERIFIED + + VERIFIED + + + +
+
+
+
+
+ + maybe: 0x2064 before:0x1a08 + + looks kinda funny, same as in .16 + OK + OK + + + +
+
+ + + +
+ + +
+ + +
+ + +
+ One of those really. Which one is the right one? + 0x16dad78 + 0x16dad88 +
+ + +
maybe +
+
+
+ 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 + + + +
+
+ + + .-"""-. ' \ @@ -1943,6 +2133,7 @@ + @@ -2329,6 +2520,115 @@ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + +
+
+ + +
+ + + +
+ + + + +
+
+ + + +
+ + +
+
+
+
+
+ ------------------- + !!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 + + + diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 094d598b3..6fc1452fe 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -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}) diff --git a/library/DFContext.cpp b/library/DFContext.cpp index c0be5b872..fa4f85b59 100644 --- a/library/DFContext.cpp +++ b/library/DFContext.cpp @@ -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 *) ¬e.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 &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;ip_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; jp->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; -} -*/ - diff --git a/library/DFProcess-SHM.cpp b/library/DFProcess-SHM.cpp new file mode 100644 index 000000000..2d907c32b --- /dev/null +++ b/library/DFProcess-SHM.cpp @@ -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 (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); +} diff --git a/library/DFProcess-linux-SHM.cpp b/library/DFProcess-linux-SHM.cpp index 4eaf47b90..2f5889908 100644 --- a/library/DFProcess-linux-SHM.cpp +++ b/library/DFProcess-linux-SHM.cpp @@ -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 & 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 & 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::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( 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 & 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 (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 (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; +} diff --git a/library/DFProcess-linux-base.cpp b/library/DFProcess-linux-base.cpp new file mode 100644 index 000000000..f704d384e --- /dev/null +++ b/library/DFProcess-linux-base.cpp @@ -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 +#include +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 & threads ) +{ + return false; +} + +//FIXME: cross-reference with ELF segment entries? +void LinuxProcessBase::getMemRanges( vector & 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)); +} diff --git a/library/DFProcess-linux-wine.cpp b/library/DFProcess-linux-wine.cpp index cd96b3e54..073efcaa2 100644 --- a/library/DFProcess-linux-wine.cpp +++ b/library/DFProcess-linux-wine.cpp @@ -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 @@ -30,37 +32,28 @@ distribution. #include 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 & known_versions); -}; +} + +Process* DFHack::createWineProcess(uint32_t pid, VersionInfoFactory * factory) +{ + return new WineProcess(pid, factory); +} -WineProcess::WineProcess(uint32_t pid, vector & 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 & 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 & 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::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( 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 & threads ) -{ - return false; -} - -//FIXME: cross-reference with ELF segment entries? -void WineProcess::getMemRanges( vector & 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); } diff --git a/library/DFProcess-linux.cpp b/library/DFProcess-linux.cpp index 431dee508..216f4babe 100644 --- a/library/DFProcess-linux.cpp +++ b/library/DFProcess-linux.cpp @@ -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 #include 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 & 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 & known_versions) -{ - md5wrapper md5; - // get hash of the running DF process - string hash = md5.getHashFromFile(exe_file); - vector::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( 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 & threads ) -{ - return false; -} - -//FIXME: cross-reference with ELF segment entries? -void NormalProcess::getMemRanges( vector & 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)); -} diff --git a/library/DFProcess-windows-SHM.cpp b/library/DFProcess-windows-SHM.cpp index 5630d3146..50c410b39 100644 --- a/library/DFProcess-windows-SHM.cpp +++ b/library/DFProcess-windows-SHM.cpp @@ -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 & 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 & 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 & 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 *)§ions ); - // iterate over the list of memory locations - vector::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 & threads ) { @@ -428,137 +293,18 @@ bool SHMProcess::getThreadIDs(vector & threads ) //FIXME: use VirtualQuery to probe for memory ranges, cross-reference with base-corrected PE segment entries void SHMProcess::getMemRanges( vector & 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 (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 (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; } diff --git a/library/DFProcess-windows.cpp b/library/DFProcess-windows.cpp index b9f3fd86e..1bf92ccea 100644 --- a/library/DFProcess-windows.cpp +++ b/library/DFProcess-windows.cpp @@ -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 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 & threads ); + void getMemRanges(std::vector & 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 & 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 *)§ions ); - 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::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 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 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 & 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 & 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 & 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 & 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 & 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 heaps; + uint64_t movingStart = 0; + map 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("\\"))); -} \ No newline at end of file +} diff --git a/library/DFProcessEnumerator.cpp b/library/DFProcessEnumerator.cpp index 5105be170..94e40450a 100644 --- a/library/DFProcessEnumerator.cpp +++ b/library/DFProcessEnumerator.cpp @@ -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 diff --git a/library/MicrosoftSTL.cpp b/library/MicrosoftSTL.cpp new file mode 100644 index 000000000..9d619600e --- /dev/null +++ b/library/MicrosoftSTL.cpp @@ -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 +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; +} diff --git a/library/VersionInfo.cpp b/library/VersionInfo.cpp index 7773b7470..0569b90f8 100644 --- a/library/VersionInfo.cpp +++ b/library/VersionInfo.cpp @@ -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; } diff --git a/library/VersionInfoFactory.cpp b/library/VersionInfoFactory.cpp index d25af91c1..9a242153a 100644 --- a/library/VersionInfoFactory.cpp +++ b/library/VersionInfoFactory.cpp @@ -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 , 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 (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) { diff --git a/library/depends/ntdll/ntdll.lib b/library/depends/ntdll/ntdll.lib new file mode 100644 index 000000000..9f9cf2108 Binary files /dev/null and b/library/depends/ntdll/ntdll.lib differ diff --git a/library/depends/tinyxml/tinyxml.cpp b/library/depends/tinyxml/tinyxml.cpp index 5de21f6de..9be6c6a66 100644 --- a/library/depends/tinyxml/tinyxml.cpp +++ b/library/depends/tinyxml/tinyxml.cpp @@ -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 // @@ -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) diff --git a/library/depends/tinyxml/tinyxml.h b/library/depends/tinyxml/tinyxml.h index c6f40cc27..01822911c 100644 --- a/library/depends/tinyxml/tinyxml.h +++ b/library/depends/tinyxml/tinyxml.h @@ -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, no children of this node or its sibilings 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 diff --git a/library/depends/tinyxml/tinyxmlerror.cpp b/library/depends/tinyxml/tinyxmlerror.cpp index d24f63b2e..d66f6ffb6 100644 --- a/library/depends/tinyxml/tinyxmlerror.cpp +++ b/library/depends/tinyxml/tinyxmlerror.cpp @@ -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.", diff --git a/library/depends/tinyxml/tinyxmlparser.cpp b/library/depends/tinyxml/tinyxmlparser.cpp index 5793f0528..666a4f757 100644 --- a/library/depends/tinyxml/tinyxmlparser.cpp +++ b/library/depends/tinyxml/tinyxmlparser.cpp @@ -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 (""; // 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: + // and + // + // 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 ) { diff --git a/library/include/DFHack_C.h b/library/include/DFHack_C.h index 6c5dd004f..b23457fda 100644 --- a/library/include/DFHack_C.h +++ b/library/include/DFHack_C.h @@ -25,6 +25,8 @@ distribution. #ifndef DFHACK_C_API #define DFHACK_C_API +#include "dfhack/DFPragma.h" + #include #include #include diff --git a/library/include/dfhack/DFError.h b/library/include/dfhack/DFError.h index 28e11df33..0e4d00145 100644 --- a/library/include/dfhack/DFError.h +++ b/library/include/dfhack/DFError.h @@ -26,6 +26,7 @@ distribution. #define ERROR_H_INCLUDED #include "DFExport.h" +#include "DFPragma.h" #include #include #include diff --git a/library/include/dfhack/DFPragma.h b/library/include/dfhack/DFPragma.h index e678093cc..fb0d395c4 100644 --- a/library/include/dfhack/DFPragma.h +++ b/library/include/dfhack/DFPragma.h @@ -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 diff --git a/library/include/dfhack/DFProcess.h b/library/include/dfhack/DFProcess.h index 3743cf4b7..140bdd505 100644 --- a/library/include/dfhack/DFProcess.h +++ b/library/include/dfhack/DFProcess.h @@ -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 & 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 & 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 & threads ); - void getMemRanges(std::vector & 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 & 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 & threads ); - void getMemRanges(std::vector & 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 & 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 & threads ); - void getMemRanges(std::vector & 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 diff --git a/library/include/dfhack/VersionInfo.h b/library/include/dfhack/VersionInfo.h index b5c7dac14..39e838234 100644 --- a/library/include/dfhack/VersionInfo.h +++ b/library/include/dfhack/VersionInfo.h @@ -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); diff --git a/library/include/dfhack/VersionInfoFactory.h b/library/include/dfhack/VersionInfoFactory.h index 7f811f588..76fe7ad21 100644 --- a/library/include/dfhack/VersionInfoFactory.h +++ b/library/include/dfhack/VersionInfoFactory.h @@ -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 versions; private: void ParseVTable(TiXmlElement* vtable, VersionInfo* mem); diff --git a/library/include/dfhack/modules/Maps.h b/library/include/dfhack/modules/Maps.h index a9ce94a06..d8b92f3bc 100644 --- a/library/include/dfhack/modules/Maps.h +++ b/library/include/dfhack/modules/Maps.h @@ -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* veins, std::vector* ices = 0, - std::vector* splatter = 0); - + std::vector* splatter = 0, + std::vector* grass = 0, + std::vector* constructions = 0 + ); + /// read all plants in this block + bool ReadVegetation(uint32_t x, uint32_t y, uint32_t z, std::vector* plants); private: struct Private; Private *d; diff --git a/library/include/dfhack/modules/Materials.h b/library/include/dfhack/modules/Materials.h index f3ac6425c..9c686f968 100644 --- a/library/include/dfhack/modules/Materials.h +++ b/library/include/dfhack/modules/Materials.h @@ -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; diff --git a/library/include/dfhack/modules/World.h b/library/include/dfhack/modules/World.h index 7834d4719..80208643c 100644 --- a/library/include/dfhack/modules/World.h +++ b/library/include/dfhack/modules/World.h @@ -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; diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 7541bb33e..20b4cb88e 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -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 (d->owner, d->buildings_vector); numbuildings = d->p_bld->size(); d->Started = true; @@ -148,7 +162,7 @@ bool Buildings::Finish() bool Buildings::ReadCustomWorkshopTypes(map & btypes) { - if(!d->Started) + if(!d->Inited) return false; if(!d->hasCustomWorkshops) return false; diff --git a/library/modules/Creatures.cpp b/library/modules/Creatures.cpp index 39e734355..27bc320db 100644 --- a/library/modules/Creatures.cpp +++ b/library/modules/Creatures.cpp @@ -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 (d->owner, d->creatures.vector); - d->Started = true; - numcreatures = d->p_cre->size(); - return true; + if(d->Ft_basic) + { + d->p_cre = new DfVector (d->owner, d->creatures.vector); + d->Started = true; + numcreatures = d->p_cre->size(); + return true; + } + return false; } bool Creatures::Finish() diff --git a/library/modules/Graveyard.cpp b/library/modules/Graveyard.cpp new file mode 100644 index 000000000..4abe854f1 --- /dev/null +++ b/library/modules/Graveyard.cpp @@ -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 *) ¬e.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; +} diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index c381184d8..dcb137848 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -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 &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;ip_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; jp->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; +} +*/ \ No newline at end of file diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 4df9c38a0..cfd6e61cd 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -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 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 * veins, vector * ices, vector *splatter) +bool Maps::ReadVeins(uint32_t x, uint32_t y, uint32_t z, vector * veins, vector * ices, vector *splatter, vector *grass, vector *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(__int16 block_X, int X, __i bool Maps::ReadGeology (vector < vector >& 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 >& assign) bool Maps::ReadLocalFeatures( std::map > & 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 > & bool Maps::ReadGlobalFeatures( std::vector & 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 & features) return true; } +bool Maps::ReadVegetation(uint32_t x, uint32_t y, uint32_t z, std::vector* 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 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; +} \ No newline at end of file diff --git a/library/modules/Maps_C.cpp b/library/modules/Maps_C.cpp index 0c5647b16..58e9bb76c 100644 --- a/library/modules/Maps_C.cpp +++ b/library/modules/Maps_C.cpp @@ -22,6 +22,7 @@ must not be misrepresented as being the original software. distribution. */ +#include "dfhack/DFPragma.h" #include #include diff --git a/library/modules/World.cpp b/library/modules/World.cpp index 3e1a670de..d7b86caec 100644 --- a/library/modules/World.cpp +++ b/library/modules/World.cpp @@ -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 diff --git a/library/private/Internal.h b/library/private/Internal.h index 3f5d561fa..8a6ad69b0 100644 --- a/library/private/Internal.h +++ b/library/private/Internal.h @@ -81,6 +81,57 @@ using namespace std; #include #include #include + #include + 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 diff --git a/library/private/LinuxProcess.h b/library/private/LinuxProcess.h new file mode 100644 index 000000000..e3e04cb35 --- /dev/null +++ b/library/private/LinuxProcess.h @@ -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 & threads ); + void getMemRanges(std::vector & 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 diff --git a/library/private/MicrosoftSTL.h b/library/private/MicrosoftSTL.h new file mode 100644 index 000000000..a25ee1797 --- /dev/null +++ b/library/private/MicrosoftSTL.h @@ -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 + +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); + }; +} diff --git a/library/private/ProcessFactory.h b/library/private/ProcessFactory.h new file mode 100644 index 000000000..ed697d570 --- /dev/null +++ b/library/private/ProcessFactory.h @@ -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 diff --git a/library/private/SHMProcess.h b/library/private/SHMProcess.h new file mode 100644 index 000000000..e10476aad --- /dev/null +++ b/library/private/SHMProcess.h @@ -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 & threads ); + void getMemRanges(std::vector & 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 diff --git a/library/shm/mod-maps.h b/library/shm/mod-maps.h index aafc355cd..9bc026a52 100644 --- a/library/shm/mod-maps.h +++ b/library/shm/mod-maps.h @@ -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 diff --git a/tools/examples/treedump.cpp b/tools/examples/treedump.cpp index 410587173..793bb5966 100644 --- a/tools/examples/treedump.cpp +++ b/tools/examples/treedump.cpp @@ -12,6 +12,29 @@ using namespace std; #define DFHACK_WANT_MISCUTILS #include +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 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; } } diff --git a/tools/playground/CMakeLists.txt b/tools/playground/CMakeLists.txt index 291845660..b47eba0e0 100644 --- a/tools/playground/CMakeLists.txt +++ b/tools/playground/CMakeLists.txt @@ -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) diff --git a/tools/playground/digger2.cpp b/tools/playground/digger2.cpp index 91507bcc1..dc4e9d271 100644 --- a/tools/playground/digger2.cpp +++ b/tools/playground/digger2.cpp @@ -21,7 +21,6 @@ #include using namespace std; -#include #include #include #define BLOCK_SIZE 16 diff --git a/tools/playground/hellhole.cpp b/tools/playground/hellhole.cpp index cc12554be..340d5169e 100644 --- a/tools/playground/hellhole.cpp +++ b/tools/playground/hellhole.cpp @@ -1,1283 +1,1283 @@ -// Burn a hole straight to hell! - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -using namespace std; - -#include -#include -#include -using namespace DFHack; - - -#ifdef LINUX_BUILD -#include -void waitmsec (int delay) -{ - usleep(delay); -} -#else -#include -void waitmsec (int delay) -{ - Sleep(delay); -} -#endif - -#define minmax(MinV,V,MaxV) (max((MinV),min((MaxV),(V)))) - -//User interaction enums. -//Pit Type (these only have meaning within hellhole, btw) -#define PITTYPEMACRO \ - X(pitTypeChasm,"Bottomless Chasm" ) \ - X(pitTypeEerie,"Bottomless Eerie Pit" ) \ - X(pitTypeFloor,"Pit with floor" ) \ - X(pitTypeSolid,"Solid Pillar" ) \ - X(pitTypeOasis,"Oasis Pit (ends at magma, no hell access)" ) \ - X(pitTypeOPool,"Oasis Pool, with partial aquifer (default 5 z-levels)" ) \ - X(pitTypeMagma,"Magma Pit (similar to volcano, no hell access)" ) \ - X(pitTypeMPool,"Magma Pool (default 5 z-levels)" ) -//end PITTYPEMACRO - -#define X(name,desc) name, -enum e_pitType -{ - pitTypeInvalid=-1, - PITTYPEMACRO - pitTypeCount, -}; -#undef X - - -#define X(name,desc) desc, -const char * pitTypeDesc[pitTypeCount+1] = -{ - PITTYPEMACRO - "" -}; -#undef X - - - - -int getyesno( const char * msg , int default_value ) -{ - const int bufferlen=4; - static char buf[bufferlen]; - memset(buf,0,bufferlen); - while (-1) - { - if (msg) printf("\n%s (default=%s)\n:" , msg , (default_value?"yes":"no") ); - fflush(stdin); - fgets(buf,bufferlen,stdin); - switch (buf[0]) - { - case 0: - case 0x0d: - case 0x0a: - return default_value; - case 'y': - case 'Y': - case 'T': - case 't': - case '1': - return -1; - case 'n': - case 'N': - case 'F': - case 'f': - case '0': - return 0; - } - } - return 0; -} - -int getint( const char * msg , int min, int max, int default_value ) { - const int bufferlen=16; - static char buf[bufferlen]; - int n=0; - memset(buf,0,bufferlen); - while (-1) - { - if (msg) printf("\n%s (default=%d)\n:" , msg , default_value); - fflush(stdin); - fgets(buf,bufferlen,stdin); - if ( !buf[0] || 0x0a==buf[0] || 0x0d==buf[0] ) - { - return default_value; - } - if ( sscanf(buf,"%d", &n) ) - { - if (n>=min && n<=max ) - { - return n; - } - } - } -} - -int getint( const char * msg , int min, int max ) -{ - const int bufferlen=16; - static char buf[bufferlen]; - int n=0; - memset(buf,0,bufferlen); - while (-1) - { - if (msg) - { - printf("\n%s \n:" , msg ); - } - fflush(stdin); - fgets(buf,bufferlen,stdin); - - if ( !buf[0] || 0x0a==buf[0] || 0x0d==buf[0] ) - { - continue; - } - if ( sscanf(buf,"%d", &n) ) - { - if (n>=min && n<=max ) - { - return n; - } - } - } -} - - - -//Interactive, get pit type from user -e_pitType selectPitType() -{ - while ( -1 ) - { - printf("Enter the type of hole to dig:\n" ); - for (int n=0;n=0, replace with v. -//Returns number of neighbors found. -int checkneighbors(unsigned char pattern[16][16], int x, int y, unsigned char n , char v ) -{ - int r=0; - if ( x>0 && y>0 && n==pattern[x-1][y-1] ) - { - ++r; - if (v>-1) pattern[x][y]=v; - } - if ( x>0 && n==pattern[x-1][y ] ) - { - ++r; - if (v>-1) pattern[x][y]=v; - } - if ( y>0 && n==pattern[x ][y-1] ) - { - ++r; - if (v>-1) pattern[x][y]=v; - } - if ( x<15 && n==pattern[x+1][y ] ) - { - ++r; - if (v>-1) pattern[x][y]=v; - } - if ( x<15 && y>0 && n==pattern[x+1][y-1] ) - { - ++r; - if (v>-1) pattern[x][y]=v; - } - if ( x<15 && y<15 && n==pattern[x+1][y+1] ) - { - ++r; - if (v>-1) pattern[x][y]=v; - } - if ( y<15 && n==pattern[x ][y+1] ) - { - ++r; - if (v>-1) pattern[x][y]=v; - } - if ( x>0 && y<15 && n==pattern[x-1][y+1] ) - { - ++r; - if (v>-1) pattern[x][y]=v; - } - return r; -} -//convenience -int checkneighbors(unsigned char pattern[16][16], int x, int y, unsigned char n ) -{ - return checkneighbors(pattern,x,y,n,-1); -} - -void settileat(unsigned char pattern[16][16], const unsigned char needle, const unsigned char v, const int index ) -{ - int ok=0; - int safety=256*256; - int y,x,i=0; - //Scan for sequential index - while ( !ok && --safety ) - { - for (y=0 ; !ok && y<16 ; ++y ) - { - for (x=0 ; !ok && x<16 ; ++x ) - { - if ( needle==pattern[x][y] ) - { - ++i; - if ( index==i ) - { - //Got it! - pattern[x][y]=v; - ok=-1; - } - } - } - } - } -} - - -//FIXME: good candidate for adding to dfhack. Maybe the Maps should have those cached so they can be queried? -//Is a given feature present at the given tile? -int isfeature( - vector global_features, - std::map > local_features, - const mapblock40d &block, const planecoord &pc, const int x, const int y, const e_feature Feat -) -{ - //const TileRow * tp; - //tp = getTileTypeP(block.tiletypes[x][y]); - const t_designation * d; - d = &block.designation[x][y]; - - if ( block.local_feature > -1 && d->bits.feature_local ) { - if ( Feat==local_features[pc][block.local_feature]->type ) return Feat; - } - if ( block.global_feature > -1 && d->bits.feature_global ) { - if ( Feat==global_features[block.global_feature].type ) return Feat; - } - - return 0; -} - -// FIXME: use block cache, break into manageable bits -int main (void) -{ - srand ( (unsigned int)time(NULL) ); - - //Message of intent - cout << - "DF Hole" << endl << - "This tool will instantly dig a chasm, pit, pipe, etc through hell, wherever your cursor is." << endl << - "This can not be undone! End program now if you don't want hellish fun." << endl - ; - - //User selection of settings should have it own routine, a structure for settings, I know - //sloppy mess, but this is just a demo utility. - - //Pit Types. - e_pitType pittype = selectPitType(); - - //Here are all the settings. - //Default values are set here. - int pitdepth=0; - int roof=-1; - int holeradius=6; - int wallthickness=1; - int wallpillar=1; - int holepillar=1; - int exposehell = 0; - int fillmagma=0; - int fillwater=0; - int stopatmagma=0; - int exposemagma=0; - int aquify=0; - - //The Tile Type to use for the walls lining the hole - //263 is semi-molten rock, 331 is obsidian - uint32_t whell=263, wmolten=263, wmagma=331, wcave=331; - //The Tile Type to use for the hole's floor at bottom of the map - //35 is chasm, 42 is eerie pit , 340 is obsidian floor, 344 is featstone floor, 264 is 'magma flow' floor - uint32_t floor=35, cap=340; - int floorvar=0; - - - //Modify default settings based on pit type. - switch ( pittype ) - { - case pitTypeChasm: - floor=35; - break; - case pitTypeEerie: - floor=42; - break; - case pitTypeFloor: - floor=344; - floorvar=3; - break; - case pitTypeSolid: - holeradius=0; - wallthickness=7; - wallpillar=4; - break; - case pitTypeOasis: - stopatmagma=-1; - fillwater=-1; - holeradius=5; - wallthickness=2; - //aquify=-1; - floor=340; - floorvar=3; - break; - case pitTypeOPool: - pitdepth=5; - fillwater=-1; - holeradius=5; - wallthickness=2; - //aquify=-1; - floor=340; - floorvar=3; - break; - case pitTypeMagma: - stopatmagma=-1; - exposemagma=-1; - wallthickness=2; - fillmagma=-1; - floor=264; - break; - case pitTypeMPool: - pitdepth=5; - wallthickness=2; - fillmagma=-1; - floor=340; - floorvar=3; - break; - } - - - //Should tiles be revealed? - int reveal=0; - - - int accept = getyesno("Use default settings?",1); - - while ( !accept ) - { - //Pit Depth - pitdepth = getint( "Enter max depth (0 for bottom of map)", 0, INT_MAX, pitdepth ); - - //Hole Size - holeradius = getint( "Enter hole radius, 0 to 16", 0, 16, holeradius ); - - //Wall thickness - wallthickness = getint( "Enter wall thickness, 0 to 16", 0, 16, wallthickness ); - - //Obsidian Pillars - holepillar = getint( "Number of Obsidian Pillars in hole, 0 to 255", 0, 255, holepillar ); - wallpillar = getint( "Number of Obsidian Pillars in wall, 0 to 255", 0, 255, wallpillar ); - - //Open Hell? - exposehell=getyesno("Expose the pit to hell (no walls in hell)?",exposehell); - - //Stop when magma sea is hit? - stopatmagma=getyesno("Stop at magma sea?",stopatmagma); - exposemagma=getyesno("Expose magma sea (no walls in magma)?",exposemagma); - - //Fill? - fillmagma=getyesno("Fill with magma?",fillmagma); - if (fillmagma) aquify=fillwater=0; - fillwater=getyesno("Fill with water?",fillwater); - //aquify=getyesno("Aquifer?",aquify); - - - /////////////////////////////////////////////////////////////////////////////////////////////// - //Print settings. - //If a settings struct existed, this could be in a routine - printf("Using Settings:\n"); - printf("Pit Type......: %d = %s\n", pittype, pitTypeDesc[pittype]); - printf("Depth.........: %d\n", pitdepth); - printf("Hole Radius...: %d\n", holeradius); - printf("Wall Thickness: %d\n", wallthickness); - printf("Pillars, Hole.: %d\n", holepillar); - printf("Pillars, Wall.: %d\n", wallpillar); - printf("Expose Hell...: %c\n", (exposehell?'Y':'N') ); - printf("Stop at Magma.: %c\n", (stopatmagma?'Y':'N') ); - printf("Expose Magma..: %c\n", (exposemagma?'Y':'N') ); - printf("Magma Fill....: %c\n", (fillmagma?'Y':'N') ); - printf("Water Fill....: %c\n", (fillwater?'Y':'N') ); - printf("Aquifer.......: %c\n", (aquify?'Y':'N') ); - - accept = getyesno("Accept these settings?",1); - } - - - int64_t n; - uint32_t x_max,y_max,z_max; - - - //Pattern to dig - unsigned char pattern[16][16]; - - - for (int regen=1;regen; ) - { - regen=0; - - memset(pattern,0,sizeof(pattern)); - - //Calculate a randomized circle. - //These values found through experimentation. - int x=0, y=0, n=0; - - //Two concentric irregular circles - //Outer circle, solid. - if ( wallthickness ) - { - drawcircle(holeradius+wallthickness, pattern, 2); - } - //Inner circle, hole. - if ( holeradius ) - { - drawcircle(holeradius, pattern, 1); - } - - - //Post-process to be certain the wall totally encloses hole. - if (wallthickness) - { - for (y=0;y<16;++y) - { - for (x=0;x<16;++x) - { - if ( 1==pattern[x][y] ) - { - //No hole at edges. - if ( x<1 || x>14 || y<1 || y>14 ) - { - pattern[x][y]=2; - } - } - else if ( 0==pattern[x][y] ) - { - //check neighbors - checkneighbors( pattern , x,y, 1, 2); - } - } - } - } - - //Makes sure that somewhere random gets a vertical pillar of rock which is safe - //to dig stairs down, to permit access to anywhere within the pit from the top. - for (n=holepillar; n ; --n) - { - settileat( pattern , 1 , 3 , rand()&255 ); - } - for (n=wallpillar; n ; --n) - { - settileat( pattern , 2 , 3 , rand()&255 ); - } - - //Note: - //At this point, the pattern holds: - //0 for all tiles which will be ignored. - //1 for all tiles set to empty pit space. - //2 for all normal walls. - //3 for the straight obsidian top-to-bottom wall. - //4 is randomized between wall or floor (!not implemented!) - - printf("\nPattern:\n"); - const char patternkey[] = ".cW!?567890123"; - - //Print the pattern - for (y=0;y<16;++y) - { - for (x=0;x<16;++x) - { - cout << patternkey[ pattern[x][y] ]; - } - cout << endl; - } - cout << endl; - - regen = !getyesno("Acceptable Pattern?",1); - } - - //Post-process settings to fix problems here - if (pitdepth<1) - { - pitdepth=INT_MAX; - } - - - /////////////////////////////////////////////////////////////////////////////////////////////// - - - cerr << "Loading memory map..." << endl; - - //Connect to DF! - DFHack::ContextManager DFMgr("Memory.xml"); - DFHack::Context *DF = DFMgr.getSingleContext(); - - - - //Init - cerr << "Attaching to DF..." << endl; - try - { - DF->Attach(); - } - catch (exception& e) - { - cerr << e.what() << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - - // init the map - DFHack::Maps *Mapz = DF->getMaps(); - if (!Mapz->Start()) - { - cerr << "Can't init map. Exiting." << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - - Mapz->getSize(x_max,y_max,z_max); - - - //Get cursor - int32_t cursorX, cursorY, cursorZ; - DFHack::Position *Pos = DF->getPosition(); - Pos->getCursorCoords(cursorX,cursorY,cursorZ); - if (-30000==cursorX) - { - cout << "No cursor position found. Exiting." << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - - //Block coordinates - int32_t bx=cursorX/16, by=cursorY/16, bz=cursorZ; - //Tile coordinates within block - int32_t tx=cursorX%16, ty=cursorY%16, tz=cursorZ; - - /* - //Access the DF interface to pause the game. - //Copied from the reveal tool. - DFHack::Gui *Gui =DF->getGui(); - cout << "Pausing..." << endl; - Gui->SetPauseState(true); - DF->Resume(); - waitmsec(1000); - DF->Suspend(); - */ - - //Verify that every z-level at this location exists. - for (int32_t Z = 0; Z<= bz ;Z++) - { - if ( ! Mapz->isValidBlock(bx,by,Z) ) - { - cout << "This block does't exist! Exiting." << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - } - - //Get all the map features. - vector global_features; - if (!Mapz->ReadGlobalFeatures(global_features)) - { - cout << "Couldn't load global features! Probably a version problem." << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - - std::map > local_features; - if (!Mapz->ReadLocalFeatures(local_features)) - { - cout << "Couldn't load local features! Probably a version problem." << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - - //Get info on current tile, to determine how to generate the pit - mapblock40d topblock; - Mapz->ReadBlock40d( bx, by, bz , &topblock ); - //Related block info - planecoord pc; - pc.dim.x=bx; - pc.dim.y=by; - mapblock40d block; - const TileRow * tp; - t_designation * d; - - ////////////////////////////////////// - //From top to bottom, dig this thing. - ////////////////////////////////////// - - //Top level, cap. - //Might make this an option in the future - //For now, no wall means no cap. - if (wallthickness) - { - Mapz->ReadBlock40d( bx, by, bz , &block ); - for (uint32_t x=0;x<16;++x) - { - for (uint32_t y=0;y<16;++y) - { - if ( (pattern[x][y]>1) || (roof && pattern[x][y]) ) - { - tp = getTileTypeP(block.tiletypes[x][y]); - d = &block.designation[x][y]; - //Only modify this level if it's 'empty' - if ( EMPTY != tp->c && RAMP_TOP != tp->c && STAIR_DOWN != tp->c && DFHack::TILE_STREAM_TOP != tp->s) - { - continue; - } - - //Need a floor for empty space. - if (reveal) - { - d->bits.hidden = 0; //topblock.designation[x][y].bits.hidden; - } - //Always clear the dig designation. - d->bits.dig = designation_no; - //unlock fluids, so they fall down the pit. - d->bits.flow_forbid = d->bits.liquid_static=0; - block.blockflags.bits.liquid_1 = block.blockflags.bits.liquid_2 = 1; - //Remove aquifer, to prevent bugginess - d->bits.water_table=0; - //Set the tile. - block.tiletypes[x][y] = cap + rand()%4; - } - } - } - //Write the block. - Mapz->WriteBlockFlags(bx,by,bz, block.blockflags ); - Mapz->WriteDesignations(bx,by,bz, &block.designation ); - Mapz->WriteTileTypes(bx,by,bz, &block.tiletypes ); - Mapz->WriteDirtyBit(bx,by,bz,1); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - //All levels in between. - int done=0; - uint32_t t,v; - int32_t z = bz-1; - int32_t bottom = max(0,bz-pitdepth-1); - assert( bottom>=0 && bottom<=bz ); - for ( ; !done && z>=bottom ; --z) - { - int watercount=0; - int magmacount=0; - int moltencount=0; - int rockcount=0; - int veincount=0; - int emptycount=0; - int hellcount=0; - int templecount=0; - int adamcount=0; - int featcount=0; - int tpat; - - cout << z << endl; - assert( Mapz->isValidBlock(bx,by,z) ); - if (!Mapz->ReadBlock40d( bx, by, z , &block )) - { - cout << "Bad block! " << bx << "," << by << "," << z << endl; - } - - //Pre-process this z-level, to get some tile statistics. - for (int32_t x=0;x<16;++x) - { - for (int32_t y=0;y<16;++y) - { - t=0; - tp = getTileTypeP(block.tiletypes[x][y]); - d = &block.designation[x][y]; - tpat=pattern[x][y]; - - //Tile type material categories - switch ( tp->m ) - { - case AIR: - ++emptycount; - break; - case MAGMA: - ++moltencount; - break; - case VEIN: - ++veincount; - break; - case FEATSTONE: - case HFS: - case OBSIDIAN: - //basicly, ignored. - break; - default: - if ( EMPTY == tp->c || RAMP_TOP == tp->c || STAIR_DOWN == tp->c ) - { - ++emptycount; - } - else - { - ++rockcount; - } - break; - } - - //Magma and water - if ( d->bits.flow_size ) - { - if (d->bits.liquid_type) - { - ++magmacount; - } - else - { - ++watercount; - } - } - - - //Check for Features - if ( block.local_feature > -1 || block.global_feature > -1 ) - { - //Count tiles which actually are in the feature. - //It is possible for a block to have a feature, but no tiles to be feature. - if ( d->bits.feature_global || d->bits.feature_local ) - { - //All features - ++featcount; - - if ( d->bits.feature_global && d->bits.feature_local ) - { - cout << "warn:tile is global and local at same time!" << endl; - } - - n=0; - if ( block.global_feature > -1 && d->bits.feature_global ) - { - n=global_features[block.global_feature].type; - switch ( n ) - { - case feature_Other: - //no count - break; - case feature_Adamantine_Tube: - ++adamcount; - break; - case feature_Underworld: - ++hellcount; - break; - case feature_Hell_Temple: - ++templecount; - break; - default: - //something here. for debugging, it may be interesting to know. - if (n) cout << '(' << n << ')'; - } - } - - n=0; - if ( block.local_feature > -1 && d->bits.feature_local ) - { - n=local_features[pc][block.local_feature]->type; - switch ( n ) - { - case feature_Other: - //no count - break; - case feature_Adamantine_Tube: - ++adamcount; - break; - case feature_Underworld: - ++hellcount; - break; - case feature_Hell_Temple: - ++templecount; - break; - default: - //something here. for debugging, it may be interesting to know. - if (n) cout << '[' << n << ']'; - } - } - } - } - } - } - - - //If stopping at magma, and no no non-feature stone in this layer, and magma found, then we're either at - //or below the magma sea / molten rock. - if ( stopatmagma && (moltencount || magmacount) && (!exposemagma || !rockcount) ) - { - //If not exposing magma, quit at the first sign of magma. - //If exposing magma, quite once magma is exposed. - done=-1; - } - - - ///////////////////////////////////////////////////////////////////////////////////////////////// - //Some checks, based on settings and stats collected - //First check, are we at illegal depth? - if ( !done && hellcount && stopatmagma ) - { - //Panic! - done=-1; - tpat=0; - cout << "error: illegal breach of hell!" << endl; - } - - ///////////////////////////////////////////////////////////////////////////////////////////////// - //Actually process the current z-level. - //These loops do the work. - for (int32_t x=0;!done && x<16;++x) - { - for (int32_t y=0;!done && y<16;++y) - { - t=0; - tp = getTileTypeP(block.tiletypes[x][y]); - d = &block.designation[x][y]; - tpat=pattern[x][y]; - - //Up front, remove aquifer, to prevent bugginess - //It may be added back if aquify is set. - d->bits.water_table=0; - - //Change behaviour based on settings and stats from this z-level - - //In hell? - if ( tpat && tpat!=3 && isfeature(global_features, local_features,block,pc,x,y,feature_Underworld ) ) - { - if ( exposehell ) - { - tpat=0; - } - } - - //Expose magma? - if ( tpat && tpat!=3 && exposemagma ) - { - //Leave certain tiles unchanged. - switch ( tp->m ) - { - case HFS: - case FEATSTONE: - case MAGMA: - tpat=0; - default: - break; - } - //Adamantine may be left unchanged... - if ( isfeature(global_features, local_features,block,pc,x,y,feature_Adamantine_Tube ) ) - { - tpat=0; - } - //Leave magma sea unchanged. - if ( d->bits.flow_size && d->bits.liquid_type) - { - tpat=0; - } - } - - - //For all situations... - //Special modification for walls, always for adamantine. - if ( isfeature(global_features, local_features,block,pc,x,y,feature_Adamantine_Tube ) ) - { - if ( 2==pattern[x][y] || 3==pattern[x][y] ) - { - tpat=2; - } - } - - - //Border or space? - switch (tpat) - { - case 0: - continue; - break; - case 1: - //Empty Space - t=32; - //d->bits.light = topblock.designation[x][y].bits.light; - //d->bits.skyview = topblock.designation[x][y].bits.skyview; - //d->bits.subterranean = topblock.designation[x][y].bits.subterranean; - - //Erase special markers? - //d->bits.feature_global = d->bits.feature_local = 0; - - //Water? Magma? - if (fillmagma || fillwater) - { - d->bits.flow_size=7; - d->bits.water_stagnant = false; - d->bits.water_salt = false; - if (fillmagma) - { - d->bits.liquid_type=liquid_magma; - } - else - { - d->bits.liquid_type=liquid_water; - } - } - else - { - //Otherwise, remove all liquids. - d->bits.flow_size=0; - d->bits.water_stagnant = false; - d->bits.water_salt = false; - d->bits.liquid_type = liquid_water; - } - - break; - case 2: - //Wall. - //First guess based on current material - switch ( tp->m ) - { - case OBSIDIAN: - t=wmagma; - break; - case MAGMA: - t=wmolten; - break; - case HFS: - //t=whell; - break; - case VEIN: - t=440; //Solid vein block - break; - case FEATSTONE: - t=335; //Solid feature stone block - break; - default: - t=wcave; - } - //Adamantine (a local feature) trumps veins. - { - //Local Feature? - if ( block.local_feature > -1 ) - { - switch ( n=local_features[pc][block.local_feature]->type ) - { - case feature_Underworld: - case feature_Hell_Temple: - //Only adopt these if there is no global feature present - if ( block.global_feature >-1 ) - { - break; - } - case feature_Adamantine_Tube: - //Always for adamantine, sometimes for others - //Whatever the feature is made of. "featstone wall" - d->bits.feature_global = 0; - d->bits.feature_local = 1; - t=335; - break; - } - } - //Global Feature? - else if (block.global_feature > -1 && !d->bits.feature_local ) - { - switch ( n=global_features[block.global_feature].type ) - { - case feature_Adamantine_Tube: - case feature_Underworld: - case feature_Hell_Temple: - //Whatever the feature is made of. "featstone wall" - d->bits.feature_global = 1; - t=335; - break; - } - } - } - - //Erase any liquids, as they cause problems. - d->bits.flow_size=0; - d->bits.water_stagnant = false; - d->bits.water_salt = false; - d->bits.liquid_type=liquid_water; - - //Placing an aquifer? - //(bugged, these aquifers don't generate water!) - if ( aquify ) - { - //Only normal stone types can be aquified - if ( tp->m!=MAGMA && tp->m!=FEATSTONE && tp->m!=HFS ) - { - //Only place next to the hole. - //If no hole, place in middle. - if ( checkneighbors(pattern,x,y,1) || (7==x && 7==y) ) - { - d->bits.water_table = 1; - //t=265; //soil wall - } - } - } - break; - case 3: - ////No obsidian walls on bottom of map! - //if(z<1 && (d->bits.feature_global || d->bits.feature_local) ) { - // t=335; - //} - - //Special wall, always sets to obsidian, to give a stairway - t=331; - - //Erase special markers - d->bits.feature_global = d->bits.feature_local = 0; - - //Erase any liquids, as they cause problems. - d->bits.flow_size=0; - d->bits.water_stagnant = false; - d->bits.water_salt = false; - d->bits.liquid_type=liquid_water; - break; - default: - cout << ".error,bad pattern."; - } - - //For all tiles. - if (reveal) - { - d->bits.hidden = 0; //topblock.designation[x][y].bits.hidden; - } - //Always clear the dig designation. - d->bits.dig=designation_no; - //Make it underground, because it is capped - d->bits.subterranean=1; - d->bits.light=0; - d->bits.skyview=0; - //unlock fluids, so they fall down the pit. - d->bits.flow_forbid = d->bits.liquid_static=0; - block.blockflags.bits.liquid_1 = block.blockflags.bits.liquid_2 = 1; - //Set the tile. - block.tiletypes[x][y] = t; - - } - } - - //Write the block. - Mapz->WriteBlockFlags(bx,by,z, block.blockflags ); - Mapz->WriteDesignations(bx,by,z, &block.designation ); - Mapz->WriteTileTypes(bx,by,z, &block.tiletypes ); - Mapz->WriteDirtyBit(bx,by,z,1); - - } - - //Re-process the last z-level handled above. - z++; - assert( z>=0 ); - - - /////////////////////////////////////////////////////////////////////////////////////////////// - //The bottom level is special. - if (-1) - { - if (!Mapz->ReadBlock40d( bx, by, z , &block )) - { - cout << "Bad block! " << bx << "," << by << "," << z << endl; - } - for (uint32_t x=0;x<16;++x) - { - for (uint32_t y=0;y<16;++y) - { - t=floor; - v=floorvar; - tp = getTileTypeP(block.tiletypes[x][y]); - d = &block.designation[x][y]; - - if ( exposehell ) - { - //Leave hell tiles unchanged when exposing hell. - if ( isfeature(global_features,local_features,block,pc,x,y,feature_Underworld) ) - { - continue; - } - } - - //Does expose magma need anything at this level? - if ( exposemagma && stopatmagma ) - { - continue; - } - - switch (pattern[x][y]) - { - case 0: - continue; - break; - case 1: - //Empty becomes floor. - - //Base floor type on the z-level first, features, then tile type. - if (!z) { - //Bottom of map, use the floor specified, always. - break; - } - - ////Only place floor where ground is already solid when exposing - //if( EMPTY == tp->c || RAMP_TOP == tp->c || STAIR_DOWN == tp->c ){ - // continue; - //} - - if ( d->bits.feature_global || d->bits.feature_global ) { - //Feature Floor! - t=344; - break; - } - - //Tile material check. - switch ( tp->m ) - { - case OBSIDIAN: - t=340; - v=3; - break; - case MAGMA: - v=0; - t=264; //magma flow - break; - case HFS: - //should only happen at bottom of map - break; - case VEIN: - t=441; //vein floor - v=3; - break; - case FEATSTONE: - t=344; - v=3; - break; - } - - break; - case 2: - case 3: - //Walls already drawn. - //Ignore. - continue; - break; - } - - //For all tiles. - if (reveal) d->bits.hidden = 0; //topblock.designation[x][y].bits.hidden; - //Always clear the dig designation. - d->bits.dig=designation_no; - //unlock fluids - d->bits.flow_forbid = d->bits.liquid_static=0; - block.blockflags.bits.liquid_1 = block.blockflags.bits.liquid_2 = 1; - - //Set the tile. - block.tiletypes[x][y] = t + ( v ? rand()&v : 0 ); - } - } - //Write the block. - Mapz->WriteBlockFlags(bx,by,z, block.blockflags ); - Mapz->WriteDesignations(bx,by,z, &block.designation ); - Mapz->WriteTileTypes(bx,by,z, &block.tiletypes ); - Mapz->WriteDirtyBit(bx,by,z,1); - } - - DF->Detach(); -#ifndef LINUX_BUILD - cout << "Done. Press any key to continue" << endl; - cin.ignore(); -#endif - return 0; -} +// Burn a hole straight to hell! + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#include +#include +#include +using namespace DFHack; + + +#ifdef LINUX_BUILD +#include +void waitmsec (int delay) +{ + usleep(delay); +} +#else +#include +void waitmsec (int delay) +{ + Sleep(delay); +} +#endif + +#define minmax(MinV,V,MaxV) (max((MinV),min((MaxV),(V)))) + +//User interaction enums. +//Pit Type (these only have meaning within hellhole, btw) +#define PITTYPEMACRO \ + X(pitTypeChasm,"Bottomless Chasm" ) \ + X(pitTypeEerie,"Bottomless Eerie Pit" ) \ + X(pitTypeFloor,"Pit with floor" ) \ + X(pitTypeSolid,"Solid Pillar" ) \ + X(pitTypeOasis,"Oasis Pit (ends at magma, no hell access)" ) \ + X(pitTypeOPool,"Oasis Pool, with partial aquifer (default 5 z-levels)" ) \ + X(pitTypeMagma,"Magma Pit (similar to volcano, no hell access)" ) \ + X(pitTypeMPool,"Magma Pool (default 5 z-levels)" ) +//end PITTYPEMACRO + +#define X(name,desc) name, +enum e_pitType +{ + pitTypeInvalid=-1, + PITTYPEMACRO + pitTypeCount, +}; +#undef X + + +#define X(name,desc) desc, +const char * pitTypeDesc[pitTypeCount+1] = +{ + PITTYPEMACRO + "" +}; +#undef X + + + + +int getyesno( const char * msg , int default_value ) +{ + const int bufferlen=4; + static char buf[bufferlen]; + memset(buf,0,bufferlen); + while (-1) + { + if (msg) printf("\n%s (default=%s)\n:" , msg , (default_value?"yes":"no") ); + fflush(stdin); + fgets(buf,bufferlen,stdin); + switch (buf[0]) + { + case 0: + case 0x0d: + case 0x0a: + return default_value; + case 'y': + case 'Y': + case 'T': + case 't': + case '1': + return -1; + case 'n': + case 'N': + case 'F': + case 'f': + case '0': + return 0; + } + } + return 0; +} + +int getint( const char * msg , int min, int max, int default_value ) { + const int bufferlen=16; + static char buf[bufferlen]; + int n=0; + memset(buf,0,bufferlen); + while (-1) + { + if (msg) printf("\n%s (default=%d)\n:" , msg , default_value); + fflush(stdin); + fgets(buf,bufferlen,stdin); + if ( !buf[0] || 0x0a==buf[0] || 0x0d==buf[0] ) + { + return default_value; + } + if ( sscanf(buf,"%d", &n) ) + { + if (n>=min && n<=max ) + { + return n; + } + } + } +} + +int getint( const char * msg , int min, int max ) +{ + const int bufferlen=16; + static char buf[bufferlen]; + int n=0; + memset(buf,0,bufferlen); + while (-1) + { + if (msg) + { + printf("\n%s \n:" , msg ); + } + fflush(stdin); + fgets(buf,bufferlen,stdin); + + if ( !buf[0] || 0x0a==buf[0] || 0x0d==buf[0] ) + { + continue; + } + if ( sscanf(buf,"%d", &n) ) + { + if (n>=min && n<=max ) + { + return n; + } + } + } +} + + + +//Interactive, get pit type from user +e_pitType selectPitType() +{ + while ( -1 ) + { + printf("Enter the type of hole to dig:\n" ); + for (int n=0;n=0, replace with v. +//Returns number of neighbors found. +int checkneighbors(unsigned char pattern[16][16], int x, int y, unsigned char n , char v ) +{ + int r=0; + if ( x>0 && y>0 && n==pattern[x-1][y-1] ) + { + ++r; + if (v>-1) pattern[x][y]=v; + } + if ( x>0 && n==pattern[x-1][y ] ) + { + ++r; + if (v>-1) pattern[x][y]=v; + } + if ( y>0 && n==pattern[x ][y-1] ) + { + ++r; + if (v>-1) pattern[x][y]=v; + } + if ( x<15 && n==pattern[x+1][y ] ) + { + ++r; + if (v>-1) pattern[x][y]=v; + } + if ( x<15 && y>0 && n==pattern[x+1][y-1] ) + { + ++r; + if (v>-1) pattern[x][y]=v; + } + if ( x<15 && y<15 && n==pattern[x+1][y+1] ) + { + ++r; + if (v>-1) pattern[x][y]=v; + } + if ( y<15 && n==pattern[x ][y+1] ) + { + ++r; + if (v>-1) pattern[x][y]=v; + } + if ( x>0 && y<15 && n==pattern[x-1][y+1] ) + { + ++r; + if (v>-1) pattern[x][y]=v; + } + return r; +} +//convenience +int checkneighbors(unsigned char pattern[16][16], int x, int y, unsigned char n ) +{ + return checkneighbors(pattern,x,y,n,-1); +} + +void settileat(unsigned char pattern[16][16], const unsigned char needle, const unsigned char v, const int index ) +{ + int ok=0; + int safety=256*256; + int y,x,i=0; + //Scan for sequential index + while ( !ok && --safety ) + { + for (y=0 ; !ok && y<16 ; ++y ) + { + for (x=0 ; !ok && x<16 ; ++x ) + { + if ( needle==pattern[x][y] ) + { + ++i; + if ( index==i ) + { + //Got it! + pattern[x][y]=v; + ok=-1; + } + } + } + } + } +} + + +//FIXME: good candidate for adding to dfhack. Maybe the Maps should have those cached so they can be queried? +//Is a given feature present at the given tile? +int isfeature( + vector global_features, + std::map > local_features, + const mapblock40d &block, const planecoord &pc, const int x, const int y, const e_feature Feat +) +{ + //const TileRow * tp; + //tp = getTileTypeP(block.tiletypes[x][y]); + const t_designation * d; + d = &block.designation[x][y]; + + if ( block.local_feature > -1 && d->bits.feature_local ) { + if ( Feat==local_features[pc][block.local_feature]->type ) return Feat; + } + if ( block.global_feature > -1 && d->bits.feature_global ) { + if ( Feat==global_features[block.global_feature].type ) return Feat; + } + + return 0; +} + +// FIXME: use block cache, break into manageable bits +int main (void) +{ + srand ( (unsigned int)time(NULL) ); + + //Message of intent + cout << + "DF Hole" << endl << + "This tool will instantly dig a chasm, pit, pipe, etc through hell, wherever your cursor is." << endl << + "This can not be undone! End program now if you don't want hellish fun." << endl + ; + + //User selection of settings should have it own routine, a structure for settings, I know + //sloppy mess, but this is just a demo utility. + + //Pit Types. + e_pitType pittype = selectPitType(); + + //Here are all the settings. + //Default values are set here. + int pitdepth=0; + int roof=-1; + int holeradius=6; + int wallthickness=1; + int wallpillar=1; + int holepillar=1; + int exposehell = 0; + int fillmagma=0; + int fillwater=0; + int stopatmagma=0; + int exposemagma=0; + int aquify=0; + + //The Tile Type to use for the walls lining the hole + //263 is semi-molten rock, 331 is obsidian + uint32_t whell=263, wmolten=263, wmagma=331, wcave=331; + //The Tile Type to use for the hole's floor at bottom of the map + //35 is chasm, 42 is eerie pit , 340 is obsidian floor, 344 is featstone floor, 264 is 'magma flow' floor + uint32_t floor=35, cap=340; + int floorvar=0; + + + //Modify default settings based on pit type. + switch ( pittype ) + { + case pitTypeChasm: + floor=35; + break; + case pitTypeEerie: + floor=42; + break; + case pitTypeFloor: + floor=344; + floorvar=3; + break; + case pitTypeSolid: + holeradius=0; + wallthickness=7; + wallpillar=4; + break; + case pitTypeOasis: + stopatmagma=-1; + fillwater=-1; + holeradius=5; + wallthickness=2; + //aquify=-1; + floor=340; + floorvar=3; + break; + case pitTypeOPool: + pitdepth=5; + fillwater=-1; + holeradius=5; + wallthickness=2; + //aquify=-1; + floor=340; + floorvar=3; + break; + case pitTypeMagma: + stopatmagma=-1; + exposemagma=-1; + wallthickness=2; + fillmagma=-1; + floor=264; + break; + case pitTypeMPool: + pitdepth=5; + wallthickness=2; + fillmagma=-1; + floor=340; + floorvar=3; + break; + } + + + //Should tiles be revealed? + int reveal=0; + + + int accept = getyesno("Use default settings?",1); + + while ( !accept ) + { + //Pit Depth + pitdepth = getint( "Enter max depth (0 for bottom of map)", 0, INT_MAX, pitdepth ); + + //Hole Size + holeradius = getint( "Enter hole radius, 0 to 16", 0, 16, holeradius ); + + //Wall thickness + wallthickness = getint( "Enter wall thickness, 0 to 16", 0, 16, wallthickness ); + + //Obsidian Pillars + holepillar = getint( "Number of Obsidian Pillars in hole, 0 to 255", 0, 255, holepillar ); + wallpillar = getint( "Number of Obsidian Pillars in wall, 0 to 255", 0, 255, wallpillar ); + + //Open Hell? + exposehell=getyesno("Expose the pit to hell (no walls in hell)?",exposehell); + + //Stop when magma sea is hit? + stopatmagma=getyesno("Stop at magma sea?",stopatmagma); + exposemagma=getyesno("Expose magma sea (no walls in magma)?",exposemagma); + + //Fill? + fillmagma=getyesno("Fill with magma?",fillmagma); + if (fillmagma) aquify=fillwater=0; + fillwater=getyesno("Fill with water?",fillwater); + //aquify=getyesno("Aquifer?",aquify); + + + /////////////////////////////////////////////////////////////////////////////////////////////// + //Print settings. + //If a settings struct existed, this could be in a routine + printf("Using Settings:\n"); + printf("Pit Type......: %d = %s\n", pittype, pitTypeDesc[pittype]); + printf("Depth.........: %d\n", pitdepth); + printf("Hole Radius...: %d\n", holeradius); + printf("Wall Thickness: %d\n", wallthickness); + printf("Pillars, Hole.: %d\n", holepillar); + printf("Pillars, Wall.: %d\n", wallpillar); + printf("Expose Hell...: %c\n", (exposehell?'Y':'N') ); + printf("Stop at Magma.: %c\n", (stopatmagma?'Y':'N') ); + printf("Expose Magma..: %c\n", (exposemagma?'Y':'N') ); + printf("Magma Fill....: %c\n", (fillmagma?'Y':'N') ); + printf("Water Fill....: %c\n", (fillwater?'Y':'N') ); + printf("Aquifer.......: %c\n", (aquify?'Y':'N') ); + + accept = getyesno("Accept these settings?",1); + } + + + int64_t n; + uint32_t x_max,y_max,z_max; + + + //Pattern to dig + unsigned char pattern[16][16]; + + + for (int regen=1;regen; ) + { + regen=0; + + memset(pattern,0,sizeof(pattern)); + + //Calculate a randomized circle. + //These values found through experimentation. + int x=0, y=0, n=0; + + //Two concentric irregular circles + //Outer circle, solid. + if ( wallthickness ) + { + drawcircle(holeradius+wallthickness, pattern, 2); + } + //Inner circle, hole. + if ( holeradius ) + { + drawcircle(holeradius, pattern, 1); + } + + + //Post-process to be certain the wall totally encloses hole. + if (wallthickness) + { + for (y=0;y<16;++y) + { + for (x=0;x<16;++x) + { + if ( 1==pattern[x][y] ) + { + //No hole at edges. + if ( x<1 || x>14 || y<1 || y>14 ) + { + pattern[x][y]=2; + } + } + else if ( 0==pattern[x][y] ) + { + //check neighbors + checkneighbors( pattern , x,y, 1, 2); + } + } + } + } + + //Makes sure that somewhere random gets a vertical pillar of rock which is safe + //to dig stairs down, to permit access to anywhere within the pit from the top. + for (n=holepillar; n ; --n) + { + settileat( pattern , 1 , 3 , rand()&255 ); + } + for (n=wallpillar; n ; --n) + { + settileat( pattern , 2 , 3 , rand()&255 ); + } + + //Note: + //At this point, the pattern holds: + //0 for all tiles which will be ignored. + //1 for all tiles set to empty pit space. + //2 for all normal walls. + //3 for the straight obsidian top-to-bottom wall. + //4 is randomized between wall or floor (!not implemented!) + + printf("\nPattern:\n"); + const char patternkey[] = ".cW!?567890123"; + + //Print the pattern + for (y=0;y<16;++y) + { + for (x=0;x<16;++x) + { + cout << patternkey[ pattern[x][y] ]; + } + cout << endl; + } + cout << endl; + + regen = !getyesno("Acceptable Pattern?",1); + } + + //Post-process settings to fix problems here + if (pitdepth<1) + { + pitdepth=INT_MAX; + } + + + /////////////////////////////////////////////////////////////////////////////////////////////// + + + cerr << "Loading memory map..." << endl; + + //Connect to DF! + DFHack::ContextManager DFMgr("Memory.xml"); + DFHack::Context *DF = DFMgr.getSingleContext(); + + + + //Init + cerr << "Attaching to DF..." << endl; + try + { + DF->Attach(); + } + catch (exception& e) + { + cerr << e.what() << endl; + #ifndef LINUX_BUILD + cin.ignore(); + #endif + return 1; + } + + // init the map + DFHack::Maps *Mapz = DF->getMaps(); + if (!Mapz->Start()) + { + cerr << "Can't init map. Exiting." << endl; + #ifndef LINUX_BUILD + cin.ignore(); + #endif + return 1; + } + + Mapz->getSize(x_max,y_max,z_max); + + + //Get cursor + int32_t cursorX, cursorY, cursorZ; + DFHack::Position *Pos = DF->getPosition(); + Pos->getCursorCoords(cursorX,cursorY,cursorZ); + if (-30000==cursorX) + { + cout << "No cursor position found. Exiting." << endl; + #ifndef LINUX_BUILD + cin.ignore(); + #endif + return 1; + } + + //Block coordinates + int32_t bx=cursorX/16, by=cursorY/16, bz=cursorZ; + //Tile coordinates within block + int32_t tx=cursorX%16, ty=cursorY%16, tz=cursorZ; + + /* + //Access the DF interface to pause the game. + //Copied from the reveal tool. + DFHack::Gui *Gui =DF->getGui(); + cout << "Pausing..." << endl; + Gui->SetPauseState(true); + DF->Resume(); + waitmsec(1000); + DF->Suspend(); + */ + + //Verify that every z-level at this location exists. + for (int32_t Z = 0; Z<= bz ;Z++) + { + if ( ! Mapz->isValidBlock(bx,by,Z) ) + { + cout << "This block does't exist! Exiting." << endl; + #ifndef LINUX_BUILD + cin.ignore(); + #endif + return 1; + } + } + + //Get all the map features. + vector global_features; + if (!Mapz->ReadGlobalFeatures(global_features)) + { + cout << "Couldn't load global features! Probably a version problem." << endl; + #ifndef LINUX_BUILD + cin.ignore(); + #endif + return 1; + } + + std::map > local_features; + if (!Mapz->ReadLocalFeatures(local_features)) + { + cout << "Couldn't load local features! Probably a version problem." << endl; + #ifndef LINUX_BUILD + cin.ignore(); + #endif + return 1; + } + + //Get info on current tile, to determine how to generate the pit + mapblock40d topblock; + Mapz->ReadBlock40d( bx, by, bz , &topblock ); + //Related block info + planecoord pc; + pc.dim.x=bx; + pc.dim.y=by; + mapblock40d block; + const TileRow * tp; + t_designation * d; + + ////////////////////////////////////// + //From top to bottom, dig this thing. + ////////////////////////////////////// + + //Top level, cap. + //Might make this an option in the future + //For now, no wall means no cap. + if (wallthickness) + { + Mapz->ReadBlock40d( bx, by, bz , &block ); + for (uint32_t x=0;x<16;++x) + { + for (uint32_t y=0;y<16;++y) + { + if ( (pattern[x][y]>1) || (roof && pattern[x][y]) ) + { + tp = getTileTypeP(block.tiletypes[x][y]); + d = &block.designation[x][y]; + //Only modify this level if it's 'empty' + if ( EMPTY != tp->c && RAMP_TOP != tp->c && STAIR_DOWN != tp->c && DFHack::TILE_STREAM_TOP != tp->s) + { + continue; + } + + //Need a floor for empty space. + if (reveal) + { + d->bits.hidden = 0; //topblock.designation[x][y].bits.hidden; + } + //Always clear the dig designation. + d->bits.dig = designation_no; + //unlock fluids, so they fall down the pit. + d->bits.flow_forbid = d->bits.liquid_static=0; + block.blockflags.bits.liquid_1 = block.blockflags.bits.liquid_2 = 1; + //Remove aquifer, to prevent bugginess + d->bits.water_table=0; + //Set the tile. + block.tiletypes[x][y] = cap + rand()%4; + } + } + } + //Write the block. + Mapz->WriteBlockFlags(bx,by,bz, block.blockflags ); + Mapz->WriteDesignations(bx,by,bz, &block.designation ); + Mapz->WriteTileTypes(bx,by,bz, &block.tiletypes ); + Mapz->WriteDirtyBit(bx,by,bz,1); + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + //All levels in between. + int done=0; + uint32_t t,v; + int32_t z = bz-1; + int32_t bottom = max(0,bz-pitdepth-1); + assert( bottom>=0 && bottom<=bz ); + for ( ; !done && z>=bottom ; --z) + { + int watercount=0; + int magmacount=0; + int moltencount=0; + int rockcount=0; + int veincount=0; + int emptycount=0; + int hellcount=0; + int templecount=0; + int adamcount=0; + int featcount=0; + int tpat; + + cout << z << endl; + assert( Mapz->isValidBlock(bx,by,z) ); + if (!Mapz->ReadBlock40d( bx, by, z , &block )) + { + cout << "Bad block! " << bx << "," << by << "," << z << endl; + } + + //Pre-process this z-level, to get some tile statistics. + for (int32_t x=0;x<16;++x) + { + for (int32_t y=0;y<16;++y) + { + t=0; + tp = getTileTypeP(block.tiletypes[x][y]); + d = &block.designation[x][y]; + tpat=pattern[x][y]; + + //Tile type material categories + switch ( tp->m ) + { + case AIR: + ++emptycount; + break; + case MAGMA: + ++moltencount; + break; + case VEIN: + ++veincount; + break; + case FEATSTONE: + case HFS: + case OBSIDIAN: + //basicly, ignored. + break; + default: + if ( EMPTY == tp->c || RAMP_TOP == tp->c || STAIR_DOWN == tp->c ) + { + ++emptycount; + } + else + { + ++rockcount; + } + break; + } + + //Magma and water + if ( d->bits.flow_size ) + { + if (d->bits.liquid_type) + { + ++magmacount; + } + else + { + ++watercount; + } + } + + + //Check for Features + if ( block.local_feature > -1 || block.global_feature > -1 ) + { + //Count tiles which actually are in the feature. + //It is possible for a block to have a feature, but no tiles to be feature. + if ( d->bits.feature_global || d->bits.feature_local ) + { + //All features + ++featcount; + + if ( d->bits.feature_global && d->bits.feature_local ) + { + cout << "warn:tile is global and local at same time!" << endl; + } + + n=0; + if ( block.global_feature > -1 && d->bits.feature_global ) + { + n=global_features[block.global_feature].type; + switch ( n ) + { + case feature_Other: + //no count + break; + case feature_Adamantine_Tube: + ++adamcount; + break; + case feature_Underworld: + ++hellcount; + break; + case feature_Hell_Temple: + ++templecount; + break; + default: + //something here. for debugging, it may be interesting to know. + if (n) cout << '(' << n << ')'; + } + } + + n=0; + if ( block.local_feature > -1 && d->bits.feature_local ) + { + n=local_features[pc][block.local_feature]->type; + switch ( n ) + { + case feature_Other: + //no count + break; + case feature_Adamantine_Tube: + ++adamcount; + break; + case feature_Underworld: + ++hellcount; + break; + case feature_Hell_Temple: + ++templecount; + break; + default: + //something here. for debugging, it may be interesting to know. + if (n) cout << '[' << n << ']'; + } + } + } + } + } + } + + + //If stopping at magma, and no no non-feature stone in this layer, and magma found, then we're either at + //or below the magma sea / molten rock. + if ( stopatmagma && (moltencount || magmacount) && (!exposemagma || !rockcount) ) + { + //If not exposing magma, quit at the first sign of magma. + //If exposing magma, quite once magma is exposed. + done=-1; + } + + + ///////////////////////////////////////////////////////////////////////////////////////////////// + //Some checks, based on settings and stats collected + //First check, are we at illegal depth? + if ( !done && hellcount && stopatmagma ) + { + //Panic! + done=-1; + tpat=0; + cout << "error: illegal breach of hell!" << endl; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + //Actually process the current z-level. + //These loops do the work. + for (int32_t x=0;!done && x<16;++x) + { + for (int32_t y=0;!done && y<16;++y) + { + t=0; + tp = getTileTypeP(block.tiletypes[x][y]); + d = &block.designation[x][y]; + tpat=pattern[x][y]; + + //Up front, remove aquifer, to prevent bugginess + //It may be added back if aquify is set. + d->bits.water_table=0; + + //Change behaviour based on settings and stats from this z-level + + //In hell? + if ( tpat && tpat!=3 && isfeature(global_features, local_features,block,pc,x,y,feature_Underworld ) ) + { + if ( exposehell ) + { + tpat=0; + } + } + + //Expose magma? + if ( tpat && tpat!=3 && exposemagma ) + { + //Leave certain tiles unchanged. + switch ( tp->m ) + { + case HFS: + case FEATSTONE: + case MAGMA: + tpat=0; + default: + break; + } + //Adamantine may be left unchanged... + if ( isfeature(global_features, local_features,block,pc,x,y,feature_Adamantine_Tube ) ) + { + tpat=0; + } + //Leave magma sea unchanged. + if ( d->bits.flow_size && d->bits.liquid_type) + { + tpat=0; + } + } + + + //For all situations... + //Special modification for walls, always for adamantine. + if ( isfeature(global_features, local_features,block,pc,x,y,feature_Adamantine_Tube ) ) + { + if ( 2==pattern[x][y] || 3==pattern[x][y] ) + { + tpat=2; + } + } + + + //Border or space? + switch (tpat) + { + case 0: + continue; + break; + case 1: + //Empty Space + t=32; + //d->bits.light = topblock.designation[x][y].bits.light; + //d->bits.skyview = topblock.designation[x][y].bits.skyview; + //d->bits.subterranean = topblock.designation[x][y].bits.subterranean; + + //Erase special markers? + //d->bits.feature_global = d->bits.feature_local = 0; + + //Water? Magma? + if (fillmagma || fillwater) + { + d->bits.flow_size=7; + d->bits.water_stagnant = false; + d->bits.water_salt = false; + if (fillmagma) + { + d->bits.liquid_type=liquid_magma; + } + else + { + d->bits.liquid_type=liquid_water; + } + } + else + { + //Otherwise, remove all liquids. + d->bits.flow_size=0; + d->bits.water_stagnant = false; + d->bits.water_salt = false; + d->bits.liquid_type = liquid_water; + } + + break; + case 2: + //Wall. + //First guess based on current material + switch ( tp->m ) + { + case OBSIDIAN: + t=wmagma; + break; + case MAGMA: + t=wmolten; + break; + case HFS: + //t=whell; + break; + case VEIN: + t=440; //Solid vein block + break; + case FEATSTONE: + t=335; //Solid feature stone block + break; + default: + t=wcave; + } + //Adamantine (a local feature) trumps veins. + { + //Local Feature? + if ( block.local_feature > -1 ) + { + switch ( n=local_features[pc][block.local_feature]->type ) + { + case feature_Underworld: + case feature_Hell_Temple: + //Only adopt these if there is no global feature present + if ( block.global_feature >-1 ) + { + break; + } + case feature_Adamantine_Tube: + //Always for adamantine, sometimes for others + //Whatever the feature is made of. "featstone wall" + d->bits.feature_global = 0; + d->bits.feature_local = 1; + t=335; + break; + } + } + //Global Feature? + else if (block.global_feature > -1 && !d->bits.feature_local ) + { + switch ( n=global_features[block.global_feature].type ) + { + case feature_Adamantine_Tube: + case feature_Underworld: + case feature_Hell_Temple: + //Whatever the feature is made of. "featstone wall" + d->bits.feature_global = 1; + t=335; + break; + } + } + } + + //Erase any liquids, as they cause problems. + d->bits.flow_size=0; + d->bits.water_stagnant = false; + d->bits.water_salt = false; + d->bits.liquid_type=liquid_water; + + //Placing an aquifer? + //(bugged, these aquifers don't generate water!) + if ( aquify ) + { + //Only normal stone types can be aquified + if ( tp->m!=MAGMA && tp->m!=FEATSTONE && tp->m!=HFS ) + { + //Only place next to the hole. + //If no hole, place in middle. + if ( checkneighbors(pattern,x,y,1) || (7==x && 7==y) ) + { + d->bits.water_table = 1; + //t=265; //soil wall + } + } + } + break; + case 3: + ////No obsidian walls on bottom of map! + //if(z<1 && (d->bits.feature_global || d->bits.feature_local) ) { + // t=335; + //} + + //Special wall, always sets to obsidian, to give a stairway + t=331; + + //Erase special markers + d->bits.feature_global = d->bits.feature_local = 0; + + //Erase any liquids, as they cause problems. + d->bits.flow_size=0; + d->bits.water_stagnant = false; + d->bits.water_salt = false; + d->bits.liquid_type=liquid_water; + break; + default: + cout << ".error,bad pattern."; + } + + //For all tiles. + if (reveal) + { + d->bits.hidden = 0; //topblock.designation[x][y].bits.hidden; + } + //Always clear the dig designation. + d->bits.dig=designation_no; + //Make it underground, because it is capped + d->bits.subterranean=1; + d->bits.light=0; + d->bits.skyview=0; + //unlock fluids, so they fall down the pit. + d->bits.flow_forbid = d->bits.liquid_static=0; + block.blockflags.bits.liquid_1 = block.blockflags.bits.liquid_2 = 1; + //Set the tile. + block.tiletypes[x][y] = t; + + } + } + + //Write the block. + Mapz->WriteBlockFlags(bx,by,z, block.blockflags ); + Mapz->WriteDesignations(bx,by,z, &block.designation ); + Mapz->WriteTileTypes(bx,by,z, &block.tiletypes ); + Mapz->WriteDirtyBit(bx,by,z,1); + + } + + //Re-process the last z-level handled above. + z++; + assert( z>=0 ); + + + /////////////////////////////////////////////////////////////////////////////////////////////// + //The bottom level is special. + if (-1) + { + if (!Mapz->ReadBlock40d( bx, by, z , &block )) + { + cout << "Bad block! " << bx << "," << by << "," << z << endl; + } + for (uint32_t x=0;x<16;++x) + { + for (uint32_t y=0;y<16;++y) + { + t=floor; + v=floorvar; + tp = getTileTypeP(block.tiletypes[x][y]); + d = &block.designation[x][y]; + + if ( exposehell ) + { + //Leave hell tiles unchanged when exposing hell. + if ( isfeature(global_features,local_features,block,pc,x,y,feature_Underworld) ) + { + continue; + } + } + + //Does expose magma need anything at this level? + if ( exposemagma && stopatmagma ) + { + continue; + } + + switch (pattern[x][y]) + { + case 0: + continue; + break; + case 1: + //Empty becomes floor. + + //Base floor type on the z-level first, features, then tile type. + if (!z) { + //Bottom of map, use the floor specified, always. + break; + } + + ////Only place floor where ground is already solid when exposing + //if( EMPTY == tp->c || RAMP_TOP == tp->c || STAIR_DOWN == tp->c ){ + // continue; + //} + + if ( d->bits.feature_global || d->bits.feature_global ) { + //Feature Floor! + t=344; + break; + } + + //Tile material check. + switch ( tp->m ) + { + case OBSIDIAN: + t=340; + v=3; + break; + case MAGMA: + v=0; + t=264; //magma flow + break; + case HFS: + //should only happen at bottom of map + break; + case VEIN: + t=441; //vein floor + v=3; + break; + case FEATSTONE: + t=344; + v=3; + break; + } + + break; + case 2: + case 3: + //Walls already drawn. + //Ignore. + continue; + break; + } + + //For all tiles. + if (reveal) d->bits.hidden = 0; //topblock.designation[x][y].bits.hidden; + //Always clear the dig designation. + d->bits.dig=designation_no; + //unlock fluids + d->bits.flow_forbid = d->bits.liquid_static=0; + block.blockflags.bits.liquid_1 = block.blockflags.bits.liquid_2 = 1; + + //Set the tile. + block.tiletypes[x][y] = t + ( v ? rand()&v : 0 ); + } + } + //Write the block. + Mapz->WriteBlockFlags(bx,by,z, block.blockflags ); + Mapz->WriteDesignations(bx,by,z, &block.designation ); + Mapz->WriteTileTypes(bx,by,z, &block.tiletypes ); + Mapz->WriteDirtyBit(bx,by,z,1); + } + + DF->Detach(); +#ifndef LINUX_BUILD + cout << "Done. Press any key to continue" << endl; + cin.ignore(); +#endif + return 0; +} diff --git a/tools/playground/primitives.cpp b/tools/playground/primitives.cpp new file mode 100644 index 000000000..bdf837d62 --- /dev/null +++ b/tools/playground/primitives.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include +#include +#include +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; +} diff --git a/tools/supported/CMakeLists.txt b/tools/supported/CMakeLists.txt index bc080448a..138ac6a20 100644 --- a/tools/supported/CMakeLists.txt +++ b/tools/supported/CMakeLists.txt @@ -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 ) diff --git a/tools/playground/SegmentedFinder.h b/tools/supported/SegmentedFinder.h similarity index 91% rename from tools/playground/SegmentedFinder.h rename to tools/supported/SegmentedFinder.h index eae2d1bf3..6a0c1042e 100644 --- a/tools/playground/SegmentedFinder.h +++ b/tools/supported/SegmentedFinder.h @@ -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 bool Find (needleType needle, const uint8_t increment , vector &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 bool Filter (needleType needle, vector &found, vector &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(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(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(big*16 + small); } } else diff --git a/tools/supported/autosearch.cpp b/tools/supported/autosearch.cpp new file mode 100644 index 000000000..6bdff90a3 --- /dev/null +++ b/tools/supported/autosearch.cpp @@ -0,0 +1,782 @@ +// this is an incremental search tool. It only works on Linux. +// here be dragons... and ugly code :P +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +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 +#endif + +#include +#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(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(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(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(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(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(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 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 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(*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(*(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 & selected_ranges) +{ + vector 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:" <::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 &found, const char * what) +{ + cout << what << ":" << endl; + for(int i = 0; i < found.size();i++) + { + cout << hex << "0x" << found[i] << endl; + } +} + +void printFoundStrVec(vector &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(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(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 + T * Build() + { + return new T; + } + template + 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 & ranges, DFHack::OSType platform) +{ + cout << "stealing memory..." << endl; + SegmentedFinder sf(ranges, DF); + TokenFactory tf(platform); + cout << "done!" << endl; + Struct maps; + maps.Add(tf.Build()); + maps.Add(tf.Build()); + /* + vector allVectors; + vector filtVectors; + vector to_filter; + + cout << "stealing memory..." << endl; + SegmentedFinder sf(ranges, DF); + cout << "looking for vectors..." << endl; + sf.Find(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("ABBEY",to_filter, vectorStringFirst); + uint64_t lang_addr = to_filter[0]; + + // find dwarven language word table + to_filter = filtVectors; + sf.Filter("kulet",to_filter, vectorStringFirst); + kulet_vector = to_filter[0]; + + // find vector of languages + to_filter = filtVectors; + sf.Filter("DWARF",to_filter, vectorStringFirst); + + // verify + for(int i = 0; i < to_filter.size(); i++) + { + vecTriplet * vec = sf.Translate(to_filter[i]); + if(((vec->finish - vec->start) / 4) == 4) // verified + { + DWARF_vector = to_filter[i]; + DWARF_object = sf.Read(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(257 * 4,4,to_filter,vectorLength); + sf.Filter("IRON",to_filter, vectorString); + sf.Filter("ONYX",to_filter, vectorString); + sf.Filter("RAW_ADAMANTINE",to_filter, vectorString); + sf.Filter("BLOODSTONE",to_filter, vectorString); + printFound(to_filter,"inorganics"); + + // organics vector + to_filter = filtVectors; + sf.Filter(52 * 4,to_filter,vectorLength); + sf.Filter("MUSHROOM_HELMET_PLUMP",to_filter, vectorStringFirst); + printFound(to_filter,"organics"); + + // tree vector + to_filter = filtVectors; + sf.Filter(31 * 4,to_filter,vectorLength); + sf.Filter("MANGROVE",to_filter, vectorStringFirst); + printFound(to_filter,"trees"); + + // plant vector + to_filter = filtVectors; + sf.Filter(21 * 4,to_filter,vectorLength); + sf.Filter("MUSHROOM_HELMET_PLUMP",to_filter, vectorStringFirst); + printFound(to_filter,"plants"); + + // color descriptors + //AMBER, 112 + to_filter = filtVectors; + sf.Filter(112 * 4,to_filter,vectorLength); + sf.Filter("AMBER",to_filter, vectorStringFirst); + printFound(to_filter,"color descriptors"); + if(!to_filter.empty()) + { + uint64_t vec = to_filter[0]; + vecTriplet *vtColors = sf.Translate(vec); + uint32_t colorObj = sf.Read(vtColors->start); + cout << "Amber color:" << hex << "0x" << colorObj << endl; + // TODO: find string 'amber', the floats + } + + // all descriptors + //AMBER, 338 + to_filter = filtVectors; + sf.Filter(338 * 4,to_filter,vectorLength); + sf.Filter("AMBER",to_filter, vectorStringFirst); + printFound(to_filter,"all descriptors"); + + // creature type + //ELEPHANT, ?? (demons abound) + to_filter = filtVectors; + //sf.Find(338 * 4,4,to_filter,vectorLength); + sf.Filter("ELEPHANT",to_filter, vectorString); + sf.Filter("CAT",to_filter, vectorString); + sf.Filter("DWARF",to_filter, vectorString); + sf.Filter("WAMBLER_FLUFFY",to_filter, vectorString); + sf.Filter("TOAD",to_filter, vectorString); + sf.Filter("DEMON_1",to_filter, vectorString); + + vector toad_first = to_filter; + vector elephant_first = to_filter; + sf.Filter("TOAD",toad_first, vectorStringFirst); + sf.Filter("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(to_use); + uint32_t elephant = sf.Read(vtCretypes->start); + uint64_t Eoffset; + cout << "Elephant: 0x" << hex << elephant << endl; + cout << "Elephant: rawname = 0x0" << endl; + uint8_t letter_E = 'E'; + Eoffset = sf.FindInRange (letter_E,equalityP, elephant, 0x300 ); + if(Eoffset) + { + cout << "Elephant: big E = 0x" << hex << Eoffset - elephant << endl; + } + Eoffset = sf.FindInRange ("FEMALE",vectorStringFirst, elephant, 0x300 ); + if(Eoffset) + { + cout << "Elephant: caste vector = 0x" << hex << Eoffset - elephant << endl; + } + Eoffset = sf.FindInRange ("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 (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(to_use); + uint32_t toad = sf.Read(vtCretypes->start); + uint64_t Eoffset; + cout << "Toad: 0x" << hex << toad << endl; + cout << "Toad: rawname = 0x0" << endl; + Eoffset = sf.FindInRange (0xF9,equalityP, toad, 0x300 ); + if(Eoffset) + { + cout << "Toad: character (not reliable) = 0x" << hex << Eoffset - toad << endl; + } + Eoffset = sf.FindInRange ("FEMALE",vectorStringFirst, toad, 0x300 ); + if(Eoffset) + { + cout << "Toad: caste vector = 0x" << hex << Eoffset - toad << endl; + } + Eoffset = sf.FindInRange ("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 (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 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; +} diff --git a/tools/supported/cleanmap.cpp b/tools/supported/cleanmap.cpp index e4b97b703..efba907d4 100644 --- a/tools/supported/cleanmap.cpp +++ b/tools/supported/cleanmap.cpp @@ -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]; diff --git a/tools/playground/incrementalsearch.cpp b/tools/supported/incrementalsearch.cpp similarity index 84% rename from tools/playground/incrementalsearch.cpp rename to tools/supported/incrementalsearch.cpp index 8c347d0c7..f95fba34f 100644 --- a/tools/playground/incrementalsearch.cpp +++ b/tools/supported/incrementalsearch.cpp @@ -21,146 +21,12 @@ using namespace std; #include #include "SegmentedFinder.h" -template -class holder -{ - public: - vector 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(addr_) << "\"" << endl; - } - bool equals(SegmentedFinder & sf,const char * rhs) - { - uint32_t addr2 = *(sf.Translate(addr_)); - return strcmp(sf.Translate(addr2), rhs) == 0; - } - template - 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(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(*(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 & selected_ranges) { vector ranges; @@ -182,12 +48,12 @@ bool getRanges(DFHack::Process * p, vector & 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 & selected_ranges } it++; } + return true; } bool getNumber (string prompt, int & output, int def, bool pdef = true) @@ -312,10 +179,14 @@ bool Incremental ( vector &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 & range DF->Detach(); } } -/* - while(Incremental(found, "integer",test1)) + +bool TriggerIncremental ( vector &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 & ranges) +{ + vector 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(test1,alignment,found, equalityP); - break; - case 2: - sf.Incremental(test1,alignment,found, equalityP); - break; - case 4: - sf.Incremental(test1,alignment,found, equalityP); - break; + select.insert(x); + select.insert(y); + select.insert(z); + } + else + { + select.insert(x); + select.insert(y); + select.insert(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 & ranges) { int element_size; @@ -736,7 +669,7 @@ struct tilecolors }; #pragma pack() -void automatedLangtables(DFHack::Context * DF, vector & ranges) +void autoSearch(DFHack::Context * DF, vector & ranges) { vector allVectors; vector filtVectors; @@ -820,6 +753,15 @@ void automatedLangtables(DFHack::Context * DF, vector & rang sf.Filter("MUSHROOM_HELMET_PLUMP",to_filter, vectorStringFirst); printFound(to_filter,"organics"); + // new organics vector + to_filter = filtVectors; + sf.Filter("MUSHROOM_HELMET_PLUMP",to_filter, vectorString); + sf.Filter("MEADOW-GRASS",to_filter, vectorString); + sf.Filter("TUNNEL_TUBE",to_filter, vectorString); + sf.Filter("WEED_BLADE",to_filter, vectorString); + sf.Filter("EYEBALL",to_filter, vectorString); + printFound(to_filter,"organics 31.19"); + // tree vector to_filter = filtVectors; sf.Filter(31 * 4,to_filter,vectorLength); @@ -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(); diff --git a/tools/supported/position.cpp b/tools/supported/position.cpp index a6383e0ef..ffac17969 100644 --- a/tools/supported/position.cpp +++ b/tools/supported/position.cpp @@ -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 { diff --git a/tools/supported/vdig.cpp b/tools/supported/vdig.cpp index 17a7c2f2c..19cb037a5 100644 --- a/tools/supported/vdig.cpp +++ b/tools/supported/vdig.cpp @@ -10,9 +10,11 @@ using namespace std; #include #include -#include +//#include #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: 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) { diff --git a/tools/supported/veinlook.cpp b/tools/supported/veinlook.cpp index 90e2f9c29..624baae1e 100644 --- a/tools/supported/veinlook.cpp +++ b/tools/supported/veinlook.cpp @@ -536,6 +536,8 @@ main(int argc, char *argv[]) vector veinVector; vector IceVeinVector; vector splatter; + vector grass; + vector 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); + } } } }