diff --git a/.gitignore b/.gitignore index 356bf6399..1a06c65bc 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +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 b794b7143..ceffb938e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ SET( DATA_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/output CACHE PATH "Output directory fo OPTION(BUILD_DFHACK_DOCUMENTATION "Create doxygen documentation for developers" OFF) OPTION(BUILD_DFHACK_EXAMPLES "Build example tools" OFF) OPTION(BUILD_DFHACK_PLAYGROUND "Build tools from the playground folder" OFF) -OPTION(BUILD_DFHACK_C_BINDIGS "Build the C portion of the library" ON) +OPTION(BUILD_DFHACK_C_BINDINGS "Build the C portion of the library" ON) OPTION(BUILD_OFFSET_EDITOR "Build the Offset GUI editor (not ready for use)." OFF) OPTION(BUILD_DFHACK_SUPPORTED "Build the supported toold." ON) OPTION(BUILD_DFHACK_SHM "Build the SHM." OFF) diff --git a/LICENSE b/LICENSE index 5b904f714..4cfc6a66d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,6 @@ -www.sourceforge.net/projects/dfhack +---------------------------------------------------------------------- +License of dfhack +github.com/peterix/dfhack Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf This software is provided 'as-is', without any express or implied @@ -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/build-MinGW32-release-trace.bat b/build/build-MinGW32-release-trace.bat index ca7df86d3..f75da5ff8 100644 --- a/build/build-MinGW32-release-trace.bat +++ b/build/build-MinGW32-release-trace.bat @@ -1,6 +1,6 @@ -mkdir build-real -cd build-real -cmake ..\.. -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE:string=Release --trace > trace-stdout.txt 2> trace-stderr.txt -mingw32-make 2> log.txt -pause +mkdir build-real +cd build-real +cmake ..\.. -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE:string=Release --trace > trace-stdout.txt 2> trace-stderr.txt +mingw32-make 2> log.txt +pause dir file.xxx \ No newline at end of file diff --git a/build/build-MinGW32-release.bat b/build/build-MinGW32-release.bat index 18e5bcab4..dd0dc6b88 100644 --- a/build/build-MinGW32-release.bat +++ b/build/build-MinGW32-release.bat @@ -1,5 +1,5 @@ -mkdir build-real -cd build-real -cmake ..\.. -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE:string=Release -mingw32-make 2> log.txt +mkdir build-real +cd build-real +cmake ..\.. -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE:string=Release +mingw32-make 2> log.txt 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/build/generate-MSVC-2010.bat b/build/generate-MSVC-2010.bat index 50828f7ea..588051bc6 100644 --- a/build/generate-MSVC-2010.bat +++ b/build/generate-MSVC-2010.bat @@ -1,4 +1,4 @@ -mkdir build-real -cd build-real -cmake ..\.. -G"Visual Studio 10" +mkdir build-real +cd build-real +cmake ..\.. -G"Visual Studio 10" pause \ No newline at end of file diff --git a/data/Memory-ng.xml b/data/Memory-ng.xml index 7f7e4c48c..55f7b19be 100644 --- a/data/Memory-ng.xml +++ b/data/Memory-ng.xml @@ -664,6 +664,10 @@ + + + +
-
+
MAYBE +
+ + + + + + +
@@ -1500,7 +1512,11 @@ - 0x01482874 - current race + + + + +
@@ -1513,7 +1529,7 @@
-
+
@@ -1536,15 +1552,15 @@
- - - - - - - - - + + + + + + + + + @@ -1618,24 +1634,383 @@
- + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + is it a vector? + + + + + + +
- Second possible: 0x16568a0 (can't decide) + + + +
Second possible: 0x0169fb7C + + + + + + + + + + +
+ + +
+ + + +
+
+ + + +
+
+ + +
+
+
+
+
+ + +
+
+ + + +
Second possible: 0x016a0b88 + + + + + + + + + + + + +
0x16445F0 + 0x5DC0C see code at 0x9CFB01 + + +
+
+ + +
+ AS BOGUS AS IT GETS +
+
+
+
+ + +
+ + +
MAYBE +
+
+ + +
+ + + + + + + + + + + + + +
+ +
+
+ + +
+
+
+
+
+ + +
+ + + + +
+ + +
+ + + +
+
+ + + + 0x016a12c0 vector + + +
MAYBE... THE DETAILS WILL BE DIFFERENT +
+ WTF IS THIS, I DON'T EVEN... +
+
+
+ + +
+ + +
+
+ + + + +
+ List of offsets in the VTable : + + + + + + + + + + (in the vtable) + + +
+
+
+ + + + + + + + + + + + +
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
TODO: it's signed! +
+
+ + + + + + + + + + + + + + +
+ + + + + +
+ + + + + LOOKS FINE? +
+
VERIFIED + + VERIFIED + + VERIFIED + + + +
+
+
+
+
+ + +
+ + +
+ + +
+ + +
+ One of those really. Which one is the right one? + 0x16dad78 + 0x16dad88 +
+ Let's assume those didn't change... + + + +
+
+
+ + - .-"""-. ' \ |,. ,-. | _________________________ @@ -1662,7 +2037,7 @@
- + - + - + @@ -1703,7 +2078,7 @@
- + @@ -1743,7 +2118,7 @@
- + @@ -1801,7 +2176,7 @@
- + VERIFY @@ -1876,15 +2251,15 @@ - + VERIFY - + - + CHMOD @@ -1995,11 +2370,76 @@
-
+
+ + +
+ + +
+ + +
+
+
+ + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ + +
0x933c0fc + + +
+ + + +
+
+ + + +
Alt:0x92febe8 + + + +
+ + + +
+
+
+
+
+
+ + + + + + + + + + + + + +
+ + +
+ + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + +
+
+ + +
+ + + +
+ + + + +
+
+ + + +
+ + +
+
+
+
+
+ ------------------- + !!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 0b72d10fe..fdf49f8d3 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 @@ -86,8 +90,11 @@ VersionInfo.cpp VersionInfoFactory.cpp DFContextManager.cpp DFContext.cpp +DFTileTypes.cpp DFProcessEnumerator.cpp ContextShared.cpp +DFProcess-SHM.cpp +MicrosoftSTL.cpp depends/md5/md5.cpp depends/md5/md5wrapper.cpp @@ -137,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 @@ -190,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/ContextShared.cpp b/library/ContextShared.cpp index 62ca3fff1..01842ee1f 100644 --- a/library/ContextShared.cpp +++ b/library/ContextShared.cpp @@ -31,7 +31,7 @@ DFContextShared::DFContextShared() DFContextShared::~DFContextShared() { // invalidate all modules - for(int i = 0 ; i < allModules.size(); i++) + for(unsigned int i = 0 ; i < allModules.size(); i++) { delete allModules[i]; } diff --git a/library/DFContext.cpp b/library/DFContext.cpp index 8b67f193f..fa4f85b59 100644 --- a/library/DFContext.cpp +++ b/library/DFContext.cpp @@ -93,68 +93,12 @@ bool Context::Detach() } d->shm_start = 0; // invalidate all modules - for(int i = 0 ; i < d->allModules.size(); i++) + for(unsigned int i = 0 ; i < d->allModules.size(); i++) { delete d->allModules[i]; } 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; } @@ -174,7 +118,7 @@ bool Context::AsyncSuspend() bool Context::Resume() { - for(int i = 0 ; i < d->allModules.size(); i++) + for(unsigned int i = 0 ; i < d->allModules.size(); i++) { d->allModules[i]->OnResume(); } @@ -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::MissingMemoryDefinition) - { - 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::MissingMemoryDefinition&) - { - 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::MissingMemoryDefinition&) - { - 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::MissingMemoryDefinition&) - { - 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/DFContextManager.cpp b/library/DFContextManager.cpp index f338ce6ee..634794029 100644 --- a/library/DFContextManager.cpp +++ b/library/DFContextManager.cpp @@ -69,7 +69,7 @@ BadContexts::~BadContexts() bool BadContexts::Contains(Process* p) { - for(int i = 0; i < d->bad.size(); i++) + for(unsigned int i = 0; i < d->bad.size(); i++) { if((d->bad[i])->getProcess() == p) return true; @@ -79,7 +79,7 @@ bool BadContexts::Contains(Process* p) bool BadContexts::Contains(Context* c) { - for(int i = 0; i < d->bad.size(); i++) + for(unsigned int i = 0; i < d->bad.size(); i++) { if(d->bad[i] == c) return true; @@ -94,7 +94,7 @@ uint32_t BadContexts::size() void BadContexts::clear() { - for(int i = 0; i < d->bad.size(); i++) + for(unsigned int i = 0; i < d->bad.size(); i++) { // delete both Process and Context! // process has to be deleted after context, because Context does some @@ -227,7 +227,7 @@ Context * ContextManager::getSingleContext() { Refresh(); } - for(int i = 0; i < d->contexts.size();i++) + for(unsigned int i = 0; i < d->contexts.size();i++) { if(d->contexts[i]->isValid()) { @@ -239,8 +239,8 @@ Context * ContextManager::getSingleContext() void ContextManager::purge(void) { - for(int i = 0; i < d->contexts.size();i++) + for(unsigned int i = 0; i < d->contexts.size();i++) delete d->contexts[i]; d->contexts.clear(); d->pEnum->purge(); -} \ No newline at end of file +} diff --git a/library/DFProcess-SHM.cpp b/library/DFProcess-SHM.cpp new file mode 100644 index 000000000..3246e567e --- /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, vector & known_versions) +{ + return new SHMProcess(pid, known_versions); +} + +SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) +: 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(known_versions); + // 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 ace2dacf8..8229b6adb 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,50 +200,21 @@ 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) { @@ -333,13 +246,13 @@ bool SHMProcess::Private::validate(vector & known_versions) { VersionInfo *m = new VersionInfo(**it); memdescriptor = m; - m->setParentProcess(dynamic_cast( self )); + m->setParentProcess(self); identified = true; // cerr << "identified " << m->getVersion() << endl; return true; } } - catch (Error::MissingMemoryDefinition&) + catch (Error::AllMemdef&) { continue; } @@ -347,27 +260,6 @@ bool SHMProcess::Private::validate(vector & known_versions) return false; } -SHMProcess::~SHMProcess() -{ - if(d->attached) - { - detach(); - } - if(d->memdescriptor) - delete d->memdescriptor; - delete d; -} - -VersionInfo * SHMProcess::getDescriptor() -{ - return d->memdescriptor; -} - -int SHMProcess::getPID() -{ - return d->process_ID; -} - // there is only one we care about. bool SHMProcess::getThreadIDs(vector & threads ) { @@ -403,123 +295,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 +319,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,334 +378,34 @@ 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) +string SHMProcess::getPath() { - if(!d->locked) throw Error::MemoryAccessDenied(); - - modulelookup * payload = D_SHMDATA(modulelookup); - payload->version = version; - - strncpy(payload->name,name,255); - payload->name[255] = 0; + char cwd_name[256]; + char target_name[1024]; + int target_result; - 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; + 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)); } 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; + 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 891a4f820..7f28f8d9e 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,30 @@ distribution. #include using namespace DFHack; -class WineProcess::Private -{ - public: - Private(Process * self_) - { - my_descriptor = NULL; - my_handle = NULL; - my_pid = 0; - attached = false; - suspended = false; - memFileHandle = 0; - self = self_; +namespace { + class WineProcess : public LinuxProcessBase + { + private: + MicrosoftSTL stl; + public: + WineProcess(uint32_t pid, std::vector & 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: + bool validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions); }; - ~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, vector & known_versions) +{ + return new WineProcess(pid, known_versions); +} -WineProcess::WineProcess(uint32_t pid, vector & known_versions) -: d(new Private(this)) +WineProcess::WineProcess(uint32_t pid, vector & known_versions) : LinuxProcessBase(pid) { char dir_name [256]; char exe_link_name [256]; @@ -70,8 +65,8 @@ 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); @@ -107,27 +102,14 @@ WineProcess::WineProcess(uint32_t pid, vector & known_versions) 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); + identified = 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) +bool WineProcess::validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions) { md5wrapper md5; // get hash of the running DF process @@ -137,490 +119,44 @@ bool WineProcess::Private::validate(char* exe_file, uint32_t pid, char* mem_file // 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::MissingMemoryDefinition& 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) + if (hash == (*it)->getMD5()) // are the md5 hashes the same? { - writeQuad(offset, *(uint64_t *) (source + indexptr)); - offset +=8; - indexptr +=8; - size -=8; + if (OS_WINDOWS == (*it)->getOS()) + { + // keep track of created memory_info object so we can destroy it later + my_descriptor = new VersionInfo(**it); + my_descriptor->setParentProcess(this); + // tell Process about the /proc/PID/mem file + memFile = memFile; + identified = true; + + stl.init(this); + return 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) + catch (Error::AllMemdef&) { - 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; + continue; } } + return false; } -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; + stl.readClassName(vptr); } diff --git a/library/DFProcess-linux.cpp b/library/DFProcess-linux.cpp index 164834543..dda919663 100644 --- a/library/DFProcess-linux.cpp +++ b/library/DFProcess-linux.cpp @@ -22,42 +22,36 @@ 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, std::vector & 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: + bool validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions); }; - ~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); -}; +} + +Process* DFHack::createNormalProcess(uint32_t pid, vector & known_versions) +{ + return new NormalProcess(pid, known_versions); +} -NormalProcess::NormalProcess(uint32_t pid, vector< VersionInfo* >& known_versions) -: d(new Private(this)) +NormalProcess::NormalProcess(uint32_t pid, vector & known_versions) : LinuxProcessBase(pid) { char dir_name [256]; char exe_link_name [256]; @@ -67,8 +61,8 @@ 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); @@ -89,26 +83,12 @@ NormalProcess::NormalProcess(uint32_t pid, vector< VersionInfo* >& known_version if (strstr(target_name, "dwarfort.exe") != 0 || strstr(target_name,"Dwarf_Fortress") != 0) { // create linux process, add it to the vector - d->identified = d->validate(target_name,pid,mem_name,known_versions ); + identified = 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) +bool NormalProcess::validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions) { md5wrapper md5; // get hash of the running DF process @@ -120,29 +100,21 @@ bool NormalProcess::Private::validate(char * exe_file,uint32_t pid, char * memFi { try { - //cout << hash << " ?= " << (*it)->getMD5() << endl; - if(hash == (*it)->getMD5()) // are the md5 hashes the same? + if (hash == (*it)->getMD5()) // are the md5 hashes the same? { - VersionInfo * m = *it; - if (VersionInfo::OS_LINUX == m->getOS()) + if (OS_LINUX == (*it)->getOS()) { - VersionInfo *m2 = new VersionInfo(*m); - my_descriptor = m2; - m2->setParentProcess(dynamic_cast( self )); - my_handle = my_pid = pid; + // keep track of created memory_info object so we can destroy it later + my_descriptor = new VersionInfo(**it); + my_descriptor->setParentProcess(this); + // tell Process about the /proc/PID/mem file + memFile = memFile; + identified = true; + return true; } - 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::MissingMemoryDefinition&) + catch (Error::AllMemdef&) { continue; } @@ -150,400 +122,6 @@ bool NormalProcess::Private::validate(char * exe_file,uint32_t pid, char * memFi 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) - { - 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 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 +132,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 +144,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,8 +157,8 @@ 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(); diff --git a/library/DFProcess-windows-SHM.cpp b/library/DFProcess-windows-SHM.cpp index 9742ceeca..37d74697a 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) { // 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,7 +242,7 @@ 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 ); @@ -353,7 +255,7 @@ bool SHMProcess::Private::validate(vector & known_versions) { pe_timestamp = (*it)->getPE(); } - catch(Error::MissingMemoryDefinition&) + catch(Error::AllMemdef&) { continue; } @@ -371,29 +273,6 @@ bool SHMProcess::Private::validate(vector & known_versions) } 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 +307,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; - } + return ( WaitForSingleObject(d->DFCLSuspendMutex,INFINITE) == 0 ); } -bool SHMProcess::forceresume() +bool SHMProcess::releaseSuspendLock() { - 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( 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 +336,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 +378,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,330 +393,30 @@ 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; } -// get module index by name and version. bool 0 = error -bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) +string SHMProcess::getPath() { - 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; + HMODULE hmod; + DWORD junk; + char String[255]; + HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, d->process_ID ); //get the handle from the process ID + EnumProcessModules(hProcess, &hmod, 1 * sizeof(HMODULE), &junk); //get the module from the handle + GetModuleFileNameEx(hProcess,hmod,String,sizeof(String)); //get the filename from the module + string out(String); + return(out.substr(0,out.find_last_of("\\"))); } 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 0b3d7959d..9f637e944 100644 --- a/library/DFProcess-windows.cpp +++ b/library/DFProcess-windows.cpp @@ -22,79 +22,136 @@ 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, std::vector & known_versions); + ~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, vector & known_versions) +{ + return new NormalProcess(pid, known_versions); +} NormalProcess::NormalProcess(uint32_t pid, vector & known_versions) -: d(new Private()) +: my_pid(pid) { + my_descriptor = NULL; + my_main_thread = NULL; + attached = false; + suspended = false; + base = 0; + sections = 0; + 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); // 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); + my_handle = 0; } catch (exception &) { - CloseHandle(hProcess); - d->my_handle = 0; + CloseHandle(my_handle); + my_handle = 0; return; } @@ -103,7 +160,7 @@ NormalProcess::NormalProcess(uint32_t pid, vector & known_versio for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) { // filter by OS - if(VersionInfo::OS_WINDOWS != (*it)->getOS()) + if(OS_WINDOWS != (*it)->getOS()) continue; uint32_t pe_timestamp; // filter by timestamp, skip entries without a timestamp @@ -111,7 +168,7 @@ NormalProcess::NormalProcess(uint32_t pid, vector & known_versio { pe_timestamp = (*it)->getPE(); } - catch(Error::MissingMemoryDefinition&) + catch(Error::AllMemdef&) { continue; } @@ -121,26 +178,21 @@ NormalProcess::NormalProcess(uint32_t pid, vector & known_versio // all went well { printf("Match found! Using version %s.\n", (*it)->getVersion().c_str()); - d->identified = true; + 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); + my_descriptor = new VersionInfo(**it); + my_descriptor->RebaseAll(base); // keep track of created memory_info object so we can destroy it later - d->my_descriptor = m; - m->setParentProcess(this); + my_descriptor->setParentProcess(this); // process is responsible for destroying its data model - d->my_pid = pid; - d->my_handle = hProcess; - d->identified = true; + my_handle = my_handle; + 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"); + my_main_thread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads[0]); + stl.init(this); found = true; break; // break the iterator loop } @@ -148,7 +200,7 @@ NormalProcess::NormalProcess(uint32_t pid, vector & known_versio // close handle of processes that aren't DF if(!found) { - CloseHandle(hProcess); + CloseHandle(my_handle); } } /* @@ -156,45 +208,46 @@ NormalProcess::NormalProcess(uint32_t pid, vector & known_versio 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 +257,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 +308,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 +334,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 +343,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)) - 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)) + if(!ReadProcessMemory(my_handle, (int*) offset, &result, sizeof(uint8_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)) + if(!ReadProcessMemory(my_handle, (int*) offset, &result, sizeof(uint64_t), NULL)) throw Error::MemoryAccessDenied(); } -float NormalProcess::readFloat (const uint32_t offset) -{ - float result; - if(!ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(float), NULL)) - throw Error::MemoryAccessDenied(); - 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,55 +546,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(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("\\"))); } diff --git a/library/DFProcessEnumerator.cpp b/library/DFProcessEnumerator.cpp index b790cb0dd..be33d41fd 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" @@ -63,7 +63,7 @@ BadProcesses::~BadProcesses() bool BadProcesses::Contains(Process* p) { - for(int i = 0; i < d->bad.size(); i++) + for(unsigned int i = 0; i < d->bad.size(); i++) { if(d->bad[i] == p) return true; @@ -96,7 +96,7 @@ uint32_t BadProcesses::size() void BadProcesses::clear() { - for(int i = 0; i < d->bad.size(); i++) + for(unsigned int i = 0; i < d->bad.size(); i++) { delete d->bad[i]; } @@ -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->versions); if(p1->isIdentified()) return p1; else delete p1; - Process *p2 = new NormalProcess(ID.pid,meminfo->versions); + Process *p2 = createNormalProcess(ID.pid,meminfo->versions); 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->versions); if(p3->isIdentified()) return p3; else diff --git a/library/DFTileTypes.cpp b/library/DFTileTypes.cpp new file mode 100644 index 000000000..964233e50 --- /dev/null +++ b/library/DFTileTypes.cpp @@ -0,0 +1,31 @@ +// vim: sts=4 sta et shiftwidth=4: +#include "dfhack/DFIntegers.h" +#include "dfhack/DFTileTypes.h" +#include "dfhack/DFExport.h" + +namespace DFHack { + //set tile class string lookup table (e.g. for printing to user) +#define X(name,comment) #name, + const char * TileClassString[tileclass_count+1] = { + TILECLASS_MACRO + 0 + }; +#undef X + + //string lookup table (e.g. for printing to user) +#define X(name,comment) #name, + const char * TileMaterialString[tilematerial_count+1] = { + TILEMATERIAL_MACRO + 0 + }; +#undef X + + //string lookup table (e.g. for printing to user) +#define X(name,comment) #name, + const char * TileSpecialString[tilespecial_count+1] = { + TILESPECIAL_MACRO + 0 + }; +#undef X + +} diff --git a/library/DFTypes_C.cpp b/library/DFTypes_C.cpp index 9677ae9f1..7543e4b6c 100644 --- a/library/DFTypes_C.cpp +++ b/library/DFTypes_C.cpp @@ -39,6 +39,31 @@ using namespace std; using namespace DFHack; +/* +I believe this is what they call "the bad kind of clever", but writing out registration functions for each callback just feels *so* wrong... + +The output of this macro is something like this... + +void RegisterByteBufferCallback(int(*fptr)(int8_t*, uint32_t)) +{ + alloc_byte_buffer_callback = fptr; +} + +*/ +#define BUILD(a) a ## BufferCallback +#define REG_MACRO(type_name, type, callback) void BUILD(Register ## type_name) (int (*fptr)(type, uint32_t)) { callback = fptr; } + +/* +The output of this macro is something like this... + +void UnregisterByteBufferCallback() +{ + alloc_byte_buffer_callback = NULL; +} + +*/ +#define UNREG_MACRO(type_name, callback) void BUILD(Unregister ## type_name) () { callback = NULL; } + #ifdef __cplusplus extern "C" { #endif @@ -80,6 +105,114 @@ int (*alloc_vein_buffer_callback)(t_vein*, uint32_t) = NULL; int (*alloc_frozenliquidvein_buffer_callback)(t_frozenliquidvein*, uint32_t) = NULL; int (*alloc_spattervein_buffer_callback)(t_spattervein*, uint32_t) = NULL; +REG_MACRO(Byte, int8_t*, alloc_byte_buffer_callback) +REG_MACRO(Short, int16_t*, alloc_short_buffer_callback) +REG_MACRO(Int, int32_t*, alloc_int_buffer_callback) +REG_MACRO(UByte, uint8_t*, alloc_ubyte_buffer_callback) +REG_MACRO(UShort, uint16_t*, alloc_ushort_buffer_callback) +REG_MACRO(UInt, uint32_t*, alloc_uint_buffer_callback) +REG_MACRO(Char, char*, alloc_char_buffer_callback) +REG_MACRO(Matgloss, t_matgloss*, alloc_matgloss_buffer_callback) +REG_MACRO(DescriptorColor, t_descriptor_color*, alloc_descriptor_buffer_callback) +REG_MACRO(MatglossOther, t_matglossOther*, alloc_matgloss_other_buffer_callback) +REG_MACRO(Feature, t_feature*, alloc_t_feature_buffer_callback) +REG_MACRO(Hotkey, t_hotkey*, alloc_t_hotkey_buffer_callback) +REG_MACRO(Screen, t_screen*, alloc_t_screen_buffer_callback) +REG_MACRO(CustomWorkshop, t_customWorkshop*, alloc_t_customWorkshop_buffer_callback) +REG_MACRO(Material, t_material*, alloc_t_material_buffer_callback) + +UNREG_MACRO(Byte, alloc_byte_buffer_callback) +UNREG_MACRO(Short, alloc_short_buffer_callback) +UNREG_MACRO(Int, alloc_int_buffer_callback) +UNREG_MACRO(UByte, alloc_ubyte_buffer_callback) +UNREG_MACRO(UShort, alloc_ushort_buffer_callback) +UNREG_MACRO(UInt, alloc_uint_buffer_callback) +UNREG_MACRO(Char, alloc_char_buffer_callback) +UNREG_MACRO(Matgloss, alloc_matgloss_buffer_callback) +UNREG_MACRO(DescriptorColor, alloc_descriptor_buffer_callback) +UNREG_MACRO(MatglossOther, alloc_matgloss_other_buffer_callback) +UNREG_MACRO(Feature, alloc_t_feature_buffer_callback) +UNREG_MACRO(Hotkey, alloc_t_hotkey_buffer_callback) +UNREG_MACRO(Screen, alloc_t_screen_buffer_callback) +UNREG_MACRO(CustomWorkshop, alloc_t_customWorkshop_buffer_callback) +UNREG_MACRO(Material, alloc_t_material_buffer_callback) + +void RegisterEmptyColorModifierCallback(int (*funcptr)(c_colormodifier*)) +{ + alloc_empty_colormodifier_callback = funcptr; +} + +void RegisterNewColorModifierCallback(int (*funcptr)(c_colormodifier*, const char*, uint32_t)) +{ + alloc_colormodifier_callback = funcptr; +} + +REG_MACRO(ColorModifier, c_colormodifier*, alloc_colormodifier_buffer_callback) + +void RegisterEmptyCreatureCasteCallback(int (*funcptr)(c_creaturecaste*)) +{ + alloc_empty_creaturecaste_callback = funcptr; +} + +void UnregisterEmptyColorModifierCallback() +{ + alloc_empty_colormodifier_callback = NULL; +} + +void UnregisterNewColorModifierCallback() +{ + alloc_colormodifier_callback = NULL; +} + +void RegisterNewCreatureCasteCallback(int (*funcptr)(c_creaturecaste*, const char*, const char*, const char*, const char*, uint32_t, uint32_t)) +{ + alloc_creaturecaste_callback = funcptr; +} + +REG_MACRO(CreatureCaste, c_creaturecaste*, alloc_creaturecaste_buffer_callback) +UNREG_MACRO(CreatureCaste, alloc_creaturecaste_buffer_callback) + +void UnregisterEmptyCreatureCasteCallback() +{ + alloc_empty_creaturecaste_callback = NULL; +} + +void UnregisterNewCreatureCasteCallback() +{ + alloc_creaturecaste_callback = NULL; +} + +void RegisterEmptyCreatureTypeCallback(int (*funcptr)(c_creaturetype*)) +{ + alloc_empty_creaturetype_callback = funcptr; +} + +void RegisterNewCreatureTypeCallback(int (*funcptr)(c_creaturetype*, const char*, uint32_t, uint32_t, uint8_t, uint16_t, uint16_t, uint16_t)) +{ + alloc_creaturetype_callback = funcptr; +} + +REG_MACRO(CreatureType, c_creaturetype*, alloc_creaturetype_buffer_callback) +UNREG_MACRO(CreatureType, alloc_creaturetype_buffer_callback) + +void UnregisterEmptyCreatureTypeCallback() +{ + alloc_empty_creaturetype_callback = NULL; +} + +void UnregisterNewCreatureTypeCallback() +{ + alloc_creaturetype_callback = NULL; +} + +REG_MACRO(Vein, t_vein*, alloc_vein_buffer_callback) +REG_MACRO(FrozenLiquidVein, t_frozenliquidvein*, alloc_frozenliquidvein_buffer_callback) +REG_MACRO(SpatterVein, t_spattervein*, alloc_spattervein_buffer_callback) + +UNREG_MACRO(Vein, alloc_vein_buffer_callback) +UNREG_MACRO(FrozenLiquidVein, alloc_frozenliquidvein_buffer_callback) +UNREG_MACRO(SpatterVein, alloc_spattervein_buffer_callback) + int DFHack_isWallTerrain(int in) { return DFHack::isWallTerrain(in); @@ -143,7 +276,7 @@ int CreatureCasteConvert(t_creaturecaste* src, c_creaturecaste* dest) ((*alloc_creaturecaste_callback)(dest, src->rawname, src->singular, src->plural, src->adjective, src->ColorModifier.size(), src->bodypart.size())); - for(int i = 0; i < dest->colorModifierLength; i++) + for(unsigned int i = 0; i < dest->colorModifierLength; i++) ColorListConvert(&src->ColorModifier[i], &dest->ColorModifier[i]); copy(src->bodypart.begin(), src->bodypart.end(), dest->bodypart); @@ -158,7 +291,7 @@ int CreatureTypeConvert(t_creaturetype* src, c_creaturetype* dest) ((*alloc_creaturetype_callback)(dest, src->rawname, src->castes.size(), src->extract.size(), src->tile_character, src->tilecolor.fore, src->tilecolor.back, src->tilecolor.bright)); - for(int i = 0; i < dest->castesCount; i++) + for(unsigned int i = 0; i < dest->castesCount; i++) CreatureCasteConvert(&src->castes[i], &dest->castes[i]); copy(src->extract.begin(), src->extract.end(), dest->extract); 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 c0a3477af..0569b90f8 100644 --- a/library/VersionInfo.cpp +++ b/library/VersionInfo.cpp @@ -127,11 +127,11 @@ namespace DFHack */ namespace DFHack { - typedef pair nullableUint32; + typedef pair nullableUint32; typedef map ::iterator uint32_Iter; - typedef pair nullableInt32; + typedef pair nullableInt32; typedef map ::iterator int32_Iter; - typedef pair nullableString; + typedef pair nullableString; typedef map ::iterator strings_Iter; typedef map ::iterator groups_Iter; class OffsetGroupPrivate @@ -149,73 +149,126 @@ namespace DFHack void OffsetGroup::createOffset(const string & key) { - OGd->offsets[key] = nullableInt32(false, 0); + OGd->offsets[key] = nullableInt32(NOT_SET, 0); } void OffsetGroup::createAddress(const string & key) { - OGd->addresses[key] = nullableUint32(false, 0); + OGd->addresses[key] = nullableUint32(NOT_SET, 0); } void OffsetGroup::createHexValue(const string & key) { - OGd->hexvals[key] = nullableUint32(false, 0); + OGd->hexvals[key] = nullableUint32(NOT_SET, 0); } void OffsetGroup::createString(const string & key) { - OGd->strings[key] = nullableString(false, std::string()); + OGd->strings[key] = nullableString(NOT_SET, std::string()); } -void OffsetGroup::setOffset (const string & key, const string & value) +void OffsetGroup::setOffset (const string & key, const string & value, const INVAL_TYPE inval) { int32_Iter it = OGd->offsets.find(key); if(it != OGd->offsets.end()) { int32_t offset = strtol(value.c_str(), NULL, 16); (*it).second.second = offset; - (*it).second.first = true; + if(inval != NOT_SET) + (*it).second.first = inval; } else throw Error::MissingMemoryDefinition("offset", getFullName() + key); } +void OffsetGroup::setOffsetValidity (const string & key, const INVAL_TYPE inval) +{ + if(inval != NOT_SET) + { + int32_Iter it = OGd->offsets.find(key); + if(it != OGd->offsets.end()) + { + (*it).second.first = inval; + } + else throw Error::MissingMemoryDefinition("offset", getFullName() + key); + } +} -void OffsetGroup::setAddress (const string & key, const string & value) +void OffsetGroup::setAddress (const string & key, const string & value, const INVAL_TYPE inval) { uint32_Iter it = OGd->addresses.find(key); if(it != OGd->addresses.end()) { int32_t address = strtol(value.c_str(), NULL, 16); (*it).second.second = address; - (*it).second.first = true; + if(inval != NOT_SET) + (*it).second.first = inval; } else throw Error::MissingMemoryDefinition("address", getFullName() + key); } +void OffsetGroup::setAddressValidity (const string & key, const INVAL_TYPE inval) +{ + if(inval != NOT_SET) + { + uint32_Iter it = OGd->addresses.find(key); + if(it != OGd->addresses.end()) + { + (*it).second.first = inval; + } + else throw Error::MissingMemoryDefinition("address", getFullName() + key); + } +} -void OffsetGroup::setHexValue (const string & key, const string & value) + +void OffsetGroup::setHexValue (const string & key, const string & value, const INVAL_TYPE inval) { uint32_Iter it = OGd->hexvals.find(key); if(it != OGd->hexvals.end()) { (*it).second.second = strtol(value.c_str(), NULL, 16); - (*it).second.first = true; + if(inval != NOT_SET) + (*it).second.first = inval; } else throw Error::MissingMemoryDefinition("hexvalue", getFullName() + key); } +void OffsetGroup::setHexValueValidity (const string & key, const INVAL_TYPE inval) +{ + if(inval != NOT_SET) + { + uint32_Iter it = OGd->hexvals.find(key); + if(it != OGd->hexvals.end()) + { + (*it).second.first = inval; + } + else throw Error::MissingMemoryDefinition("hexvalue", getFullName() + key); + } +} -void OffsetGroup::setString (const string & key, const string & value) +void OffsetGroup::setString (const string & key, const string & value, const INVAL_TYPE inval) { strings_Iter it = OGd->strings.find(key); if(it != OGd->strings.end()) { (*it).second.second = value; - (*it).second.first = true; + if(inval != NOT_SET) + (*it).second.first = inval; } else throw Error::MissingMemoryDefinition("string", getFullName() + key); } +void OffsetGroup::setStringValidity (const string & key, const INVAL_TYPE inval) +{ + if(inval != NOT_SET) + { + strings_Iter it = OGd->strings.find(key); + if(it != OGd->strings.end()) + { + (*it).second.first = inval; + } + else throw Error::MissingMemoryDefinition("string", getFullName() + key); + } +} // Get named address uint32_t OffsetGroup::getAddress (const string & key) @@ -224,8 +277,10 @@ uint32_t OffsetGroup::getAddress (const string & key) if(iter != OGd->addresses.end()) { - if((*iter).second.first) + if((*iter).second.first == IS_VALID) return (*iter).second.second; + if((*iter).second.first == IS_INVALID) + throw Error::InvalidMemoryDefinition("address", getFullName() + key); throw Error::UnsetMemoryDefinition("address", getFullName() + key); } throw Error::MissingMemoryDefinition("address", getFullName() + key); @@ -238,8 +293,10 @@ int32_t OffsetGroup::getOffset (const string & key) int32_Iter iter = OGd->offsets.find(key); if(iter != OGd->offsets.end()) { - if((*iter).second.first) + if((*iter).second.first == IS_VALID) return (*iter).second.second; + if((*iter).second.first == IS_INVALID) + throw Error::InvalidMemoryDefinition("offset", getFullName() + key); throw Error::UnsetMemoryDefinition("offset", getFullName() + key); } throw Error::MissingMemoryDefinition("offset", getFullName() + key); @@ -252,8 +309,10 @@ uint32_t OffsetGroup::getHexValue (const string & key) uint32_Iter iter = OGd->hexvals.find(key); if(iter != OGd->hexvals.end()) { - if((*iter).second.first) + if((*iter).second.first == IS_VALID) return (*iter).second.second; + if((*iter).second.first == IS_INVALID) + throw Error::InvalidMemoryDefinition("hexvalue", getFullName() + key); throw Error::UnsetMemoryDefinition("hexvalue", getFullName() + key); } throw Error::MissingMemoryDefinition("hexvalue", getFullName() + key); @@ -265,8 +324,10 @@ std::string OffsetGroup::getString (const string &key) strings_Iter iter = OGd->strings.find(key); if(iter != OGd->strings.end()) { - if((*iter).second.first) + if((*iter).second.first == IS_VALID) return (*iter).second.second; + if((*iter).second.first == IS_INVALID) + throw Error::InvalidMemoryDefinition("string", getFullName() + key); throw Error::UnsetMemoryDefinition("string", getFullName() + key); } throw Error::MissingMemoryDefinition("string", getFullName() + key); @@ -361,6 +422,8 @@ std::string OffsetGroup::PrintOffsets(int indentation) if((*iter).second.first) ss << " value=\"" << hex << "0x" << (*iter).second.second << "\""; ss << " />"; + if((*iter).second.first == IS_INVALID) + ss << " INVALID!"; if(!(*iter).second.first) ss << " MISSING!"; ss << endl; @@ -372,6 +435,8 @@ std::string OffsetGroup::PrintOffsets(int indentation) if((*iter2).second.first) ss << " value=\"" << hex << "0x" << (*iter2).second.second << "\""; ss << " />"; + if((*iter2).second.first == IS_INVALID) + ss << " INVALID!"; if(!(*iter2).second.first) ss << " MISSING!"; ss << endl; @@ -382,6 +447,8 @@ std::string OffsetGroup::PrintOffsets(int indentation) if((*iter).second.first) ss << " value=\"" << hex << "0x" << (*iter).second.second << "\""; ss << " />"; + if((*iter).second.first == IS_INVALID) + ss << " INVALID!"; if(!(*iter).second.first) ss << " MISSING!"; ss << endl; @@ -393,6 +460,8 @@ std::string OffsetGroup::PrintOffsets(int indentation) if((*iter3).second.first) ss << " value=\"" << (*iter3).second.second << "\""; ss << " />"; + if((*iter3).second.first == IS_INVALID) + ss << " INVALID!"; if(!(*iter3).second.first) ss << " MISSING!"; ss << endl; @@ -409,6 +478,42 @@ std::string OffsetGroup::PrintOffsets(int indentation) return ss.str(); } +// the big ugly method behind the curtain... +void OffsetGroup::setInvalid(INVAL_TYPE invalidity) +{ + if(invalidity == NOT_SET) + return; + + uint32_Iter iter; + for(iter = OGd->addresses.begin(); iter != OGd->addresses.end(); iter++) + { + if((*iter).second.first) + (*iter).second.first = invalidity; + } + int32_Iter iter2; + for(iter2 = OGd->offsets.begin(); iter2 != OGd->offsets.end(); iter2++) + { + if((*iter2).second.first) + (*iter2).second.first = invalidity; + } + for(iter = OGd->hexvals.begin(); iter != OGd->hexvals.end(); iter++) + { + if((*iter).second.first) + (*iter).second.first = invalidity; + } + strings_Iter iter3; + for(iter3 = OGd->strings.begin(); iter3 != OGd->strings.end(); iter3++) + { + if((*iter3).second.first) + (*iter3).second.first = invalidity; + } + groups_Iter iter4; + for(iter4 = OGd->groups.begin(); iter4 != OGd->groups.end(); iter4++) + { + (*iter4).second->setInvalid(invalidity); + } +} + /* * Private data */ @@ -440,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; }; @@ -602,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 b9a640663..e41923f4a 100644 --- a/library/VersionInfoFactory.cpp +++ b/library/VersionInfoFactory.cpp @@ -31,6 +31,65 @@ distribution. using namespace DFHack; +template +struct triple +{ + typedef _T1 first_type; + typedef _T2 second_type; + typedef _T3 third_type; + + _T1 first; + _T2 second; + _T3 third; + + triple() : first(), second(), third() { } + + triple(const _T1& __a, const _T2& __b, const _T3& __c) : first(__a), second(__b), third(__c) { } + + template + triple(const triple<_U1, _U2, _U3>& __p) : first(__p.first), second(__p.second), third(__p.third) { } + +}; + +template +inline bool operator==(const triple<_T1, _T2, _T3>& __x, const triple<_T1, _T2, _T3>& __y) +{ + return __x.first == __y.first && __x.second == __y.second && __x.third == __y.third; +} + +template +inline bool operator<(const triple<_T1, _T2, _T3>& __x, const triple<_T1, _T2, _T3>& __y) +{ + return + __x.first < __y.first || + (!(__y.first < __x.first) && __x.second < __y.second) || + (!(__y.first < __x.first) && !(__x.second < __y.second) && (__x.third < __y.third)); +} + +template +inline bool operator!=(const triple<_T1, _T2, _T3>& __x, const triple<_T1, _T2, _T3>& __y) +{ + return !(__x == __y); +} + +template +inline bool operator>(const triple<_T1, _T2, _T3>& __x, const triple<_T1, _T2, _T3>& __y) +{ + return __y < __x; +} + +template +inline bool operator<=(const triple<_T1, _T2, _T3>& __x, const triple<_T1, _T2, _T3>& __y) +{ + return !(__y < __x); +} + +template +inline bool operator>=(const triple<_T1, _T2, _T3>& __x, const triple<_T1, _T2, _T3>& __y) +{ + return !(__x < __y); +} + VersionInfoFactory::~VersionInfoFactory() { // for each stored version, delete @@ -95,35 +154,54 @@ void VersionInfoFactory::ParseVTable(TiXmlElement* vtable, VersionInfo* mem) } } -// FIXME: this is ripe for replacement with a more generic approach +struct breadcrumb +{ + TiXmlElement * first; + OffsetGroup * second; +}; + void VersionInfoFactory::ParseOffsets(TiXmlElement * parent, VersionInfo* target, bool initial) { // we parse the groups iteratively instead of recursively // breadcrubs acts like a makeshift stack // first pair entry stores the current element of that level // second pair entry the group object from OffsetGroup - typedef pair < TiXmlElement *, OffsetGroup * > groupPair; - vector< groupPair > breadcrumbs; + typedef triple< TiXmlElement *, OffsetGroup *, INVAL_TYPE> groupTriple; + vector< groupTriple > breadcrumbs; { TiXmlElement* pEntry; // we get the , look at the children pEntry = parent->FirstChildElement(); + const char *cstr_invalid = parent->Attribute("valid"); + INVAL_TYPE parent_inval = NOT_SET; + if(cstr_invalid) + { + if(strcmp(cstr_invalid,"false") == 0) + parent_inval = IS_INVALID; + else if(strcmp(cstr_invalid,"true") == 0) + parent_inval = IS_VALID; + } + OffsetGroup * currentGroup = reinterpret_cast (target); + currentGroup->setInvalid(parent_inval); + + // we end here if there are no child tags. if(!pEntry) return; - OffsetGroup * currentGroup = reinterpret_cast (target); - breadcrumbs.push_back(groupPair(pEntry,currentGroup)); + breadcrumbs.push_back(groupTriple(pEntry,currentGroup, parent_inval)); } // work variables OffsetGroup * currentGroup = 0; TiXmlElement * currentElem = 0; + INVAL_TYPE parent_inval = NOT_SET; //cerr << ""<< endl; while(1) { // get current work variables currentElem = breadcrumbs.back().first; currentGroup = breadcrumbs.back().second; + parent_inval = breadcrumbs.back().third; // we reached the end of the current group? if(!currentElem) @@ -144,7 +222,7 @@ void VersionInfoFactory::ParseOffsets(TiXmlElement * parent, VersionInfo* target if(!currentGroup) { - groupPair & gp = breadcrumbs.back(); + groupTriple & gp = breadcrumbs.back(); gp.first = gp.first->NextSiblingElement(); continue; } @@ -152,7 +230,7 @@ void VersionInfoFactory::ParseOffsets(TiXmlElement * parent, VersionInfo* target // skip non-elements if (currentElem->Type() != TiXmlNode::ELEMENT) { - groupPair & gp = breadcrumbs.back(); + groupTriple & gp = breadcrumbs.back(); gp.first = gp.first->NextSiblingElement(); continue; } @@ -169,18 +247,27 @@ void VersionInfoFactory::ParseOffsets(TiXmlElement * parent, VersionInfo* target // evaluate elements const char *cstr_value = currentElem->Attribute("value"); + const char *cstr_invalid = currentElem->Attribute("valid"); + + INVAL_TYPE child_inval = parent_inval; + if(cstr_invalid) + { + if(strcmp(cstr_invalid,"false") == 0) + child_inval = IS_INVALID; + else if(strcmp(cstr_invalid,"true") == 0) + child_inval = IS_VALID; + } if(type == "group") { - // FIXME: possibly use setGroup always, with the initial flag as parameter? // create or get group OffsetGroup * og; if(initial) og = currentGroup->createGroup(cstr_name); else og = currentGroup->getGroup(cstr_name); - //cerr << "" << endl; + // advance this level to the next element - groupPair & gp = breadcrumbs.back(); + groupTriple & gp = breadcrumbs.back(); gp.first = currentElem->NextSiblingElement(); if(!og) @@ -189,73 +276,82 @@ void VersionInfoFactory::ParseOffsets(TiXmlElement * parent, VersionInfo* target throw Error::MissingMemoryDefinition("group", fullname); } - // add a new level that will be processed next - breadcrumbs.push_back(groupPair(currentElem->FirstChildElement(), og)); + // add a new level that will be processed in the next step + breadcrumbs.push_back(groupTriple(currentElem->FirstChildElement(), og, child_inval)); + og->setInvalid(child_inval); continue; } else if(type == "address") { + if(child_inval == NOT_SET) + child_inval = IS_VALID; if(initial) { currentGroup->createAddress(cstr_name); } else if(cstr_value) { - currentGroup->setAddress(cstr_name, cstr_value); + currentGroup->setAddress(cstr_name, cstr_value, child_inval); } else { - // ERROR, missing attribute + currentGroup->setAddressValidity(cstr_name, child_inval); } } else if(type == "offset") { + if(child_inval == NOT_SET) + child_inval = IS_VALID; if(initial) { currentGroup->createOffset(cstr_name); } else if(cstr_value) { - currentGroup->setOffset(cstr_name, cstr_value); + currentGroup->setOffset(cstr_name, cstr_value, child_inval); } else { - // ERROR, missing attribute + currentGroup->setOffsetValidity(cstr_name, child_inval); } } else if(type == "string") { + if(child_inval == NOT_SET) + child_inval = IS_VALID; if(initial) { currentGroup->createString(cstr_name); } else if(cstr_value) { - currentGroup->setString(cstr_name, cstr_value); + currentGroup->setString(cstr_name, cstr_value, child_inval); } else { - // ERROR, missing attribute + currentGroup->setStringValidity(cstr_name, child_inval); } } else if(type == "hexvalue") { + if(child_inval == NOT_SET) + child_inval = IS_VALID; if(initial) { currentGroup->createHexValue(cstr_name); } else if(cstr_value) { - currentGroup->setHexValue(cstr_name, cstr_value); + currentGroup->setHexValue(cstr_name, cstr_value, child_inval); } else { - // ERROR, missing attribute + currentGroup->setHexValueValidity(cstr_name, child_inval); } } // advance to next element - groupPair & gp = breadcrumbs.back(); + groupTriple & gp = breadcrumbs.back(); gp.first = currentElem->NextSiblingElement(); continue; } @@ -272,7 +368,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(); @@ -590,7 +686,6 @@ bool VersionInfoFactory::loadFile(string path_to_xml) if(name) { string str_name = name; - VersionInfo *base = new VersionInfo(); mem = new VersionInfo(); ParseBase( pMemInfo , mem ); knownVersions[str_name] = v_descr (pMemInfo, mem); 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/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-c/DFTypes_C.h b/library/include/dfhack-c/DFTypes_C.h index 49bd54dc9..772a0be04 100644 --- a/library/include/dfhack-c/DFTypes_C.h +++ b/library/include/dfhack-c/DFTypes_C.h @@ -32,6 +32,11 @@ distribution. #include "dfhack/modules/Position.h" #include "dfhack/DFTileTypes.h" +#define HBUILD(a) a ## BufferCallback +#define HREG_MACRO(type_name, type) DFHACK_EXPORT void HBUILD(Register ## type_name) (int (*funcptr)(type, uint32_t)); + +#define HUNREG_MACRO(type_name) DFHACK_EXPORT void HBUILD(Unregister ## type_name) (); + #ifdef __cplusplus extern "C" { #endif @@ -54,6 +59,41 @@ DFHACK_EXPORT extern int (*alloc_t_feature_buffer_callback)(t_feature*, uint32_t DFHACK_EXPORT extern int (*alloc_t_hotkey_buffer_callback)(t_hotkey*, uint32_t); DFHACK_EXPORT extern int (*alloc_t_screen_buffer_callback)(t_screen*, uint32_t); +DFHACK_EXPORT void RegisterByteBufferCallback(int (*funcptr)(int8_t*, uint32_t)); +DFHACK_EXPORT void RegisterShortBufferCallback(int (*funcptr)(int16_t*, uint32_t)); +DFHACK_EXPORT void RegisterIntBufferCallback(int (*funcptr)(int32_t*, uint32_t)); + +DFHACK_EXPORT void RegisterUByteBufferCallback(int (*funcptr)(uint8_t*, uint32_t)); +DFHACK_EXPORT void RegisterUShortBufferCallback(int (*funcptr)(uint16_t*, uint32_t)); +DFHACK_EXPORT void RegisterUIntBufferCallback(int (*funcptr)(uint32_t*, uint32_t)); + +DFHACK_EXPORT void RegisterCharBufferCallback(int (*funcptr)(char*, uint32_t)); + +DFHACK_EXPORT void RegisterMatglossBufferCallback(int (*funcptr)(t_matgloss*, uint32_t)); +DFHACK_EXPORT void RegisterDescriptorColorBufferCallback(int (*funcptr)(t_descriptor_color*, uint32_t)); +DFHACK_EXPORT void RegisterMatglossOtherBufferCallback(int (*funcptr)(t_matglossOther*, uint32_t)); + +DFHACK_EXPORT void RegisterFeatureBufferCallback(int (*funcptr)(t_feature*, uint32_t)); +DFHACK_EXPORT void RegisterHotkeyBufferCallback(int (*funcptr)(t_hotkey*, uint32_t)); +DFHACK_EXPORT void RegisterScreenBufferCallback(int (*funcptr)(t_screen*, uint32_t)); + +HUNREG_MACRO(Byte) +HUNREG_MACRO(Short) +HUNREG_MACRO(Int) +HUNREG_MACRO(UByte) +HUNREG_MACRO(UShort) +HUNREG_MACRO(UInt) + +HUNREG_MACRO(Char) + +HUNREG_MACRO(Matgloss) +HUNREG_MACRO(DescriptorColor) +HUNREG_MACRO(MatglossOther) + +HUNREG_MACRO(Feature) +HUNREG_MACRO(Hotkey) +HUNREG_MACRO(Screen) + struct t_customWorkshop { uint32_t index; @@ -61,9 +101,14 @@ struct t_customWorkshop }; DFHACK_EXPORT extern int (*alloc_t_customWorkshop_buffer_callback)(t_customWorkshop*, uint32_t); - DFHACK_EXPORT extern int (*alloc_t_material_buffer_callback)(t_material*, uint32_t); +DFHACK_EXPORT void RegisterCustomWorkshopBufferCallback(int (*funcptr)(t_customWorkshop*, uint32_t)); +DFHACK_EXPORT void RegisterMaterialBufferCallback(int (*funcptr)(t_material*, uint32_t)); + +HUNREG_MACRO(CustomWorkshop) +HUNREG_MACRO(Material) + struct c_colormodifier { char part[128]; @@ -75,6 +120,14 @@ DFHACK_EXPORT extern int (*alloc_empty_colormodifier_callback)(c_colormodifier*) DFHACK_EXPORT extern int (*alloc_colormodifier_callback)(c_colormodifier*, const char*, uint32_t); DFHACK_EXPORT extern int (*alloc_colormodifier_buffer_callback)(c_colormodifier*, uint32_t); +DFHACK_EXPORT void RegisterEmptyColorModifierCallback(int (*funcptr)(c_colormodifier*)); +DFHACK_EXPORT void RegisterNewColorModifierCallback(int (*funcptr)(c_colormodifier*, const char*, uint32_t)); +DFHACK_EXPORT void RegisterColorModifierBufferCallback(int (*funcptr)(c_colormodifier*, uint32_t)); + +DFHACK_EXPORT void UnregisterEmptyColorModifierCallback(); +DFHACK_EXPORT void UnregisterNewColorModifierCallback(); +HUNREG_MACRO(ColorModifier) + struct c_creaturecaste { char rawname[128]; @@ -93,6 +146,14 @@ DFHACK_EXPORT extern int (*alloc_empty_creaturecaste_callback)(c_creaturecaste*) DFHACK_EXPORT extern int (*alloc_creaturecaste_callback)(c_creaturecaste*, const char*, const char*, const char*, const char*, uint32_t, uint32_t); DFHACK_EXPORT extern int (*alloc_creaturecaste_buffer_callback)(c_creaturecaste*, uint32_t); +DFHACK_EXPORT void RegisterEmptyCreatureCasteCallback(int (*funcptr)(c_creaturecaste*)); +DFHACK_EXPORT void RegisterNewCreatureCasteCallback(int (*funcptr)(c_creaturecaste*, const char*, const char*, const char*, const char*, uint32_t, uint32_t)); +DFHACK_EXPORT void RegisterCreatureCasteBufferCallback(int (*funcptr)(c_creaturecaste*, uint32_t)); + +DFHACK_EXPORT void UnregisterEmptyCreatureCasteCallback(); +DFHACK_EXPORT void UnregisterNewCreatureCasteCallback(); +HUNREG_MACRO(CreatureCaste) + struct c_creaturetype { char rawname[128]; @@ -121,14 +182,30 @@ DFHACK_EXPORT extern int (*alloc_vein_buffer_callback)(t_vein*, uint32_t); DFHACK_EXPORT extern int (*alloc_frozenliquidvein_buffer_callback)(t_frozenliquidvein*, uint32_t); DFHACK_EXPORT extern int (*alloc_spattervein_buffer_callback)(t_spattervein*, uint32_t); -DFHACK_EXPORT extern int DFHack_isWallTerrain(int in); -DFHACK_EXPORT extern int DFHack_isFloorTerrain(int in); -DFHACK_EXPORT extern int DFHack_isRampTerrain(int in); -DFHACK_EXPORT extern int DFHack_isStairTerrain(int in); -DFHACK_EXPORT extern int DFHack_isOpenTerrain(int in); -DFHACK_EXPORT extern int DFHack_getVegetationType(int in); +DFHACK_EXPORT void RegisterEmptyCreatureTypeCallback(int (*funcptr)(c_creaturetype*)); +DFHACK_EXPORT void RegisterNewCreatureTypeCallback(int (*funcptr)(c_creaturetype*, const char*, uint32_t, uint32_t, uint8_t, uint16_t, uint16_t, uint16_t)); +DFHACK_EXPORT void RegisterCreatureTypeBufferCallback(int (*funcptr)(c_creaturetype*, uint32_t)); + +DFHACK_EXPORT void RegisterVeinBufferCallback(int (*funcptr)(t_vein*, uint32_t)); +DFHACK_EXPORT void RegisterFrozenLiquidVeinBufferCallback(int (*funcptr)(t_frozenliquidvein*, uint32_t)); +DFHACK_EXPORT void RegisterSpatterVeinBufferCallback(int (*funcptr)(t_spattervein*, uint32_t)); + +DFHACK_EXPORT void UnregisterEmptyCreatureTypeCallback(); +DFHACK_EXPORT void UnregisterNewCreatureTypeCallback(); +HUNREG_MACRO(CreatureType) + +HUNREG_MACRO(Vein) +HUNREG_MACRO(FrozenLiquidVein) +HUNREG_MACRO(SpatterVein) + +DFHACK_EXPORT int DFHack_isWallTerrain(int in); +DFHACK_EXPORT int DFHack_isFloorTerrain(int in); +DFHACK_EXPORT int DFHack_isRampTerrain(int in); +DFHACK_EXPORT int DFHack_isStairTerrain(int in); +DFHACK_EXPORT int DFHack_isOpenTerrain(int in); +DFHACK_EXPORT int DFHack_getVegetationType(int in); -DFHACK_EXPORT extern int DFHack_getTileType(int index, TileRow* tPtr); +DFHACK_EXPORT int DFHack_getTileType(int index, TileRow* tPtr); #ifdef __cplusplus } diff --git a/library/include/dfhack-c/modules/Creatures_C.h b/library/include/dfhack-c/modules/Creatures_C.h index 16c357e58..34d145bde 100644 --- a/library/include/dfhack-c/modules/Creatures_C.h +++ b/library/include/dfhack-c/modules/Creatures_C.h @@ -45,6 +45,9 @@ DFHACK_EXPORT int32_t Creatures_ReadCreatureInBox(DFHackObject* cPtr, const int3 DFHACK_EXPORT int Creatures_ReadCreature(DFHackObject* cPtr, const int32_t index, t_creature* furball); DFHACK_EXPORT t_material* Creatures_ReadJob(DFHackObject* cPtr, const t_creature* furball); +DFHACK_EXPORT uint32_t* Creatures_ReadInventoryIdx(DFHackObject* cPtr, const uint32_t index); +DFHACK_EXPORT uint32_t* Creatures_ReadInventoryPtr(DFHackObject* cPtr, const uint32_t index); + DFHACK_EXPORT uint32_t Creatures_GetDwarfRaceIndex(DFHackObject* cPtr); DFHACK_EXPORT int32_t Creatures_GetDwarfCivId(DFHackObject* cPtr); diff --git a/library/include/dfhack-c/modules/Gui_C.h b/library/include/dfhack-c/modules/Gui_C.h index 8cc737f78..d57a874da 100644 --- a/library/include/dfhack-c/modules/Gui_C.h +++ b/library/include/dfhack-c/modules/Gui_C.h @@ -37,6 +37,7 @@ DFHACK_EXPORT int Gui_Start(DFHackObject* gui); DFHACK_EXPORT int Gui_Finish(DFHackObject* gui); DFHACK_EXPORT int Gui_ReadPauseState(DFHackObject* gui); +DFHACK_EXPORT int Gui_SetPauseState(DFHackObject* gui, int8_t paused); DFHACK_EXPORT int Gui_ReadViewScreen(DFHackObject* gui, t_viewscreen* viewscreen); DFHACK_EXPORT int Gui_ReadMenuState(DFHackObject* gui, uint32_t* menuState); diff --git a/library/include/dfhack-c/modules/Maps_C.h b/library/include/dfhack-c/modules/Maps_C.h index fdbfdf1be..3199fc3af 100644 --- a/library/include/dfhack-c/modules/Maps_C.h +++ b/library/include/dfhack-c/modules/Maps_C.h @@ -36,6 +36,8 @@ extern "C" { DFHACK_EXPORT int Maps_Start(DFHackObject* maps); DFHACK_EXPORT int Maps_Finish(DFHackObject* maps); +DFHACK_EXPORT uint16_t* Maps_ReadGeology(DFHackObject* maps); + DFHACK_EXPORT t_feature* Maps_ReadGlobalFeatures(DFHackObject* maps); DFHACK_EXPORT void Maps_getSize(DFHackObject* maps, uint32_t* x, uint32_t* y, uint32_t* z); diff --git a/library/include/dfhack/DFError.h b/library/include/dfhack/DFError.h index fcb80f724..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 @@ -39,6 +40,7 @@ namespace DFHack * the whole array of DFHack exceptions from the rest */ class DFHACK_EXPORT All : public std::exception{}; + class DFHACK_EXPORT AllMemdef : public All{}; class DFHACK_EXPORT NoProcess : public All { public: @@ -80,7 +82,7 @@ namespace DFHack }; // a call to DFHack::mem_info::get* failed - class DFHACK_EXPORT MissingMemoryDefinition : public All + class DFHACK_EXPORT MissingMemoryDefinition : public AllMemdef { public: MissingMemoryDefinition(const char* _type, const std::string _key) : type(_type), key(_key) @@ -113,7 +115,7 @@ namespace DFHack }; // a call to DFHack::mem_info::get* failed - class DFHACK_EXPORT UnsetMemoryDefinition : public All + class DFHACK_EXPORT UnsetMemoryDefinition : public AllMemdef { public: UnsetMemoryDefinition(const char* _type, const std::string _key) : type(_type), key(_key) @@ -145,7 +147,41 @@ namespace DFHack } }; - // Syntax errors and whatnot, the xml cant be read + // a call to DFHack::mem_info::get* failed + class DFHACK_EXPORT InvalidMemoryDefinition : public AllMemdef + { + public: + InvalidMemoryDefinition(const char* _type, const std::string _key) : type(_type), key(_key) + { + std::stringstream s; + s << "memory object is INVALID: type " << type << " key " << key; + full = s.str(); + } + // Used by functios using integer keys, such as getTrait + InvalidMemoryDefinition(const char* _type, uint32_t _key) : type(_type) + { + std::stringstream s1; + s1 << _key; + key = s1.str(); + + std::stringstream s; + s << "memory object is INVALID: type " << type << " key " << key; + full = s.str(); + } + virtual ~InvalidMemoryDefinition() throw(){}; + + std::string full; + const std::string type; + std::string key; + + virtual const char* what() const throw() + { + return full.c_str(); + } + }; + + + // Syntax errors and whatnot, the xml can't be read class DFHACK_EXPORT MemoryXmlParse : public All { public: @@ -156,14 +192,12 @@ namespace DFHack s << "error " << id << ": " << desc << ", at row " << row << " col " << col; full = s.str(); } - - + std::string full; const std::string desc; const int id; const int row; const int col; - virtual ~MemoryXmlParse() throw(){}; virtual const char* what() const throw() 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 282e1b95f..7e81a94de 100644 --- a/library/include/dfhack/DFProcess.h +++ b/library/include/dfhack/DFProcess.h @@ -72,6 +72,7 @@ namespace DFHack 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 @@ -163,6 +164,8 @@ namespace DFHack virtual VersionInfo *getDescriptor() = 0; /// get the DF Process ID virtual int getPID() = 0; + /// get the DF Process FilePath + virtual std::string getPath() = 0; /// get module index by name and version. bool 1 = error virtual bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) = 0; /// get the SHM start if available @@ -171,200 +174,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(); - // 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(); - // 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;}; - }; -#endif } #endif diff --git a/library/include/dfhack/DFTileTypes.h b/library/include/dfhack/DFTileTypes.h index e3b660711..5637dc2ab 100644 --- a/library/include/dfhack/DFTileTypes.h +++ b/library/include/dfhack/DFTileTypes.h @@ -26,84 +26,236 @@ distribution. #define TILETYPES_H_INCLUDED #include "DFPragma.h" +#include "DFExport.h" namespace DFHack { + // tile class -- determines the general shape of the tile - enum TileClass - { - EMPTY,// empty - - WALL, - PILLAR, - FORTIFICATION, - - STAIR_UP, - STAIR_DOWN, - STAIR_UPDOWN, - - RAMP,// ramps have no direction - RAMP_TOP,// the top of a ramp. I assume it's used for path finding. - - FLOOR,// generic floor - TREE_DEAD, - TREE_OK, - SAPLING_DEAD, - SAPLING_OK, - SHRUB_DEAD, - SHRUB_OK, - BOULDER, - PEBBLES + // enum and lookup table for string names created using X macros + #define TILECLASS_MACRO \ + X(EMPTY, "") \ + X(WALL, "") \ + X(PILLAR, "") \ + X(FORTIFICATION, "") \ + X(STAIR_UP, "") \ + X(STAIR_DOWN, "") \ + X(STAIR_UPDOWN, "") \ + X(RAMP, "ramps have no direction" ) \ + X(RAMP_TOP, "used for pathing?" ) \ + X(FLOOR, "") \ + X(TREE_DEAD, "") \ + X(TREE_OK, "") \ + X(SAPLING_DEAD, "") \ + X(SAPLING_OK, "") \ + X(SHRUB_DEAD, "") \ + X(SHRUB_OK, "") \ + X(BOULDER, "") \ + X(PEBBLES, "") + //end TILECLASS_MACRO + + //define tile class enum + #define X(name,comment) name, + enum TileClass { + tileclass_invalid=-1, + TILECLASS_MACRO + tileclass_count, }; - // material -- what material the tile is made of - enum TileMaterial - { - AIR,// empty - SOIL,// ordinary soil. material depends on geology - STONE,// ordinary layer stone. material depends on geology - FEATSTONE,// map feature stone. used for things like hell, the hell temple or adamantine tubes. material depends on local/global feature - OBSIDIAN,// cast obsidian - - VEIN,// vein stone. material depends on mineral veins present - ICE,// frozen water... not much to say. you can determine what was on the tile before it froze by looking into the 'ice vein' objects - GRASS,// grass (has 4 variants) - GRASS2,// grass (has 4 variants) - GRASS_DEAD,// dead grass (has 4 variants) - GRASS_DRY,// dry grass (has 4 variants) - DRIFTWOOD,// non-specified wood - normally on top of the local layer stone/soil. - HFS,// the stuff demon pits are made of - this makes them different from ordinary pits. - MAGMA,// material for semi-molten rock and 'magma flow' tiles - CAMPFIRE,// human armies make them when they siege. The original tile may be lost? - FIRE,// burning grass - ASHES,// what remains from a FIRE - CONSTRUCTED,// tile material depends on the construction present - CYAN_GLOW// the glowy stuff that disappears from the demon temple when you take the sword. + #undef X + + DFHACK_EXPORT extern const char *TileClassString[]; + + #define TILEMATERIAL_MACRO \ + X(AIR, "empty" ) \ + X(SOIL, "ordinary soil. material depends on geology" ) \ + X(STONE, "ordinary layer stone. material depends on geology" ) \ + X(FEATSTONE, "map special stone. used for things like hell, the hell temple or adamantine tubes. material depends on local/global special" ) \ + X(OBSIDIAN, "cast obsidian" ) \ + X(VEIN, "vein stone. material depends on mineral veins present" ) \ + X(ICE, "frozen water... not much to say. you can determine what was on the tile before it froze by looking into the 'ice vein' objects" ) \ + X(GRASS, "grass (has 4 variants)" ) \ + X(GRASS2, "grass (has 4 variants)" ) \ + X(GRASS_DEAD, "dead grass (has 4 variants)" ) \ + X(GRASS_DRY, "dry grass (has 4 variants)" ) \ + X(DRIFTWOOD, "non-specified wood - normally on top of the local layer stone/soil." ) \ + X(HFS, "the stuff demon pits are made of - this makes them different from ordinary pits." ) \ + X(MAGMA, "material for semi-molten rock and 'magma flow' tiles" ) \ + X(CAMPFIRE, "human armies make them when they siege. The original tile may be lost?" ) \ + X(FIRE, "burning grass" ) \ + X(ASHES, "what remains from a FIRE" ) \ + X(CONSTRUCTED,"tile material depends on the construction present" ) \ + X(CYAN_GLOW, "the glowy stuff that disappears from the demon temple when you take the sword." ) + //end TILEMATERIAL_MACRO + + // material enum + #define X(name,comment) name, + enum TileMaterial { + tilematerial_invalid=-1, + TILEMATERIAL_MACRO + tilematerial_count, }; + #undef X + + + DFHACK_EXPORT extern const char *TileMaterialString[]; + + // Special specials of the tile. + // Not the best way to do this, but compatible with existing code. + // When the TileType class gets created, everything should be re-thought. + #define TILESPECIAL_MACRO \ + X(NORMAL, "Default for all type, nothing present" ) \ + X(SPECIAL, "General purpose, for any unique tile which can not otherwise be differenciated" ) \ + X(POOL, "Murky Pool, will gather water from rain" ) \ + X(STREAM, "Streams (and brooks too? maybe?)" ) \ + X(STREAM_TOP, "The walkable surface of a stream/brook" ) \ + X(RIVER_SOURCE, "Rivers Source, when it exists on a map" ) \ + X(RIVER, "Rivers, and their entering and exiting tiles" ) \ + X(WATERFALL, "Special case for Waterfall Landing. How's this used?" ) \ + X(ENDLESS, "Eerie Pit and Old Chasm/Endless Pit" ) \ + X(CRACKED, "Walls being dug" ) \ + X(DAMAGED, "Walls being dug" ) \ + X(WORN, "Walls being dug ??" ) \ + X(SMOOTH, "Walls and floors." ) + //end TILESPECIAL_MACRO + + //special enum + #define X(name,comment) TILE_##name, + enum TileSpecial { + tilespecial_invalid=-1, + TILESPECIAL_MACRO + tilespecial_count, + }; + #undef X + + DFHACK_EXPORT extern const char *TileSpecialString[]; + // variants are used for tiles, where there are multiple variants of the same - like grass floors enum TileVariant { - VAR_1, + tilevariant_invalid=-1, + VAR_1, //Yes, the value of VAR_1 is 0. It's legacy. Deal with it. VAR_2, VAR_3, - VAR_4 + VAR_4, }; - + + + //Mainly walls and rivers + //Byte values are used because walls can have either 1 or 2 in any given direction. + const int TileDirectionCount = 4; + union TileDirection + { + uint32_t whole; + unsigned char b[TileDirectionCount]; + struct + { + //Maybe should add 'up' and 'down' for Z-levels? + unsigned char north,south,west,east; + }; + + inline TileDirection() + { + whole = 0; + } + TileDirection( uint32_t whole_bits) + { + whole = whole_bits; + } + TileDirection( unsigned char North, unsigned char South, unsigned char West, unsigned char East ) + { + north=North; south=South; east=East; west=West; + } + TileDirection( const char *dir ) + { + //This one just made for fun. + //Supports N S E W + const char *p = dir; + unsigned char *l=0; + north=south=east=west=0; + if(!dir) return; + + for( ;*p;++p) + { + switch(*p) + { + case 'N': //North / Up + case 'n': + ++north; l=&north; break; + case 'S': //South / Down + case 's': + ++south; l=&south; break; + case 'E': //East / Right + case 'e': + ++east; l=&east; break; + case 'W': //West / Left + case 'w': + ++west; l=&west; break; + case '-': + case ' ': + //Explicitly ensure dash and space are ignored. + //Other characters/symbols may be assigned in the future. + break; + default: + if( l && '0' <= *p && '9' >= *p ) + *l += *p - '0'; + break; + } + } + } + + //may be useful for some situations + inline uint32_t sum() const + { + return 0L + north + south + east + west; + } + + //Gives a string that represents the direction. + //This is a static string, overwritten with every call! + //Support values > 2 even though they should never happen. + //Copy string if it will be used. + inline char * getStr() const + { + static char str[16]; + //type punning trick + *( (uint64_t *)str ) = *( (uint64_t *)"--------" ); + str[8]=0; + #define DIRECTION(x,i,c) \ + if(x){ \ + str[i]=c; \ + if(1==x) ; \ + else if(2==x) str[i+1]=c; \ + else str[i+1]='0'+x; \ + } + + DIRECTION(north,0,'N') + DIRECTION(south,2,'S') + DIRECTION(west,4,'W') + DIRECTION(east,6,'E') + #undef DIRECTION + return str; + } + + + }; + struct TileRow { const char * name; TileClass c; TileMaterial m; TileVariant v; + TileSpecial s; + TileDirection d; }; - - #define TILE_TYPE_ARRAY_LENGTH 520 + + #define TILE_TYPE_ARRAY_LENGTH 520 const TileRow tileTypeTable[TILE_TYPE_ARRAY_LENGTH] = { // 0 {"void",EMPTY, AIR, VAR_1}, {"ramp top",RAMP_TOP, AIR, VAR_1}, - {"pool",FLOOR, SOIL, VAR_1}, + {"pool",FLOOR, SOIL, VAR_1, TILE_POOL}, {0, EMPTY, AIR, VAR_1}, {0, EMPTY, AIR, VAR_1}, {0, EMPTY, AIR, VAR_1}, @@ -111,7 +263,7 @@ namespace DFHack {0, EMPTY, AIR, VAR_1}, {0, EMPTY, AIR, VAR_1}, {0, EMPTY, AIR, VAR_1}, - + // 10 {0, EMPTY, AIR, VAR_1}, {0, EMPTY, AIR, VAR_1}, @@ -123,7 +275,7 @@ namespace DFHack {0, EMPTY, AIR, VAR_1}, {0, EMPTY, AIR, VAR_1}, {"driftwood stack",FLOOR, DRIFTWOOD, VAR_1}, - + // 20 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -135,31 +287,31 @@ namespace DFHack {"ice stair up",STAIR_UP, ICE, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 30 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {"empty space",EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {"shrub",SHRUB_OK, SOIL, VAR_1}, - {"chasm",FLOOR, AIR, VAR_1}, + {"chasm",FLOOR, AIR, VAR_1, TILE_ENDLESS }, {"obsidian stair up/down",STAIR_UPDOWN, OBSIDIAN, VAR_1}, {"obsidian stair down",STAIR_DOWN, OBSIDIAN, VAR_1}, {"obsidian stair up",STAIR_UP, OBSIDIAN, VAR_1}, {"soil stair up/down",STAIR_UPDOWN, SOIL, VAR_1}, - + // 40 {"soil stair down",STAIR_DOWN, SOIL, VAR_1}, {"soil stair up",STAIR_UP, SOIL, VAR_1}, - {"eerie pit",FLOOR, HFS, VAR_1}, - {"smooth stone floor",FLOOR, STONE, VAR_1}, - {"smooth obsidian floor",FLOOR, OBSIDIAN, VAR_1}, - {"smooth featstone? floor",FLOOR, FEATSTONE, VAR_1}, - {"smooth vein floor",FLOOR, VEIN, VAR_1}, - {"smooth ice floor",FLOOR, ICE, VAR_1}, + {"eerie pit",FLOOR, HFS, VAR_1, TILE_ENDLESS}, + {"smooth stone floor",FLOOR, STONE, VAR_1 , TILE_SMOOTH }, + {"smooth obsidian floor",FLOOR, OBSIDIAN, VAR_1 , TILE_SMOOTH }, + {"smooth featstone? floor",FLOOR, FEATSTONE, VAR_1 , TILE_SMOOTH }, + {"smooth vein floor",FLOOR, VEIN, VAR_1 , TILE_SMOOTH }, + {"smooth ice floor",FLOOR, ICE, VAR_1 , TILE_SMOOTH }, {0 ,EMPTY, AIR, VAR_1}, {"grass stair up/down",STAIR_UPDOWN, GRASS, VAR_1}, - + // 50 {"grass stair down",STAIR_DOWN, GRASS, VAR_1}, {"grass stair up",STAIR_UP, GRASS, VAR_1}, @@ -171,7 +323,7 @@ namespace DFHack {"stone stair up",STAIR_UP, STONE, VAR_1}, {"vein stair up/down",STAIR_UPDOWN, VEIN, VAR_1}, {"vein stair down",STAIR_DOWN, VEIN, VAR_1}, - + // 60 {"vein stair up",STAIR_UP, VEIN, VAR_1}, {"featstone? stair up/down",STAIR_UPDOWN, FEATSTONE, VAR_1}, @@ -183,7 +335,7 @@ namespace DFHack {"campfire",FLOOR, CAMPFIRE, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 70 {"fire",FLOOR, FIRE, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -195,7 +347,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {"stone pillar",PILLAR, STONE, VAR_1}, - + //80 {"obsidian pillar",PILLAR, OBSIDIAN, VAR_1}, {"featstone? pillar",PILLAR, FEATSTONE, VAR_1}, @@ -206,10 +358,10 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - {"waterfall landing",FLOOR, SOIL, VAR_1}, // verify material - + {"waterfall landing",FLOOR, SOIL, VAR_1, TILE_WATERFALL }, // verify material + // 90 - {"river source",FLOOR, SOIL, VAR_1}, // verify material + {"river source",FLOOR, SOIL, VAR_1, TILE_RIVER_SOURCE }, // verify material {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -219,7 +371,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 100 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -231,7 +383,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 110 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -243,7 +395,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 120 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -255,7 +407,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 130 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -267,7 +419,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 140 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -279,7 +431,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 150 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -291,7 +443,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 160 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -303,7 +455,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 170 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -311,11 +463,11 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - {"cracked stone wall" ,WALL, STONE, VAR_1}, - {"damaged stone wall" ,WALL, STONE, VAR_1}, - {"worn stone wall" ,WALL, STONE, VAR_1}, + {"cracked stone wall" ,WALL, STONE, VAR_1, TILE_CRACKED }, + {"damaged stone wall" ,WALL, STONE, VAR_1, TILE_DAMAGED }, + {"worn stone wall" ,WALL, STONE, VAR_1, TILE_WORN }, {0 ,EMPTY, AIR, VAR_1}, - + // 180 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -327,7 +479,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 190 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -339,7 +491,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 200 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -351,7 +503,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 210 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -363,7 +515,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {"stone wall" ,WALL, STONE, VAR_1}, - + // 220 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -375,7 +527,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 230 {0 ,EMPTY, AIR, VAR_1}, {"sapling" ,SAPLING_OK, SOIL, VAR_1}, @@ -387,7 +539,7 @@ namespace DFHack {"stone ramp" ,RAMP, STONE, VAR_1}, {"obsidian ramp" ,RAMP, OBSIDIAN, VAR_1}, {"featstone? ramp" ,RAMP, FEATSTONE, VAR_1}, - + // 240 {"vein ramp" ,RAMP, VEIN, VAR_1}, {"soil ramp" ,RAMP, SOIL, VAR_1}, @@ -399,7 +551,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 250 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -411,7 +563,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {"ice floor" ,FLOOR, ICE, VAR_2}, {"ice floor" ,FLOOR, ICE, VAR_3}, - + // 260 {"ice floor" ,FLOOR, ICE, VAR_4}, {"furrowed soil" ,FLOOR, SOIL, VAR_1}, @@ -422,80 +574,80 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {"glowing floor" ,FLOOR, CYAN_GLOW, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - {"smooth obsidian wall RD2",WALL,OBSIDIAN,VAR_1}, - + {"smooth obsidian wall RD2",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "--SS--E-" }, + // 270 - {"smooth obsidian wall R2D",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall R2U",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall RU2",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall L2U",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall LU2",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall L2D",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall LD2",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall LRUD",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall RUD",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall LRD",WALL,OBSIDIAN,VAR_1}, - + {"smooth obsidian wall R2D",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "--S---EE" }, + {"smooth obsidian wall R2U",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "N-----EE" }, + {"smooth obsidian wall RU2",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "NN----E-" }, + {"smooth obsidian wall L2U",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "N---WW--" }, + {"smooth obsidian wall LU2",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "NN--W---" }, + {"smooth obsidian wall L2D",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "--S-WW--" }, + {"smooth obsidian wall LD2",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "--SSW---" }, + {"smooth obsidian wall LRUD",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH ,"N-S-W-E-" }, + {"smooth obsidian wall RUD",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "N-S---E-" }, + {"smooth obsidian wall LRD",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "--S-W-E-" }, + // 280 - {"smooth obsidian wall LRU",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall LUD",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall RD",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall RU",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall LU",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall LD",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall UD",WALL,OBSIDIAN,VAR_1}, - {"smooth obsidian wall LR",WALL,OBSIDIAN,VAR_1}, - {"smooth featstone wall RD2",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall R2D",WALL,FEATSTONE,VAR_1}, - + {"smooth obsidian wall LRU",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "N---W-E-" }, + {"smooth obsidian wall LUD",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "N-S-W---" }, + {"smooth obsidian wall RD",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "--S---E-" }, + {"smooth obsidian wall RU",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "N-----E-" }, + {"smooth obsidian wall LU",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "N---W---" }, + {"smooth obsidian wall LD",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "--S-W---" }, + {"smooth obsidian wall UD",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "N-S-----" }, + {"smooth obsidian wall LR",WALL,OBSIDIAN,VAR_1 , TILE_SMOOTH , "----W-E-" }, + {"smooth featstone wall RD2",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "--SS--E-" }, + {"smooth featstone wall R2D",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "--S---EE" }, + // 290 - {"smooth featstone wall R2U",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall RU2",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall L2U",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall LU2",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall L2D",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall LD2",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall LRUD",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall RUD",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall LRD",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall LRU",WALL,FEATSTONE,VAR_1}, - + {"smooth featstone wall R2U",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "N-----EE" }, + {"smooth featstone wall RU2",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "NN----E-" }, + {"smooth featstone wall L2U",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "N---WW--" }, + {"smooth featstone wall LU2",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "NN--W---" }, + {"smooth featstone wall L2D",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "--S-WW--" }, + {"smooth featstone wall LD2",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "--SSW---" }, + {"smooth featstone wall LRUD",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH ,"N-S-W-E-" }, + {"smooth featstone wall RUD",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "N-S---E-" }, + {"smooth featstone wall LRD",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "--S-W-E-" }, + {"smooth featstone wall LRU",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "N---W-E-" }, + //300 - {"smooth featstone wall LUD",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall RD",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall RU",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall LU",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall LD",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall UD",WALL,FEATSTONE,VAR_1}, - {"smooth featstone wall LR",WALL,FEATSTONE,VAR_1}, - {"smooth stone wall RD2",WALL,STONE,VAR_1}, - {"smooth stone wall R2D",WALL,STONE,VAR_1}, - {"smooth stone wall R2U",WALL,STONE,VAR_1}, - + {"smooth featstone wall LUD",WALL,FEATSTONE,VAR_1, TILE_SMOOTH , "N-S-W---" }, + {"smooth featstone wall RD",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "--S---E-" }, + {"smooth featstone wall RU",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "N-----E-" }, + {"smooth featstone wall LU",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "N---W---" }, + {"smooth featstone wall LD",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "--S-W---" }, + {"smooth featstone wall UD",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "N-S-----" }, + {"smooth featstone wall LR",WALL,FEATSTONE,VAR_1 , TILE_SMOOTH , "----W-E-" }, + {"smooth stone wall RD2",WALL,STONE,VAR_1 , TILE_SMOOTH , "--SS--E-" }, + {"smooth stone wall R2D",WALL,STONE,VAR_1 , TILE_SMOOTH , "--S---EE" }, + {"smooth stone wall R2U",WALL,STONE,VAR_1 , TILE_SMOOTH , "N-----EE" }, + //310 - {"smooth stone wall RU2",WALL,STONE,VAR_1}, - {"smooth stone wall L2U",WALL,STONE,VAR_1}, - {"smooth stone wall LU2",WALL,STONE,VAR_1}, - {"smooth stone wall L2D",WALL,STONE,VAR_1}, - {"smooth stone wall LD2",WALL,STONE,VAR_1}, - {"smooth stone wall LRUD",WALL,STONE,VAR_1}, - {"smooth stone wall RUD",WALL,STONE,VAR_1}, - {"smooth stone wall LRD",WALL,STONE,VAR_1}, - {"smooth stone wall LRU",WALL,STONE,VAR_1}, - {"smooth stone wall LUD",WALL,STONE,VAR_1}, - + {"smooth stone wall RU2",WALL,STONE,VAR_1 , TILE_SMOOTH , "NN----E-" }, + {"smooth stone wall L2U",WALL,STONE,VAR_1 , TILE_SMOOTH , "N---WW--" }, + {"smooth stone wall LU2",WALL,STONE,VAR_1 , TILE_SMOOTH , "NN--W---" }, + {"smooth stone wall L2D",WALL,STONE,VAR_1 , TILE_SMOOTH , "--S-WW--" }, + {"smooth stone wall LD2",WALL,STONE,VAR_1 , TILE_SMOOTH , "--SSW---" }, + {"smooth stone wall LRUD",WALL,STONE,VAR_1 , TILE_SMOOTH , "N-S-W-E-" }, + {"smooth stone wall RUD",WALL,STONE,VAR_1 , TILE_SMOOTH , "N-S---E-" }, + {"smooth stone wall LRD",WALL,STONE,VAR_1 , TILE_SMOOTH , "--S-W-E-" }, + {"smooth stone wall LRU",WALL,STONE,VAR_1 , TILE_SMOOTH , "N---W-E-" }, + {"smooth stone wall LUD",WALL,STONE,VAR_1 , TILE_SMOOTH , "N-S-W---" }, + //320 - {"smooth stone wall RD",WALL,STONE,VAR_1}, - {"smooth stone wall RU",WALL,STONE,VAR_1}, - {"smooth stone wall LU",WALL,STONE,VAR_1}, - {"smooth stone wall LD",WALL,STONE,VAR_1}, - {"smooth stone wall UD",WALL,STONE,VAR_1}, - {"smooth stone wall LR",WALL,STONE,VAR_1}, + {"smooth stone wall RD",WALL,STONE,VAR_1 , TILE_SMOOTH , "--S---E-" }, + {"smooth stone wall RU",WALL,STONE,VAR_1 , TILE_SMOOTH , "N-----E-" }, + {"smooth stone wall LU",WALL,STONE,VAR_1 , TILE_SMOOTH , "N---W---" }, + {"smooth stone wall LD",WALL,STONE,VAR_1 , TILE_SMOOTH , "--S-W---" }, + {"smooth stone wall UD",WALL,STONE,VAR_1 , TILE_SMOOTH , "N-S-----" }, + {"smooth stone wall LR",WALL,STONE,VAR_1 , TILE_SMOOTH , "----W-E-" }, {"obsidian fortification",FORTIFICATION,OBSIDIAN,VAR_1}, {"featstone? fortification",FORTIFICATION,FEATSTONE,VAR_1}, - {"cracked obsidian wall",WALL,OBSIDIAN,VAR_1}, - {"damaged obsidian wall",WALL,OBSIDIAN,VAR_1}, - + {"cracked obsidian wall",WALL,OBSIDIAN,VAR_1, TILE_CRACKED }, + {"damaged obsidian wall",WALL,OBSIDIAN,VAR_1, TILE_DAMAGED }, + // 330 {"worn obsidian wall",WALL,OBSIDIAN,VAR_1}, {"obsidian wall",WALL,OBSIDIAN,VAR_1}, @@ -503,15 +655,15 @@ namespace DFHack MAPTILE_FEATSTONE_WALL_WORN2, MAPTILE_FEATSTONE_WALL_WORN3, MAPTILE_FEATSTONE_WALL,*/ - {"cracked featstone wall",WALL,STONE,VAR_1}, - {"damaged featstone wall",WALL,STONE,VAR_1}, - {"worn featstone wall",WALL,STONE,VAR_1}, + {"cracked featstone wall",WALL,STONE,VAR_1, TILE_CRACKED }, + {"damaged featstone wall",WALL,STONE,VAR_1, TILE_DAMAGED }, + {"worn featstone wall",WALL,STONE,VAR_1, TILE_WORN }, {"featstone wall",WALL,STONE,VAR_1}, {"stone floor",FLOOR,STONE,VAR_1}, {"stone floor",FLOOR,STONE,VAR_2}, {"stone floor",FLOOR,STONE,VAR_3}, {"stone floor",FLOOR,STONE,VAR_4}, - + // 340 {"obsidian floor",FLOOR,OBSIDIAN,VAR_1}, {"obsidian floor",FLOOR,OBSIDIAN,VAR_2}, @@ -523,7 +675,7 @@ namespace DFHack {"featstone floor 4",FLOOR,FEATSTONE,VAR_4}, {"grass 1",FLOOR,GRASS,VAR_1}, {"grass 2",FLOOR,GRASS,VAR_2}, - + // 350 {"grass 3",FLOOR,GRASS,VAR_3}, {"grass 4",FLOOR,GRASS,VAR_4}, @@ -535,34 +687,34 @@ namespace DFHack {"wet soil floor",FLOOR,SOIL,VAR_2}, {"wet soil floor",FLOOR,SOIL,VAR_3}, {"wet soil floor",FLOOR,SOIL,VAR_4}, - + // 360 {"ice fortification",FORTIFICATION,ICE,VAR_1}, - {"cracked ice wall",WALL,ICE,VAR_1}, - {"damaged ice wall",WALL,ICE,VAR_1}, - {"worn ice wall",WALL,ICE,VAR_1}, + {"cracked ice wall",WALL,ICE,VAR_1, TILE_CRACKED}, + {"damaged ice wall",WALL,ICE,VAR_1, TILE_DAMAGED}, + {"worn ice wall",WALL,ICE,VAR_1, TILE_WORN }, {"ice wall",WALL,ICE,VAR_1}, - {"river N",FLOOR,SOIL,VAR_1}, - {"river S",FLOOR,SOIL,VAR_1}, - {"river E",FLOOR,SOIL,VAR_1}, - {"river W",FLOOR,SOIL,VAR_1}, - {"river NW",FLOOR,SOIL,VAR_1}, - + {"river N",FLOOR,SOIL,VAR_1, TILE_RIVER , "N" }, + {"river S",FLOOR,SOIL,VAR_1, TILE_RIVER , "S" }, + {"river E",FLOOR,SOIL,VAR_1, TILE_RIVER , "E" }, + {"river W",FLOOR,SOIL,VAR_1, TILE_RIVER , "W" }, + {"river NW",FLOOR,SOIL,VAR_1, TILE_RIVER, "NW"}, + //370 - {"river NE",FLOOR,SOIL,VAR_1}, - {"river SW",FLOOR,SOIL,VAR_1}, - {"river SE",FLOOR,SOIL,VAR_1}, - {"stream bed N",FLOOR,SOIL,VAR_1}, - {"stream bed S",FLOOR,SOIL,VAR_1}, - {"stream bed E",FLOOR,SOIL,VAR_1}, - {"stream bed W",FLOOR,SOIL,VAR_1}, - {"stream bed NW",FLOOR,SOIL,VAR_1}, - {"stream bed NE",FLOOR,SOIL,VAR_1}, - {"stream bed SW",FLOOR,SOIL,VAR_1}, - + {"river NE",FLOOR,SOIL,VAR_1, TILE_RIVER , "NE" }, + {"river SW",FLOOR,SOIL,VAR_1, TILE_RIVER , "SW" }, + {"river SE",FLOOR,SOIL,VAR_1, TILE_RIVER , "SE" }, + {"stream bed N",FLOOR,SOIL,VAR_1, TILE_STREAM , "N" }, + {"stream bed S",FLOOR,SOIL,VAR_1, TILE_STREAM , "S" }, + {"stream bed E",FLOOR,SOIL,VAR_1, TILE_STREAM , "E" }, + {"stream bed W",FLOOR,SOIL,VAR_1, TILE_STREAM , "W" }, + {"stream bed NW",FLOOR,SOIL,VAR_1, TILE_STREAM, "NW" }, + {"stream bed NE",FLOOR,SOIL,VAR_1, TILE_STREAM, "NE" }, + {"stream bed SW",FLOOR,SOIL,VAR_1, TILE_STREAM, "SW" }, + // 380 - {"stream bed SE",FLOOR,SOIL,VAR_1}, - {"stream top",FLOOR,SOIL,VAR_1}, + {"stream bed SE",FLOOR,SOIL,VAR_1, TILE_STREAM, "SE" }, + {"stream top",FLOOR,SOIL,VAR_1, TILE_STREAM_TOP }, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -571,7 +723,7 @@ namespace DFHack {"dry grass 1",FLOOR,GRASS_DRY,VAR_1}, {"dry grass 2",FLOOR,GRASS_DRY,VAR_2}, {"dry grass 3",FLOOR,GRASS_DRY,VAR_3}, - + // 390 {"dry grass 4",FLOOR,GRASS_DRY,VAR_4}, {"dead tree",TREE_DEAD,SOIL,VAR_1}, @@ -583,7 +735,7 @@ namespace DFHack {"dead grass 4",FLOOR,GRASS_DEAD,VAR_4}, {"grass B1",FLOOR,GRASS2,VAR_1}, {"grass B2",FLOOR,GRASS2,VAR_2}, - + // 400 {"grass B3",FLOOR,GRASS2,VAR_3}, {"grass B4",FLOOR,GRASS2,VAR_4}, @@ -595,7 +747,7 @@ namespace DFHack {"pebbles 3",PEBBLES,STONE,VAR_3}, {"pebbles 4",PEBBLES,STONE,VAR_4}, {"obsidian shards",PEBBLES,OBSIDIAN,VAR_1}, - + // 410 {"obsidian shards",PEBBLES,OBSIDIAN,VAR_2}, {"obsidian shards",PEBBLES,OBSIDIAN,VAR_3}, @@ -604,34 +756,34 @@ namespace DFHack {"featstone? pebbles",PEBBLES,FEATSTONE,VAR_2}, {"featstone? pebbles",PEBBLES,FEATSTONE,VAR_3}, {"featstone? pebbles",PEBBLES,FEATSTONE,VAR_4}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "--SS--E-"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "--S---EE"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "N-----EE" }, + // 420 - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "NN----E-"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "N---WW--"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "NN--W---"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "--S-WW--" }, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "--SSW---"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "N-S-W-E-"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "N-S---E-"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "--S-W-E-"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "N---W-E-"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "N-S-W---"}, + // 430 - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, - {"smooth vein wall",WALL,VEIN,VAR_1}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "--S---E-"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "N-----E-"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "N---W---"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "--S-W---"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "N-S-----"}, + {"smooth vein wall",WALL,VEIN,VAR_1 , TILE_SMOOTH , "----W-E-"}, {"vein fortification",FORTIFICATION,VEIN,VAR_1}, - {"cracked vein wall",WALL,VEIN,VAR_1}, - {"damaged vein wall",WALL,VEIN,VAR_1}, - {"worn vein wall",WALL,VEIN,VAR_1}, - + {"cracked vein wall",WALL,VEIN,VAR_1, TILE_CRACKED }, + {"damaged vein wall",WALL,VEIN,VAR_1, TILE_DAMAGED }, + {"worn vein wall",WALL,VEIN,VAR_1 , TILE_WORN }, + // 440 {"vein wall",WALL,VEIN,VAR_1}, {"vein floor",FLOOR,VEIN,VAR_1}, @@ -643,31 +795,31 @@ namespace DFHack {"vein pebbles",PEBBLES,VEIN,VAR_2}, {"vein pebbles",PEBBLES,VEIN,VAR_3}, {"vein pebbles",PEBBLES,VEIN,VAR_4}, - + // 450 - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "--SS--E-"}, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "--S---EE" }, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "N-----EE" }, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "NN----E-"}, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "N---WW--" }, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "NN--W---" }, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "--S-WW--" }, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "--SSW---" }, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "N-S-W-E-"}, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "N-S---E-" }, + // 460 - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {"smooth ice wall",WALL,ICE,VAR_1}, - {0 ,EMPTY, AIR, VAR_1}, - + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "--S-W-E-" }, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "N---W-E-" }, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "N-S-W---"}, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "--S---E-"}, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "N-----E-" }, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "N---W---" }, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "--S-W---" }, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "N-S-----" }, + {"smooth ice wall",WALL,ICE,VAR_1 , TILE_SMOOTH , "----W-E-"}, + {0 ,EMPTY, AIR, VAR_1}, + // 470 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -679,7 +831,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 480 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -691,7 +843,7 @@ namespace DFHack {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, - + // 490 {0 ,EMPTY, AIR, VAR_1}, {0 ,EMPTY, AIR, VAR_1}, @@ -699,36 +851,36 @@ namespace DFHack {"constructed floor",FLOOR,CONSTRUCTED, VAR_1}, {"constructed fortification",FORTIFICATION,CONSTRUCTED, VAR_1}, {"constructed pillar",PILLAR,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "--SS--E-" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "--S---EE" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "N-----EE" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "NN----E-" }, + // 500 - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "N---WW--" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "NN--W---" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "--S-WW--" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "--SSW---" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "N-S-W-E-" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "N-S---E-" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "--S-W-E-" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "N---W-E-" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "N-S-W---" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "--S---E-" }, + // 510 - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, - {"constructed wall",WALL,CONSTRUCTED, VAR_1}, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "N-----E-" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "N---W---" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "--S-W---" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "N-S-----" }, + {"constructed wall",WALL,CONSTRUCTED, VAR_1 ,TILE_NORMAL, "----W-E-" }, {"constructed stair up/down",STAIR_UPDOWN,CONSTRUCTED, VAR_1}, {"constructed stair down",STAIR_DOWN,CONSTRUCTED, VAR_1}, {"constructed stair up",STAIR_UP,CONSTRUCTED, VAR_1}, {"constructed ramp",RAMP,CONSTRUCTED, VAR_1}, {0 ,EMPTY, AIR, VAR_1} // end }; - + inline bool isWallTerrain(int in) { @@ -740,30 +892,112 @@ namespace DFHack { return tileTypeTable[in].c >= FLOOR && tileTypeTable[in].c <= PEBBLES; } - + inline bool isRampTerrain(int in) { return tileTypeTable[in].c == RAMP; } - + inline bool isStairTerrain(int in) { return tileTypeTable[in].c >= STAIR_UP && tileTypeTable[in].c <= STAIR_UPDOWN; } - + inline bool isOpenTerrain(int in) { return tileTypeTable[in].c == EMPTY; } - + inline int getVegetationType(int in) { return tileTypeTable[in].c; } + + //zilpin: for convenience, when you'll be using the tile information a lot. + inline const + TileRow * getTileTypeP(int in) + { + if( in<0 || in>=TILE_TYPE_ARRAY_LENGTH ) return 0; + return ( const TileRow * ) &tileTypeTable[in]; + } + + //zilpin: Find the first tile entry which matches the given search criteria. + //All parameters are optional. + //To omit, use the 'invalid' enum for that type (e.g. tileclass_invalid, tilematerial_invalid, etc) + //For tile directions, pass NULL to omit. + //Returns matching index in tileTypeTable, or -1 if none found. + inline + int32_t findTileType( const TileClass tclass, const TileMaterial tmat, const TileVariant tvar, const TileSpecial tspecial, const TileDirection tdir ) + { + int32_t tt; + for(tt=0;tt-1 ) if( tclass != tileTypeTable[tt].c ) continue; + if( tmat>-1 ) if( tmat != tileTypeTable[tt].m ) continue; + if( tvar>-1 ) if( tvar != tileTypeTable[tt].v ) continue; + if( tspecial>-1 ) if( tspecial != tileTypeTable[tt].s ) continue; + if( tdir.whole ) if( tdir.whole != tileTypeTable[tt].d.whole ) continue; + //Match! + return tt; + } + return -1; + } + //Convenience version of the above, to pass strings as the direction + inline + int32_t findTileType( const TileClass tclass, const TileMaterial tmat, const TileVariant tvar, const TileSpecial tspecial, const char *tdirStr ) + { + if(tdirStr){ + TileDirection tdir(tdirStr); + return findTileType(tclass,tmat,tvar,tspecial, tdir ); + }else{ + return findTileType(tclass,tmat,tvar,tspecial, 0 ); + } + } + + + //zilpin: Find a tile type similar to the one given, but with a different class. + //Useful for tile-editing operations. + //If no match found, returns the sourceType + //Definitely needs improvement for wall directions, etc. + inline + int32_t findSimilarTileType( const int32_t sourceTileType, const TileClass tclass ){ + int32_t tt, maybe=0, match=0; + int value=0, matchv=0; + const TileRow *source = &tileTypeTable[sourceTileType]; + + #ifdef assert + assert( sourceTileType >=0 && sourceTileType < TILE_TYPE_ARRAY_LENGTH ); + #endif + + for(tt=0;tts != tileTypeTable[tt].s ) continue; + + maybe=tt; value=0; + //Material is high-value match + if( tileTypeTable[tt].m == source->m ) value|=8; + //Direction is medium value match + if( tileTypeTable[tt].d.whole == source->d.whole ) value|=4; + //Variant is low-value match + if( tileTypeTable[tt].v == source->v ) value|=1; + + //Check value against last match + if( value>matchv ){ + match=tt; + matchv=value; + } + } + } + if( match ) return match; + return sourceTileType; + } } diff --git a/library/include/dfhack/VersionInfo.h b/library/include/dfhack/VersionInfo.h index 84a7087da..39e838234 100644 --- a/library/include/dfhack/VersionInfo.h +++ b/library/include/dfhack/VersionInfo.h @@ -41,6 +41,13 @@ namespace DFHack class VersionInfoPrivate; class OffsetGroupPrivate; + enum INVAL_TYPE + { + NOT_SET, + IS_INVALID, + IS_VALID + }; + /* * Offset Group */ @@ -68,31 +75,36 @@ namespace DFHack std::string getString (const std::string & key); OffsetGroup * getGroup ( const std::string & name ); - void setOffset (const std::string & key, const std::string & value); - void setAddress (const std::string & key, const std::string & value); - void setHexValue (const std::string & key, const std::string & value); - void setString (const std::string & key, const std::string & value); + void setOffset (const std::string& key, const std::string& value, const DFHack::INVAL_TYPE inval = IS_VALID); + void setOffsetValidity(const std::string& key, const DFHack::INVAL_TYPE inval = IS_VALID); + void setAddress (const std::string& key, const std::string& value, const DFHack::INVAL_TYPE inval = IS_VALID); + void setAddressValidity(const std::string& key, const DFHack::INVAL_TYPE inval = IS_VALID); + void setHexValue (const std::string& key, const std::string& value, const DFHack::INVAL_TYPE inval = IS_VALID); + void setHexValueValidity(const std::string& key, const DFHack::INVAL_TYPE inval = IS_VALID); + void setString (const std::string& key, const std::string& value, const DFHack::INVAL_TYPE inval = IS_VALID); + void setStringValidity(const std::string& key, const DFHack::INVAL_TYPE inval = IS_VALID); std::string PrintOffsets(int indentation); std::string getName(); std::string getFullName(); OffsetGroup * getParent(); + void setInvalid(INVAL_TYPE arg1); }; /* * 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/modules/Items.h b/library/include/dfhack/modules/Items.h index 3810e6037..8940b6c95 100644 --- a/library/include/dfhack/modules/Items.h +++ b/library/include/dfhack/modules/Items.h @@ -1,10 +1,6 @@ #ifndef CL_MOD_ITEMS #define CL_MOD_ITEMS -/* - * DEPRECATED, DO NOT USE UNTIL FURTHER NOTICE! - **/ - /* * Creatures */ diff --git a/library/include/dfhack/modules/Maps.h b/library/include/dfhack/modules/Maps.h index d1957c6b8..110877c62 100644 --- a/library/include/dfhack/modules/Maps.h +++ b/library/include/dfhack/modules/Maps.h @@ -99,7 +99,17 @@ 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; + }; enum BiomeOffset { eNorthWest, @@ -148,7 +158,15 @@ 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? @@ -188,8 +206,9 @@ namespace DFHack unsigned int feature_local : 1; /// this tile is a part of a global feature. can be combined with 'featstone' tiles unsigned int feature_global : 1; - /// those ripples on streams? - unsigned int liquid_character : 2; + unsigned int water_stagnant : 1; + unsigned int water_salt : 1; + // e_liquidcharacter liquid_character : 2; }; union t_designation @@ -417,7 +436,9 @@ 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 + ); private: struct Private; 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/WindowIO.h b/library/include/dfhack/modules/WindowIO.h index 6e1af9fcb..5244d2a1c 100644 --- a/library/include/dfhack/modules/WindowIO.h +++ b/library/include/dfhack/modules/WindowIO.h @@ -25,9 +25,9 @@ distribution. #ifndef KEYS_H_INCLUDED #define KEYS_H_INCLUDED -#include "dfhack/DFPragma.h" -#include "dfhack/DFExport.h" -#include "dfhack/DFModule.h" +#include "../DFPragma.h" +#include "../DFExport.h" +#include "../DFModule.h" namespace DFHack { diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 5e82be992..a5e14797f 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::UnsetMemoryDefinition &e) + catch(DFHack::Error::AllMemdef &e) { - cerr << "Custom workshops not available. Unset 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; diff --git a/library/modules/Buildings_C.cpp b/library/modules/Buildings_C.cpp index 6017a75e5..ed3e3e74e 100644 --- a/library/modules/Buildings_C.cpp +++ b/library/modules/Buildings_C.cpp @@ -74,7 +74,7 @@ t_customWorkshop* Buildings_ReadCustomWorkshopTypes(DFHackObject* b_Ptr) if(b_Ptr != NULL) { int i; - t_customWorkshop* cw_Ptr; + t_customWorkshop* cw_Ptr = NULL; std::map bTypes; map::iterator bIter; diff --git a/library/modules/Creatures.cpp b/library/modules/Creatures.cpp index 0d26f336b..27bc320db 100644 --- a/library/modules/Creatures.cpp +++ b/library/modules/Creatures.cpp @@ -69,9 +69,10 @@ Creatures::Creatures(DFContextShared* _d) { d = new Private; d->d = _d; - Process * p = d->owner = _d->p; + 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() @@ -183,7 +188,6 @@ bool Creatures::ReadCreature (const int32_t index, t_creature & furball) } */ // non-SHM slow path - VersionInfo * minfo = d->d->offset_descriptor; // read pointer from vector at position uint32_t temp = d->p_cre->at (index); @@ -471,6 +475,8 @@ bool Creatures::WriteSex(const uint32_t index, const uint8_t sex) uint32_t temp = d->p_cre->at (index); Process * p = d->owner; p->writeByte (temp + d->creatures.sex_offset, sex); + + return true; } bool Creatures::WriteTraits(const uint32_t index, const t_soul &soul) @@ -596,7 +602,6 @@ bool Creatures::ReadJob(const t_creature * furball, vector & mat) if(!d->Inited) return false; if(!furball->current_job.active) return false; Process * p = d->owner; - VersionInfo * minfo = d->d->offset_descriptor; DfVector cmats(p, furball->current_job.occupationPtr + d->OG_jobs->getOffset("materials_vector")); mat.resize(cmats.size()); @@ -614,7 +619,6 @@ bool Creatures::ReadJob(const t_creature * furball, vector & mat) bool Creatures::ReadInventoryIdx(const uint32_t index, std::vector & item) { if(!d->Started) return false; - Process * p = d->owner; uint32_t temp = d->p_cre->at (index); return this->ReadInventoryPtr(temp, item); } diff --git a/library/modules/Creatures_C.cpp b/library/modules/Creatures_C.cpp index 1370258b2..297a15460 100644 --- a/library/modules/Creatures_C.cpp +++ b/library/modules/Creatures_C.cpp @@ -84,7 +84,7 @@ t_material* Creatures_ReadJob(DFHackObject* cPtr, const t_creature* furball) if(mat.size() <= 0) return NULL; - t_material* buf; + t_material* buf = NULL; (*alloc_t_material_buffer_callback)(buf, mat.size()); @@ -104,6 +104,64 @@ t_material* Creatures_ReadJob(DFHackObject* cPtr, const t_creature* furball) return NULL; } +uint32_t* Creatures_ReadInventoryIdx(DFHackObject* cPtr, const uint32_t index) +{ + if(cPtr != NULL) + { + std::vector item; + + if(((DFHack::Creatures*)cPtr)->ReadInventoryIdx(index, item)) + { + if(item.size() <= 0) + return NULL; + + uint32_t* buf = NULL; + + (*alloc_uint_buffer_callback)(buf, item.size()); + + if(buf != NULL) + { + copy(item.begin(), item.end(), buf); + + return buf; + } + else + return NULL; + } + } + + return NULL; +} + +uint32_t* Creatures_ReadInventoryPtr(DFHackObject* cPtr, const uint32_t index) +{ + if(cPtr != NULL) + { + std::vector item; + + if(((DFHack::Creatures*)cPtr)->ReadInventoryPtr(index, item)) + { + if(item.size() <= 0) + return NULL; + + uint32_t* buf = NULL; + + (*alloc_uint_buffer_callback)(buf, item.size()); + + if(buf != NULL) + { + copy(item.begin(), item.end(), buf); + + return buf; + } + else + return NULL; + } + } + + return NULL; +} + uint32_t Creatures_GetDwarfRaceIndex(DFHackObject* cPtr) { if(cPtr != NULL) 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/Gui_C.cpp b/library/modules/Gui_C.cpp index 8870b4e87..d2696370b 100644 --- a/library/modules/Gui_C.cpp +++ b/library/modules/Gui_C.cpp @@ -58,6 +58,23 @@ int Gui_ReadPauseState(DFHackObject* gui) return -1; } +int Gui_SetPauseState(DFHackObject* gui, int8_t paused) +{ + if(gui != NULL) + { + bool pauseState = false; + + if(paused > 0) + pauseState = true; + + ((DFHack::Gui*)gui)->SetPauseState(pauseState); + + return 1; + } + + return -1; +} + int Gui_ReadViewScreen(DFHackObject* gui, t_viewscreen* viewscreen) { if(gui != NULL) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 6ae558dcd..dcb137848 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -116,6 +116,21 @@ Accessor::Accessor(uint32_t function, Process *p) this->offset1 = (funcText>>24) & 0xffff; return; } + if( (funcText&0x000000FF00FFFFFFLL) == 0x000000C300418B66LL ) + { + /* mov ax, [ecx+xx]; ret; (shorter instruction)*/ + this->type = ACCESSOR_INDIRECT; + this->offset1 = (funcText>>24) & 0xff; + return; + } + if( (funcText&0x00000000FF00FFFFLL) == 0x00000000C300418BLL ) + { + /* mov eax, [ecx+xx]; ret; */ + this->type = ACCESSOR_INDIRECT; + this->offset1 = (funcText>>16) & 0xff; + this->dataWidth = 4; + return; + } if( (funcText&0xFFFFFFFF0000FFFFLL) == 0x8B6600000000818BLL ) { uint64_t funcText2 = p->readQuad(function+8); @@ -134,6 +149,13 @@ Accessor::Accessor(uint32_t function, Process *p) this->offset1 = (funcText>>24) & 0xffff; return; } + if( (funcText&0x000000FF00FFFFFFLL) == 0x000000C30041BF0FLL ) + { + /* movsx eax, word ptr [ecx+xx]; ret (shorter opcode)*/ + this->type = ACCESSOR_INDIRECT; + this->offset1 = (funcText>>24) & 0xff; + return; + } if( (funcText&0xFFFFFFFF0000FFFFLL) == 0xCCC300000000818BLL ) { /* mov eax, [ecx+xx]; ret; */ @@ -240,8 +262,17 @@ Items::Items(DFContextShared * d_) d->d = d_; d->owner = d_->p; } -bool Items::Start(){return true;} -bool Items::Finish(){return true;} + +bool Items::Start() +{ + return true; +} + +bool Items::Finish() +{ + return true; +} + Items::~Items() { Finish(); @@ -250,6 +281,7 @@ Items::~Items() while (it != d->descVTable.end()) { delete (*it).second; + ++it; } d->descType.clear(); d->descVTable.clear(); @@ -262,10 +294,10 @@ bool Items::getItemData(uint32_t itemptr, DFHack::t_item &item) Process * p = d->owner; ItemDesc * desc; - it = d->descVTable.find(itemptr); - if(it==d->descVTable.end()) + uint32_t vtable = p->readDWord(itemptr); + it = d->descVTable.find(vtable); + if(it == d->descVTable.end()) { - uint32_t vtable = p->readDWord(itemptr); desc = new ItemDesc(vtable, p); d->descVTable[vtable] = desc; d->descType[desc->mainType] = desc; @@ -282,7 +314,7 @@ std::string Items::getItemClass(int32_t index) std::string out; it = d->descType.find(index); - if(it==d->descType.end()) + if(it == d->descType.end()) { /* these are dummy values for mood decoding */ switch(index) @@ -325,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/Items_C.cpp b/library/modules/Items_C.cpp index f0231617f..bfc31f2f4 100644 --- a/library/modules/Items_C.cpp +++ b/library/modules/Items_C.cpp @@ -58,7 +58,7 @@ char* Items_getItemDescription(DFHackObject* items, uint32_t itemptr, DFHackObje if(desc.size() > 0) { - char* buf; + char* buf = NULL; (*alloc_char_buffer_callback)(buf,desc.size()); if(buf != NULL) @@ -86,7 +86,7 @@ char* Items_getItemClass(DFHackObject* items, int32_t index) if(iclass.size() > 0) { - char* buf; + char* buf = NULL; (*alloc_char_buffer_callback)(buf, iclass.size()); if(buf != NULL) { diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 124e9bea9..1657b3498 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -57,6 +57,8 @@ struct Maps::Private OffsetGroup *OG_vector; bool Inited; bool Started; + bool hasGeology; + bool hasFeatures; // map between feature address and the read object map local_feature_store; @@ -74,6 +76,7 @@ Maps::Maps(DFContextShared* _d) DFHack::VersionInfo * mem = p->getDescriptor(); Server::Maps::maps_offsets &off = d->offsets; + d->hasFeatures = d->hasGeology = true; // get the offsets once here OffsetGroup *OG_Maps = mem->getGroup("Maps"); @@ -87,7 +90,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 +102,31 @@ 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 { - 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"); + 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 &) + { + d->hasFeatures = 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 +136,10 @@ 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); // upload offsets to SHM server if possible d->maps_module = 0; @@ -517,18 +536,20 @@ 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) { MAPS_GUARD t_vein v; t_frozenliquidvein fv; t_spattervein sv; + t_grassvein gv; 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(); Server::Maps::maps_offsets &off = d->offsets; if (addr) @@ -568,6 +589,14 @@ 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 { string cname = p->readClassName(type); @@ -586,6 +615,11 @@ try_again: off.vein_spatter_vptr = type; goto try_again; } + else if(grass && cname=="block_square_event_grassst") + { + off.vein_grass_vptr = type; + goto try_again; + } #ifdef DEBUG else { @@ -661,7 +695,7 @@ __int16 __userpurge GetGeologicalRegion(__int16 block_X, int X, __i bool Maps::ReadGeology (vector < vector >& assign) { MAPS_GUARD - VersionInfo * minfo = d->d->offset_descriptor; + 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; @@ -731,12 +765,12 @@ 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; Process * p = d->owner; - VersionInfo * mem = p->getDescriptor(); // deref pointer to the humongo-structure uint32_t base = p->readDWord(d->OG_local_features->getAddress("start_ptr")); if(!base) @@ -824,6 +858,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; diff --git a/library/modules/Maps_C.cpp b/library/modules/Maps_C.cpp index 911f4e0f9..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 @@ -54,6 +55,48 @@ int Maps_Finish(DFHackObject* maps) return -1; } +uint16_t* Maps_ReadGeology(DFHackObject* maps) +{ + if(maps != NULL) + { + std::vector < std::vector > geology; + + if(((DFHack::Maps*)maps)->ReadGeology(geology)) + { + uint16_t* buf = NULL; + uint32_t geoLength = 0; + + for(unsigned int i = 0; i < geology.size(); i++) + { + for(unsigned int j = 0; j < geology[i].size(); j++) + { + geoLength += geology[i].size(); + } + } + + (*alloc_ushort_buffer_callback)(buf, geoLength); + + if(buf != NULL) + { + uint16_t* bufCopyPtr = buf; + + for(unsigned int i = 0; i < geology.size(); i++) + { + copy(geology[i].begin(), geology[i].end(), bufCopyPtr); + + bufCopyPtr += geology[i].size(); + } + + return buf; + } + else + return NULL; + } + } + + return NULL; +} + t_feature* Maps_ReadGlobalFeatures(DFHackObject* maps) { if(maps != NULL) @@ -65,7 +108,7 @@ t_feature* Maps_ReadGlobalFeatures(DFHackObject* maps) if(featureVec.size() <= 0) return NULL; - t_feature* buf; + t_feature* buf = NULL; (*alloc_t_feature_buffer_callback)(buf, featureVec.size()); @@ -255,6 +298,8 @@ int Maps_WriteEmptyLocalFeature(DFHackObject* maps, uint32_t x, uint32_t y, uint { return ((DFHack::Maps*)maps)->WriteLocalFeature(x, y, z, -1); } + + return -1; } int Maps_WriteGlobalFeature(DFHackObject* maps, uint32_t x, uint32_t y, uint32_t z, int16_t local) @@ -273,6 +318,8 @@ int Maps_WriteEmptyGlobalFeature(DFHackObject* maps, uint32_t x, uint32_t y, uin { return ((DFHack::Maps*)maps)->WriteGlobalFeature(x, y, z, -1); } + + return -1; } int Maps_ReadBlockFlags(DFHackObject* maps, uint32_t x, uint32_t y, uint32_t z, t_blockflags* blockflags) diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 608e7b663..7ab9f8218 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -402,7 +402,7 @@ bool Materials::ReadCreatureTypesEx (void) p->readSTLString (caste_start + sizeof_string, caste.singular, sizeof(caste.singular)); p->readSTLString (caste_start + 2 * sizeof_string, caste.plural, sizeof(caste.plural)); p->readSTLString (caste_start + 3 * sizeof_string, caste.adjective, sizeof(caste.adjective)); - cout << "Caste " << caste.rawname << " " << caste.singular << ": 0x" << hex << caste_start << endl; + //cout << "Caste " << caste.rawname << " " << caste.singular << ": 0x" << hex << caste_start << endl; if(have_advanced) { /* color mod reading */ @@ -459,7 +459,7 @@ void Materials::ReadAllMaterials(void) this->ReadCreatureTypes(); this->ReadCreatureTypesEx(); this->ReadDescriptorColors(); - //this->ReadOthers(); + this->ReadOthers(); } std::string Materials::getDescription(t_material & mat) @@ -478,6 +478,12 @@ std::string Materials::getDescription(t_material & mat) { if (mat.subIndex>=this->other.size()) { + if (mat.itemType == 0) { + if(mat.subIndex<0) + return "any inorganic"; + else + return this->inorganic[mat.subIndex].id; + } if(mat.subIndex<0) return "any"; if(mat.subIndex>=this->raceEx.size()) diff --git a/library/modules/Materials_C.cpp b/library/modules/Materials_C.cpp index 4fa8b7705..90b16adf9 100644 --- a/library/modules/Materials_C.cpp +++ b/library/modules/Materials_C.cpp @@ -232,7 +232,7 @@ t_matgloss* Materials_getInorganic(DFHackObject* mat) if(materials->inorganic.size() > 0) { - t_matgloss* buf; + t_matgloss* buf = NULL; ((*alloc_matgloss_buffer_callback)(buf, materials->inorganic.size())); @@ -256,7 +256,7 @@ t_matgloss* Materials_getOrganic(DFHackObject* mat) if(materials->organic.size() > 0) { - t_matgloss* buf; + t_matgloss* buf = NULL; ((*alloc_matgloss_buffer_callback)(buf, materials->organic.size())); @@ -280,7 +280,7 @@ t_matgloss* Materials_getTree(DFHackObject* mat) if(materials->tree.size() > 0) { - t_matgloss* buf; + t_matgloss* buf = NULL; ((*alloc_matgloss_buffer_callback)(buf, materials->tree.size())); @@ -304,7 +304,7 @@ t_matgloss* Materials_getPlant(DFHackObject* mat) if(materials->plant.size() > 0) { - t_matgloss* buf; + t_matgloss* buf = NULL; ((*alloc_matgloss_buffer_callback)(buf, materials->plant.size())); @@ -328,7 +328,7 @@ t_matgloss* Materials_getRace(DFHackObject* mat) if(materials->race.size() > 0) { - t_matgloss* buf; + t_matgloss* buf = NULL; ((*alloc_matgloss_buffer_callback)(buf, materials->race.size())); @@ -354,7 +354,7 @@ c_creaturetype* Materials_getRaceEx(DFHackObject* mat) if(matSize > 0) { - c_creaturetype* buf; + c_creaturetype* buf = NULL; ((*alloc_creaturetype_buffer_callback)(buf, matSize)); @@ -379,7 +379,7 @@ t_descriptor_color* Materials_getColor(DFHackObject* mat) if(materials->color.size() > 0) { - t_descriptor_color* buf; + t_descriptor_color* buf = NULL; ((*alloc_descriptor_buffer_callback)(buf, materials->color.size())); @@ -403,7 +403,7 @@ t_matglossOther* Materials_getOther(DFHackObject* mat) if(materials->other.size() > 0) { - t_matglossOther* buf; + t_matglossOther* buf = NULL; ((*alloc_matgloss_other_buffer_callback)(buf, materials->other.size())); @@ -427,7 +427,7 @@ t_matgloss* Materials_getAllDesc(DFHackObject* mat) if(materials->alldesc.size() > 0) { - t_matgloss* buf; + t_matgloss* buf = NULL; ((*alloc_matgloss_buffer_callback)(buf, materials->alldesc.size())); diff --git a/library/modules/Position_C.cpp b/library/modules/Position_C.cpp index a97a52a56..386bd1c5b 100644 --- a/library/modules/Position_C.cpp +++ b/library/modules/Position_C.cpp @@ -102,7 +102,7 @@ t_hotkey* Position_ReadHotkeys(DFHackObject* pos) { if(pos != NULL) { - t_hotkey* buf; + t_hotkey* buf = NULL; (*alloc_t_hotkey_buffer_callback)(buf, NUM_HOTKEYS); @@ -144,7 +144,7 @@ t_screen* Position_getScreenTiles(DFHackObject* pos, int32_t width, int32_t heig { if(pos != NULL) { - t_screen* buf; + t_screen* buf = NULL; (*alloc_t_screen_buffer_callback)(buf, width * height); diff --git a/library/modules/Translation_C.cpp b/library/modules/Translation_C.cpp index 2589e9640..7032f26c5 100644 --- a/library/modules/Translation_C.cpp +++ b/library/modules/Translation_C.cpp @@ -124,7 +124,7 @@ char* Translation_TranslateNameEnglish(DFHackObject* trans, const DFHack::t_name if(nameTrans.size() > 0) { - char* buf; + char* buf = NULL; (*alloc_char_buffer_callback)(buf, nameTrans.size()); @@ -155,7 +155,7 @@ char* Translation_TranslateNameNonEnglish(DFHackObject* trans, const DFHack::t_n if(nameTrans.size() > 0) { - char* buf; + char* buf = NULL; (*alloc_char_buffer_callback)(buf, nameTrans.size()); diff --git a/library/modules/WindowIO-windows.cpp b/library/modules/WindowIO-windows.cpp index 37c5eb08c..1a432ade4 100644 --- a/library/modules/WindowIO-windows.cpp +++ b/library/modules/WindowIO-windows.cpp @@ -130,20 +130,19 @@ WindowIO::~WindowIO () void WindowIO::TypeStr (const char *input, int delay, bool useShift) { //sendmessage needs a window handle HWND, so have to get that from the process HANDLE - HWND currentWindow = GetForegroundWindow(); window myWindow; myWindow.pid = d->p->getPID(); EnumWindows (EnumWindowsProc, (LPARAM) &myWindow); - char cChar; - DWORD dfProccess = GetWindowThreadProcessId(myWindow.windowHandle,NULL); - DWORD currentProccess = GetWindowThreadProcessId(currentWindow,NULL); - AttachThreadInput(currentProccess,dfProccess,TRUE); //The two threads have to have attached input in order to change the keyboard state, which is needed to set the shift state + DWORD dfProcess = GetWindowThreadProcessId(myWindow.windowHandle,NULL); + DWORD currentProcess = GetCurrentThreadId(); + AttachThreadInput(currentProcess,dfProcess,TRUE); //The two threads have to have attached input in order to change the keyboard state, which is needed to set the shift state while ( (cChar = *input++)) // loops through chars { short vk = VkKeyScan (cChar); // keycode of char if (useShift || (vk >> 8) &1) // char is capital, so need to hold down shift { + vk = vk & 0xFF; // remove the shift state from the virtual key code BYTE keybstate[256] = {0}; BYTE keybstateOrig[256] = {0}; GetKeyboardState((LPBYTE)&keybstateOrig); @@ -160,7 +159,7 @@ void WindowIO::TypeStr (const char *input, int delay, bool useShift) SendMessage(myWindow.windowHandle,WM_KEYUP,vk,0); } } - AttachThreadInput(currentProccess,dfProccess,FALSE); //detach the threads + AttachThreadInput(currentProcess,dfProcess,FALSE); //detach the threads Sleep (delay); } 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..cc9df1a05 --- /dev/null +++ b/library/private/LinuxProcess.h @@ -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. +*/ + +#ifndef LINUX_PROCESS_H_INCLUDED +#define LINUX_PROCESS_H_INCLUDED + +#ifdef LINUX_BUILD + +#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 +#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..217dbe839 --- /dev/null +++ b/library/private/ProcessFactory.h @@ -0,0 +1,38 @@ +/* +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" + +namespace DFHack +{ + Process* createNormalProcess(uint32_t pid, std::vector & known_versions); + Process* createSHMProcess(uint32_t pid, std::vector & known_versions); +#ifdef LINUX_BUILD + Process* createWineProcess(uint32_t pid, std::vector & known_versions); +#endif +} +#endif diff --git a/library/private/SHMProcess.h b/library/private/SHMProcess.h new file mode 100644 index 000000000..3f8a8f75c --- /dev/null +++ b/library/private/SHMProcess.h @@ -0,0 +1,144 @@ +/* +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" + +namespace DFHack +{ + class DFHACK_EXPORT SHMProcess : public Process + { + private: + class 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(); + + 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(std::vector< VersionInfo* >& known_versions); + + 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..46c8894eb 100644 --- a/library/shm/mod-maps.h +++ b/library/shm/mod-maps.h @@ -57,6 +57,7 @@ typedef struct uint32_t vein_mineral_vptr; uint32_t vein_ice_vptr; uint32_t vein_spatter_vptr; + uint32_t vein_grass_vptr; /* GEOLOGY */ diff --git a/tools/examples/creaturedump.cpp b/tools/examples/creaturedump.cpp index dddd6e018..ae221a1e6 100644 --- a/tools/examples/creaturedump.cpp +++ b/tools/examples/creaturedump.cpp @@ -302,7 +302,15 @@ void printCreature(DFHack::Context * DF, const DFHack::t_creature & creature) { cout << ", "; } - cout << mem->getSkill(creature.defaultSoul.skills[i].id) << ": " << creature.defaultSoul.skills[i].rating; + try + { + cout << mem->getSkill(creature.defaultSoul.skills[i].id) << ": " << creature.defaultSoul.skills[i].rating; + } + catch(DFHack::Error::AllMemdef &e) + { + cout << "Unknown skill! : " << creature.defaultSoul.skills[i].id <<", rating: " << creature.defaultSoul.skills[i].rating << endl; + cout << e.what() << endl; + } } cout << endl; cout << "Traits" << endl; diff --git a/tools/examples/dfitemdump.cpp b/tools/examples/dfitemdump.cpp index f3a3dd39e..7633a243a 100644 --- a/tools/examples/dfitemdump.cpp +++ b/tools/examples/dfitemdump.cpp @@ -2,6 +2,12 @@ * dumps vtables, items types and class name for all items in game * best used this way : ./dfitemdump | sort -ug */ + +// THIS IS NOT A GOOD EXAMPLE! +// ... just look at all the magic numbers. +// I'm not fixing it though. +// ~px + #include #include #include @@ -41,7 +47,8 @@ int main () Materials = DF->getMaterials(); Materials->ReadAllMaterials(); p = DF->getProcess(); - DFHack::DfVector p_items (p, p->getDescriptor()->getAddress ("items_vector")); + DFHack::OffsetGroup* itemGroup = mem->getGroup("Items"); + DFHack::DfVector p_items (p, itemGroup->getAddress("items_vector")); uint32_t size = p_items.size(); Items = DF->getItems(); @@ -49,6 +56,7 @@ int main () printf("type\tvtable\tname\tquality\tdecorate\n"); for (i=0;ireadDWord(p_items[i]); uint32_t func0 = p->readDWord(vtable); uint64_t funct0 = p->readQuad(func0); @@ -84,6 +92,8 @@ int main () if (funct1 == 0xC300000092818B66LL) quality = p->readWord(p_items[i]+0x92); + if (funct1 == 0xC300000082818B66LL) + quality = p->readWord(p_items[i]+0x82); else if (funct1 == 0xCCCCCCCCCCC3C033LL) quality = 0; else @@ -115,6 +125,11 @@ int main () uint32_t off1 = (funcBt>>24) & 0xffff; typeB = p->readWord(p_items[i] + off1); } + else if ( (funcBt&0x000000FF00FFFFFFLL) == 0x000000C300418B66LL ) + { + uint32_t off1 = (funcBt>>24) & 0xff; + typeB = p->readWord(p_items[i] + off1); + } else printf("bad typeB func @%p\n", (void*) funcB); } @@ -126,6 +141,16 @@ int main () uint32_t off1 = (funcCt>>24)&0xffff; typeC = p->readWord(p_items[i] + off1); } + else if ( (funcCt&0x000000FF00FFFFFFLL) == 0x000000C300418B66LL ) + { + uint32_t off1 = (funcCt>>24) & 0xff; + typeC = p->readWord(p_items[i] + off1); + } + else if ( (funcCt&0x00000000FF00FFFFLL) == 0x00000000C300418BLL ) + { + uint32_t off1 = (funcCt>>16) & 0xff; + typeC = p->readWord(p_items[i] + off1); + } else printf("bad typeC func @%p\n", (void*) funcC); @@ -134,12 +159,27 @@ int main () else if ( (funcDt&0xFFFFFFFF0000FFFFLL) == 0xCCC300000000818BLL ) { uint32_t off1 = (funcDt>>16) & 0xffff; - typeD = p->readDWord(p_items[i] + off1); + typeD = p->readWord(p_items[i] + off1); } else if ( (funcDt&0xFFFFFF0000FFFFFFLL) == 0xC30000000081BF0FLL ) { uint32_t off1 = (funcDt>>24) & 0xffff; - typeD = (int16_t) p->readWord(p_items[i] + off1); + typeD = p->readWord(p_items[i] + off1); + } + else if ( (funcDt&0x000000FF00FFFFFFLL) == 0x000000C30041BF0FLL ) + { + uint32_t off1 = (funcDt>>24) & 0xff; + typeD = p->readWord(p_items[i] + off1); + } + else if ( (funcDt&0x000000FF00FFFFFFLL) == 0x000000C300418B66LL ) + { + uint32_t off1 = (funcDt>>24) & 0xff; + typeD = p->readWord(p_items[i] + off1); + } + else if ( (funcDt&0x00000000FF00FFFFLL) == 0x00000000C300418BLL ) + { + uint32_t off1 = (funcDt>>16) & 0xff; + typeD = p->readDWord(p_items[i] + off1); } else printf("bad typeD func @%p\n", (void*) funcD); @@ -155,8 +195,8 @@ int main () { bool sep = false; printf("\tdeco=["); - uint32_t decStart = p->readDWord(p_items[i] + 0xAC); - uint32_t decEnd = p->readDWord(p_items[i] + 0xB0); + uint32_t decStart = p->readDWord(p_items[i] + 0x90); // 0xAC pre .13 + uint32_t decEnd = p->readDWord(p_items[i] + 0x94); // 0xB0 pre .13 if (decStart != decEnd) { for (j=decStart;j +#include +#include +#include +#include +#include +using namespace std; + +#include +#include + + +int main (void) +{ + uint32_t x_max,y_max,z_max; + uint32_t num_blocks = 0; + uint32_t bytes_read = 0; + DFHack::designations40d designations; + DFHack::tiletypes40d tiles; + DFHack::tiletypes40d tilesAbove; + + //DFHack::TileRow *ptile; + int32_t oldT, newT; + int16_t t; + + int dirty=0, count=0; + + DFHack::ContextManager DFMgr("Memory.xml"); + DFHack::Context *DF = DFMgr.getSingleContext(); + + //sanity check + assert( sizeof(designations) == (16*16*sizeof(DFHack::t_designation)) ); + + //Init + try + { + DF->Attach(); + } + catch (exception& e) + { + cerr << e.what() << endl; + #ifndef LINUX_BUILD + cin.ignore(); + #endif + return 1; + } + DFHack::Maps *Mapz = DF->getMaps(); + + // init the map + if (!Mapz->Start()) + { + cerr << "Can't init map." << endl; + #ifndef LINUX_BUILD + cin.ignore(); + #endif + return 1; + } + + Mapz->getSize(x_max,y_max,z_max); + + uint8_t zeroes [16][16] = {0}; + + // walk the map + for (uint32_t x = 0; x< x_max;x++) + { + for (uint32_t y = 0; y< y_max;y++) + { + for (uint32_t z = 0; z< z_max;z++) + { + if (Mapz->isValidBlock(x,y,z)) + { + dirty=0; + Mapz->ReadDesignations(x,y,z, &designations); + Mapz->ReadTileTypes(x,y,z, &tiles); + if (Mapz->isValidBlock(x,y,z+1)) + { + Mapz->ReadTileTypes(x,y,z+1, &tilesAbove); + } + else + { + memset(&tilesAbove,0,sizeof(tilesAbove)); + } + + for (uint32_t ty=0;ty<16;++ty) + { + for (uint32_t tx=0;tx<16;++tx) + { + //Only the remove ramp designation (ignore channel designation, etc) + oldT = tiles[tx][ty]; + if ( DFHack::designation_default == designations[tx][ty].bits.dig + && DFHack::RAMP==DFHack::tileTypeTable[oldT].c) + { + //Current tile is a ramp. + //Set current tile, as accurately as can be expected + newT = DFHack::findSimilarTileType(oldT,DFHack::FLOOR); + + //If no change, skip it (couldn't find a good tile type) + if ( oldT == newT) continue; + //Set new tile type, clear designation + tiles[tx][ty] = newT; + designations[tx][ty].bits.dig = DFHack::designation_no; + + //Check the tile above this one, in case a downward slope needs to be removed. + if ( DFHack::RAMP_TOP == DFHack::tileTypeTable[tilesAbove[tx][ty]].c ) + { + tilesAbove[tx][ty] = 32; + } + + dirty=-1; + ++count; + } + } + } + //If anything was changed, write it all. + if (dirty) + { + Mapz->WriteDesignations(x,y,z, &designations); + Mapz->WriteTileTypes(x,y,z, &tiles); + if (Mapz->isValidBlock(x,y,z+1)) + { + Mapz->WriteTileTypes(x,y,z+1, &tilesAbove); + } + } + } + } + } + } + DF->Detach(); + cout << "Found and changed " << count << " tiles." << endl; +#ifndef LINUX_BUILD + cout << "Done. Press any key to continue" << endl; + cin.ignore(); +#endif + return 0; +} 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 new file mode 100644 index 000000000..340d5169e --- /dev/null +++ b/tools/playground/hellhole.cpp @@ -0,0 +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; +} diff --git a/tools/playground/paths.cpp b/tools/playground/paths.cpp new file mode 100644 index 000000000..cc00f79a3 --- /dev/null +++ b/tools/playground/paths.cpp @@ -0,0 +1,31 @@ +#include +using namespace std; + +#include +using namespace DFHack; + +int main () +{ + 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 * Process = DF->getProcess(); + DFHack::Gui * gui = DF->getGui(); + cout << Process->getPath() << endl; + #ifndef LINUX_BUILD + cout << "Done. Press any key to continue" << endl; + cin.ignore(); + #endif + return 0; +} \ No newline at end of file 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/playground/printtiletypes.cpp b/tools/playground/printtiletypes.cpp new file mode 100644 index 000000000..b9e9d75ce --- /dev/null +++ b/tools/playground/printtiletypes.cpp @@ -0,0 +1,100 @@ +// Prints all the Tile Types known by DFHack. +// File is both fixed-field and CSV parsable. + +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#include +#include + +using namespace DFHack; + +int main (int argc, char **argv) +{ + FILE *f=stdout; + const int Columns = 7; + const char * Headings[Columns] = {"TileTypeID","Class","Material","V","Special","Direction","Description"}; + size_t Size[ Columns ] = {}; + int i; + + //First, figure out column widths. + for(i=0;i(Size[1],strlen(TileClassString[i])); + fprintf(f,"%4i ; %s\n", i, TileClassString[i] ,0 ); + } + + //Materials + fprintf(f,"\nTile Type Materials:\n"); + for(i=0;i(Size[2],strlen(TileMaterialString[i])); + fprintf(f,"%4i ; %s\n", i, TileMaterialString[i] ,0 ); + } + + //Specials + fprintf(f,"\nTile Type Specials:\n"); + for(i=0;i(Size[4],strlen(TileSpecialString[i])); + fprintf(f,"%4i ; %s\n", i, TileSpecialString[i] ,0 ); + } + + /* - Not needed for now - + //Direction is tricky + for(i=0;igetOffset("creature_name") + mem->getOffset("name_nickname"); p->writeSTLString(toChange.origin+nickname,changeString); } - catch (DFHack::Error::MissingMemoryDefinition&) + catch (DFHack::Error::AllMemdef&) { cerr << "Writing creature nicknames unsupported in this version!" << endl; } @@ -461,7 +461,7 @@ start: uint32_t custom_prof = mem->getOffset("creature_custom_profession"); p->writeSTLString(toChange.origin+custom_prof,changeString); } - catch (DFHack::Error::MissingMemoryDefinition&) + catch (DFHack::Error::AllMemdef&) { cerr << "Writing creature custom profession unsupported in this version!" << endl; } diff --git a/tools/supported/CMakeLists.txt b/tools/supported/CMakeLists.txt index 22482f5ea..138ac6a20 100644 --- a/tools/supported/CMakeLists.txt +++ b/tools/supported/CMakeLists.txt @@ -9,6 +9,10 @@ ENDIF(UNIX) ADD_EXECUTABLE(dfreveal reveal.cpp) TARGET_LINK_LIBRARIES(dfreveal dfhack) +# force pause! +ADD_EXECUTABLE(dfpause forcepause.cpp) +TARGET_LINK_LIBRARIES(dfpause dfhack) + # prospector - produces a list of available materials and their quantities ADD_EXECUTABLE(dfprospector prospector.cpp) TARGET_LINK_LIBRARIES(dfprospector dfhack) @@ -68,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") @@ -113,6 +123,7 @@ dfvdig dfcleanmap dfunstuck dfprobe +dfpause dfdoffsets dfattachtest dfcleartask @@ -121,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 60% rename from tools/playground/SegmentedFinder.h rename to tools/supported/SegmentedFinder.h index 98de87711..6a0c1042e 100644 --- a/tools/playground/SegmentedFinder.h +++ b/tools/supported/SegmentedFinder.h @@ -1,5 +1,8 @@ #ifndef SEGMENTED_FINDER_H #define SEGMENTED_FINDER_H +#include +#include +#include class SegmentedFinder; class SegmentFinder @@ -9,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) { @@ -33,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) @@ -46,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])) @@ -62,6 +86,7 @@ class SegmentFinder SegmentedFinder * _SF; DFHack::Context * _DF; DFHack::t_memrange mr_; + bool valid; }; class SegmentedFinder @@ -286,15 +311,207 @@ bool vectorAll (SegmentedFinder* s, vecTriplet *x, int ) return false; } -struct Bytestream +class Bytestreamdata { - uint32_t length; - void * object; + public: + void * object; + uint32_t length; + uint32_t allocated; + uint32_t n_used; }; +class Bytestream +{ +public: + Bytestream(void * obj, uint32_t len, bool alloc = false) + { + d = new Bytestreamdata(); + d->allocated = alloc; + d->object = obj; + d->length = len; + d->n_used = 1; + constant = false; + } + Bytestream() + { + d = new Bytestreamdata(); + d->allocated = false; + d->object = 0; + d->length = 0; + d->n_used = 1; + constant = false; + } + Bytestream( Bytestream & bs) + { + d =bs.d; + d->n_used++; + constant = false; + } + Bytestream( const Bytestream & bs) + { + d =bs.d; + d->n_used++; + constant = true; + } + ~Bytestream() + { + d->n_used --; + if(d->allocated && d->object && d->n_used == 0) + { + free (d->object); + free (d); + } + } + bool Allocate(size_t bytes) + { + if(constant) + return false; + if(d->allocated) + { + d->object = realloc(d->object, bytes); + } + else + { + d->object = malloc( bytes ); + } + + if(d->object) + { + d->allocated = bytes; + return true; + } + else + { + d->allocated = 0; + return false; + } + } + template < class T > + bool insert( T what ) + { + if(constant) + return false; + 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; +}; +std::ostream& operator<< ( std::ostream& out, Bytestream& bs ) +{ + if(bs.d->object) + { + out << "bytestream " << dec << bs.d->length << "/" << bs.d->allocated << " bytes" << endl; + for(int i = 0; i < bs.d->length; i++) + { + out << hex << (int) ((uint8_t *) bs.d->object)[i] << " "; + } + out << endl; + } + else + { + out << "empty bytestresm" << endl; + } + return out; +} + +std::istream& operator>> ( std::istream& out, Bytestream& bs ) +{ + string read; + while(!out.eof()) + { + string tmp; + out >> tmp; + read.append(tmp); + } + cout << read << endl; + bs.d->length = 0; + size_t first = read.find_first_of("\""); + size_t last = read.find_last_of("\""); + size_t start = first + 1; + if(first == read.npos) + { + std::transform(read.begin(), read.end(), read.begin(), (int(*)(int)) tolower); + bs.Allocate(read.size()); // overkill. size / 2 should be good, but this is safe + int state = 0; + char big = 0; + char small = 0; + string::iterator it = read.begin(); + // iterate through string, construct a bytestream out of 00-FF bytes + while(it != read.end()) + { + char reads = *it; + if((reads >='0' && reads <= '9')) + { + if(state == 0) + { + big = reads - '0'; + state = 1; + } + else if(state == 1) + { + small = reads - '0'; + state = 0; + bs.insert(big*16 + small); + } + } + if((reads >= 'a' && reads <= 'f')) + { + if(state == 0) + { + big = reads - 'a' + 10; + state = 1; + } + else if(state == 1) + { + small = reads - 'a' + 10; + state = 0; + bs.insert(big*16 + small); + } + } + it++; + } + // we end in state= 1. should we add or should we trim... or throw errors? + // I decided on adding + if (state == 1) + { + small = 0; + bs.insert(big*16 + small); + } + } + else + { + if(last == first) + { + // only one " + last = read.size(); + } + size_t length = last - start; + // construct bytestream out of stuff between "" + bs.d->length = length; + if(length) + { + // todo: Bytestream should be able to handle this without external code + bs.Allocate(length); + bs.d->length = length; + const char* strstart = read.c_str(); + memcpy(bs.d->object, strstart + start, length); + } + else + { + bs.d->object = 0; + } + } + cout << bs; + return out; +} + bool findBytestream (SegmentedFinder* s, void *addr, Bytestream compare ) { - if(memcmp(addr, compare.object, compare.length) == 0) + if(memcmp(addr, compare.d->object, compare.d->length) == 0) return true; return false; } 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 78081f7fd..efba907d4 100644 --- a/tools/supported/cleanmap.cpp +++ b/tools/supported/cleanmap.cpp @@ -8,13 +8,24 @@ using namespace std; #include -int main (void) +int main (int argc, char** argv) { + + bool quiet = false; + for(int i = 1; i < argc; i++) + { + string test = argv[i]; + if(test == "-q") + { + quiet = true; + } + } + uint32_t x_max,y_max,z_max; uint32_t num_blocks = 0; uint32_t bytes_read = 0; vector splatter; - + DFHack::ContextManager DFMgr("Memory.xml"); DFHack::Context *DF = DFMgr.getSingleContext(); try @@ -30,7 +41,7 @@ int main (void) return 1; } DFHack::Maps *Mapz = DF->getMaps(); - + // init the map if(!Mapz->Start()) { @@ -40,11 +51,12 @@ int main (void) #endif return 1; } - + 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++) { @@ -55,10 +67,18 @@ int main (void) 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]; - if(vein.mat1 != 0xC) + // filter away snow and mud + if(vein.mat1 != 0xC && vein.mat1 != 0x6) { uint32_t addr = vein.address_of; uint32_t offset = offsetof(DFHack::t_spattervein, intensity); @@ -71,8 +91,11 @@ int main (void) } DF->Detach(); #ifndef LINUX_BUILD - cout << "Done. Press any key to continue" << endl; - cin.ignore(); + if (!quiet) + { + cout << "Done. Press any key to continue" << endl; + cin.ignore(); + } #endif return 0; } diff --git a/tools/supported/cleartask.cpp b/tools/supported/cleartask.cpp index 08f86f781..6ef34abac 100644 --- a/tools/supported/cleartask.cpp +++ b/tools/supported/cleartask.cpp @@ -39,7 +39,7 @@ int main () { item_vec_offset = p->getDescriptor()->getAddress ("items_vector"); } - catch(DFHack::Error::MissingMemoryDefinition & e) + catch(DFHack::Error::AllMemdef & e) { cerr << "missing offset for the item vector, exiting :(" << endl; #ifndef LINUX_BUILD diff --git a/tools/supported/forcepause.cpp b/tools/supported/forcepause.cpp new file mode 100644 index 000000000..4288ccefe --- /dev/null +++ b/tools/supported/forcepause.cpp @@ -0,0 +1,49 @@ +// This forces the game to pause. + +#include +#include +#include +using namespace std; + +#include +#include + +int main (int argc, char** argv) +{ + bool quiet = false; + for(int i = 1; i < argc; i++) + { + string test = argv[i]; + if(test == "-q") + { + quiet = true; + } + } + + DFHack::ContextManager DFMgr("Memory.xml"); + DFHack::Context *DF; + try + { + DF = DFMgr.getSingleContext(); + DF->Attach(); + } + catch (exception& e) + { + cerr << e.what() << endl; + #ifndef LINUX_BUILD + if(!quiet) cin.ignore(); + #endif + return 1; + } + + DFHack::Gui *Gui =DF->getGui(); + cout << "Pausing..." << endl; + + Gui->SetPauseState(true); + DF->Resume(); + #ifndef LINUX_BUILD + cout << "Done. The current game frame will have to finish first. This can take some time on bugged maps." << endl; + if (!quiet) cin.ignore(); + #endif + return 0; +} diff --git a/tools/playground/incrementalsearch.cpp b/tools/supported/incrementalsearch.cpp similarity index 70% rename from tools/playground/incrementalsearch.cpp rename to tools/supported/incrementalsearch.cpp index 918577944..f95fba34f 100644 --- a/tools/playground/incrementalsearch.cpp +++ b/tools/supported/incrementalsearch.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -20,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; @@ -181,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()); @@ -225,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) @@ -273,7 +141,7 @@ bool getString (string prompt, string & output) template bool Incremental ( vector &found, const char * what, T& output, - const char *singular = "address", const char *plural = "addresses" ) + const char *singular = "address", const char *plural = "addresses", bool numberz = false ) { string select; if(found.empty()) @@ -311,26 +179,33 @@ 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( sscanf(select.c_str(),"0x%x", &output) == 1 ) + if(numberz) { - //cout << dec << output << endl; - return true; - } - if( sscanf(select.c_str(),"%d", &output) == 1 ) - { - //cout << dec << output << endl; - return true; + if( sscanf(select.c_str(),"0x%x", &output) == 1 ) + { + //cout << dec << output << endl; + return true; + } + if( sscanf(select.c_str(),"%d", &output) == 1 ) + { + //cout << dec << output << endl; + return true; + } } - stringstream ss (stringstream::in | stringstream::out); ss << select; ss >> output; + cout << output; if(!ss.fail()) { return true; @@ -358,7 +233,7 @@ void FindIntegers(DFHack::ContextManager & DFMgr, vector & r uint32_t test1; vector found; found.reserve(100); - while(Incremental(found, "integer",test1)) + while(Incremental(found, "integer",test1,"address", "addresses",true)) { DFMgr.Refresh(); DFHack::Context * DF = DFMgr.getSingleContext(); @@ -514,6 +389,220 @@ void FindStrings(DFHack::ContextManager & DFMgr, vector & ra } } +void FindData(DFHack::ContextManager & DFMgr, vector & ranges) +{ + vector found; + Bytestream select; + while (Incremental(found,"byte stream",select,"byte stream","byte streams")) + { + DFMgr.Refresh(); + DFHack::Context * DF = DFMgr.getSingleContext(); + DF->Attach(); + SegmentedFinder sf(ranges,DF); + sf.Incremental< Bytestream ,uint32_t>(select,1,found, findBytestream); + DF->Detach(); + } +} + +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(); + 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) + { + 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; + do + { + getNumber("Set search granularity",element_size, 4); + } while (element_size < 1); + + vector found; + set check; // to detect circles + uint32_t select; + while (Incremental(found,"address",select,"addresses","addresses",true)) + { + DFMgr.Refresh(); + found.clear(); + DFHack::Context * DF = DFMgr.getSingleContext(); + DF->Attach(); + SegmentedFinder sf(ranges,DF); + cout <<"Starting: 0x" << hex << select << endl; + while(sf.getSegmentForAddress(select)) + { + sf.Incremental(select,element_size,found, equalityP); + if(found.empty()) + { + cout << "."; + cout.flush(); + select -=element_size; + continue; + } + cout << endl; + cout <<"Object start: 0x" << hex << select << endl; + cout <<"Pointer: 0x" << hex << found[0] << endl; + // make sure we don't go in circles' + if(check.count(select)) + { + break; + } + check.insert(select); + // ascend + select = found[0]; + found.clear(); + } + DF->Detach(); + } +} +/* +{ + vector found; + Bytestream select; + while (Incremental(found,"byte stream",select,"byte stream","byte streams")) + { + DFMgr.Refresh(); + DFHack::Context * DF = DFMgr.getSingleContext(); + DF->Attach(); + SegmentedFinder sf(ranges,DF); + sf.Incremental< Bytestream ,uint32_t>(select,1,found, findBytestream); + DF->Detach(); + } +} +*/ +void DataPtrTrace(DFHack::ContextManager & DFMgr, vector & ranges) +{ + int element_size; + do + { + getNumber("Set search granularity",element_size, 4); + } while (element_size < 1); + + vector found; + set check; // to detect circles + uint32_t select; + Bytestream bs_select; + DFHack::Context * DF = DFMgr.getSingleContext(); + DF->Attach(); + DFMgr.Refresh(); + found.clear(); + SegmentedFinder sf(ranges,DF); + while(found.empty()) + { + Incremental(found,"byte stream",bs_select,"byte stream","byte streams"); + + sf.Incremental< Bytestream ,uint32_t>(bs_select,1,found, findBytestream); + } + select = found[0]; + + + + + cout <<"Starting: 0x" << hex << select << endl; + while(sf.getSegmentForAddress(select)) + { + sf.Incremental(select,element_size,found, equalityP); + if(found.empty()) + { + cout << "."; + cout.flush(); + select -=element_size; + continue; + } + cout << endl; + cout <<"Object start: 0x" << hex << select << endl; + cout <<"Pointer: 0x" << hex << found[0] << endl; + // make sure we don't go in circles' + if(check.count(select)) + { + break; + } + check.insert(select); + // ascend + select = found[0]; + found.clear(); + } + DF->Detach(); +} + void printFound(vector &found, const char * what) { cout << what << ":" << endl; @@ -580,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; @@ -664,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); @@ -726,7 +824,8 @@ void automatedLangtables(DFHack::Context * DF, vector & rang uint64_t Eoffset; cout << "Elephant: 0x" << hex << elephant << endl; cout << "Elephant: rawname = 0x0" << endl; - Eoffset = sf.FindInRange ('E',equalityP, elephant, 0x300 ); + uint8_t letter_E = 'E'; + Eoffset = sf.FindInRange (letter_E,equalityP, elephant, 0x300 ); if(Eoffset) { cout << "Elephant: big E = 0x" << hex << Eoffset - elephant << endl; @@ -742,7 +841,8 @@ void automatedLangtables(DFHack::Context * DF, vector & rang cout << "Elephant: extract? vector = 0x" << hex << Eoffset - elephant << endl; } tilecolors eletc = {7,0,0}; - Bytestream bs_eletc = {sizeof(tilecolors), &eletc}; + Bytestream bs_eletc(&eletc, sizeof(tilecolors)); + cout << bs_eletc; Eoffset = sf.FindInRange (bs_eletc, findBytestream, elephant, 0x300 ); if(Eoffset) { @@ -775,7 +875,7 @@ void automatedLangtables(DFHack::Context * DF, vector & rang cout << "Toad: extract? vector = 0x" << hex << Eoffset - toad << endl; } tilecolors toadtc = {2,0,0}; - Bytestream bs_toadc = {sizeof(tilecolors), &toadtc}; + Bytestream bs_toadc(&toadtc, sizeof(tilecolors)); Eoffset = sf.FindInRange (bs_toadc, findBytestream, toad, 0x300 ); if(Eoffset) { @@ -810,58 +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\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 > 9 ); - 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; - 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/liquids.cpp b/tools/supported/liquids.cpp index ceff8c3fd..46596f6d8 100644 --- a/tools/supported/liquids.cpp +++ b/tools/supported/liquids.cpp @@ -1,22 +1,31 @@ -// This will create 7 deep magama on the square the cursor is on. It does not -// enable magma buildings at this time. +// TO BE DEPRECATED SOON. #include #include #include +#include using namespace std; #include #include -int main (void) +int main (int argc, char** argv) { + bool quiet = false; + for(int i = 1; i < argc; i++) + { + string test = argv[i]; + if(test == "-q") + { + quiet = true; + } + } int32_t x,y,z; DFHack::designations40d designations; DFHack::tiletypes40d tiles; DFHack::t_temperatures temp1,temp2; uint32_t x_max,y_max,z_max; - + DFHack::ContextManager DFMgr("Memory.xml"); DFHack::Context *DF; DFHack::Maps * Maps; @@ -45,6 +54,8 @@ int main (void) string flowmode="f+"; string setmode ="s."; int amount = 7; + int width = 1; + int height = 1; while(!end) { DF->Resume(); @@ -68,7 +79,8 @@ int main (void) << "f- - make the spawned liquid static" << endl << "0-7 - set liquid amount" << endl << "Brush:" << endl - << "point - single tile" << endl + << "point - single tile [p]" << endl + << "range - rectangle with cursor at top left [r]" << endl << "block - block with cursor in it" << endl << "Other:" << endl << "q - quit" << endl @@ -106,10 +118,22 @@ int main (void) { mode = "flowbits"; } - else if(command == "point") + else if(command == "point" || command == "p") { brush = "point"; } + else if(command == "range" || command == "r") + { + cout << " :set range width<" << width << "># "; + getline(cin, command); + width = command == "" ? width : atoi (command.c_str()); + if(width < 1) width = 1; + cout << " :set range height<" << height << "># "; + getline(cin, command); + height = command == "" ? height : atoi (command.c_str()); + if(height < 1) height = 1; + brush = "range"; + } else if(command == "block") { brush = "block"; @@ -214,7 +238,6 @@ int main (void) cout << "sizeof(tiletypes) = " << sizeof(tiles) << endl; for(uint32_t xx = 0; xx < 16; xx++) for(uint32_t yy = 0; yy < 16; yy++) { - cout<< xx << " " << yy <<": " << tiles[xx][yy] << endl; tiles[xx][yy] = 335;// 45 DFHack::naked_designation & des = designations[xx][yy].bits; @@ -224,10 +247,8 @@ int main (void) des.skyview = 0; des.light = 0; des.subterranean = 1; - temp1[xx][yy] = 10015; temp2[xx][yy] = 10015; - } Maps->WriteTemperatures((x/16),(y/16),z, &temp1, &temp2); Maps->WriteDesignations((x/16),(y/16),z, &designations); @@ -257,46 +278,18 @@ int main (void) else { // place the magma - Maps->ReadDesignations((x/16),(y/16),z, &designations); - Maps->ReadTemperatures((x/16),(y/16),z, &temp1, &temp2); - if(brush == "point") - { - if(mode != "flowbits") - { - // fix temperatures so we don't produce lethal heat traps - if(amount == 0 || designations[x%16][y%16].bits.liquid_type == DFHack::liquid_magma && mode == "water") - temp1[x%16][y%16] = temp2[x%16][y%16] = 10015; - DFHack::naked_designation & flow = designations[x%16][y%16].bits; - if(setmode == "s.") - { - flow.flow_size = amount; - } - else if(setmode == "s+") - { - if(flow.flow_size < amount) - flow.flow_size = amount; - } - else if(setmode == "s-") - { - if (flow.flow_size > amount) - flow.flow_size = amount; - } - } - if(mode == "magma") - designations[x%16][y%16].bits.liquid_type = DFHack::liquid_magma; - else if(mode == "water") - designations[x%16][y%16].bits.liquid_type = DFHack::liquid_water; - } - else + if(brush != "range") { - for(uint32_t xx = 0; xx < 16; xx++) for(uint32_t yy = 0; yy < 16; yy++) + Maps->ReadDesignations((x/16),(y/16),z, &designations); + Maps->ReadTemperatures((x/16),(y/16),z, &temp1, &temp2); + if(brush == "point") { if(mode != "flowbits") { // fix temperatures so we don't produce lethal heat traps - if(amount == 0 || designations[xx][yy].bits.liquid_type == DFHack::liquid_magma && mode == "water") - temp1[xx%16][yy%16] = temp2[xx%16][yy%16] = 10015; - DFHack::naked_designation & flow= designations[xx][yy].bits; + if(amount == 0 || designations[x%16][y%16].bits.liquid_type == DFHack::liquid_magma && mode == "water") + temp1[x%16][y%16] = temp2[x%16][y%16] = 10015; + DFHack::naked_designation & flow = designations[x%16][y%16].bits; if(setmode == "s.") { flow.flow_size = amount; @@ -313,36 +306,162 @@ int main (void) } } if(mode == "magma") - designations[xx][yy].bits.liquid_type = DFHack::liquid_magma; + designations[x%16][y%16].bits.liquid_type = DFHack::liquid_magma; else if(mode == "water") - designations[xx][yy].bits.liquid_type = DFHack::liquid_water; + designations[x%16][y%16].bits.liquid_type = DFHack::liquid_water; + } + else + { + for(uint32_t xx = 0; xx < 16; xx++) for(uint32_t yy = 0; yy < 16; yy++) + { + if(mode != "flowbits") + { + // fix temperatures so we don't produce lethal heat traps + if(amount == 0 || designations[xx][yy].bits.liquid_type == DFHack::liquid_magma && mode == "water") + temp1[xx%16][yy%16] = temp2[xx%16][yy%16] = 10015; + DFHack::naked_designation & flow= designations[xx][yy].bits; + if(setmode == "s.") + { + flow.flow_size = amount; + } + else if(setmode == "s+") + { + if(flow.flow_size < amount) + flow.flow_size = amount; + } + else if(setmode == "s-") + { + if (flow.flow_size > amount) + flow.flow_size = amount; + } + } + if(mode == "magma") + designations[xx][yy].bits.liquid_type = DFHack::liquid_magma; + else if(mode == "water") + designations[xx][yy].bits.liquid_type = DFHack::liquid_water; + } } + Maps->WriteTemperatures((x/16),(y/16),z, &temp1, &temp2); + Maps->WriteDesignations(x/16,y/16,z, &designations); + + // make the magma flow :) + DFHack::t_blockflags bflags; + Maps->ReadBlockFlags((x/16),(y/16),z,bflags); + // 0x00000001 = job-designated + // 0x0000000C = run flows? - both bit 3 and 4 required for making magma placed on a glacier flow + if(flowmode == "f+") + { + bflags.bits.liquid_1 = true; + bflags.bits.liquid_2 = true; + } + else if(flowmode == "f-") + { + bflags.bits.liquid_1 = false; + bflags.bits.liquid_2 = false; + } + else + { + cout << "flow bit 1 = " << bflags.bits.liquid_1 << endl; + cout << "flow bit 2 = " << bflags.bits.liquid_2 << endl; + } + Maps->WriteBlockFlags((x/16),(y/16),z,bflags); } - Maps->WriteTemperatures((x/16),(y/16),z, &temp1, &temp2); - Maps->WriteDesignations(x/16,y/16,z, &designations); - } - - // make the magma flow :) - DFHack::t_blockflags bflags; - Maps->ReadBlockFlags((x/16),(y/16),z,bflags); - // 0x00000001 = job-designated - // 0x0000000C = run flows? - both bit 3 and 4 required for making magma placed on a glacier flow - if(flowmode == "f+") - { - bflags.bits.liquid_1 = true; - bflags.bits.liquid_2 = true; - } - else if(flowmode == "f-") - { - bflags.bits.liquid_1 = false; - bflags.bits.liquid_2 = false; - } - else - { - cout << "flow bit 1 = " << bflags.bits.liquid_1 << endl; - cout << "flow bit 2 = " << bflags.bits.liquid_2 << endl; + else if (brush == "range") + { + // Crop the range into each block if necessary + int beginxblock = x/16; + int endxblock = (x+width)/16; + int beginyblock = y/16; + int endyblock = (y+height)/16; + for(uint32_t bx = beginxblock; bx < endxblock+1; bx++) for(uint32_t by = beginyblock; by < endyblock+1; by++) + { + if(Maps->isValidBlock(bx,by,z)) + { + Maps->ReadDesignations(bx,by,z, &designations); + Maps->ReadTemperatures(bx,by,z, &temp1, &temp2); + // Take original range and crop it into current block + int nx = x; + int ny = y; + int nwidth = width; + int nheight = height; + if(x/16 < bx) //Start point is left of block + { + nx = bx*16; + nwidth -= nx - x; + } + if (nx/16 < (nx+nwidth-1)/16)// End point is right of block + { + nwidth = (bx*16)+16-nx; + } + if(y/16 < by) //Start point is above block + { + ny = by*16; + nheight -= ny - y; + } + if (ny/16 < (ny+nheight-1)/16) // End point is below block + { + nheight = (by*16)+16-ny; + } + cout << " Block:" << bx << "," << by << ":" << endl; + cout << " Start:" << nx << "," << ny << ":" << endl; + cout << " Area: " << nwidth << "," << nheight << ":" << endl; + for(uint32_t xx = nx; xx < nx+nwidth; xx++) for(uint32_t yy = ny; yy < ny+nheight; yy++) + { + if(mode != "flowbits") + { + // fix temperatures so we don't produce lethal heat traps + if(amount == 0 || designations[xx%16][yy%16].bits.liquid_type == DFHack::liquid_magma && mode == "water") + temp1[xx%16][yy%16] = temp2[xx%16][yy%16] = 10015; + DFHack::naked_designation & flow= designations[xx%16][yy%16].bits; + if(setmode == "s.") + { + flow.flow_size = amount; + } + else if(setmode == "s+") + { + if(flow.flow_size < amount) + flow.flow_size = amount; + } + else if(setmode == "s-") + { + if (flow.flow_size > amount) + flow.flow_size = amount; + } + } + if(mode == "magma") + designations[xx%16][yy%16].bits.liquid_type = DFHack::liquid_magma; + else if(mode == "water") + designations[xx%16][yy%16].bits.liquid_type = DFHack::liquid_water; + } + Maps->WriteTemperatures(bx,by,z, &temp1, &temp2); + Maps->WriteDesignations(bx,by,z, &designations); + + // make the magma flow :) + DFHack::t_blockflags bflags; + Maps->ReadBlockFlags(bx,by,z,bflags); + // 0x00000001 = job-designated + // 0x0000000C = run flows? - both bit 3 and 4 required for making magma placed on a glacier flow + if(flowmode == "f+") + { + bflags.bits.liquid_1 = true; + bflags.bits.liquid_2 = true; + } + else if(flowmode == "f-") + { + bflags.bits.liquid_1 = false; + bflags.bits.liquid_2 = false; + } + else + { + cout << "flow bit 1 = " << bflags.bits.liquid_1 << endl; + cout << "flow bit 2 = " << bflags.bits.liquid_2 << endl; + } + Maps->WriteBlockFlags(bx,by,z,bflags); + } + } + } + } - Maps->WriteBlockFlags((x/16),(y/16),z,bflags); cout << "OK" << endl; Maps->Finish(); } while (0); @@ -350,8 +469,11 @@ int main (void) } DF->Detach(); #ifndef LINUX_BUILD - cout << "Done. Press any key to continue" << endl; - cin.ignore(); + if(!quiet) + { + cout << "Done. Press any key to continue" << endl; + cin.ignore(); + } #endif return 0; } diff --git a/tools/supported/position.cpp b/tools/supported/position.cpp index 5f666ec4e..a6383e0ef 100644 --- a/tools/supported/position.cpp +++ b/tools/supported/position.cpp @@ -8,8 +8,18 @@ using namespace std; #include -int main (void) +int main (int argc, char** argv) { + bool quiet = false; + for(int i = 1; i < argc; i++) + { + string test = argv[i]; + if(test == "-q") + { + quiet = true; + } + } + DFHack::Position * Position = 0; DFHack::ContextManager DFMgr("Memory.xml"); DFHack::Context * DF; @@ -31,7 +41,7 @@ int main (void) { int32_t x,y,z; int32_t width,height; - + if(Position->getViewCoords(x,y,z)) cout << "view coords: " << x << "/" << y << "/" << z << endl; if(Position->getCursorCoords(x,y,z)) @@ -43,15 +53,18 @@ int main (void) { cerr << "cursor and window parameters are unsupported on your version of DF" << endl; } - + if(!DF->Detach()) { cerr << "Can't detach from DF" << endl; } - + #ifndef LINUX_BUILD - cout << "Done. Press any key to continue" << endl; - cin.ignore(); + if(!quiet) + { + cout << "Done. Press any key to continue" << endl; + cin.ignore(); + } #endif return 0; } diff --git a/tools/supported/probe.cpp b/tools/supported/probe.cpp index 57992f470..eb4ec284c 100644 --- a/tools/supported/probe.cpp +++ b/tools/supported/probe.cpp @@ -8,8 +8,8 @@ #include #include -#define DFHACK_WANT_MISCUTILS -#define DFHACK_WANT_TILETYPES +#define DFHACK_WANT_MISCUTILS 1 +#define DFHACK_WANT_TILETYPES 1 #include using namespace DFHack; @@ -17,7 +17,8 @@ int main (int numargs, const char ** args) { DFHack::ContextManager DFMgr("Memory.xml"); DFHack::Context *DF = DFMgr.getSingleContext(); - + + BEGIN_PROBE: try { DF->Attach(); @@ -31,6 +32,7 @@ int main (int numargs, const char ** args) return 1; } + DFHack::Position *Pos = DF->getPosition(); DFHack::VersionInfo* mem = DF->getMemoryInfo(); DFHack::Maps *Maps = DF->getMaps(); @@ -57,6 +59,11 @@ int main (int numargs, const char ** args) Maps->Start(); + vector global_features; + std::map > local_features; + Maps->ReadLocalFeatures(local_features); + Maps->ReadGlobalFeatures(global_features); + int32_t cursorX, cursorY, cursorZ; Pos->getCursorCoords(cursorX,cursorY,cursorZ); if(cursorX != -30000) @@ -82,7 +89,15 @@ int main (int numargs, const char ** args) // tiletype std::cout <<"tiletype: " << tiletype; if(tileTypeTable[tiletype].name) - std::cout << " = " << tileTypeTable[tiletype].name; + std::cout << " = " << tileTypeTable[tiletype].name << std::endl; + + printf("%-10s: %4d %s\n","Class",tileTypeTable[tiletype].c,TileClassString[ tileTypeTable[tiletype].c ] , 0); + printf("%-10s: %4d %s\n","Material",tileTypeTable[tiletype].c,TileMaterialString[ tileTypeTable[tiletype].m ] , 0); + printf("%-10s: %4d %s\n","Special",tileTypeTable[tiletype].c,TileSpecialString[ tileTypeTable[tiletype].s ] , 0); + printf("%-10s: %4d\n","Variant",tileTypeTable[tiletype].v , 0); + printf("%-10s: %s\n","Direction",tileTypeTable[tiletype].d.getStr() , 0); + + std::cout << std::endl; std::cout <<"temperature1: " << tmpb1[tileX][tileY] << " U" << std::endl; std::cout <<"temperature2: " << tmpb2[tileX][tileY] << " U" << std::endl; @@ -109,24 +124,45 @@ int main (int numargs, const char ** args) std::cout << "smooth?" << std::endl; uint32_t designato = block.origin + designatus + (tileX * 16 + tileY) * sizeof(t_designation); printf("designation offset: 0x%x\n", designato); - if(des.light) - std::cout << "Light "; - else - std::cout << " "; - if(des.skyview) - std::cout << "SkyView "; - else - std::cout << " "; - if(des.subterranean) - std::cout << "Underground "; - else - std::cout << " "; + + #define PRINT_FLAG( X ) printf("%-16s= %c\n", #X , ( des.X ? 'Y' : ' ' ) ) + PRINT_FLAG( hidden ); + PRINT_FLAG( light ); + PRINT_FLAG( skyview ); + PRINT_FLAG( subterranean ); + PRINT_FLAG( water_table ); + //PRINT_FLAG( rained ); + + planecoord pc; + pc.dim.x=blockX; pc.dim.y=blockY; + + PRINT_FLAG( feature_local ); + if( des.feature_local ) + { + printf("%-16s %4d (%2d) %s\n", "", + block.local_feature, + local_features[pc][block.local_feature]->type, + sa_feature[local_features[pc][block.local_feature]->type] + ); + } + + PRINT_FLAG( feature_global ); + if( des.feature_global ){ + printf("%-16s %4d (%2d) %s\n", "", + block.global_feature, + global_features[block.global_feature].type, + sa_feature[global_features[block.global_feature].type] + ); + } + #undef PRINT_FLAG std::cout << std::endl; } } + DF->Detach(); #ifndef LINUX_BUILD - std::cout << "Done. Press any key to continue" << std::endl; - cin.ignore(); + std::cout << "Press any key to refresh..." << std::endl; + cin.ignore(); + goto BEGIN_PROBE; #endif return 0; } diff --git a/tools/supported/prospector.cpp b/tools/supported/prospector.cpp index 77ba6afcb..4b036fb8d 100644 --- a/tools/supported/prospector.cpp +++ b/tools/supported/prospector.cpp @@ -41,10 +41,9 @@ struct compare_pair_second int main (int argc, const char* argv[]) { - bool showhidden = false; bool showbaselayers = false; - for(int i = 0; i < argc; i++) + for(int i = 1; i < argc; i++) { string test = argv[i]; if(test == "-a") @@ -76,9 +75,9 @@ int main (int argc, const char* argv[]) materials.clear(); vector global_features; std::map > local_features; - + vector< vector > layerassign; - + DFHack::ContextManager DFMgr("Memory.xml"); DFHack::Context *DF; try @@ -94,11 +93,10 @@ int main (int argc, const char* argv[]) #endif return 1; } - DFHack::Maps * Maps = DF->getMaps(); DFHack::Materials * Mats = DF->getMaterials(); - + // init the map if(!Maps->Start()) { @@ -109,7 +107,7 @@ int main (int argc, const char* argv[]) return 1; } Maps->getSize(x_max,y_max,z_max); - + if(!Maps->ReadGlobalFeatures(global_features)) { cerr << "Can't get global features." << endl; @@ -137,7 +135,7 @@ int main (int argc, const char* argv[]) #endif return 1; } - + // get region geology if(!Maps->ReadGeology( layerassign )) { @@ -147,7 +145,7 @@ int main (int argc, const char* argv[]) #endif return 1; } - + int16_t tempvein [16][16]; vector veins; uint32_t maximum_regionoffset = 0; @@ -161,16 +159,16 @@ int main (int argc, const char* argv[]) { if(!Maps->isValidBlock(x,y,z)) continue; - + // read data Maps->ReadBlock40d(x,y,z, &Block); //Maps->ReadTileTypes(x,y,z, &tiletypes); //Maps->ReadDesignations(x,y,z, &designations); - + memset(tempvein, -1, sizeof(tempvein)); veins.clear(); Maps->ReadVeins(x,y,z,&veins); - + if(showbaselayers) { //Maps->ReadRegionOffsets(x,y,z, ®ionoffsets); @@ -194,7 +192,7 @@ int main (int argc, const char* argv[]) } } } - + // for each vein for(int i = 0; i < (int)veins.size();i++) { @@ -214,7 +212,7 @@ int main (int argc, const char* argv[]) } } } - + // global feature overrides int16_t idx = Block.global_feature; if( idx != -1 && uint16_t(idx) < global_features.size() && global_features[idx].type == DFHack::feature_Underworld) @@ -234,7 +232,7 @@ int main (int argc, const char* argv[]) } } } - + idx = Block.local_feature; if( idx != -1 ) { @@ -263,7 +261,7 @@ int main (int argc, const char* argv[]) } } } - + // count the material types for(uint32_t xi = 0 ; xi< 16 ; xi++) { @@ -275,7 +273,7 @@ int main (int argc, const char* argv[]) continue; if(tempvein[xi][yi] < 0) continue; - + if(materials.count(tempvein[xi][yi])) { materials[tempvein[xi][yi]] += 1; diff --git a/tools/supported/vdig.cpp b/tools/supported/vdig.cpp index 80cab23e7..19cb037a5 100644 --- a/tools/supported/vdig.cpp +++ b/tools/supported/vdig.cpp @@ -10,45 +10,44 @@ using namespace std; #include #include -#include +//#include #define MAX_DIM 0x300 -class Point + +//TODO: turn into the official coord class for DFHack/DF +class Vertex { public: - Point(uint32_t x, uint32_t y, uint32_t z) - { - this->x = x; - this->y = y; - this->z = z; - } - Point() + Vertex(uint32_t _x, uint32_t _y, uint32_t _z):x(_x),y(_y),z(_z) {} + Vertex() { x = y = z = 0; } - bool operator==(const Point &other) const + bool operator==(const Vertex &other) const { return (other.x == x && other.y == y && other.z == z); } - bool operator<(const Point &other) const + // 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)); } - Point operator/(int number) const + Vertex operator/(int number) const { - return Point(x/number, y/number, z); + return Vertex(x/number, y/number, z); } - Point operator%(int number) const + Vertex operator%(int number) const { - return Point(x%number, y%number, z); + return Vertex(x%number, y%number, z); } - Point operator-(int number) const + Vertex operator-(int number) const { - return Point(x,y,z-number); + return Vertex(x,y,z-number); } - Point operator+(int number) const + Vertex operator+(int number) const { - return Point(x,y,z+number); + return Vertex(x,y,z+number); } uint32_t x; uint32_t y; @@ -58,7 +57,7 @@ class Point class Block { public: - Block(DFHack::Maps *_m, Point _bcoord) + Block(DFHack::Maps *_m, Vertex _bcoord) { vector veins; m = _m; @@ -97,23 +96,23 @@ class Block valid = true; } } - int16_t MaterialAt(Point p) + int16_t MaterialAt(Vertex p) { return materials[p.x][p.y]; } - void ClearMaterialAt(Point p) + void ClearMaterialAt(Vertex p) { materials[p.x][p.y] = -1; } - int16_t TileTypeAt(Point p) + int16_t TileTypeAt(Vertex p) { return raw.tiletypes[p.x][p.y]; } - DFHack::t_designation DesignationAt(Point p) + DFHack::t_designation DesignationAt(Vertex p) { return raw.designation[p.x][p.y]; } - bool setDesignationAt(Point p, DFHack::t_designation des) + bool setDesignationAt(Vertex p, DFHack::t_designation des) { if(!valid) return false; dirty = true; @@ -136,7 +135,7 @@ class Block volatile bool dirty; DFHack::Maps * m; DFHack::mapblock40d raw; - Point bcoord; + Vertex bcoord; int16_t materials[16][16]; int8_t bitmap[16][16]; }; @@ -153,7 +152,7 @@ class MapCache }; ~MapCache() { - map::iterator p; + map::iterator p; for(p = blocks.begin(); p != blocks.end(); p++) { delete p->second; @@ -165,11 +164,11 @@ class MapCache return valid; } - Block * BlockAt (Point blockcoord) + Block * BlockAt (Vertex blockcoord) { if(!valid) return 0; - map ::iterator iter = blocks.find(blockcoord); + map ::iterator iter = blocks.find(blockcoord); if(iter != blocks.end()) { return (*iter).second; @@ -186,7 +185,7 @@ class MapCache } } - uint16_t tiletypeAt (Point tilecoord) + uint16_t tiletypeAt (Vertex tilecoord) { Block * b= BlockAt(tilecoord / 16); if(b && b->valid) @@ -196,7 +195,7 @@ class MapCache return 0; } - int16_t materialAt (Point tilecoord) + int16_t materialAt (Vertex tilecoord) { Block * b= BlockAt(tilecoord / 16); if(b && b->valid) @@ -205,7 +204,7 @@ class MapCache } return 0; } - bool clearMaterialAt (Point tilecoord) + bool clearMaterialAt (Vertex tilecoord) { Block * b= BlockAt(tilecoord / 16); if(b && b->valid) @@ -216,7 +215,7 @@ class MapCache } - DFHack::t_designation designationAt (Point tilecoord) + DFHack::t_designation designationAt (Vertex tilecoord) { Block * b= BlockAt(tilecoord / 16); if(b && b->valid) @@ -227,7 +226,7 @@ class MapCache temp.whole = 0; return temp; } - bool setDesignationAt (Point tilecoord, DFHack::t_designation des) + bool setDesignationAt (Vertex tilecoord, DFHack::t_designation des) { Block * b= BlockAt(tilecoord / 16); if(b && b->valid) @@ -237,7 +236,7 @@ class MapCache } return false; } - bool testCoord (Point tilecoord) + bool testCoord (Vertex tilecoord) { Block * b= BlockAt(tilecoord / 16); if(b && b->valid) @@ -249,7 +248,7 @@ class MapCache bool WriteAll() { - map::iterator p; + map::iterator p; for(p = blocks.begin(); p != blocks.end(); p++) { p->second->WriteDesignations(); @@ -265,17 +264,19 @@ class MapCache uint32_t y_tmax; uint32_t z_max; DFHack::Maps * Maps; - map blocks; + map blocks; }; 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()) @@ -283,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; @@ -299,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()) { @@ -315,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) { @@ -330,7 +334,7 @@ int main (int argc, char* argv[]) DF->Suspend(); Pos->getCursorCoords(cx,cy,cz); } - Point xy ((uint32_t)cx,(uint32_t)cy,cz); + Vertex xy ((uint32_t)cx,(uint32_t)cy,cz); if(xy.x == 0 || xy.x == tx_max - 1 || xy.y == 0 || xy.y == ty_max - 1) { cerr << "I won't dig the borders. That would be cheating!" << endl; @@ -358,13 +362,13 @@ int main (int argc, char* argv[]) return 1; } printf("%d/%d/%d tiletype: %d, veinmat: %d, designation: 0x%x ... DIGGING!\n", cx,cy,cz, tt, veinmat, des.whole); - stack flood; + stack flood; flood.push(xy); while( !flood.empty() ) { - Point current = flood.top(); + Vertex current = flood.top(); flood.pop(); int16_t vmat2 = MCache->materialAt(current); @@ -402,30 +406,30 @@ int main (int argc, char* argv[]) MCache->clearMaterialAt(current); if(current.x < tx_max - 2) { - flood.push(Point(current.x + 1, current.y, current.z)); + flood.push(Vertex(current.x + 1, current.y, current.z)); if(current.y < ty_max - 2) { - flood.push(Point(current.x + 1, current.y + 1,current.z)); - flood.push(Point(current.x, current.y + 1,current.z)); + flood.push(Vertex(current.x + 1, current.y + 1,current.z)); + flood.push(Vertex(current.x, current.y + 1,current.z)); } if(current.y > 1) { - flood.push(Point(current.x + 1, current.y - 1,current.z)); - flood.push(Point(current.x, current.y - 1,current.z)); + flood.push(Vertex(current.x + 1, current.y - 1,current.z)); + flood.push(Vertex(current.x, current.y - 1,current.z)); } } if(current.x > 1) { - flood.push(Point(current.x - 1, current.y,current.z)); + flood.push(Vertex(current.x - 1, current.y,current.z)); if(current.y < ty_max - 2) { - flood.push(Point(current.x - 1, current.y + 1,current.z)); - flood.push(Point(current.x, current.y + 1,current.z)); + flood.push(Vertex(current.x - 1, current.y + 1,current.z)); + flood.push(Vertex(current.x, current.y + 1,current.z)); } if(current.y > 1) { - flood.push(Point(current.x - 1, current.y - 1,current.z)); - flood.push(Point(current.x, current.y - 1,current.z)); + flood.push(Vertex(current.x - 1, current.y - 1,current.z)); + flood.push(Vertex(current.x, current.y - 1,current.z)); } } if(updown) diff --git a/tools/supported/veinlook.cpp b/tools/supported/veinlook.cpp index 7eaa2419f..81fcde949 100644 --- a/tools/supported/veinlook.cpp +++ b/tools/supported/veinlook.cpp @@ -536,6 +536,7 @@ main(int argc, char *argv[]) vector veinVector; vector IceVeinVector; vector splatter; + vector grass; t_temperatures b_temp1; t_temperatures b_temp2; @@ -549,7 +550,6 @@ main(int argc, char *argv[]) { pDF = DF = DFMgr.getSingleContext(); DF->Attach(); - Mats = DF->getMaterials(); Maps = DF->getMaps(); } catch (exception& e) @@ -560,6 +560,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 +577,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 )) @@ -620,7 +634,9 @@ main(int argc, char *argv[]) { TEMP_NO, TEMP_1, - TEMP_2 + TEMP_2, + WATER_SALT, + WATER_STAGNANT }; e_tempmode temperature = TEMP_NO; @@ -691,6 +707,12 @@ main(int argc, char *argv[]) case 'm': temperature = TEMP_2; break; + case 'c': + temperature = WATER_SALT; + break; + case 'v': + temperature = WATER_STAGNANT; + break; case 27: // escape key DF->Detach(); return 0; @@ -715,15 +737,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)) @@ -739,22 +775,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); + // get pointer to block blockaddr = Maps->getBlockPtr(cursorX+i,cursorY+j,cursorZ); blockaddr2 = Block->origin; - + // dig all veins and trees if(dig) { @@ -849,11 +884,12 @@ 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 totalVeinSize = mineralsize+ icesize + splattersize + grasssize; 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()) { if(vein != -1 && vein < totalVeinSize) { @@ -883,8 +919,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) { @@ -906,7 +945,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]; @@ -923,8 +962,31 @@ main(int argc, char *argv[]) } } } + if(hasCreatureMats) + { + gotoxy(50,3); + cprintf("Spatter: %s",PrintSplatterType(splatter[realvein].mat1,splatter[realvein].mat2,Mats->race).c_str()); + } + } + else + { + 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); + } + } + } gotoxy(50,3); - cprintf("Spatter: %s",PrintSplatterType(splatter[realvein].mat1,splatter[realvein].mat2,Mats->race).c_str()); + cprintf("Grass: 0x%x, %s",grassy.address_of, Mats->organic[grassy.material].id); } } } @@ -940,6 +1002,30 @@ main(int argc, char *argv[]) } } } + else if(temperature == WATER_SALT) + { + for(int x = 0; x < 16; x++) for(int y = 0; y < 16; y++) + { + if(Block->designation[x][y].bits.water_salt) + { + putch(x + 16,y + 16,'@',COLOR_WHITE); + } + } + gotoxy (50,8); + cprintf ("Salt water"); + } + else if(temperature == WATER_STAGNANT) + { + for(int x = 0; x < 16; x++) for(int y = 0; y < 16; y++) + { + if(Block->designation[x][y].bits.water_stagnant) + { + putch(x + 16,y + 16,'@',COLOR_WHITE); + } + } + gotoxy (50,8); + cprintf ("Stagnant water"); + } else { if(temperature == TEMP_1) diff --git a/tools/supported/weather.cpp b/tools/supported/weather.cpp index 6d441b00b..8e8a936bc 100644 --- a/tools/supported/weather.cpp +++ b/tools/supported/weather.cpp @@ -43,8 +43,25 @@ void printWeather(DFHack::WeatherType current) } using namespace DFHack; -int main (int numargs, const char ** args) +int main (int argc, char** argv) { + string command = ""; + bool quiet = false; + bool cmdarg = false; + for(int i = 1; i < argc; i++) + { + string test = argv[i]; + if(test == "-q") + { + quiet = true; + } + else + { + command = test; + cmdarg = true; + } + } + DFHack::ContextManager DFMgr("Memory.xml"); DFHack::Context *DF = DFMgr.getSingleContext(); @@ -68,9 +85,8 @@ int main (int numargs, const char ** args) { WeatherType current = (WeatherType) W->ReadCurrentWeather(); DF->Resume(); - string command = ""; printWeather(current); - getline(cin, command); + if (command == "") getline(cin, command); // only read from stdin if command hasn't been passed on the console DF->Suspend(); if(command == "c") { @@ -88,11 +104,17 @@ int main (int numargs, const char ** args) { end = true; } + command = ""; + if(cmdarg) end = true; // exit the loop when a cmd line arg has been passed. } #ifndef LINUX_BUILD - std::cout << "Done. Press any key to continue" << std::endl; - cin.ignore(); + if (!quiet) + { + std::cout << "Done. Press any key to continue" << std::endl; + cin.ignore(); + } #endif + DF->Resume(); DF->Detach(); return 0; }