From fac88478bd72056e4043e73ff1e8832ce1474a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 14 Sep 2009 00:02:46 +0000 Subject: [PATCH] code moved from khazad --- CMakeLists.txt | 30 + COMPILE | 55 + LICENSE | 21 + README | 47 + dfhack.kdev4 | 3 + library/CMakeLists.txt | 25 + library/DFCommon.h | 63 + library/DFDataModel.cpp | 105 ++ library/DFDataModel.h | 54 + library/DFHackAPI.cpp | 596 +++++++++ library/DFHackAPI.h | 160 +++ library/DFMemAccess.h | 34 + library/DFMemInfo.cpp | 382 ++++++ library/DFMemInfo.h | 113 ++ library/DFProcess.cpp | 160 +++ library/DFProcessManager.cpp | 526 ++++++++ library/DFProcessManager.h | 97 ++ library/DFTileTypes.cpp | 1024 ++++++++++++++++ library/DFTileTypes.h | 37 + library/DFTypes.h | 250 ++++ library/DFVector.h | 67 + library/LinuxMemAccess.h | 173 +++ library/WindowsMemAccess.h | 156 +++ library/deprecated/DfMap.cpp | 661 ++++++++++ library/deprecated/DfMap.h | 204 ++++ library/deprecated/DfMapHeader.h | 41 + library/deprecated/Extract.cpp | 433 +++++++ library/deprecated/Extract.h | 51 + library/deprecated/LoadSave.cpp | 188 +++ library/deprecated/LoadSaveV1.cpp | 106 ++ library/deprecated/LoadSaveV2.cpp | 196 +++ library/deprecated/ZlibHelper.h | 160 +++ library/md5/md5.cpp | 332 +++++ library/md5/md5.h | 92 ++ library/md5/md5wrapper.cpp | 140 +++ library/md5/md5wrapper.h | 73 ++ library/tinyxml/tinystr.cpp | 116 ++ library/tinyxml/tinystr.h | 319 +++++ library/tinyxml/tinyxml.cpp | 1888 +++++++++++++++++++++++++++++ library/tinyxml/tinyxml.h | 1802 +++++++++++++++++++++++++++ library/tinyxml/tinyxmlerror.cpp | 53 + library/tinyxml/tinyxmlparser.cpp | 1638 +++++++++++++++++++++++++ output/Memory.xml | 553 +++++++++ tools/CMakeLists.txt | 22 + tools/cleanmap.cpp | 50 + tools/expbench.cpp | 50 + tools/prospector.cpp | 168 +++ tools/reveal.cpp | 50 + 48 files changed, 13564 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 COMPILE create mode 100644 LICENSE create mode 100644 README create mode 100644 dfhack.kdev4 create mode 100644 library/CMakeLists.txt create mode 100644 library/DFCommon.h create mode 100644 library/DFDataModel.cpp create mode 100644 library/DFDataModel.h create mode 100644 library/DFHackAPI.cpp create mode 100644 library/DFHackAPI.h create mode 100644 library/DFMemAccess.h create mode 100644 library/DFMemInfo.cpp create mode 100644 library/DFMemInfo.h create mode 100644 library/DFProcess.cpp create mode 100644 library/DFProcessManager.cpp create mode 100644 library/DFProcessManager.h create mode 100644 library/DFTileTypes.cpp create mode 100644 library/DFTileTypes.h create mode 100644 library/DFTypes.h create mode 100644 library/DFVector.h create mode 100644 library/LinuxMemAccess.h create mode 100644 library/WindowsMemAccess.h create mode 100644 library/deprecated/DfMap.cpp create mode 100644 library/deprecated/DfMap.h create mode 100644 library/deprecated/DfMapHeader.h create mode 100644 library/deprecated/Extract.cpp create mode 100644 library/deprecated/Extract.h create mode 100644 library/deprecated/LoadSave.cpp create mode 100644 library/deprecated/LoadSaveV1.cpp create mode 100644 library/deprecated/LoadSaveV2.cpp create mode 100644 library/deprecated/ZlibHelper.h create mode 100644 library/md5/md5.cpp create mode 100644 library/md5/md5.h create mode 100644 library/md5/md5wrapper.cpp create mode 100644 library/md5/md5wrapper.h create mode 100644 library/tinyxml/tinystr.cpp create mode 100644 library/tinyxml/tinystr.h create mode 100644 library/tinyxml/tinyxml.cpp create mode 100644 library/tinyxml/tinyxml.h create mode 100644 library/tinyxml/tinyxmlerror.cpp create mode 100644 library/tinyxml/tinyxmlparser.cpp create mode 100644 output/Memory.xml create mode 100644 tools/CMakeLists.txt create mode 100644 tools/cleanmap.cpp create mode 100644 tools/expbench.cpp create mode 100644 tools/prospector.cpp create mode 100644 tools/reveal.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..ab8f95695 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +# main project file. use it from a build sub-folder, see COMPILE for details +PROJECT (dfhack) +cmake_minimum_required(VERSION 2.6) + +# disable warning, autosearch +if(COMMAND cmake_policy) + cmake_policy(SET CMP0003 NEW) +endif(COMMAND cmake_policy) + +if("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") + message(SEND_ERROR "In-source builds are not allowed.") +endif("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") + +IF(NOT DEFINED CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") +ENDIF(NOT DEFINED CMAKE_BUILD_TYPE) + +SET(SO_MAJOR_VERSION "0") +SET(SO_MINOR_VERSION "0") +SET(SO_BUILD_VERSION "1") +SET(SO_VERSION "${SO_MAJOR_VERSION}.${SO_MINOR_VERSION}.${SO_BUILD_VERSION}") + + +SET( LIBRARY_OUTPUT_PATH ${dfhack_SOURCE_DIR}/output CACHE PATH "Output directory for the dfhack library" ) +SET( EXECUTABLE_OUTPUT_PATH ${dfhack_SOURCE_DIR}/output CACHE PATH "Output directory for the dfhack tools" ) + +include_directories (${CMAKE_SOURCE_DIR}/library/) + +add_subdirectory (library) +add_subdirectory (tools) \ No newline at end of file diff --git a/COMPILE b/COMPILE new file mode 100644 index 000000000..19a5d2e55 --- /dev/null +++ b/COMPILE @@ -0,0 +1,55 @@ +Here's how you build dfhack! +---------------------------- + +First, there is one dependency: + cmake - it's the build system + +building the library is simple. Enter the build folder, run th tools. Like this: + cd build + cmake .. -DCMAKE_BUILD_TYPE:string=Release + make + +Changing folder to build and using 'cmake ..' is important! + +This will build the library and its tools and place them in /output. +You can also use a cmake-friendly IDE like KDevelop 4 or the cmake GUI program. + +Building on Windows: +-------------------- + +You need cmake. Get the win32 installer version from the official site: http://www.cmake.org/cmake/resources/software.html +It has the usual installer wizard thing. + +* Using mingw: + +You also need a compiler. I build dfhack using mingw. You can get it from the mingw site: +Get the automated installer, it will download newest version of mingw and set things up nicely. +You'll have to add C:\MinGW\ to your PATH variable. + + - Building: + open up cmd and navigate to the dfhack\build folder, run cmake and the mingw version of make: + cd build + cmake .. -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE:string=Release + mingw32-make + +* Using some other compiler: + +I'm afraid you are on your own. dfhack wasn't tested with any other compiler. +Try using a different cmake generator that's intended for your tools. + +Build targets +------------- +dfhack has a few build targets. If you're only after the library run 'make dfhack'. +'make' will build everything. +'make expbench' will build the expbench throughput testing program and the library. + +Build types +----------- +cmake allows you to pick a build type by changing this variable: CMAKE_BUILD_TYPE + +cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE + +Without specifying a build type or 'None', cmake uses the CMAKE_CXX_FLAGS variable for building. +Valid build types include 'Release' and 'Debug'. There are others, but they aren't really that useful. + +Have fun. \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..5b904f714 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +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. diff --git a/README b/README new file mode 100644 index 000000000..d28c1d10a --- /dev/null +++ b/README @@ -0,0 +1,47 @@ +Introduction +------------ +DFHack is a Dwarf Fortress memory access library and a set of basic tools using this library. +The library is a work in progress, so things might change as more tools are written for it. + +Getting DFHack +---------------- +You can get the code or a release at DFHack's sourceforge site: + https://sourceforge.net/projects/dfhack/ + +* subversion access: + svn co https://dfhack.svn.sourceforge.net/svnroot/dfhack/trunk dfhack + +Using the library +----------------- +The library should be compilable under Linux with GCC and under Windows with MinGW32 +compiler. It is using the cmake build system. See COMPILE for details. + +Main part of the API is in SimpleAPI.h +You might also have to include a few other files: +Types.h - defines many of the used data structures +TileTypes.h - functions for translating map tile type values to useful properties + (wall, flooer, etc.) + +Memory offset definitions +------------------------- + +The file with memory offset definitions used by dfhack can be found in the output folder. +TODO: write format explanation + +Tools +----- +All the DFHack tools are terminal programs. This might seem strange to Windows users, +but they are meant mostly as examples for developers. Still, they can be useful and +are cross-platform just like the library itself. + +* expbench - just exports the map 1000 times over. Meant to test how fast the + DFHack<->DF interface is. This should take 3-12 seconds. + +* reveal - plain old reveal tool. Demonstrates writing map data back to DF. + +* prospector - scans the map for minerals. by default it only scans only visible veins. + You can make it show hidden things with '-a' and base rock and soil layers + with '-b'. These can be combined ('-ab') + +* cleanmap - cleans mud, vomit, snow and all kinds of bloody mess from the map. It will + clean your irrigated farms too, so consider yourself warned. \ No newline at end of file diff --git a/dfhack.kdev4 b/dfhack.kdev4 new file mode 100644 index 000000000..771cc037c --- /dev/null +++ b/dfhack.kdev4 @@ -0,0 +1,3 @@ +[Project] +Manager=KDevCMakeManager +Name=dfhack diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt new file mode 100644 index 000000000..cf98a4888 --- /dev/null +++ b/library/CMakeLists.txt @@ -0,0 +1,25 @@ +# don't use this file directly. use the one in the root folder of the project + +SET(PROJECT_SRCS +DFDataModel.cpp +DFMemInfo.cpp +DFProcess.cpp +DFProcessManager.cpp +DFHackAPI.cpp +DFTileTypes.cpp +md5/md5.cpp +md5/md5wrapper.cpp +tinyxml/tinystr.cpp +tinyxml/tinyxml.cpp +tinyxml/tinyxmlerror.cpp +tinyxml/tinyxmlparser.cpp +) + +IF(UNIX) + add_definitions(-DLINUX_BUILD) +ELSE(UNIX) + SET(PROJECT_LIBS psapi) +ENDIF(UNIX) + +ADD_LIBRARY(dfhack SHARED ${PROJECT_SRCS}) +TARGET_LINK_LIBRARIES(dfhack ${PROJECT_LIBS}) \ No newline at end of file diff --git a/library/DFCommon.h b/library/DFCommon.h new file mode 100644 index 000000000..025acf884 --- /dev/null +++ b/library/DFCommon.h @@ -0,0 +1,63 @@ +/* +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 DFCOMMON_H_INCLUDED +#define DFCOMMON_H_INCLUDED + +///TODO: separate into extrenal and internal + +#include +#include +#include +//#include +//using namespace boost::bimaps; + +#include +using namespace std; +#include +#include +#include + +#ifdef LINUX_BUILD +#include +#include +#include +#else +#define WINVER 0x0501 // OpenThread(), PSAPI, Toolhelp32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#endif + +#include "DFTypes.h" +#include "DFDataModel.h" +#include "DFProcessManager.h" +#include "DFMemAccess.h" +#include "DFVector.h" +//#include "DfMap.h" + + +#endif // DFCOMMON_H_INCLUDED diff --git a/library/DFDataModel.cpp b/library/DFDataModel.cpp new file mode 100644 index 000000000..28205e8b8 --- /dev/null +++ b/library/DFDataModel.cpp @@ -0,0 +1,105 @@ +/* +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 "DFCommon.h" + + +DfVector DMWindows40d::readVector (uint32_t offset, uint32_t item_size) +{ + /* + MSVC++ vector is four pointers long + ptr allocator + ptr start + ptr end + ptr alloc_end + + we don't care about alloc_end because we don't try to add stuff + we also don't care about the allocator thing in front + */ + uint32_t start = MreadDWord(offset+4); + uint32_t end = MreadDWord(offset+8); + uint32_t size = (end - start) /4; + return DfVector(start,size,item_size); +}; + + +const string DMWindows40d::readSTLString (uint32_t offset) +{ + /* + MSVC++ string + ptr allocator + union + { + char[16] start; + char * start_ptr + } + Uint32 length + Uint32 capacity + */ + uint32_t start_offset = offset + 4; + uint32_t length = MreadDWord(offset + 20); + uint32_t capacity = MreadDWord(offset + 24); + char * temp = new char[capacity+1]; + + // read data from inside the string structure + if(capacity < 16) + { + Mread(start_offset, capacity, (uint8_t *)temp); + } + else // read data from what the offset + 4 dword points to + { + start_offset = MreadDWord(start_offset);// dereference the start offset + Mread(start_offset, capacity, (uint8_t *)temp); + } + + temp[length] = 0; + string ret = temp; + delete temp; + return ret; +}; + + +DfVector DMLinux40d::readVector (uint32_t offset, uint32_t item_size) +{ + /* + GNU libstdc++ vector is three pointers long + ptr start + ptr end + ptr alloc_end + + we don't care about alloc_end because we don't try to add stuff + */ + uint32_t start = MreadDWord(offset); + uint32_t end = MreadDWord(offset+4); + uint32_t size = (end - start) /4; + return DfVector(start,size,item_size); +}; + + +const string DMLinux40d::readSTLString (uint32_t offset) +{ + // GNU std::string is a single pointer (as far as we are concerned) + offset = MreadDWord(offset); + return MreadCString(offset); +}; diff --git a/library/DFDataModel.h b/library/DFDataModel.h new file mode 100644 index 000000000..5deac3af5 --- /dev/null +++ b/library/DFDataModel.h @@ -0,0 +1,54 @@ +/* +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 DATAMODEL_H_INCLUDED +#define DATAMODEL_H_INCLUDED + +class DfVector; + +// let's go pure virtual. +class DataModel +{ + public: + // read a string + virtual const string readSTLString (uint32_t offset) = 0; + // read a vector from memory + virtual DfVector readVector (uint32_t offset, uint32_t item_size) = 0; +}; + +class DMWindows40d : public DataModel +{ + virtual const string readSTLString (uint32_t offset); + // read a vector from memory + virtual DfVector readVector (uint32_t offset, uint32_t item_size); +}; + +class DMLinux40d : public DataModel +{ + virtual const string readSTLString (uint32_t offset); + // read a vector from memory + virtual DfVector readVector (uint32_t offset, uint32_t item_size); +}; + +#endif // DATAMODEL_H_INCLUDED diff --git a/library/DFHackAPI.cpp b/library/DFHackAPI.cpp new file mode 100644 index 000000000..8ce586166 --- /dev/null +++ b/library/DFHackAPI.cpp @@ -0,0 +1,596 @@ +/* +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 "DFCommon.h" +#include "DFVector.h" +#include "DFHackAPI.h" +#include "DFMemInfo.h" + +/* TODO: Test these + ReadVegetation + ReadConstruction + ReadBuilding + matgloss other than stone/soil +*/ + +// TODO: templating for vectors, simple copy constructor for stl vectors +// TODO: encapsulate access to multidimensional arrays. + +DFHackAPI::DFHackAPI(const string path_to_xml) +{ + xml = path_to_xml; + constructionsInited = false; + buildingsInited = false; + vegetationInited = false; + pm = NULL; +} + + +/*-----------------------------------* + * Init the mapblock pointer array * + *-----------------------------------*/ +bool DFHackAPI::InitMap() +{ + uint32_t map_loc, // location of the X array + temp_loc, // block location + temp_locx, // iterator for the X array + temp_locy, // iterator for the Y array + temp_locz; // iterator for the Z array + uint32_t map_offset = offset_descriptor->getAddress("map_data"); + uint32_t x_count_offset = offset_descriptor->getAddress("x_count"); + uint32_t y_count_offset = offset_descriptor->getAddress("y_count"); + uint32_t z_count_offset = offset_descriptor->getAddress("z_count"); + + // get the offsets once here + tile_type_offset = offset_descriptor->getOffset("type"); + designation_offset = offset_descriptor->getOffset("designation"); + occupancy_offset = offset_descriptor->getOffset("occupancy"); + + // get the map pointer + map_loc = MreadDWord(map_offset); + if (!map_loc) + { + // bad stuffz happend + return false; + } + + // get the size + x_block_count = MreadDWord(x_count_offset); + y_block_count = MreadDWord(y_count_offset); + z_block_count = MreadDWord(z_count_offset); + + // alloc array for pinters to all blocks + block = new uint32_t[x_block_count*y_block_count*z_block_count]; + + //read the memory from the map blocks - x -> map slice + for(uint32_t x = 0; x < x_block_count; x++) + { + temp_locx = map_loc + ( 4 * x ); + temp_locy = MreadDWord(temp_locx); + + // y -> map column + for(uint32_t y = 0; y < y_block_count; y++) + { + temp_locz = MreadDWord(temp_locy); + temp_locy += 4; + + // z -> map block (16x16) + for(uint32_t z = 0; z < z_block_count; z++) + { + temp_loc = MreadDWord(temp_locz); + temp_locz += 4; + block[x*y_block_count*z_block_count + y*z_block_count + z] = temp_loc; + } + } + } + return true; +} + +bool DFHackAPI::isValidBlock(uint32_t x, uint32_t y, uint32_t z) +{ + return block[x*y_block_count*z_block_count + y*z_block_count + z] != NULL; +} + +// 256 * sizeof(uint16_t) +bool DFHackAPI::ReadTileTypes(uint32_t x, uint32_t y, uint32_t z, uint16_t *buffer) +{ + uint32_t addr = block[x*y_block_count*z_block_count + y*z_block_count + z]; + if (addr!=NULL) + { + Mread(addr+tile_type_offset, 256 * sizeof(uint16_t), (uint8_t *)buffer); + return true; + } + return false; +} + + +// 256 * sizeof(uint32_t) +bool DFHackAPI::ReadDesignations(uint32_t x, uint32_t y, uint32_t z, uint32_t *buffer) +{ + uint32_t addr = block[x*y_block_count*z_block_count + y*z_block_count + z]; + if (addr!=NULL) + { + Mread(addr+designation_offset, 256 * sizeof(uint32_t), (uint8_t *)buffer); + return true; + } + return false; +} + + +// 256 * sizeof(uint32_t) +bool DFHackAPI::ReadOccupancy(uint32_t x, uint32_t y, uint32_t z, uint32_t *buffer) +{ + uint32_t addr = block[x*y_block_count*z_block_count + y*z_block_count + z]; + if (addr!=NULL) + { + Mread(addr+occupancy_offset, 256 * sizeof(uint32_t), (uint8_t *)buffer); + return true; + } + return false; +} + + +// 256 * sizeof(uint16_t) +bool DFHackAPI::WriteTileTypes(uint32_t x, uint32_t y, uint32_t z, uint16_t *buffer) +{ + uint32_t addr = block[x*y_block_count*z_block_count + y*z_block_count + z]; + if (addr!=NULL) + { + Mwrite(addr+tile_type_offset, 256 * sizeof(uint16_t), (uint8_t *)buffer); + return true; + } + return false; +} + + +// 256 * sizeof(uint32_t) +bool DFHackAPI::WriteDesignations(uint32_t x, uint32_t y, uint32_t z, uint32_t *buffer) +{ + uint32_t addr = block[x*y_block_count*z_block_count + y*z_block_count + z]; + if (addr!=NULL) + { + Mwrite(addr+designation_offset, 256 * sizeof(uint32_t), (uint8_t *)buffer); + return true; + } + return false; +} + + +// 256 * sizeof(uint32_t) +bool DFHackAPI::WriteOccupancy(uint32_t x, uint32_t y, uint32_t z, uint32_t *buffer) +{ + uint32_t addr = block[x*y_block_count*z_block_count + y*z_block_count + z]; + if (addr!=NULL) + { + Mwrite(addr+occupancy_offset, 256 * sizeof(uint32_t), (uint8_t *)buffer); + return true; + } + return false; +} + + +//16 of them? IDK... there's probably just 7. Reading more doesn't cause errors as it's an array nested inside a block +// 16 * sizeof(uint8_t) +bool DFHackAPI::ReadRegionOffsets(uint32_t x, uint32_t y, uint32_t z, uint8_t *buffer) +{ + uint32_t biome_stuffs = offset_descriptor->getOffset("biome_stuffs"); + uint32_t addr = block[x*y_block_count*z_block_count + y*z_block_count + z]; + if (addr!=NULL) + { + Mread(addr+biome_stuffs, 16 * sizeof(uint8_t), buffer); + return true; + } + return false; +} + + +// veins of a block, expects empty vein vector +bool DFHackAPI::ReadVeins(uint32_t x, uint32_t y, uint32_t z, vector & veins) +{ + uint32_t addr = block[x*y_block_count*z_block_count + y*z_block_count + z]; + int veinvector = offset_descriptor->getOffset("v_vein"); + int veinsize = offset_descriptor->getHexValue("v_vein_size"); + veins.clear(); + if(addr!=NULL && veinvector && veinsize) + { + assert(sizeof(t_vein) == veinsize); + // veins are stored as a vector of pointers to veins + /*pointer is 4 bytes! we work with a 32bit program here, no matter what architecture we compile khazad for*/ + DfVector p_veins = dm->readVector(addr + veinvector, 4); + + // read all veins + for (uint32_t i = 0; i< p_veins.getSize();i++) + { + t_vein v; + uint32_t temp; + // read the vein pointer from the vector + p_veins.read((uint32_t)i,(uint8_t *)&temp); + // read the vein data (dereference pointer) + Mread(temp, veinsize, (uint8_t *)&v); + // store it in the vector + veins.push_back(v); + } + return true; + } + return false; +} + + +// getter for map size +void DFHackAPI::getSize(uint32_t& x, uint32_t& y, uint32_t& z) +{ + x = x_block_count; + y = y_block_count; + z = z_block_count; +} + +bool DFHackAPI::ReadStoneMatgloss(vector & stones) +{ + int matgloss_address = offset_descriptor->getAddress("matgloss"); + int matgloss_offset = offset_descriptor->getHexValue("matgloss_skip"); + int matgloss_colors = offset_descriptor->getOffset("matgloss_stone_color"); + DfVector p_matgloss = dm->readVector(matgloss_address + matgloss_offset, 4); + + stones.clear(); + + for (uint32_t i = 0; i< p_matgloss.getSize();i++) + { + uint32_t temp; + // read the matgloss pointer from the vector into temp + p_matgloss.read((uint32_t)i,(uint8_t *)&temp); + // read the string pointed at by + t_matgloss mat; + mat.id = dm->readSTLString(temp); // reads a C string given an address + mat.fore = MreadWord(temp + matgloss_colors); + mat.back = MreadWord(temp + matgloss_colors + 2); + mat.bright = MreadWord(temp + matgloss_colors + 4); + stones.push_back(mat); + } + return true; +} + + +bool DFHackAPI::ReadMetalMatgloss(vector & metals) +{ + int matgloss_address = offset_descriptor->getAddress("matgloss"); + int matgloss_offset = offset_descriptor->getHexValue("matgloss_skip"); + int matgloss_colors = offset_descriptor->getOffset("matgloss_metal_color"); + DfVector p_matgloss = dm->readVector(matgloss_address + matgloss_offset*2, 4); + + metals.clear(); + + for (uint32_t i = 0; i< p_matgloss.getSize();i++) + { + uint32_t temp; + + // read the matgloss pointer from the vector into temp + p_matgloss.read((uint32_t)i,(uint8_t *)&temp); + + // read the string pointed at by + t_matgloss mat; + mat.id = dm->readSTLString(temp); // reads a C string given an address + mat.fore = MreadWord(temp + matgloss_colors); + mat.back = MreadWord(temp + matgloss_colors + 2); + mat.bright = MreadWord(temp + matgloss_colors + 4); + metals.push_back(mat); + } + return true; +} + + +bool DFHackAPI::ReadWoodMatgloss(vector & woods) +{ + int matgloss_address = offset_descriptor->getAddress("matgloss"); + // TODO: find flag for autumnal coloring? + DfVector p_matgloss = dm->readVector(matgloss_address, 4); + + woods.clear(); + + t_matgloss mat; + // TODO: use brown? + mat.fore = 7; + mat.back = 0; + mat.bright = 0; + for (uint32_t i = 0; i< p_matgloss.getSize();i++) + { + uint32_t temp; + + // read the matgloss pointer from the vector into temp + p_matgloss.read((uint32_t)i,(uint8_t *)&temp); + + // read the string pointed at by + mat.id = dm->readSTLString(temp); // reads a C string given an address + woods.push_back(mat); + } + return true; +} + + +bool DFHackAPI::ReadPlantMatgloss(vector & plants) +{ + int matgloss_address = offset_descriptor->getAddress("matgloss"); + int matgloss_offset = offset_descriptor->getHexValue("matgloss_skip"); + DfVector p_matgloss = dm->readVector(matgloss_address + matgloss_offset*3, 4); + + plants.clear(); + + // TODO: use green? + t_matgloss mat; + mat.fore = 7; + mat.back = 0; + mat.bright = 0; + for (uint32_t i = 0; i< p_matgloss.getSize();i++) + { + uint32_t temp; + + // read the matgloss pointer from the vector into temp + p_matgloss.read((uint32_t)i,(uint8_t *)&temp); + + // read the string pointed at by + mat.id = dm->readSTLString(temp); // reads a C string given an address + plants.push_back(mat); + } + return true; +} + +//vector v_geology[eBiomeCount]; +bool DFHackAPI::ReadGeology( vector < vector >& assign ) +{ + // get needed addresses and offsets + int region_x_offset = offset_descriptor->getAddress("region_x"); + int region_y_offset = offset_descriptor->getAddress("region_y"); + int region_z_offset = offset_descriptor->getAddress("region_z"); + int world_offset = offset_descriptor->getAddress("world"); + int world_regions_offset = offset_descriptor->getOffset("w_regions_arr"); + int region_size = offset_descriptor->getHexValue("region_size"); + int region_geo_index_offset = offset_descriptor->getOffset("region_geo_index_off"); + int world_geoblocks_offset = offset_descriptor->getOffset("w_geoblocks"); + int world_size_x = offset_descriptor->getOffset("world_size_x"); + int world_size_y = offset_descriptor->getOffset("world_size_y"); + int geolayer_geoblock_offset = offset_descriptor->getOffset("geolayer_geoblock_offset"); + + uint32_t regionX, regionY, regionZ; + uint16_t worldSizeX, worldSizeY; + + // check if we have 'em all + if( + !( + region_x_offset && region_y_offset && region_z_offset && world_size_x && world_size_y + && world_offset && world_regions_offset && world_geoblocks_offset && region_size + && region_geo_index_offset && geolayer_geoblock_offset + ) + ) + { + // fail if we don't have them + return false; + } + + // read position of the region inside DF world + MreadDWord(region_x_offset, regionX); + MreadDWord(region_y_offset, regionY); + MreadDWord(region_z_offset, regionZ); + + // get world size + MreadWord(world_offset + world_size_x, worldSizeX); + MreadWord(world_offset + world_size_y, worldSizeY); + + // get pointer to first part of 2d array of regions + uint32_t regions = MreadDWord(world_offset + world_regions_offset); + + // read the geoblock vector + DfVector geoblocks = dm->readVector(world_offset + world_geoblocks_offset,4); + + // iterate over 8 surrounding regions + local region + for(int i = eNorthWest; i< eBiomeCount; i++) + { + // check bounds, fix them if needed + int bioRX = regionX / 16 + (i%3) - 1; + if( bioRX < 0) bioRX = 0; + if( bioRX >= worldSizeX) bioRX = worldSizeX - 1; + int bioRY = regionY / 16 + (i/3) - 1; + if( bioRY < 0) bioRY = 0; + if( bioRY >= worldSizeY) bioRY = worldSizeY - 1; + + // get pointer to column of regions + uint32_t geoX; + MreadDWord(regions + bioRX*4, geoX); + + // get index into geoblock vector + uint16_t geoindex; + MreadWord(geoX + bioRY*region_size + region_geo_index_offset, geoindex); + + // get the geoblock from the geoblock vector using the geoindex + uint32_t geoblock_off; + geoblocks.read(geoindex,(uint8_t *) &geoblock_off); + + // get the vector with pointer to layers + DfVector geolayers = dm->readVector(geoblock_off + geolayer_geoblock_offset , 4); // let's hope + // make sure we don't load crap + assert(geolayers.getSize() > 0 && geolayers.getSize() <= 16); + + // finally, read the layer matgloss + for(uint32_t j = 0;j< geolayers.getSize();j++) + { + int geol_offset; + // read pointer to a layer + geolayers.read(j, (uint8_t *) & geol_offset); + // read word at pointer + 2, store in our geology vectors + v_geology[i].push_back(MreadWord(geol_offset + 2)); + } + } + assign.clear(); + // TODO: clean this up + for(int i = 0; i< eBiomeCount;i++) + { + assign.push_back(v_geology[i]); + } + return true; +} + + +// returns number of buildings, expects v_buildingtypes that will later map t_building.type to its name +uint32_t DFHackAPI::InitReadBuildings(vector &v_buildingtypes) +{ + buildingsInited = true; + int buildings = offset_descriptor->getAddress("buildings"); + assert(buildings); + p_bld = new DfVector( dm->readVector(buildings,4)); + offset_descriptor->copyBuildings(v_buildingtypes); + return p_bld->getSize(); +} + + +// read one building +bool DFHackAPI::ReadBuilding(const uint32_t &index, t_building & building) +{ + assert(buildingsInited); + uint32_t temp; + t_building_df40d bld_40d; + + // read pointer from vector at position + p_bld->read(index,(uint8_t *)&temp); + + //read building from memory + Mread(temp, sizeof(t_building_df40d), (uint8_t *)&bld_40d); + + // transform + int32_t type = -1; + offset_descriptor->resolveClassId(temp, type); + building.vtable = bld_40d.vtable; + building.x1 = bld_40d.x1; + building.x2 = bld_40d.x2; + building.y1 = bld_40d.y1; + building.y2 = bld_40d.y2; + building.z = bld_40d.z; + building.material = bld_40d.material; + building.type = type; +} + + +void DFHackAPI::FinishReadBuildings() +{ + buildingsInited = false; +} + + +//TODO: maybe do construction reading differently - this could go slow with many of them. +// returns number of constructions, prepares a vector, returns total number of constructions +uint32_t DFHackAPI::InitReadConstructions() +{ + constructionsInited = true; + int constructions = offset_descriptor->getAddress("constructions"); + assert(constructions); + + p_cons = new DfVector(dm->readVector(constructions,4)); + return p_cons->getSize(); +} + + +bool DFHackAPI::ReadConstruction(const uint32_t &index, t_construction & construction) +{ + assert(constructionsInited); + t_construction_df40d c_40d; + uint32_t temp; + + // read pointer from vector at position + p_cons->read((uint32_t)index,(uint8_t *)&temp); + + //read construction from memory + Mread(temp, sizeof(t_construction_df40d), (uint8_t *)&c_40d); + + // transform + construction.x = c_40d.x; + construction.y = c_40d.y; + construction.z = c_40d.z; + construction.material = c_40d.material; +} + + +void DFHackAPI::FinishReadConstructions() +{ + constructionsInited = false; +} + + +uint32_t DFHackAPI::InitReadVegetation() +{ + vegetationInited = true; + int vegetation = offset_descriptor->getAddress("vegetation"); + treeoffset = offset_descriptor->getOffset("tree_desc_offset"); + assert(vegetation && treeoffset); + p_veg = new DfVector(dm->readVector(vegetation,4)); + return p_veg->getSize(); +} + + +bool DFHackAPI::ReadVegetation(const uint32_t &index, t_tree_desc & shrubbery) +{ + assert(vegetationInited); + uint32_t temp; + // read pointer from vector at position + p_veg->read(index,(uint8_t *)&temp); + //read construction from memory + Mread(temp + treeoffset, sizeof(t_tree_desc), (uint8_t *) &shrubbery); + // FIXME: this is completely wrong. type isn't just tree/shrub but also different kinds of trees. stuff that grows around ponds has its own type ID + if(shrubbery.material.type == 3) shrubbery.material.type = 2; +} + + +void DFHackAPI::FinishReadVegetation() +{ + vegetationInited = false; +} + + +bool DFHackAPI::Attach() +{ + // detach all processes, destroy manager + if(pm != NULL) + delete pm; + // find a process (ProcessManager can find multiple when used properly) + pm = new ProcessManager(xml); // FIXME: handle bad XML better + if(!pm->findProcessess()) + return false; // FIXME: throw exceptions to distinguish errors? provide error code? + p = (*pm)[0]; // we just use the first found process + if(!p->attach()) + return false; // couldn't attach to process, no go + offset_descriptor = p->getDescriptor(); + dm = p->getDataModel(); + // process is attached, everything went just fine... hopefully + return true; +} + + +// TODO: clean inited stuff here +bool DFHackAPI::Detach() +{ + p->detach(); + if(pm != NULL) + delete pm; + pm = NULL; + p = NULL; + offset_descriptor = NULL; + dm = NULL; + return true; +} diff --git a/library/DFHackAPI.h b/library/DFHackAPI.h new file mode 100644 index 000000000..7ab67bd7d --- /dev/null +++ b/library/DFHackAPI.h @@ -0,0 +1,160 @@ +/* +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 SIMPLEAPI_H_INCLUDED +#define SIMPLEAPI_H_INCLUDED + +class memory_info; +class DfVector; +class ProcessManager; +class Process; +class DataModel; + +//FIXME: better control over state, creation and destruction +//TODO: give this the pimpl treatment? +class DFHackAPI +{ +private: + // internals + uint32_t * block; + uint32_t x_block_count, y_block_count, z_block_count; + uint32_t regionX, regionY, regionZ; + uint32_t worldSizeX, worldSizeY; + + uint32_t tile_type_offset; + uint32_t designation_offset; + uint32_t occupancy_offset; + + ProcessManager* pm; + Process* p; + DataModel* dm; + memory_info* offset_descriptor; + vector v_geology[eBiomeCount]; + string xml; + + bool constructionsInited; + bool buildingsInited; + bool vegetationInited; + uint32_t treeoffset; + DfVector *p_cons; + DfVector *p_bld; + DfVector *p_veg; + + +public: + DFHackAPI(const string path_to_xml); + + bool Attach(); + bool Detach(); + bool isAttached(); + /** + * Matgloss. next four methods look very similar. I could use two and move the processing one level up... + * I'll keep it like this, even with the code duplication as it will hopefully get more features and separate data types later. + * Yay for nebulous plans for a rock survey tool that tracks how much of which metal could be smelted from available resorces + */ + bool ReadStoneMatgloss(vector & output); + bool ReadWoodMatgloss (vector & output); + bool ReadMetalMatgloss(vector & output); + bool ReadPlantMatgloss(vector & output); + // FIXME: add creatures for all the creature products + + // read region surroundings, get their vectors of geolayers so we can do translation (or just hand the translation table to the client) + // returns an array of 9 vectors of indices into stone matgloss + /** + Method for reading the geological surrounding of the currently loaded region. + assign is a reference to an array of nine vectors of unsigned words that are to be filled with the data + array is indexed by the BiomeOffset enum + + I omitted resolving the layer matgloss in this API, because it would + introduce overhead by calling some method for each tile. You have to do it + yourself. First get the stuff from ReadGeology and then for each block get + the RegionOffsets. For each tile get the real region from RegionOffsets and + cross-reference it with the geology stuff (region -- array of vectors, depth -- + vector). I'm thinking about turning that Geology stuff into a + two-dimensional array with static size. + + this is the algorithm for applying matgloss: + void DfMap::applyGeoMatgloss(Block * b) + { + // load layer matgloss + for(int x_b = 0; x_b < BLOCK_SIZE; x_b++) + { + for(int y_b = 0; y_b < BLOCK_SIZE; y_b++) + { + int geolayer = b->designation[x_b][y_b].bits.geolayer_index; + int biome = b->designation[x_b][y_b].bits.biome; + b->material[x_b][y_b].type = Mat_Stone; + b->material[x_b][y_b].index = v_geology[b->RegionOffsets[biome]][geolayer]; + } + } + } + */ + bool ReadGeology( vector < vector >& assign ); + + /* + * BLOCK DATA + */ + /// allocate and read pointers to map blocks + bool InitMap(); + /// destroy the mapblock cache + bool DestroyMap(); + /// get size of the map in tiles + void getSize(uint32_t& x, uint32_t& y, uint32_t& z); + + /** + * Return false/0 on failure, buffer allocated by client app, 256 items long + */ + bool isValidBlock(uint32_t blockx, uint32_t blocky, uint32_t blockz); + + bool ReadTileTypes(uint32_t blockx, uint32_t blocky, uint32_t blockz, uint16_t *buffer); // 256 * sizeof(uint16_t) + bool WriteTileTypes(uint32_t blockx, uint32_t blocky, uint32_t blockz, uint16_t *buffer); // 256 * sizeof(uint16_t) + + bool ReadDesignations(uint32_t blockx, uint32_t blocky, uint32_t blockz, uint32_t *buffer); // 256 * sizeof(uint32_t) + bool WriteDesignations (uint32_t blockx, uint32_t blocky, uint32_t blockz, uint32_t *buffer); + + bool ReadOccupancy(uint32_t blockx, uint32_t blocky, uint32_t blockz, uint32_t *buffer); // 256 * sizeof(uint32_t) + bool WriteOccupancy(uint32_t blockx, uint32_t blocky, uint32_t blockz, uint32_t *buffer); // 256 * sizeof(uint32_t) + + /// read region offsets of a block + bool ReadRegionOffsets(uint32_t blockx, uint32_t blocky, uint32_t blockz, uint8_t *buffer); // 16 * sizeof(uint8_t) + + /// read aggregated veins of a block + bool ReadVeins(uint32_t blockx, uint32_t blocky, uint32_t blockz, vector & veins); + + /** + * Buildings, constructions, plants, all pretty straighforward. InitReadBuildings returns all the building types as a mapping between a numeric values and strings + */ + uint32_t InitReadConstructions(); + bool ReadConstruction(const uint32_t &index, t_construction & construction); + void FinishReadConstructions(); + + uint32_t InitReadBuildings(vector &v_buildingtypes); + bool ReadBuilding(const uint32_t &index, t_building & building); + void FinishReadBuildings(); + + uint32_t InitReadVegetation(); + bool ReadVegetation(const uint32_t &index, t_tree_desc & shrubbery); + void FinishReadVegetation(); +}; +#endif // SIMPLEAPI_H_INCLUDED diff --git a/library/DFMemAccess.h b/library/DFMemAccess.h new file mode 100644 index 000000000..de6a0bc9f --- /dev/null +++ b/library/DFMemAccess.h @@ -0,0 +1,34 @@ +/* +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 PROCESSUTIL_H_INCLUDED +#define PROCESSUTIL_H_INCLUDED + +#ifdef LINUX_BUILD + #include "LinuxMemAccess.h" +#else + #include "WindowsMemAccess.h" +#endif + +#endif // PROCESSUTIL_H_INCLUDED diff --git a/library/DFMemInfo.cpp b/library/DFMemInfo.cpp new file mode 100644 index 000000000..da12d57b5 --- /dev/null +++ b/library/DFMemInfo.cpp @@ -0,0 +1,382 @@ +/* +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 "DFCommon.h" +#include "DFMemInfo.h" +#include +#include + +memory_info::memory_info() +{ + base = 0; + classindex = 0; +} + + +void memory_info::setVersion(const char * v) +{ + version = v; +} + + +void memory_info::setVersion(string v) +{ + version = v; +} + + +string memory_info::getVersion() +{ + return version; +} + + +void memory_info::setOS(const char *os) +{ + string oss = os; + if(oss == "windows") + OS = OS_WINDOWS; + else if(oss == "linux") + OS = OS_LINUX; + else + OS = OS_BAD; +} + + +void memory_info::setOS(string os) +{ + if(os == "windows") + OS = OS_WINDOWS; + else if(os == "linux") + OS = OS_LINUX; + else + OS = OS_BAD; +} + + +void memory_info::setOS(OSType os) +{ + if(os >= OS_WINDOWS && os < OS_BAD) + { + OS = os; + return; + } + OS = OS_BAD; +} + + +memory_info::OSType memory_info::getOS() +{ + return OS; +} + + +// copy constructor +memory_info::memory_info(const memory_info &old) +{ + version = old.version; + OS = old.OS; + addresses = old.addresses; + offsets = old.offsets; + hexvals = old.hexvals; + strings = old.strings; + base = old.base; + classes = old.classes; + classsubtypes = old.classsubtypes; + classindex = old.classindex; +} + + +uint32_t memory_info::getBase () +{ + return base; +} + + +void memory_info::setBase (string s) +{ + base = strtol(s.c_str(), NULL, 16); +} + + +void memory_info::setBase (uint32_t b) +{ + base = b; +} + + +void memory_info::setOffset (string key, string value) +{ + uint32_t offset = strtol(value.c_str(), NULL, 16); + offsets[key] = offset; +} + + +void memory_info::setAddress (string key, string value) +{ + uint32_t address = strtol(value.c_str(), NULL, 16); + addresses[key] = address; +} + + +void memory_info::setHexValue (string key, string value) +{ + uint32_t hexval = strtol(value.c_str(), NULL, 16); + hexvals[key] = hexval; +} + + +void memory_info::setString (string key, string value) +{ + strings[key] = value; +} + + +// FIXME: next three methods should use some kind of custom container so it doesn't have to search so much. +void memory_info::setClass (const char * name, const char * vtable) +{ + for (uint32_t i=0; i thistypes; + classsubtypes.push_back(thistypes); + //cout << "multiclass " << name << ", assign " << cls.assign << ", vtable " << cls.vtable << endl; + return classsubtypes.size() - 1; +} + + +void memory_info::setMultiClassChild (uint32_t multi_index, const char * name, const char * type) +{ + vector & vec = classsubtypes[multi_index]; + for (uint32_t i=0; i& vec = classsubtypes[classes[i].multi_index]; + uint32_t type = MreadWord(address + classes[i].type_offset); + //printf ("class %d:%s offset 0x%x\n", i , classes[i].classname.c_str(), classes[i].type_offset); + // return typed building if successful + for (uint32_t k = 0; k < vec.size();k++) + { + if(vec[k].type == type) + { + //cout << " multi " << address + classes[i].type_offset << " " << vec[k].classname << endl; + classid = vec[k].assign; + return true; + } + } + } + // otherwise return the class we found + classid = classes[i].assign; + return true; + } + } + // we failed to find anything that would match + return false; +} + +// Flatten vtables into a index<->name mapping +void memory_info::copyBuildings(vector & v_buildingtypes) +{ + for(uint32_t i = 0;i< classes.size();i++) + { + v_buildingtypes.push_back(classes[i].classname); + if(!classes[i].is_multiclass) + { + continue; + } + vector & vec = classsubtypes[classes[i].multi_index]; + for (uint32_t k = 0; k < vec.size();k++) + { + v_buildingtypes.push_back(vec[k].classname); + } + } +} + + +// change base of all addresses/vtable entries +void memory_info::RebaseAddresses(int32_t new_base) +{ + map::iterator iter; + int32_t rebase = - (int32_t)base + new_base; + for(iter = addresses.begin(); iter != addresses.end(); iter++) + { + addresses[iter->first] = iter->second + rebase; + } +} + + +// change base of all addresses/vtable entries +void memory_info::RebaseAll(int32_t new_base) +{ + map::iterator iter; + int32_t rebase = - (int32_t)base + new_base; + for(iter = addresses.begin(); iter != addresses.end(); iter++) + { + addresses[iter->first] = iter->second + rebase; + } + RebaseVTable(rebase); +} + + +// change all vtable entries by offset +void memory_info::RebaseVTable(int32_t offset) +{ + vector::iterator iter; + for(iter = classes.begin(); iter != classes.end(); iter++) + { + iter->vtable += offset; + } +} + + +// Get named address +uint32_t memory_info::getAddress (string key) +{ + if(addresses.count(key)) + { + return addresses[key]; + } + return 0; +} + + +// Get named offset +uint32_t memory_info::getOffset (string key) +{ + if(offsets.count(key)) + { + return offsets[key]; + } + return 0; +} + + +// Get named string +std::string memory_info::getString (string key) +{ + if(strings.count(key)) + { + return strings[key]; + } + else return string(""); +} + + +// Get named numerical value +uint32_t memory_info::getHexValue (string key) +{ + if(hexvals.count(key)) + { + return hexvals[key]; + } + return 0; +} + + +// Reset everything +void memory_info::flush() +{ + base = 0; + addresses.clear(); + offsets.clear(); + strings.clear(); + hexvals.clear(); + classes.clear(); + classsubtypes.clear(); + classindex = 0; + version = ""; + OS = OS_BAD; +} diff --git a/library/DFMemInfo.h b/library/DFMemInfo.h new file mode 100644 index 000000000..1a6954301 --- /dev/null +++ b/library/DFMemInfo.h @@ -0,0 +1,113 @@ +/* +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 MEMINFO_H_INCLUDED +#define MEMINFO_H_INCLUDED + +class memory_info +{ +public: + enum OSType + { + OS_WINDOWS, + OS_LINUX, + OS_BAD + }; + struct t_class + { + string classname; + uint32_t vtable; + bool is_multiclass; + uint32_t multi_index; + uint32_t assign;// index to typeclass array if multiclass. return value if not. + uint32_t type_offset; // offset of type data for multiclass + }; + struct t_type + { + string classname; + uint32_t assign; + uint32_t type; + }; + memory_info(); + memory_info(const memory_info&); + + + void RebaseAddresses(int32_t new_base); + void RebaseAll(int32_t new_base); + uint32_t getBase (); + void setBase (string); + void setBase (uint32_t); + + uint32_t getOffset (string); + uint32_t getAddress (string); + uint32_t getHexValue (string); + string getString (string); + + void setVersion(const char *); + void setVersion(string); + string getVersion(); + + void setOS(const char *); + void setOS(string); + void setOS(OSType); + OSType getOS(); + + void setOffset (string, int32_t); + void setAddress (string, uint32_t); + void setHexValue (string, uint32_t); + + void setOffset (string, const char *); + void setAddress (string, const char *); + void setHexValue (string, const char *); + void setString (string, const char *); + + void setOffset (string, string); + void setAddress (string, string); + void setHexValue (string, string); + void setString (string, string); + + void RebaseVTable(int32_t offset); + void setClass (const char * name, const char * vtable); + uint32_t setMultiClass (const char * name, const char * vtable, const char * typeoffset); + void setMultiClassChild (uint32_t multi_index, const char * name, const char * type); + + // ALERT: uses memory reading directly + bool resolveClassId(uint32_t address, int32_t & classid); + void copyBuildings(vector & v_buildingtypes); + + void flush(); + +private: + map addresses; + map offsets; + map hexvals; + map strings; + vector classes; + vector > classsubtypes; + int32_t base; + uint32_t classindex; + string version; + OSType OS; +}; +#endif // MEMINFO_H_INCLUDED diff --git a/library/DFProcess.cpp b/library/DFProcess.cpp new file mode 100644 index 000000000..66ea54898 --- /dev/null +++ b/library/DFProcess.cpp @@ -0,0 +1,160 @@ +/* +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 "DFCommon.h" +#ifdef LINUX_BUILD +#include +#endif + + +Process::Process(DataModel * dm, memory_info* mi, ProcessHandle ph, uint32_t pid) +{ + my_datamodel = dm; + my_descriptor = mi; + my_handle = ph; + my_pid = pid; + attached = false; +} + + +Process::~Process() +{ + if(attached) + { + detach(); + } + // destroy data model. this is assigned by processmanager + delete my_datamodel; + freeResources(); +} + + +DataModel *Process::getDataModel() +{ + return my_datamodel; +} + + +memory_info * Process::getDescriptor() +{ + return my_descriptor; +} + + +void Process::setMemFile(const string & memf) +{ + assert(!attached); + memFile = memf; +} + + +#ifdef LINUX_BUILD +/* + * LINUX PART + */ +bool Process::attach() +{ + // TODO: check for errors! + if(g_pProcess != NULL) + { + return false; + } + + ptrace(PTRACE_ATTACH , my_handle, NULL, NULL); + wait(NULL); // wait for DF to be stopped. + attached = true; + + // HACK: Set the global process variables + g_pProcess = this; + g_ProcessHandle = my_handle; + g_ProcessMemFile = fopen(memFile.c_str(),"rb"); + return true; // we are attached +} + + +bool Process::detach() +{ + // TODO: check for errors. + ptrace(PTRACE_DETACH, my_handle, NULL, NULL); + attached = false; + + g_pProcess = NULL; + g_ProcessHandle = 0; + fclose(g_ProcessMemFile);// close /proc/PID/mem + g_ProcessMemFile = NULL; + return true; +} + + +void Process::freeResources() +{ + // nil +}; + + +#else +/* + * WINDOWS PART + */ + +//FIXME: should support stopping and resuming the process + +bool Process::attach() +{ + if(DebugActiveProcess(my_pid)) + { + attached = true; + g_pProcess = this; + g_ProcessHandle = my_handle; + + return true; + } + return false; +} + + +bool Process::detach() +{ + if(!attached) + { + return false; + } + if(DebugActiveProcessStop(my_pid)) + { + attached = false; + g_pProcess = NULL; + g_ProcessHandle = 0; + return true; + } + return false; +} + + +void Process::freeResources() +{ + // opened by process manager + CloseHandle(my_handle); +}; +#endif + diff --git a/library/DFProcessManager.cpp b/library/DFProcessManager.cpp new file mode 100644 index 000000000..3fc92cd27 --- /dev/null +++ b/library/DFProcessManager.cpp @@ -0,0 +1,526 @@ +/* +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 "DFCommon.h" + +#include "DFDataModel.h" +#include "DFMemInfo.h" + +#include "tinyxml/tinyxml.h" +#include + +/// HACK: global variables (only one process can be attached at the same time.) +Process * g_pProcess; ///< current process. non-NULL when picked +ProcessHandle g_ProcessHandle; ///< cache of handle to current process. used for speed reasons +FILE * g_ProcessMemFile; ///< opened /proc/PID/mem, valid when attached + + +#ifdef LINUX_BUILD +/* + * LINUX version of the process finder. + */ + +#include "md5/md5wrapper.h" + +Process* ProcessManager::addProcess(const string & exe,ProcessHandle PH, const string & memFile) +{ + md5wrapper md5; + // get hash of the running DF process + string hash = md5.getHashFromFile(exe); + vector::iterator it; + + // iterate over the list of memory locations + for ( it=meminfo.begin() ; it < meminfo.end(); it++ ) + { + if(hash == (*it).getString("md5")) // are the md5 hashes the same? + { + memory_info * m = &*it; + Process * ret; + //cout <<"Found process " << PH << ". It's DF version " << m->getVersion() << "." << endl; + + // df can run under wine on Linux + if(memory_info::OS_WINDOWS == (*it).getOS()) + { + ret= new Process(new DMWindows40d(),m,PH, PH); + } + else if (memory_info::OS_LINUX == (*it).getOS()) + { + ret= new Process(new DMLinux40d(),m,PH, PH); + } + else + { + // some error happened, continue with next process + continue; + } + // tell Process about the /proc/PID/mem file + ret->setMemFile(memFile); + processes.push_back(ret); + return ret; + } + } + return NULL; +} + +bool ProcessManager::findProcessess() +{ + DIR *dir_p; + struct dirent *dir_entry_p; + string dir_name; + string exe_link; + string cwd_link; + string cmdline_path; + string cmdline; + + // ALERT: buffer overrun potential + char target_name[1024]; + int target_result; + int errorcount; + int result; + + errorcount=0; + result=0; + // Open /proc/ directory + dir_p = opendir("/proc/"); + // Reading /proc/ entries + while(NULL != (dir_entry_p = readdir(dir_p))) + { + // Only PID folders (numbers) + if (strspn(dir_entry_p->d_name, "0123456789") != strlen(dir_entry_p->d_name)) + { + continue; + } + + // string manipulation - get /proc/PID/exe link and /proc/PID/mem names + dir_name = "/proc/"; + dir_name += dir_entry_p->d_name; + dir_name += "/"; + exe_link = dir_name + "exe"; + string mem_name = dir_name + "mem"; + + // resolve /proc/PID/exe link + target_result = readlink(exe_link.c_str(), target_name, sizeof(target_name)-1); + if (target_result == -1) + { + // bad result from link resolution, continue with another processed + continue; + } + // make sure we have a null terminated string... + target_name[target_result] = 0; + + // is this the regular linux DF? + if (strstr(target_name, "dwarfort.exe") != NULL) + { + exe_link = target_name; + // get PID + result = atoi(dir_entry_p->d_name); + // create linux process, add it to the vector + addProcess(exe_link,result,mem_name); + // continue with next process + continue; + } + + // FIXME: this fails when the wine process isn't started from the 'current working directory'. strip path data from cmdline + // DF in wine? + if(strstr(target_name, "wine-preloader")!= NULL) + { + // get working directory + cwd_link = dir_name + "cwd"; + target_result = readlink(cwd_link.c_str(), target_name, sizeof(target_name)-1); + target_name[target_result] = 0; + + // got path to executable, do the same for its name + cmdline_path = dir_name + "cmdline"; + ifstream ifs ( cmdline_path.c_str() , ifstream::in ); + getline(ifs,cmdline); + if (cmdline.find("dwarfort.exe") != string::npos || cmdline.find("Dwarf Fortress.exe") != string::npos) + { + // put executable name and path together + exe_link = target_name; + exe_link += "/"; + exe_link += cmdline; + + // get PID + result = atoi(dir_entry_p->d_name); + + // create wine process, add it to the vector + addProcess(exe_link,result,mem_name); + } + } + } + closedir(dir_p); + // return value depends on if we found some DF processes + if(processes.size()) + { + return true; + } + return false; +} + +#else + +// some magic - will come in handy when we start doing debugger stuff on Windows +bool EnableDebugPriv() +{ + bool bRET = FALSE; + TOKEN_PRIVILEGES tp; + HANDLE hToken; + + if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid)) + { + if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + if (hToken != INVALID_HANDLE_VALUE) + { + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + tp.PrivilegeCount = 1; + if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, 0, 0)) + { + bRET = TRUE; + } + CloseHandle(hToken); + } + } + } + return bRET; +} + +// WINDOWS version of the process finder +bool ProcessManager::findProcessess() +{ + // Get the list of process identifiers. + //TODO: make this dynamic. (call first to get the array size and second to really get process handles) + DWORD ProcArray[512], memoryNeeded, numProccesses; + HMODULE hmod = NULL; + DWORD junk; + HANDLE hProcess; + bool found = false; + + IMAGE_NT_HEADERS32 pe_header; + IMAGE_SECTION_HEADER sections[16]; + + EnableDebugPriv(); + if ( !EnumProcesses( ProcArray, sizeof(ProcArray), &memoryNeeded ) ) + { + return false; + } + + // Calculate how many process identifiers were returned. + numProccesses = memoryNeeded / sizeof(DWORD); + + // iterate through processes + for ( int i = 0; i < numProccesses; i++ ) + { + found = false; + // open process + hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, ProcArray[i] ); + if (NULL == hProcess) + continue; + // we've got some process, look at its first module + if(EnumProcessModules(hProcess, &hmod, 1 * sizeof(HMODULE), &junk)) + { + // TODO: check module filename to verify that it's DF! + // got base ;) + uint32_t base = (uint32_t)hmod; + // read from this process + g_ProcessHandle = hProcess; + uint32_t pe_offset = MreadDWord(base+0x3C); + Mread(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header); + Mread(base + pe_offset+ sizeof(pe_header), sizeof(sections) , (uint8_t *)§ions ); + // see if there's a version entry that matches this process + vector::iterator it; + for ( it=meminfo.begin() ; it < meminfo.end(); it++ ) + { + // filter by OS + if(memory_info::OS_WINDOWS == (*it).getOS()) + { + uint32_t pe_timestamp = (*it).getHexValue("pe_timestamp"); + if (pe_timestamp == pe_header.FileHeader.TimeDateStamp) + { + printf("Match found! Using version %s.\n", (*it).getVersion().c_str()); + // give the process a data model and memory layout fixed for the base of first module + memory_info *m = new memory_info(*it); + m->RebaseAll(base); + // keep track of created memory_info objects so we can destroy them later + destroy_meminfo.push_back(m); + // process is responsible for destroying its data model + Process *ret= new Process(new DMWindows40d(),m,hProcess, ProcArray[i]); + processes.push_back(ret); + found = true; + break; // break the iterator loop + } + } + } + // close handle of processes that aren't DF + if(!found) + { + CloseHandle(hProcess); + } + } + } + if(processes.size()) + return true; + return false; +} +#endif + + +void ProcessManager::ParseVTable(TiXmlElement* vtable, memory_info& mem) +{ + TiXmlElement* pClassEntry; + TiXmlElement* pClassSubEntry; + const char * rebase = vtable->Attribute("rebase"); + if(rebase) + { + int32_t rebase_offset = strtol(rebase, NULL, 16); + mem.RebaseVTable(rebase_offset); + } + pClassEntry = vtable->FirstChildElement(); + for(;pClassEntry;pClassEntry=pClassEntry->NextSiblingElement()) + { + string type = pClassEntry->Value(); + const char *cstr_name = pClassEntry->Attribute("name"); + const char *cstr_vtable = pClassEntry->Attribute("vtable"); + if(type== "class") + { + mem.setClass(cstr_name, cstr_vtable); + } + else if (type == "multiclass") + { + const char *cstr_typeoffset = pClassEntry->Attribute("typeoffset"); + int mclass = mem.setMultiClass(cstr_name, cstr_vtable, cstr_typeoffset); + pClassSubEntry = pClassEntry->FirstChildElement(); + for(;pClassSubEntry;pClassSubEntry=pClassSubEntry->NextSiblingElement()) + { + type = pClassSubEntry->Value(); + if(type== "class") + { + cstr_name = pClassSubEntry->Attribute("name"); + const char *cstr_value = pClassSubEntry->Attribute("type"); + mem.setMultiClassChild(mclass,cstr_name,cstr_value); + } + } + } + } +} + + + +void ProcessManager::ParseEntry (TiXmlElement* entry, memory_info& mem, map & knownEntries) +{ + TiXmlElement* pMemEntry; + const char *cstr_version = entry->Attribute("version"); + const char *cstr_os = entry->Attribute("os"); + const char *cstr_base = entry->Attribute("base"); + const char *cstr_rebase = entry->Attribute("rebase"); + if(cstr_base) + { + string base = cstr_base; + ParseEntry(knownEntries[base], mem, knownEntries); + } + // mandatory attributes missing? + if(!(cstr_version && cstr_os)) + { + cerr << "Bad entry in memory.xml detected, version or os attribute is missing."; + // skip if we don't have valid attributes + return; + } + string os = cstr_os; + mem.setVersion(cstr_version); + mem.setOS(cstr_os); + + // offset inherited addresses by 'rebase'. + int32_t rebase = 0; + if(cstr_rebase) + { + rebase = mem.getBase() + strtol(cstr_rebase, NULL, 16); + mem.RebaseAddresses(rebase); + } + + //set base to default, we're overwriting this because the previous rebase could cause havoc on Vista/7 + if(os == "windows") + { + // set default image base. this is fixed for base relocation later + mem.setBase(0x400000); + } + else if(os == "linux") + { + // this is wrong... I'm not going to do base image relocation on linux though. + // users are free to use a sane kernel that doesn't do this kind of **** by default + mem.setBase(0x0); + } + else + { + cerr << "unknown operating system " << os << endl; + return; + } + // process additional entries + //cout << "Entry " << cstr_version << " " << cstr_os << endl; + pMemEntry = entry->FirstChildElement()->ToElement(); + for(;pMemEntry;pMemEntry=pMemEntry->NextSiblingElement()) + { + // only elements get processed + const char *cstr_type = pMemEntry->Value(); + const char *cstr_name = pMemEntry->Attribute("name"); + const char *cstr_value = pMemEntry->GetText(); + // check for missing parts + string type, name, value; + type = cstr_type; + if(type == "VTable") + { + ParseVTable(pMemEntry, mem); + continue; + } + if( !(cstr_name && cstr_value)) + { + cerr << "underspecified MemInfo entry" << endl; + continue; + } + name = cstr_name; + value = cstr_value; + if (type == "HexValue") + { + mem.setHexValue(name, value); + } + else if (type == "Address") + { + mem.setAddress(name, value); + } + else if (type == "Offset") + { + mem.setOffset(name, value); + } + else if (type == "String") + { + mem.setString(name, value); + } + else + { + cerr << "Unknown MemInfo type: " << type << endl; + } + } // for +} // method + + +// load the XML file with offsets +bool ProcessManager::loadDescriptors(string path_to_xml) +{ + TiXmlDocument doc( path_to_xml.c_str() ); + bool loadOkay = doc.LoadFile(); + TiXmlHandle hDoc(&doc); + TiXmlElement* pElem; + TiXmlHandle hRoot(0); + memory_info mem; + + if ( loadOkay ) + { + // block: name + { + pElem=hDoc.FirstChildElement().Element(); + // should always have a valid root but handle gracefully if it does + if (!pElem) + { + cerr << "no pElem found" << endl; + return false; + } + string m_name=pElem->Value(); + if(m_name != "DFExtractor") + { + cerr << "DFExtractor != " << m_name << endl; + return false; + } + //cout << "got DFExtractor XML!" << endl; + // save this for later + hRoot=TiXmlHandle(pElem); + } + // transform elements + { + // trash existing list + meminfo.clear(); + TiXmlElement* pMemInfo=hRoot.FirstChild( "MemoryDescriptors" ).FirstChild( "Entry" ).Element(); + map map_pNamedEntries; + vector v_pEntries; + for( ; pMemInfo; pMemInfo=pMemInfo->NextSiblingElement("Entry")) + { + v_pEntries.push_back(pMemInfo); + const char *id; + if(id= pMemInfo->Attribute("id")) + { + string str_id = id; + map_pNamedEntries[str_id] = pMemInfo; + } + } + for(uint32_t i = 0; i< v_pEntries.size();i++) + { + memory_info mem; + //FIXME: add a set of entries processed in a step of this cycle, use it to check for infinite loops + /* recursive */ParseEntry( v_pEntries[i] , mem , map_pNamedEntries); + meminfo.push_back(mem); + } + // process found things here + } + return true; + } + else + { + // load failed + cerr << "Can't load memory offsets from memory.xml" << endl; + return false; + } +} + + +uint32_t ProcessManager::size() +{ + return processes.size(); +}; + + +Process * ProcessManager::operator[](uint32_t index) +{ + assert(index < processes.size()); + return processes[index]; +}; + + +ProcessManager::ProcessManager( string path_to_xml ) +{ + currentProcess = NULL; + currentProcessHandle = 0; + loadDescriptors( path_to_xml ); +} + + +ProcessManager::~ProcessManager() +{ + // delete all processes + for(uint32_t i = 0;i < processes.size();i++) + { + delete processes[i]; + } + //delete all generated memory_info stuff + for(uint32_t i = 0;i < destroy_meminfo.size();i++) + { + delete destroy_meminfo[i]; + } +} diff --git a/library/DFProcessManager.h b/library/DFProcessManager.h new file mode 100644 index 000000000..85a38ca1b --- /dev/null +++ b/library/DFProcessManager.h @@ -0,0 +1,97 @@ +/* +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 PROCESSMANAGER_H_INCLUDED +#define PROCESSMANAGER_H_INCLUDED + +#ifdef LINUX_BUILD +typedef pid_t ProcessHandle; +#else +typedef HANDLE ProcessHandle; +#endif + +class memory_info; +class DataModel; +class TiXmlElement; +class Process; + +/* +* Currently attached process and its handle +*/ +extern Process * g_pProcess; ///< current process. non-NULL when picked +extern ProcessHandle g_ProcessHandle; ///< cache of handle to current process. used for speed reasons +extern FILE * g_ProcessMemFile; ///< opened /proc/PID/mem, valid when attached + +class Process +{ + friend class ProcessManager; + protected: + Process(DataModel * dm, memory_info* mi, ProcessHandle ph, uint32_t pid); + ~Process(); + DataModel* my_datamodel; + memory_info * my_descriptor; + ProcessHandle my_handle; + uint32_t my_pid; + string memFile; + bool attached; + void freeResources(); + void setMemFile(const string & memf); + public: + // Set up stuff so we can read memory + bool attach(); + bool detach(); + // is the process still there? + memory_info *getDescriptor(); + DataModel *getDataModel(); +}; + +/* + * Process manager + */ +class ProcessManager +{ +public: + ProcessManager( string path_to_xml); + ~ProcessManager(); + bool findProcessess(); + uint32_t size(); + Process * operator[](uint32_t index); + +private: + // memory info entries loaded from a file + std::vector meminfo; + // vector to keep track of dynamically created memory_info entries + std::vector destroy_meminfo; + Process * currentProcess; + ProcessHandle currentProcessHandle; + std::vector processes; + bool loadDescriptors( string path_to_xml); + void ParseVTable(TiXmlElement* vtable, memory_info& mem); + void ParseEntry (TiXmlElement* entry, memory_info& mem, map & knownEntries); +#ifdef LINUX_BUILD + Process* addProcess(const string & exe,ProcessHandle PH,const string & memFile); +#endif +}; + +#endif // PROCESSMANAGER_H_INCLUDED diff --git a/library/DFTileTypes.cpp b/library/DFTileTypes.cpp new file mode 100644 index 000000000..b17c7aeb5 --- /dev/null +++ b/library/DFTileTypes.cpp @@ -0,0 +1,1024 @@ +/* +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 "DFTileTypes.h" + +bool isWallTerrain(int in) +{ + switch (in) + { + case 65: //stone fortification + case 79: //stone pillar + case 80: //lavastone pillar + case 81: //featstone pillar + case 82: //minstone pillar + case 83: //frozen liquid pillar + case 176: //stone wall worn1 (most worn) + case 177: //stone wall worn2 (sorta worn) + case 178: //stone wall worn3 (least worn) + case 219: //stone wall (not worn) + case 265: //soil wall + case 269: //lavastone wall rd2 + case 270: //lavastone wall r2d + case 271: //lavastone wall r2u + case 272: //lavastone wall ru2 + case 273: //lavastone wall l2u + case 274: //lavastone wall lu2 + case 275: //lavastone wall l2d + case 276: //lavastone wall ld2 + case 277: //lavastone wall lrud + case 278: //lavastone wall rud + case 279: //lavastone wall lrd + case 280: //lavastone wall lru + case 281: //lavastone wall lud + case 282: //lavastone wall rd + case 283: //lavastone wall ru + case 284: //lavastone wall lu + case 285: //lavastone wall ld + case 286: //lavastone wall ud + case 287: //lavastone wall lr + case 288: //featstone wall rd2 + case 289: //featstone wall r2d + case 290: //featstone wall r2u + case 291: //featstone wall ru2 + case 292: //featstone wall l2u + case 293: //featstone wall lu2 + case 294: //featstone wall l2d + case 295: //featstone wall ld2 + case 296: //featstone wall lrud + case 297: //featstone wall rud + case 298: //featstone wall lrd + case 299: //featstone wall lru + case 300: //featstone wall lud + case 301: //featstone wall rd + case 382: //featstone wall ru + case 303: //featstone wall lu + case 304: //featstone wall ld + case 305: //featstone wall ud + case 306: //featstone wall lr + case 307: //stone wall rd2 + case 308: //stone wall r2d + case 309: //stone wall r2u + case 310: //stone wall ru2 + case 311: //stone wall l2u + case 312: //stone wall lu2 + case 313: //stone wall l2d + case 314: //stone wall ld2 + case 315: //stone wall lrud + case 316: //stone wall rud + case 317: //stone wall lrd + case 318: //stone wall lru + case 319: //stone wall lud + case 320: //stone wall rd + case 321: //stone wall ru + case 322: //stone wall lu + case 323: //stone wall ld + case 324: //stone wall ud + case 325: //stone wall lr + case 326: //lavastone fortification + case 327: //featstone fortification + case 328: //lavastone wall worn1 (most worn) + case 329: //lavastone wall worn2 (middle worn) + case 330: //lavastone wall worn3 (least worn) + case 331: //lavastone wall + case 332: //featstone wall worn1 (most worn) + case 333: //featstone wall worn2 (middle worn) + case 334: //featstone wall worn3 (least worn) + case 335: //featstone wall + case 360: //frozen liquid fortification + case 361: //frozen liquid wall worn1 (most worn) + case 362: //frozen liquid wall worn2 (middle worn) + case 363: //frozen liquid wall worn3 (least worn) + case 364: //frozen liquid wall + case 417: //minstone wall rd2 + case 418: //minstone wall r2d + case 419: //minstone wall r2u + case 420: //minstone wall ru2 + case 421: //minstone wall l2u + case 422: //minstone wall lu2 + case 423: //minstone wall l2d + case 424: //minstone wall ld2 + case 425: //minstone wall lrud + case 426: //minstone wall rud + case 427: //minstone wall lrd + case 428: //minstone wall lru + case 429: //minstone wall lud + case 430: //minstone wall rd + case 431: //minstone wall ru + case 432: //minstone wall lu + case 433: //minstone wall ld + case 434: //minstone wall ud + case 435: //minstone wall lr + case 436: //minstone fortification + case 437: //minstone wall worn1 + case 438: //minstone wall worn2 + case 439: //minstone wall worn3 + case 440: //minstone wall worn4 + case 450: //frozen liquid wall rd2 + case 451: //frozen liquid wall r2d + case 452: //frozen liquid wall r2u + case 453: //frozen liquid wall ru2 + case 454: //frozen liquid wall l2u + case 455: //frozen liquid wall lu2 + case 456: //frozen liquid wall l2d + case 457: //frozen liquid wall ld2 + case 458: //frozen liquid wall lrud + case 459: //frozen liquid wall rud + case 460: //frozen liquid wall lrd + case 461: //frozen liquid wall lru + case 462: //frozen liquid wall lud + case 463: //frozen liquid wall rd + case 464: //frozen liquid wall ru + case 465: //frozen liquid wall lu + case 466: //frozen liquid wall ld + case 467: //frozen liquid wall ud + case 468: //frozen liquid wall lr + case 494: //constructed fortification + case 495: //constructed pillar + case 496: //constructed wall rd2 + case 497: //constructed wall r2d + case 498: //constructed wall r2u + case 499: //constructed wall ru2 + case 500: //constructed wall l2u + case 501: //constructed wall lu2 + case 502: //constructed wall l2d + case 503: //constructed wall ld2 + case 504: //constructed wall lrud + case 505: //constructed wall rud + case 506: //constructed wall lrd + case 507: //constructed wall lru + case 508: //constructed wall lud + case 509: //constructed wall rd + case 510: //constructed wall ru + case 511: //constructed wall lu + case 512: //constructed wall ld + case 513: //constructed wall ud + case 514: //constructed wall lr + return true; + break; + } + + return false; +} + +bool isFloorTerrain(int in) +{ + switch (in) + { + case 2: //murky pool + + case 19: //driftwood stack + case 24: //tree + // case 27: //up stair frozen liquid + case 34: //shrub + case 35: //Chasm + // case 38: //up stair lavastone + // case 41: //up stair soil + case 42: //eerie pit + case 43: //stone floor detailed + case 44: //lavastone floor detailed + case 45: //featstone? floor detailed + case 46: //minstone? floor detailed [calcite] + case 47: //frozen liquid floor detailed + /* + case 51: //up stair grass1 [muddy?] + case 54: //up stair grass2 + case 57: //up stair stone + case 60: //up stair minstone + case 63: //up stair featstone + */ + case 67: //campfire + case 70: //fire + /* + case 79: //stone pillar + case 80: //lavastone pillar + case 81: //featstone pillar + case 82: //minstone pillar + case 83: //frozen liquid pillar + */ + case 89: //waterfall landing + case 90: //river source + + case 231: //sapling + /* + case 233: //ramp grass dry + case 234: //ramp grass dead + case 235: //ramp grass1 [muddy?] + case 236: //ramp grass2 + case 237: //ramp stone + case 238: //ramp lavastone + case 239: //ramp featstone + case 240: //ramp minstone + case 241: //ramp soil + */ + case 242: //ash1 + case 243: //ash2 + case 244: //ash3 + // frozen floors / ramps + case 245: //ramp frozen liquid + case 258: //frozen liquid 1 + case 259: //frozen liquid 2 + case 260: //frozen liquid 3 + case 262: //frozen liquid 0 + case 261: //furrowed soil [road?] +// case 262: //Ice floor + case 264: //Lava bottom of map + case 336: //stone floor 1 (raw stone) + case 337: //stone floor 2 (raw stone) + case 338: //stone floor 3 (raw stone) + case 339: //stone floor 4 (raw stone) + case 340: //lavastone floor 1 (raw stone) + case 341: //lavastone floor 2 (raw stone) + case 342: //lavastone floor 3 (raw stone) + case 343: //lavastone floor 4 (raw stone) + case 344: //featstone floor 1 (raw stone) + case 345: //featstone floor 2 (raw stone) + case 346: //featstone floor 3 (raw stone) + case 347: //featstone floor 4 (raw stone) + case 348: //grass floor 1 (raw) + case 349: //grass floor 2 (raw) + case 350: //grass floor 3 (raw) + case 351: //grass floor 4 (raw) + case 352: //soil floor 1 (raw) + case 353: //soil floor 2 (raw) + case 354: //soil floor 3 (raw) + case 355: //soil floor 4 (raw) + case 356: //soil floor 1 wet (raw) [red sand?] + case 357: //soil floor 2 wet (raw) [red sand?] + case 358: //soil floor 3 wet (raw) [red sand?] + case 359: //soil floor 4 wet (raw) [red sand?] + + case 365: //river n + case 366: //river s + case 367: //river e + case 368: //river w + case 369: //river nw + case 370: //river ne + case 371: //river sw + case 372: //river se + + case 373: //stream wall n (below) + case 374: //stream wall s (below) + case 375: //stream wall e (below) + case 376: //stream wall w (below) + case 377: //stream wall nw (below) + case 378: //stream wall ne (below) + case 379: //stream wall sw (below) + case 380: //stream wall se (below) + + case 387: //dry grass floor1 + case 388: //dry grass floor2 + case 389: //dry grass floor3 + case 390: //dry grass floor4 + case 391: //dead tree + case 392: //dead sapling + case 393: //dead shrub + case 394: //dead grass floor1 + case 395: //dead grass floor2 + case 396: //dead grass floor3 + case 397: //dead grass floor4 + case 398: //grass floor1b + case 399: //grass floor2b + case 400: //grass floor3b + case 401: //grass floor4b + case 402: //stone boulder + case 403: //lavastone boulder + case 404: //featstone boulder + case 405: //stone pebbles 1 + case 406: //stone pebbles 2 + case 407: //stone pebbles 3 + case 408: //stone pebbles 4 + case 409: //lavastone pebbles 1 + case 410: //lavastone pebbles 2 + case 411: //lavastone pebbles 3 + case 412: //lavastone pebbles 4 + case 413: //featstone pebbles 1 + case 414: //featstone pebbles 2 + case 415: //featstone pebbles 3 + case 416: //featstone pebbles 4 + case 441: //minstone floor 1 (cavern raw) + case 442: //minstone floor 2 (cavern raw) + case 443: //minstone floor 3 (cavern raw) + case 444: //minstone floor 4 (cavern raw) + case 445: //minstone boulder + case 446: //minstone pebbles 1 + case 447: //minstone pebbles 2 + case 448: //minstone pebbles 3 + case 449: //minstone pebbles 4 + case 493: //constructed floor detailed + //case 495: //constructed pillar + case 517: //stair up constructed + //case 518: //ramp constructed + return true; + break; + } + + return false; +} + +bool isRampTerrain(int in) +{ + switch (in) + { + case 233: //ramp grass dry + case 234: //ramp grass dead + case 235: //ramp grass1 [muddy?] + case 236: //ramp grass2 + case 237: //ramp stone + case 238: //ramp lavastone + case 239: //ramp featstone + case 240: //ramp minstone + case 241: //ramp soil + case 245: //ramp frozen liquid + case 518: //ramp constructed + return true; + break; + } + + return false; +} + +bool isStairTerrain(int in) +{ + switch (in) + { + case 25: //up-down stair frozen liquid + case 26: //down stair frozen liquid + case 27: //up stair frozen liquid + case 36: //up-down stair lavastone + case 37: //down stair lavastone + case 38: //up stair lavastone + case 39: //up-down stair soil + case 40: //down stair soil + case 41: //up stair soil + case 49: //up-down stair grass1 [muddy?] + case 50: //down stair grass1 [muddy?] + case 51: //up stair grass1 [muddy?] + case 52: //up-down stair grass2 + case 53: //down stair grass2 + case 54: //up stair grass2 + case 55: //up-down stair stone + case 56: //down stair stone + case 57: //up stair stone + case 58: //up-down stair minstone + case 59: //down stair minstone + case 60: //up stair minstone + case 61: //up-down stair featstone + case 62: //down stair featstone + case 63: //up stair featstone + case 515: //stair up-down constructed + case 516: //stair down constructed + case 517: //stair up constructed + return true; + break; + } + + return false; +} + +bool isOpenTerrain(int in) +{ + switch (in) + { + //case -1: //uninitialized tile + case 1: //slope down + case 19: //driftwood stack + case 24: //tree + case 25: //up-down stair frozen liquid + case 26: //down stair frozen liquid + case 27: //up stair frozen liquid + case 32: //open space + case 34: //shrub + case 35: //chasm + case 36: //up-down stair lavastone + case 37: //down stair lavastone + case 38: //up stair lavastone + case 39: //up-down stair soil + case 40: //down stair soil + case 41: //up stair soil + case 42: //eerie pit + case 43: //stone floor detailed + case 44: //lavastone floor detailed + case 45: //featstone? floor detailed + case 46: //minstone? floor detailed [calcite] + case 47: //frozen liquid floor detailed + case 49: //up-down stair grass1 [muddy?] + case 50: //down stair grass1 [muddy?] + case 51: //up stair grass1 [muddy?] + case 52: //up-down stair grass2 + case 53: //down stair grass2 + case 54: //up stair grass2 + case 55: //up-down stair stone + case 56: //down stair stone + case 57: //up stair stone + case 58: //up-down stair minstone + case 59: //down stair minstone + case 60: //up stair minstone + case 61: //up-down stair featstone + case 62: //down stair featstone + case 63: //up stair featstone + case 67: //campfire + case 70: //fire + /* + case 79: //stone pillar + case 80: //lavastone pillar + case 81: //featstone pillar + case 82: //minstone pillar + case 83: //frozen liquid pillar + */ + case 231: //sapling + case 233: //ramp grass dry + case 234: //ramp grass dead + case 235: //ramp grass1 [muddy?] + case 236: //ramp grass2 + case 237: //ramp stone + case 238: //ramp lavastone + case 239: //ramp featstone + case 240: //ramp minstone + case 241: //ramp soil + case 242: //ash1 + case 243: //ash2 + case 244: //ash3 + case 245: //ramp frozen liquid + case 261: //furrowed soil [road?] + case 262: //Ice floor + case 336: //stone floor 1 (raw stone) + case 337: //stone floor 2 (raw stone) + case 338: //stone floor 3 (raw stone) + case 339: //stone floor 4 (raw stone) + case 340: //lavastone floor 1 (raw stone) + case 341: //lavastone floor 2 (raw stone) + case 342: //lavastone floor 3 (raw stone) + case 343: //lavastone floor 4 (raw stone) + case 344: //featstone floor 1 (raw stone) + case 345: //featstone floor 2 (raw stone) + case 346: //featstone floor 3 (raw stone) + case 347: //featstone floor 4 (raw stone) + case 348: //grass floor 1 (raw) + case 349: //grass floor 2 (raw) + case 350: //grass floor 3 (raw) + case 351: //grass floor 4 (raw) + case 352: //soil floor 1 (raw) + case 353: //soil floor 2 (raw) + case 354: //soil floor 3 (raw) + case 355: //soil floor 4 (raw) + case 356: //soil floor 1 wet (raw) [red sand?] + case 357: //soil floor 2 wet (raw) [red sand?] + case 358: //soil floor 3 wet (raw) [red sand?] + case 359: //soil floor 4 wet (raw) [red sand?] + case 381: //stream top (above) + case 387: //dry grass floor1 + case 388: //dry grass floor2 + case 389: //dry grass floor3 + case 390: //dry grass floor4 + case 391: //dead tree + case 392: //dead sapling + case 393: //dead shrub + case 394: //dead grass floor1 + case 395: //dead grass floor2 + case 396: //dead grass floor3 + case 397: //dead grass floor4 + case 398: //grass floor1b + case 399: //grass floor2b + case 400: //grass floor3b + case 401: //grass floor4b + case 402: //stone boulder + case 403: //lavastone boulder + case 404: //featstone boulder + case 405: //stone pebbles 1 + case 406: //stone pebbles 2 + case 407: //stone pebbles 3 + case 408: //stone pebbles 4 + case 409: //lavastone pebbles 1 + case 410: //lavastone pebbles 2 + case 411: //lavastone pebbles 3 + case 412: //lavastone pebbles 4 + case 413: //featstone pebbles 1 + case 414: //featstone pebbles 2 + case 415: //featstone pebbles 3 + case 416: //featstone pebbles 4 + case 441: //minstone floor 1 (cavern raw) + case 442: //minstone floor 2 (cavern raw) + case 443: //minstone floor 3 (cavern raw) + case 444: //minstone floor 4 (cavern raw) + case 445: //minstone boulder + case 446: //minstone pebbles 1 + case 447: //minstone pebbles 2 + case 448: //minstone pebbles 3 + case 449: //minstone pebbles 4 + case 493: //constructed floor detailed + //case 495: //constructed pillar + case 515: //stair up-down constructed + case 516: //stair down constructed + case 517: //stair up constructed + case 518: //ramp constructed + + return true; + break; + } + + return false; +} + +int picktexture(int in) +{ + switch ( in ) + { + case 1: //slope down + return 3; + + case 2: //murky pool + return 20; + + case 19: //driftwood stack + return 8; + + case 24: //tree + //return 3; + return 15; + + case 25: //up-down stair frozen liquid + case 26: //down stair frozen liquid + case 27: //up stair frozen liquid + return 25; + + case 32: //open space + return 5; + + case 34: //shrub + return 14; + + case 35: //chasm + return 31; + + case 36: //up-down stair lavastone + case 37: //down stair lavastone + case 38: //up stair lavastone + return 32; + + case 39: //up-down stair soil + case 40: //down stair soil + case 41: //up stair soil + return 10; + + case 42: //eerie pit + return 31; + + case 43: //stone floor detailed + return 7; + + case 44: //lavastone floor detailed + return 32; + + case 45: //featstone? floor detailed + return 18; + + case 46: //minstone? floor detailed [calcite] + return 9; + + case 47: //frozen liquid floor detailed + return 27; + + case 49: //up-down stair grass1 [muddy?] + case 50: //down stair grass1 [muddy?] + case 51: //up stair grass1 [muddy?] + return 0; + + case 52: //up-down stair grass2 + case 53: //down stair grass2 + case 54: //up stair grass2 + return 0; //16; + + case 55: //up-down stair stone + case 56: //down stair stone + case 57: //up stair stone + return 1; + + case 58: //up-down stair minstone + case 59: //down stair minstone + case 60: //up stair minstone + return 9; + + case 61: //up-down stair featstone + case 62: //down stair featstone + case 63: //up stair featstone + return 18; + + case 65: //stone fortification + return 22; + + case 67: //campfire + return 3; + + case 70: //fire + return 3; + + case 79: //stone pillar + return 1; + + case 80: //lavastone pillar + return 32; + + case 81: //featstone pillar + return 18; + + case 82: //minstone pillar + return 9; + + case 83: //frozen liquid pillar + return 27; + + case 89: //waterfall landing + return 20; + + case 90: //river source + return 20; + + case 176: //stone wall worn1 (most worn) + case 177: //stone wall worn2 (sorta worn) + case 178: //stone wall worn3 (least worn) + case 219: //stone wall (not worn) + return 1; + + case 231: //sapling + return 15; + + case 233: //ramp grass dry + return 33; + + case 234: //ramp grass dead + return 33; + + case 235: //ramp grass1 [muddy?] + return 0; + + case 236: //ramp grass2 + return 0; //16; + + case 237: //ramp stone + return 1; + + case 238: //ramp lavastone + return 32; + + case 239: //ramp featstone + return 18; + + case 240: //ramp minstone + return 9; + + case 241: //ramp soil + return 10; + + case 242: //ash1 + case 243: //ash2 + case 244: //ash3 + return 32; + + case 245: //ramp frozen liquid + return 27; + + case 258: //frozen liquid 1 + case 259: //frozen liquid 2 + case 260: //frozen liquid 3 + return 25; + + case 261: //furrowed soil [road?] + return 21; + + case 262: //frozen liquid 0 + return 25; + + case 264: //lava + return 24; + + case 265: //soil wall + return 10; + + case 269: //lavastone wall rd2 + case 270: //lavastone wall r2d + case 271: //lavastone wall r2u + case 272: //lavastone wall ru2 + case 273: //lavastone wall l2u + case 274: //lavastone wall lu2 + case 275: //lavastone wall l2d + case 276: //lavastone wall ld2 + case 277: //lavastone wall lrud + case 278: //lavastone wall rud + case 279: //lavastone wall lrd + case 280: //lavastone wall lru + case 281: //lavastone wall lud + case 282: //lavastone wall rd + case 283: //lavastone wall ru + case 284: //lavastone wall lu + case 285: //lavastone wall ld + case 286: //lavastone wall ud + case 287: //lavastone wall lr + return 32; + + case 288: //featstone wall rd2 + case 289: //featstone wall r2d + case 290: //featstone wall r2u + case 291: //featstone wall ru2 + case 292: //featstone wall l2u + case 293: //featstone wall lu2 + case 294: //featstone wall l2d + case 295: //featstone wall ld2 + case 296: //featstone wall lrud + case 297: //featstone wall rud + case 298: //featstone wall lrd + case 299: //featstone wall lru + case 300: //featstone wall lud + case 301: //featstone wall rd + case 382: //featstone wall ru + case 303: //featstone wall lu + case 304: //featstone wall ld + case 305: //featstone wall ud + case 306: //featstone wall lr + return 18; + + case 307: //stone wall rd2 + case 308: //stone wall r2d + case 309: //stone wall r2u + case 310: //stone wall ru2 + case 311: //stone wall l2u + case 312: //stone wall lu2 + case 313: //stone wall l2d + case 314: //stone wall ld2 + case 315: //stone wall lrud + case 316: //stone wall rud + case 317: //stone wall lrd + case 318: //stone wall lru + case 319: //stone wall lud + case 320: //stone wall rd + case 321: //stone wall ru + case 322: //stone wall lu + case 323: //stone wall ld + case 324: //stone wall ud + case 325: //stone wall lr + return 1; + + case 326: //lavastone fortification + return 32; + + case 327: //featstone fortification + return 18; + + case 328: //lavastone wall worn1 (most worn) + case 329: //lavastone wall worn2 (middle worn) + case 330: //lavastone wall worn3 (least worn) + case 331: //lavastone wall + return 32; + + case 332: //featstone wall worn1 (most worn) + case 333: //featstone wall worn2 (middle worn) + case 334: //featstone wall worn3 (least worn) + case 335: //featstone wall + return 18; + + case 336: //stone floor 1 (raw stone) + case 337: //stone floor 2 (raw stone) + case 338: //stone floor 3 (raw stone) + case 339: //stone floor 4 (raw stone) + return 17; + + case 340: //lavastone floor 1 (raw stone) + case 341: //lavastone floor 2 (raw stone) + case 342: //lavastone floor 3 (raw stone) + case 343: //lavastone floor 4 (raw stone) + return 32; + + case 344: //featstone floor 1 (raw stone) + case 345: //featstone floor 2 (raw stone) + case 346: //featstone floor 3 (raw stone) + case 347: //featstone floor 4 (raw stone) + return 18; + + case 348: //grass floor 1 (raw) + case 349: //grass floor 2 (raw) + case 350: //grass floor 3 (raw) + case 351: //grass floor 4 (raw) + return 0; + + case 352: //soil floor 1 (raw) + case 353: //soil floor 2 (raw) + case 354: //soil floor 3 (raw) + case 355: //soil floor 4 (raw) + return 10; + + case 356: //soil floor 1 wet (raw) [red sand?] + case 357: //soil floor 2 wet (raw) [red sand?] + case 358: //soil floor 3 wet (raw) [red sand?] + case 359: //soil floor 4 wet (raw) [red sand?] + return 10; + + case 360: //frozen liquid fortification + return 27; + + case 361: //frozen liquid wall worn1 (most worn) + case 362: //frozen liquid wall worn2 (middle worn) + case 363: //frozen liquid wall worn3 (least worn) + case 364: //frozen liquid wall + return 25; + + case 365: //river n + case 366: //river s + case 367: //river e + case 368: //river w + case 369: //river nw + case 370: //river ne + case 371: //river sw + case 372: //river se + return 19; + + case 373: //stream wall n (below) + case 374: //stream wall s (below) + case 375: //stream wall e (below) + case 376: //stream wall w (below) + case 377: //stream wall nw (below) + case 378: //stream wall ne (below) + case 379: //stream wall sw (below) + case 380: //stream wall se (below) + case 381: //stream top (above) + return 19; + + case 387: //dry grass floor1 + case 388: //dry grass floor2 + case 389: //dry grass floor3 + case 390: //dry grass floor4 + return 33; + + case 391: //dead tree + case 392: //dead sapling + case 393: //dead shrub + return 13; + + case 394: //dead grass floor1 + case 395: //dead grass floor2 + case 396: //dead grass floor3 + case 397: //dead grass floor4 + return 33; + + case 398: //grass floor1b + case 399: //grass floor2b + case 400: //grass floor3b + case 401: //grass floor4b + return 0; //16; + + case 402: //stone boulder + case 403: //lavastone boulder + case 404: //featstone boulder + return 18; + + case 405: //stone pebbles 1 + case 406: //stone pebbles 2 + case 407: //stone pebbles 3 + case 408: //stone pebbles 4 + return 12; + + case 409: //lavastone pebbles 1 + case 410: //lavastone pebbles 2 + case 411: //lavastone pebbles 3 + case 412: //lavastone pebbles 4 + return 12; + + case 413: //featstone pebbles 1 + case 414: //featstone pebbles 2 + case 415: //featstone pebbles 3 + case 416: //featstone pebbles 4 + return 12; + + case 417: //minstone wall rd2 + case 418: //minstone wall r2d + case 419: //minstone wall r2u + case 420: //minstone wall ru2 + case 421: //minstone wall l2u + case 422: //minstone wall lu2 + case 423: //minstone wall l2d + case 424: //minstone wall ld2 + case 425: //minstone wall lrud + case 426: //minstone wall rud + case 427: //minstone wall lrd + case 428: //minstone wall lru + case 429: //minstone wall lud + case 430: //minstone wall rd + case 431: //minstone wall ru + case 432: //minstone wall lu + case 433: //minstone wall ld + case 434: //minstone wall ud + case 435: //minstone wall lr + return 9; + + case 436: //minstone fortification + return 21; + + case 437: //minstone wall worn1 + case 438: //minstone wall worn2 + case 439: //minstone wall worn3 + case 440: //minstone wall worn4 + return 21; + + case 441: //minstone floor 1 (cavern raw) + case 442: //minstone floor 2 (cavern raw) + case 443: //minstone floor 3 (cavern raw) + case 444: //minstone floor 4 (cavern raw) + return 9; + + case 445: //minstone boulder + return 18; + + case 446: //minstone pebbles 1 + case 447: //minstone pebbles 2 + case 448: //minstone pebbles 3 + case 449: //minstone pebbles 4 + return 12; + + case 450: //frozen liquid wall rd2 + case 451: //frozen liquid wall r2d + case 452: //frozen liquid wall r2u + case 453: //frozen liquid wall ru2 + case 454: //frozen liquid wall l2u + case 455: //frozen liquid wall lu2 + case 456: //frozen liquid wall l2d + case 457: //frozen liquid wall ld2 + case 458: //frozen liquid wall lrud + case 459: //frozen liquid wall rud + case 460: //frozen liquid wall lrd + case 461: //frozen liquid wall lru + case 462: //frozen liquid wall lud + case 463: //frozen liquid wall rd + case 464: //frozen liquid wall ru + case 465: //frozen liquid wall lu + case 466: //frozen liquid wall ld + case 467: //frozen liquid wall ud + case 468: //frozen liquid wall lr + return 25; + + case 493: //constructed floor detailed + return 7; + + case 494: //constructed fortification + return 7; + + case 495: //constructed pillar + return 7; + + case 496: //constructed wall rd2 + case 497: //constructed wall r2d + case 498: //constructed wall r2u + case 499: //constructed wall ru2 + case 500: //constructed wall l2u + case 501: //constructed wall lu2 + case 502: //constructed wall l2d + case 503: //constructed wall ld2 + case 504: //constructed wall lrud + case 505: //constructed wall rud + case 506: //constructed wall lrd + case 507: //constructed wall lru + case 508: //constructed wall lud + case 509: //constructed wall rd + case 510: //constructed wall ru + case 511: //constructed wall lu + case 512: //constructed wall ld + case 513: //constructed wall ud + case 514: //constructed wall lr + return 22; + + case 515: //stair up-down constructed + case 516: //stair down constructed + case 517: //stair up constructed + return 4; + + case 518: //ramp constructed + return 4; + + case -1: //not assigned memory + return 6; + + default: //none of the above + return -1; + } + + return 6; +} diff --git a/library/DFTileTypes.h b/library/DFTileTypes.h new file mode 100644 index 000000000..0a195a72b --- /dev/null +++ b/library/DFTileTypes.h @@ -0,0 +1,37 @@ +/* +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 TILETYPES_H_INCLUDED +#define TILETYPES_H_INCLUDED + +/// TODO: turn into XML + +bool isWallTerrain(int in); +bool isFloorTerrain(int in); +bool isRampTerrain(int in); +bool isStairTerrain(int in); +bool isOpenTerrain(int in); +int picktexture(int in); + +#endif // TILETYPES_H_INCLUDED diff --git a/library/DFTypes.h b/library/DFTypes.h new file mode 100644 index 000000000..2928e6d6e --- /dev/null +++ b/library/DFTypes.h @@ -0,0 +1,250 @@ +/* +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 TYPES_H_INCLUDED +#define TYPES_H_INCLUDED + +struct t_matgloss +{ + string id; + uint8_t fore; // Annoyingly the offset for this differs between types + uint8_t back; + uint8_t bright; +}; +struct t_vein +{ + uint32_t vtable; + int16_t type; + int16_t assignment[16]; + int16_t unknown; + uint32_t flags; +}; + +struct t_matglossPair +{ + int16_t type; + int16_t index; +}; + +// raw +struct t_construction_df40d +{ + int16_t x; + int16_t y; + int16_t z; + int16_t unk1; + int16_t unk2; + t_matglossPair material; // 4B +// int16_t mat_type; +// int16_t mat_idx; +}; + +// cooked +struct t_construction +{ + uint16_t x; + uint16_t y; + uint16_t z; + t_matglossPair material; +// int16_t mat_type; +// int16_t mat_idx; +}; + +/* + dword vtable; + int minx; + int miny; + int centerx; + int maxx; + int maxy; + int centery; + int z; + dword height_not_used; + word mattype; + word matgloss; + word type; // NOTE: the actual field is in a different place +*/ + +//raw +struct t_building_df40d +{ + uint32_t vtable; + uint32_t x1; + uint32_t y1; + uint32_t centerx; + uint32_t x2; + uint32_t y2; + uint32_t centery; + uint32_t z; + uint32_t height; + t_matglossPair material; + // not complete +}; + +//cooked +struct t_building +{ + uint32_t vtable; + + uint32_t x1; + uint32_t y1; + + uint32_t x2; + uint32_t y2; + + uint32_t z; + + t_matglossPair material; + + uint32_t type; + // FIXME: not complete, we need building presence bitmaps for stuff like farm plots and stockpiles, orientation (N,E,S,W) and state (open/closed) +}; + +struct t_tree_desc +{ + t_matglossPair material; + uint16_t x; + uint16_t y; + uint16_t z; +}; + +// FIXME: in order in which the raw vectors appear in df memory, move to XML +enum RawType +{ + Mat_Wood, + Mat_Stone, + Mat_Plant, + Mat_Metal, + NUM_MATGLOSS_TYPES +}; + +enum BiomeOffset +{ + eNorthWest, + eNorth, + eNorthEast, + eWest, + eHere, + eEast, + eSouthWest, + eSouth, + eSouthEast, + eBiomeCount +}; + +// TODO: research this further? consult DF hacker wizards? +union t_designation +{ + uint32_t whole; + struct { + unsigned int flow_size : 3; // how much liquid is here? + unsigned int pile : 1; // stockpile? +/* + * All the different dig designations... needs more info, probably an enum + */ + unsigned int dig : 3; + unsigned int detail : 1;///<- wtf + unsigned int detail_event : 1;///<- more wtf + unsigned int hidden :1; + +/* + * This one is rather involved, but necessary to retrieve the base layer matgloss index + * see http://www.bay12games.com/forum/index.php?topic=608.msg253284#msg253284 for details + */ + unsigned int geolayer_index :4; + unsigned int light : 1; + unsigned int subterranean : 1; // never seen the light of day? + unsigned int skyview : 1; // sky is visible now, it rains in here when it rains + +/* + * Probably similar to the geolayer_index. Only with a different set of offsets and different data. + * we don't use this yet + */ + unsigned int biome : 4; +/* +0 = water +1 = magma +*/ + unsigned int liquid_type : 1; + unsigned int water_table : 1; // srsly. wtf? + unsigned int rained : 1; // does this mean actual rain (as in the blue blocks) or a wet tile? + unsigned int traffic : 2; // needs enum + unsigned int flow_forbid : 1; // idk wtf bbq + unsigned int liquid_static : 1; + unsigned int moss : 1;// I LOVE MOSS + unsigned int feature_present : 1; // another wtf... is this required for magma pipes to work? + unsigned int liquid_character : 2; // those ripples on streams? + } bits; +}; + +// occupancy flags (rat,dwarf,horse,built wall,not build wall,etc) +union t_occupancy +{ + uint32_t whole; + struct { + unsigned int building : 3;// building type... should be an enum? + // 7 = door + unsigned int unit : 1; + unsigned int unit_grounded : 1; + unsigned int item : 1; + // splatter. everyone loves splatter. + unsigned int mud : 1; + unsigned int vomit :1; + unsigned int debris1 :1; + unsigned int debris2 :1; + unsigned int debris3 :1; + unsigned int debris4 :1; + unsigned int blood_g : 1; + unsigned int blood_g2 : 1; + unsigned int blood_b : 1; + unsigned int blood_b2 : 1; + unsigned int blood_y : 1; + unsigned int blood_y2 : 1; + unsigned int blood_m : 1; + unsigned int blood_m2 : 1; + unsigned int blood_c : 1; + unsigned int blood_c2 : 1; + unsigned int blood_w : 1; + unsigned int blood_w2 : 1; + unsigned int blood_o : 1; + unsigned int blood_o2 : 1; + unsigned int slime : 1; + unsigned int slime2 : 1; + unsigned int blood : 1; + unsigned int blood2 : 1; + unsigned int debris5 : 1; + unsigned int snow : 1; + } bits; + struct { + unsigned int building : 3;// building type... should be an enum? + // 7 = door + unsigned int unit : 1; + unsigned int unit_grounded : 1; + unsigned int item : 1; + // splatter. everyone loves splatter. + unsigned int splatter : 26; + } unibits; +}; + +#endif // TYPES_H_INCLUDED diff --git a/library/DFVector.h b/library/DFVector.h new file mode 100644 index 000000000..79704b4a9 --- /dev/null +++ b/library/DFVector.h @@ -0,0 +1,67 @@ +/* +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 DFVECTOR_H_INCLUDED +#define DFVECTOR_H_INCLUDED + +class DfVector +{ +private: + // starting offset + uint32_t start; + // vector size + uint32_t size; + // vector item size + uint32_t item_size; + +public: + DfVector(uint32_t _start, uint32_t _size, uint32_t _item_size): + start(_start),size(_size),item_size(_item_size) {}; + DfVector(const DfVector & vec) + { + start = vec.start; + size = vec.size; + item_size = vec.item_size; + }; + DfVector(){}; + // get offset of the specified index + inline uint32_t operator[](uint32_t index) + { + assert(index < size); + return start + index*item_size; + }; + // get vector size + inline uint32_t getSize() + { + return size; + }; + // read item_size bytes from the right offset + inline void read (uint32_t index, uint8_t *target) + { + assert(index < size); + Mread (start + index*item_size, item_size, target); + }; +}; + +#endif // DFVECTOR_H_INCLUDED diff --git a/library/LinuxMemAccess.h b/library/LinuxMemAccess.h new file mode 100644 index 000000000..7971a5eb3 --- /dev/null +++ b/library/LinuxMemAccess.h @@ -0,0 +1,173 @@ +/* +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. +*/ + +/** + * DO NOT USE THIS FILE DIRECTLY! USE MemAccess.h INSTEAD! + */ +#include + +inline +uint8_t MreadByte (const uint32_t &offset) +{ + uint8_t val; + fseek(g_ProcessMemFile, offset,SEEK_SET); + fread ( &val, sizeof(uint8_t), 1, g_ProcessMemFile ); + return val; +} + +inline +void MreadByte (const uint32_t &offset, uint8_t &val ) +{ + fseek(g_ProcessMemFile, offset,SEEK_SET); + fread ( &val, sizeof(uint8_t), 1, g_ProcessMemFile ); +} + +inline +uint16_t MreadWord (const uint32_t &offset) +{ + uint16_t val; + fseek(g_ProcessMemFile, offset,SEEK_SET); + fread ( &val, sizeof(uint16_t), 1, g_ProcessMemFile ); + return val; +} + +inline +void MreadWord (const uint32_t &offset, uint16_t &val) +{ + fseek(g_ProcessMemFile, offset,SEEK_SET); + fread ( &val, sizeof(uint16_t), 1, g_ProcessMemFile ); +} + +inline +uint32_t MreadDWord (const uint32_t &offset) +{ + uint32_t val; + fseek(g_ProcessMemFile, offset,SEEK_SET); + fread ( &val, sizeof(uint32_t), 1, g_ProcessMemFile ); + return val; +} +inline +void MreadDWord (const uint32_t &offset, uint32_t &val) +{ + fseek(g_ProcessMemFile, offset,SEEK_SET); + fread ( &val, sizeof(uint32_t), 1, g_ProcessMemFile ); +} + +inline +uint64_t MreadQuad (const uint32_t &offset) +{ + uint64_t val; + fseek(g_ProcessMemFile, offset,SEEK_SET); + fread ( &val, sizeof(uint32_t), 1, g_ProcessMemFile ); + return val; +} + +inline +void MreadQuad (const uint32_t &offset, uint64_t &val) +{ + fseek(g_ProcessMemFile, offset,SEEK_SET); + fread ( &val, sizeof(uint32_t), 1, g_ProcessMemFile ); +} + +inline +void Mread (const uint32_t &offset, const uint32_t &size, uint8_t *target) +{ + fseek(g_ProcessMemFile, offset,SEEK_SET); + fread ( target, 1, size, g_ProcessMemFile ); +} + +inline +void MwriteDWord (uint32_t offset, uint32_t data) +{ + ptrace(PTRACE_POKEDATA,g_ProcessHandle, offset, data); +} + +// using these is expensive. +inline +void MwriteWord (uint32_t offset, uint16_t data) +{ + uint32_t orig = MreadDWord(offset); + orig |= 0x0000FFFF; + orig &= data; + ptrace(PTRACE_POKEDATA,g_ProcessHandle, offset, orig); +} + +inline +void MwriteByte (uint32_t offset, uint8_t data) +{ + uint32_t orig = MreadDWord(offset); + orig |= 0x000000FF; + orig &= data; + ptrace(PTRACE_POKEDATA,g_ProcessHandle, offset, orig); +} + +// blah. I hate the kernel devs for crippling /proc/PID/mem. THIS IS RIDICULOUS +inline +bool Mwrite (uint32_t offset, uint32_t size, uint8_t *source) +{ + uint32_t indexptr = 0; + while (size > 0) + { + // default: we push 4 bytes + if(size >= 4) + { + MwriteDWord(offset, *(uint32_t *) (source + indexptr)); + offset +=4; + indexptr +=4; + size -=4; + } + // last is either three or 2 bytes + else if(size >= 2) + { + MwriteWord(offset, *(uint16_t *) (source + indexptr)); + offset +=2; + indexptr +=2; + size -=2; + } + // finishing move + else if(size == 1) + { + MwriteByte(offset, *(uint8_t *) (source + indexptr)); + return true; + } + } +} + +inline +const std::string MreadCString (uint32_t offset) +{ + std::string temp; + char temp_c[256]; + int counter = 0; + char r; + do + { + r = MreadByte(offset+counter); + temp_c[counter] = r; + counter++; + } while (r); + temp_c[counter] = 0; + temp = temp_c; + return temp; +} diff --git a/library/WindowsMemAccess.h b/library/WindowsMemAccess.h new file mode 100644 index 000000000..a9d77880a --- /dev/null +++ b/library/WindowsMemAccess.h @@ -0,0 +1,156 @@ +/* +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. +*/ + +/** + * DO NOT USE THIS FILE DIRECTLY! USE MemAccess.h INSTEAD! + */ + +// let's hope this commented crap is never needed + /* + char buffer[256]; + DWORD oldProtect = 0; + DWORD numRead = 0; + VirtualProtectEx( hProc, (LPVOID)0x77810F34, 256, PAGE_EXECUTE_READWRITE, &oldProtect ); + ReadProcessMemory( hProc, (LPVOID)0x77810F34, buffer, 256, &numRead ); + VirtualProtectEx( hProc, (LPVOID)0x77810F34, 256, oldProtect, NULL ); //restore the original protection when you're done + */ + +// it would be possible to replace all this by macros + +inline +uint8_t MreadByte (const uint32_t &offset) +{ + uint8_t result; + ReadProcessMemory(g_ProcessHandle, (int*) offset, &result, sizeof(uint8_t), NULL); + return result; +} + + +inline +void MreadByte (const uint32_t &offset,uint8_t &result) +{ + ReadProcessMemory(g_ProcessHandle, (int*) offset, &result, sizeof(uint8_t), NULL); +} + + +inline +uint16_t MreadWord (const uint32_t &offset) +{ + uint16_t result; + ReadProcessMemory(g_ProcessHandle, (int*) offset, &result, sizeof(uint16_t), NULL); + return result; +} + + +inline +void MreadWord (const uint32_t &offset, uint16_t &result) +{ + ReadProcessMemory(g_ProcessHandle, (int*) offset, &result, sizeof(uint16_t), NULL); +} + + +inline +uint32_t MreadDWord (const uint32_t &offset) +{ + uint32_t result; + ReadProcessMemory(g_ProcessHandle, (int*) offset, &result, sizeof(uint32_t), NULL); + return result; +} + + +inline +void MreadDWord (const uint32_t &offset, uint32_t &result) +{ + ReadProcessMemory(g_ProcessHandle, (int*) offset, &result, sizeof(uint32_t), NULL); +} + + +inline +uint64_t MreadQuad (const uint32_t &offset) +{ + uint64_t result; + ReadProcessMemory(g_ProcessHandle, (int*) offset, &result, sizeof(uint64_t), NULL); + return result; +} + + +inline +void MreadQuad (const uint32_t &offset, uint64_t &result) +{ + ReadProcessMemory(g_ProcessHandle, (int*) offset, &result, sizeof(uint64_t), NULL); +} + + +inline +void Mread (const uint32_t &offset, uint32_t size, uint8_t *target) +{ + ReadProcessMemory(g_ProcessHandle, (int*) offset, target, size, NULL); +} + +// WRITING +inline +void MwriteDWord (const uint32_t offset, uint32_t data) +{ + WriteProcessMemory(g_ProcessHandle, (int*) offset, &data, sizeof(uint32_t), NULL); +} + +// using these is expensive. +inline +void MwriteWord (uint32_t offset, uint16_t data) +{ + WriteProcessMemory(g_ProcessHandle, (int*) offset, &data, sizeof(uint16_t), NULL); +} + +inline +void MwriteByte (uint32_t offset, uint8_t data) +{ + WriteProcessMemory(g_ProcessHandle, (int*) offset, &data, sizeof(uint8_t), NULL); +} + +inline +bool Mwrite (uint32_t offset, uint32_t size, uint8_t *source) +{ + WriteProcessMemory(g_ProcessHandle, (int*) offset, source, size, NULL); +} + + + +///FIXME: reduce use of temporary objects +inline +const string MreadCString (const uint32_t &offset) +{ + string temp; + char temp_c[256]; + int counter = 0; + char r; + do + { + r = MreadByte(offset+counter); + temp_c[counter] = r; + counter++; + } while (r); + temp_c[counter] = 0; + temp = temp_c; + return temp; +} diff --git a/library/deprecated/DfMap.cpp b/library/deprecated/DfMap.cpp new file mode 100644 index 000000000..10feaf2a6 --- /dev/null +++ b/library/deprecated/DfMap.cpp @@ -0,0 +1,661 @@ +/* +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 "DFCommon.h" +// zlib helper functions for de/compressing files +#include "ZlibHelper.h" +#include "DfMapHeader.h" + +// some bounds checking in debug mode. used in asserts +#define CheckBounds x < x_cell_count && x >= 0 && y < y_cell_count && y >= 0 && z < z_block_count && z >= 0 +#define CheckBoundsXY x < x_cell_count && x >= 0 && y < y_cell_count && y >= 0 +#define CheckBlockBounds x < x_block_count && x >= 0 && y < y_block_count && y >= 0 && z < z_block_count && z >= 0 + +// this expands into lots of ugly switch statement functions. some of them unused?, but kept for reference +#include "DFTileTypes.h" + +// process vein vector into matgloss values... +void Block::collapseVeins() +{ + // iterate through assigned veins + for( uint32_t i = 0; i < veins.size(); i++) + { + t_vein v = veins[i]; + //iterate through vein assignment bit arrays - one for every row + for(uint32_t j = 0;j<16;j++) + { + //iterate through the bits + for (uint32_t k = 0; k< 16;k++) + { + // and the bit array with a one-bit mask, check if the bit is set + bool set = ((1 << k) & v.assignment[j]) >> k; + if(set) + { + material[k][j].type = Mat_Stone; + material[k][j].index = v.type; + } + } + } + } +} + + +DfMap::~DfMap() +{ + clear(); +} + + +/// TODO: make this sync +void DfMap::clear() +{ + if(valid) + { + valid = false; + for(uint32_t i = 0; i < x_block_count*y_block_count*z_block_count;i++) + { + Block * b = block[i]; + if(b!=NULL) + { + delete b; + } + } + delete[] block; + } + for (uint32_t i = eNorthWest; i< eBiomeCount;i++) + { + v_geology[i].clear(); + //geodebug[i].clear(); + //geoblockadresses[i] = 0; + //regionadresses[i] = 0; + } + for(uint32_t counter = Mat_Wood; counter < NUM_MATGLOSS_TYPES; counter++) + { + v_matgloss[counter].clear(); + } + // delete buildings, clear vector + for(uint32_t i = 0; i < v_buildings.size(); i++) + { + delete v_buildings[i]; + } + v_buildings.clear(); + + // delete vegetation, clear vector + for(uint32_t i = 0; i < v_trees.size(); i++) + { + delete v_trees[i]; + } + v_trees.clear(); + // clear construction vector + v_constructions.clear(); + blocks_allocated = 0; + ///FIXME: destroy all the extracted data here +} + + +void DfMap::getRegionCoords (uint32_t &x,uint32_t &y,uint32_t &z) +{ + x= regionX; + y= regionY; + z= regionZ; +} + + +void DfMap::setRegionCoords (uint32_t x,uint32_t y,uint32_t z) +{ + regionX = x; + regionY = y; + regionZ = z; +} + + +void DfMap::allocBlockArray(uint32_t x,uint32_t y, uint32_t z) +{ + clear(); + x_block_count = x; + y_block_count = y; + z_block_count = z; + updateCellCount(); + block = new Block*[x_block_count*y_block_count*z_block_count]; + for (uint32_t i = 0; i < x_block_count*y_block_count*z_block_count; i++ ) + { + block[i] = NULL; + } + blocks_allocated = 0; + valid = true; +} + + +DfMap::DfMap(uint32_t x, uint32_t y, uint32_t z) +{ + valid = false; + allocBlockArray(x,y,z); +} + + +DfMap::DfMap(string FileName) +{ + valid = false; + valid = load( FileName); +} + + +bool DfMap::isValid () +{ + return valid; +} + + +Block * DfMap::getBlock (uint32_t x,uint32_t y,uint32_t z) +{ + if(isValid()) + { + return block[x*y_block_count*z_block_count + y*z_block_count + z]; + } + return NULL; +} + + +vector * DfMap::getBlockBuildingsVector(uint32_t x,uint32_t y,uint32_t z) +{ + Block * b = getBlock(x,y,z); + if(b) + { + return &b->v_buildings; + } + return NULL; +} + + +vector * DfMap::getBlockVegetationVector(uint32_t x,uint32_t y,uint32_t z) +{ + Block * b = getBlock(x,y,z); + if(b) + { + return &b->v_trees; + } + return NULL; +} + + +t_tree_desc *DfMap::getTree (uint32_t x, uint32_t y, uint32_t z) +{ + for(uint32_t i = 0; i< v_trees.size();i++) + { + if(x == v_trees[i]->x + && y == v_trees[i]->y + && z == v_trees[i]->z) + { + return v_trees[i]; + } + } + return 0; +} + + +t_building *DfMap::getBuilding (uint32_t x, uint32_t y, uint32_t z) +{ + for(uint32_t i = 0; i< v_buildings.size();i++) + { + if(x >= v_buildings[i]->x1 && x <= v_buildings[i]->x2 + && y >= v_buildings[i]->y1 && y <= v_buildings[i]->y2 + && z == v_buildings[i]->z) + { + return v_buildings[i]; + } + } + return 0; +} + + +Block * DfMap::allocBlock (uint32_t x,uint32_t y,uint32_t z) +{ + if(isValid()) + { + if(block[x*y_block_count*z_block_count + y*z_block_count + z]) + { + return block[x*y_block_count*z_block_count + y*z_block_count + z]; + } + Block *b = new Block; + block[x*y_block_count*z_block_count + y*z_block_count + z] = b; + blocks_allocated++; + return b; + } + return NULL; +} + + +void DfMap::updateCellCount() +{ + x_cell_count = x_block_count * BLOCK_SIZE; + y_cell_count = y_block_count * BLOCK_SIZE; + z_cell_count = z_block_count; +} + + +void DfMap::applyGeoMatgloss(Block * b) +{ + // load layer matgloss + for(int x_b = 0; x_b < BLOCK_SIZE; x_b++) + { + for(int y_b = 0; y_b < BLOCK_SIZE; y_b++) + { + int geolayer = b->designation[x_b][y_b].bits.geolayer_index; + int biome = b->designation[x_b][y_b].bits.biome; + b->material[x_b][y_b].type = Mat_Stone; + b->material[x_b][y_b].index = v_geology[b->RegionOffsets[biome]][geolayer]; + } + } +} + + +uint8_t DfMap::getLiquidLevel(uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return b->designation[x2][y2].bits.flow_size; + } + return 0; +} + + +uint16_t DfMap::getTileType(uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return b->tile_type[x2][y2]; + } + if(isTileSky(x,y,z,x2,y2)) + { + return 32; + } + return -1; +} + + +uint16_t DfMap::getTileType(uint32_t x, uint32_t y, uint32_t z, uint32_t blockX, uint32_t blockY) +{ + assert(CheckBlockBounds); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return b->tile_type[blockX][blockY]; + } + if(isTileSky(x,y,z,blockX,blockY)) + { + return 32; + } + return -1; +} + + +uint32_t DfMap::getDesignations(uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return b->designation[x2][y2].whole; + } + return -1; +} + + +bool DfMap::isBlockInitialized(uint32_t x, uint32_t y, uint32_t z) +{ + // because of the way DfMap is done, more than one check must be made. + return getBlock(x,y,z) != NULL; +} + + +uint32_t DfMap::getOccupancies(uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return b->occupancy[x2][y2].whole; + } + return -1; +} + + +void DfMap::getGeoRegion (uint32_t x, uint32_t y, uint32_t z, int32_t& geoX, int32_t& geoY) +{ + assert(CheckBoundsXY); + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + int biome = b->designation[x2][y2].bits.biome; + int BiomeOffset = b->RegionOffsets[biome]; + int16_t X_biomeB = (regionX / 16) + (BiomeOffset % 3) - 1; + int16_t Y_biomeB = (regionY / 16) + (BiomeOffset / 3) - 1; + if(X_biomeB < 0) X_biomeB = 0; + if(Y_biomeB < 0) Y_biomeB = 0; + if( (uint32_t)X_biomeB >= worldSizeX) + { + X_biomeB = worldSizeX - 1; + } + if( (uint32_t)Y_biomeB >= worldSizeY) + { + Y_biomeB = worldSizeY - 1; + } + geoX = X_biomeB; + geoY = Y_biomeB; + } + else + { + geoX = regionX / 16; + geoY = regionY / 16; + } +} + + +t_matglossPair DfMap::getMaterialPair (uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return b->material[x2][y2]; + } + t_matglossPair fail = {-1,-1}; + return fail; +}; + + +// this is what the vein structures say it is +string DfMap::getGeoMaterialString (uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return getMaterialString(b->material[x2][y2].type, b->material[x2][y2].index); + } + string fallback = "UNKNOWN"; + return fallback; +} + + +string DfMap::getMaterialTypeString (uint32_t type) +{ + string ret = ""; + switch (type) + { + case 0: + ret += "wood"; + break; + case 1: + ret += "stone/soil"; + break; + case 2: + ret += "metal"; + break; + case 3: + ret += "plant"; + break; + case 10: + ret += "leather"; + break; + case 11: + ret += "silk cloth"; + break; + case 12: + ret += "plant thread cloth"; + break; + case 13: // green glass + ret += "green glass"; + break; + case 14: // clear glass + ret += "clear glass"; + break; + case 15: // crystal glass + ret += "crystal glass"; + break; + case 17: + ret += "ice"; + break; + case 18: + ret += "charcoal"; + break; + case 19: + ret += "potash"; + break; + case 20: + ret += "ashes"; + break; + case 21: + ret += "pearlash"; + break; + case 24: + ret += "soap"; + break; + default: + ret += "unknown"; + break; + } + return ret; +} + + +string DfMap::getMaterialString (uint32_t type, uint32_t index) +{ + if(index != 65535 && type >= 0 && type < NUM_MATGLOSS_TYPES) + { + if(index < v_matgloss[type].size()) + { + return v_matgloss[type][index]; + } + else + { + string fallback = "ERROR"; + return fallback; + } + } + string fallback = "UNKNOWN"; + return fallback; +} + + +uint16_t DfMap::getNumMatGloss(uint16_t type) +{ + return v_matgloss[type].size(); +} + + +string DfMap::getBuildingTypeName(uint32_t index) +{ + if(index < v_buildingtypes.size()) + { + return v_buildingtypes[index]; + } + return string("error"); +} + + +string DfMap::getMatGlossString(uint16_t type,uint16_t index) +{ + if(index < v_matgloss[type].size()) + { + return v_matgloss[type][index]; + } + return string("error"); +} + + +// matgloss part of the designation +unsigned int DfMap::getGeolayerIndex (uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return b->designation[x2][y2].bits.geolayer_index; + } + return -1; +} + + +// matgloss part of the designation +unsigned int DfMap::getBiome (uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return b->designation[x2][y2].bits.biome; + } + return -1; +} + + +bool DfMap::isHidden (uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return (b->designation[x2][y2].bits.hidden); + } + return false; +} + + +bool DfMap::isSubterranean (uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return (b->designation[x2][y2].bits.subterranean); + } + if(isTileSky( x, y, z, x2, y2)) + { + return false; + } + return true; +} + + +// x,y,z - coords of block +// blockX,blockY - coords of tile inside block +bool DfMap::isTileSky(uint32_t x, uint32_t y, uint32_t z, uint32_t blockX, uint32_t blockY) +{ + assert(CheckBounds); + Block *b; + // trace down through blocks until we hit an inited one or the base + for (int i = z; i>= 0;i--) + { + b = getBlock(x,y,i); + if(b) + { + // is the inited block open to the sky? + return b->designation[blockX][blockY].bits.skyview; + } + } + // we hit base + return false; +} + + +// is the sky above this tile visible? +bool DfMap::isSkyView (uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return (b->designation[x2][y2].bits.skyview); + } + if(isTileSky(x,y,z,x2,y2)) + { + return true; + } + return false; +} + + +// is there light in this tile? +bool DfMap::isSunLit (uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return (b->designation[x2][y2].bits.light); + } + return false; +} + + +bool DfMap::isMagma (uint32_t x, uint32_t y, uint32_t z) +{ + assert(CheckBounds); + uint32_t x2, y2; + convertToDfMapCoords(x, y, x, y, x2, y2); + Block *b = getBlock(x,y,z); + if(b != NULL) + { + return (b->designation[x2][y2].bits.liquid_type); + } + return false; +} diff --git a/library/deprecated/DfMap.h b/library/deprecated/DfMap.h new file mode 100644 index 000000000..9debe5e7e --- /dev/null +++ b/library/deprecated/DfMap.h @@ -0,0 +1,204 @@ +/* +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 DFMAP_H_INCLUDED +#define DFMAP_H_INCLUDED + +#define BLOCK_SIZE 16 + +class DfMapHeader; + +class Block +{ +public: + // where does the Block come from? + uint32_t origin; + // generic tile type. determines how the tile behaves ingame + uint16_t tile_type[BLOCK_SIZE][BLOCK_SIZE]; + t_designation designation[BLOCK_SIZE][BLOCK_SIZE]; + t_occupancy occupancy[BLOCK_SIZE][BLOCK_SIZE]; + // veins + vector veins; + t_matglossPair material[BLOCK_SIZE][BLOCK_SIZE]; + vector v_buildings; + vector v_trees; + void collapseVeins(); + /** + // region offset modifiers... what a hack. + // here we have double indexed offset into regions. + // once inside t_designation, pointing into this, second time from here as a index modifier into region array (2d) + // disassembled code where it's used follows. biome is biome from t_designation + biome_stuffs = *(_BYTE *)((char)biome + offset_Block + 0x1D84); + biome_stuffs_mod3 = biome_stuffs % 3; + biome_stuffs_div3 = biome_stuffs / 3; + biome_stuffs_mod3_ = biome_stuffs_mod3; + if ( !biome_stuffs_mod3_ ) + --*(_WORD *)X_stuff; + if ( biome_stuffs_mod3_ == 2 ) + ++*(_WORD *)X_stuff; + if ( !biome_stuffs_div3 ) + --*(_WORD *)Y_stuff_; + if ( biome_stuffs_div3 == 2 ) + ++*(_WORD *)Y_stuff_; + */ + uint8_t RegionOffsets[16];// idk if the length is right here +}; +/** + * This class can load and save DF maps + */ +class DfMap +{ +private: + // allow extractor direct access to our data, avoid call lag and lots of self-serving methods + friend class Extractor; + + Block **block; + uint32_t blocks_allocated; + bool valid; + + // converts the (x,y,z) cell coords to internal coords + // out_y, out_x - block coords + // out_y2, out_x2 - cell coords in that block + inline void convertToDfMapCoords(uint32_t x, uint32_t y, uint32_t &out_x, uint32_t &out_y, uint32_t &out_x2, uint32_t &out_y2) + { + out_x = x / BLOCK_SIZE; + out_x2 = x % BLOCK_SIZE; + out_y = y / BLOCK_SIZE; + out_y2 = y % BLOCK_SIZE; + }; + + void allocBlockArray(uint32_t x,uint32_t y, uint32_t z); + void updateCellCount(); + + bool loadVersion1(FILE * Decompressed,DfMapHeader & h); + bool writeVersion1(FILE * SaveFile); + + bool loadMatgloss2(FILE * Decompressed); + bool loadBlocks2(FILE * Decompressed,DfMapHeader & h); + bool loadRegion2(FILE * Decompressed); + bool loadVersion2(FILE * Decompressed,DfMapHeader & h); + + void writeMatgloss2(FILE * SaveFile); + void writeBlocks2(FILE * SaveFile); + void writeRegion2(FILE * SaveFile); + bool writeVersion2(FILE * SaveFile); + + uint32_t regionX; + uint32_t regionY; + uint32_t regionZ; + + ///FIXME: these belong to some world structure + uint32_t worldSizeX; + uint32_t worldSizeY; + + vector v_geology[eBiomeCount]; + vector v_matgloss[NUM_MATGLOSS_TYPES]; + vector v_buildingtypes; + vector v_constructions; + vector v_buildings; + vector v_trees; + unsigned x_block_count, y_block_count, z_block_count; // block count + unsigned x_cell_count, y_cell_count, z_cell_count; // cell count + +public: + DfMap(); + DfMap(uint32_t x, uint32_t y, uint32_t z); + DfMap(string file_name); + ~DfMap(); + + /// TODO: rework matgloss + void applyGeoMatgloss(Block * b); + // accessing vectors of materials + uint16_t getNumMatGloss(uint16_t type); + string getMaterialTypeString (uint32_t type); + string getMatGlossString(uint16_t type, uint16_t index); + // accessing vectors of building types + uint32_t getNumBuildingTypes(); + string getBuildingTypeName(uint32_t index); + + bool isValid(); + bool load(string FilePath); + bool write(string FilePath); + void clear(); + + Block* getBlock(uint32_t x, uint32_t y, uint32_t z); + Block* allocBlock(uint32_t x, uint32_t y, uint32_t z); + bool deallocBlock(uint32_t x, uint32_t y, uint32_t z); + + vector * getBlockBuildingsVector(uint32_t x,uint32_t y,uint32_t z); + vector * getBlockVegetationVector(uint32_t x,uint32_t y,uint32_t z); + + inline unsigned int getXBlocks() { return x_block_count; } + inline unsigned int getYBlocks() { return y_block_count; } + inline unsigned int getZBlocks() { return z_block_count; } + + bool isTileSky(uint32_t x, uint32_t y, uint32_t z, uint32_t blockX, uint32_t blockY); + uint16_t getTileType(uint32_t x, uint32_t y, uint32_t z); + uint16_t getTileType(uint32_t x, uint32_t y, uint32_t z, uint32_t blockX, uint32_t blockY); + + uint32_t getDesignations(uint32_t x, uint32_t y, uint32_t z); + uint32_t getOccupancies(uint32_t x, uint32_t y, uint32_t z); + + // get tile material + t_matglossPair getMaterialPair (uint32_t x, uint32_t y, uint32_t z); + string getGeoMaterialString (uint32_t x, uint32_t y, uint32_t z); + string getMaterialString (uint32_t type, uint32_t index); + + // get coords of region used for materials + void getGeoRegion (uint32_t x, uint32_t y, uint32_t z, int32_t& geoX, int32_t& geoY); + + // matgloss part of the designation + uint32_t getGeolayerIndex (uint32_t x, uint32_t y, uint32_t z); + + void getRegionCoords (uint32_t &x,uint32_t &y,uint32_t &z); + void setRegionCoords (uint32_t x,uint32_t y,uint32_t z); + + // what kind of building is here? + //uint16_t getBuilding (uint32_t x, uint32_t y, uint32_t z); + t_building *getBuilding (uint32_t x, uint32_t y, uint32_t z); + t_tree_desc *getTree (uint32_t x, uint32_t y, uint32_t z); + + unsigned int getBiome (uint32_t x, uint32_t y, uint32_t z); + + int picktexture(int); +/* + bool isOpenTerrain(int); + bool isStairTerrain(int); + bool isRampTerrain(int); + bool isFloorTerrain(int); + bool isWallTerrain(int); +*/ + bool isBlockInitialized(uint32_t x, uint32_t y, uint32_t z); + + bool isHidden (uint32_t x, uint32_t y, uint32_t z); + bool isSubterranean (uint32_t x, uint32_t y, uint32_t z); + bool isSkyView (uint32_t x, uint32_t y, uint32_t z); + bool isSunLit (uint32_t x, uint32_t y, uint32_t z); + bool isMagma (uint32_t x, uint32_t y, uint32_t z); + + uint8_t getLiquidLevel(uint32_t x, uint32_t y, uint32_t z); +}; + + +#endif // DFMAP_H_INCLUDED diff --git a/library/deprecated/DfMapHeader.h b/library/deprecated/DfMapHeader.h new file mode 100644 index 000000000..aa886c748 --- /dev/null +++ b/library/deprecated/DfMapHeader.h @@ -0,0 +1,41 @@ +/* +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 DF_MAP_HEADER_H +#define DF_MAP_HEADER_H + +static const char dmh_id[] = "!!URIST!!"; +static const uint8_t dmh_ver = 1U; + +// a header for save files +struct DfMapHeader +{ + char identifier[10]; // !!URIST!! + uint8_t version; // DfMap/Header version; current: 1 + uint32_t reserved; // reserved 4 bytes +}; + + +#endif // DF_MAP_HEADER_H + diff --git a/library/deprecated/Extract.cpp b/library/deprecated/Extract.cpp new file mode 100644 index 000000000..9f4c61e5e --- /dev/null +++ b/library/deprecated/Extract.cpp @@ -0,0 +1,433 @@ +/* +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. +*/ + +// Extractor +#include "DFCommon.h" +using namespace std; + +#include "Extract.h" +#include "DFDataModel.h" +#include "DFMemInfo.h" + +Extractor::Extractor() +{ + df_map = NULL; // important, null pointer means we don't have a map loaded +} + + +Extractor::~Extractor() +{ + if(df_map !=NULL ) + { + delete df_map; + } +} + + +bool Extractor::dumpMemory( string path_to_xml) +{ + // create process manager, get first process + ProcessManager pm(path_to_xml); + if(!pm.findProcessess()) + { + fprintf(stderr,"Can't find any suitable DF process\n"); + return false; + } + // attach to process + printf("Attempting to Attach Process\n"); + ///FIXME: this won't do. + Process * p = pm[0]; + DataModel * dm = p->getDataModel(); + if(!p->attach()) + { + printf("Could not Attach Process, Aborting\n"); + return false; // couldn't attach to process, no go + } + printf("Process succesfully Attached\n"); + memory_info* offset_descriptor = p->getDescriptor(); + + uint32_t map_loc, // location of the X array + temp_loc, // block location + temp_locx, // iterator for the X array + temp_locy, // iterator for the Y array + temp_locz; // iterator for the Z array + unsigned blocks_read = 0U; + + // Read Map Data Blocks + int map_offset = offset_descriptor->getAddress("map_data");; + int x_count_offset = offset_descriptor->getAddress("x_count"); + int y_count_offset = offset_descriptor->getAddress("y_count"); + int z_count_offset = offset_descriptor->getAddress("z_count"); + int tile_type_offset = offset_descriptor->getOffset("type"); + int designation_offset = offset_descriptor->getOffset("designation"); + int occupancy_offset = offset_descriptor->getOffset("occupancy"); + int biome_stuffs = offset_descriptor->getOffset("biome_stuffs"); + + // layers + int region_x_offset = offset_descriptor->getAddress("region_x"); + int region_y_offset = offset_descriptor->getAddress("region_y"); + int region_z_offset = offset_descriptor->getAddress("region_z"); + int world_offset = offset_descriptor->getAddress("world"); + int world_regions_offset = offset_descriptor->getOffset("w_regions_arr"); + int region_size = offset_descriptor->getHexValue("region_size"); + int region_geo_index_offset = offset_descriptor->getOffset("region_geo_index_off"); + int world_geoblocks_offset = offset_descriptor->getOffset("w_geoblocks"); + int world_size_x = offset_descriptor->getOffset("world_size_x"); + int world_size_y = offset_descriptor->getOffset("world_size_y"); + int geolayer_geoblock_offset = offset_descriptor->getOffset("geolayer_geoblock_offset"); + // veins + int veinvector = offset_descriptor->getOffset("v_vein"); + int veinsize = offset_descriptor->getHexValue("v_vein_size"); + + int vegetation = offset_descriptor->getAddress("vegetation"); + int tree_desc_offset = offset_descriptor->getOffset("tree_desc_offset"); + // constructions + int constructions = offset_descriptor->getAddress("constructions"); + // buildings + int buildings = offset_descriptor->getAddress("buildings"); + /// TODO: what about building shape and orientation? + + // matgloss + int matgloss_address = offset_descriptor->getAddress("matgloss"); + int matgloss_skip = offset_descriptor->getHexValue("matgloss_skip"); + + bool have_geology = false; + + printf("Map offset: 0x%.8X\n", map_offset); + map_loc = MreadDWord(map_offset); + + if (!map_loc) + { + printf("Could not find DF map information in memory, Aborting\n"); + return false; + } + printf("Map data Found at: 0x%.8X\n", map_loc); + + if(df_map != NULL) + { + delete df_map; + } + df_map = new DfMap(MreadDWord(x_count_offset),MreadDWord(y_count_offset),MreadByte(z_count_offset)); + + // read matgloss data from df if we can + RawType matglossRawMapping[] = {Mat_Wood, Mat_Stone, Mat_Metal, Mat_Plant}; + if(matgloss_address && matgloss_skip) + { + uint32_t addr = matgloss_address; + uint32_t counter = Mat_Wood; + + for(; counter < NUM_MATGLOSS_TYPES; addr += matgloss_skip, counter++) + { + // get vector of matgloss pointers + DfVector p_matgloss = dm->readVector(addr, 4); + + // iterate over it + for (uint32_t i = 0; i< p_matgloss.getSize();i++) + { + uint32_t temp; + string tmpstr; + + // read the matgloss pointer from the vector into temp + p_matgloss.read((uint32_t)i,(uint8_t *)&temp); + + // read the string pointed at by temp + tmpstr = dm->readSTLString(temp); + + // store it in the block + df_map->v_matgloss[matglossRawMapping[counter]].push_back(tmpstr); + printf("%d = %s\n",i,tmpstr.c_str()); + } + } + } + + if(region_x_offset && region_y_offset && region_z_offset) + { + // we have offsets for region coordinates, get them. + df_map->setRegionCoords(MreadDWord(region_x_offset),MreadDWord(region_y_offset),MreadDWord(region_z_offset)); + + // extract layer geology data. we need all these to do that + if(world_size_x && world_size_y && world_offset && world_regions_offset && world_geoblocks_offset && region_size && region_geo_index_offset && geolayer_geoblock_offset) + { + // get world size + int worldSizeX = MreadWord(world_offset + world_size_x); + int worldSizeY = MreadWord(world_offset + world_size_y); + df_map->worldSizeX = worldSizeX; + df_map->worldSizeY = worldSizeY; + printf("World size. X=%d Y=%d\n",worldSizeX,worldSizeY); + + // get pointer to first part of 2d array of regions + uint32_t regions = MreadDWord(world_offset + world_regions_offset); + printf("regions. Offset=%d\n",regions); + + // read the 9 surrounding regions + DfVector geoblocks = dm->readVector(world_offset + world_geoblocks_offset,4); + + // iterate over surrounding biomes. make sure we don't fall off the world + for(int i = eNorthWest; i< eBiomeCount; i++) + { + // check bounds, fix them if needed + int bioRX = df_map->regionX / 16 + (i%3) - 1; + if( bioRX < 0) bioRX = 0; + if( bioRX >= worldSizeX) bioRX = worldSizeX - 1; + int bioRY = df_map->regionY / 16 + (i/3) - 1; + if( bioRY < 0) bioRY = 0; + if( bioRY >= worldSizeY) bioRY = worldSizeY - 1; + + /// TODO: encapsulate access to multidimensional arrays. + // load region stuff here + uint32_t geoX = MreadDWord(regions + bioRX*4);// get pointer to column of regions + + // geoX = base + // bioRY = index + // region_size = size of array objects + // region_geo_index_off = offset into the array object + uint16_t geoindex = MreadWord(geoX + bioRY*region_size + region_geo_index_offset); + uint32_t geoblock_off; + + // get the geoblock from the geoblock vector using the geoindex + geoblocks.read(geoindex,(uint8_t *) &geoblock_off); + + // get the layer pointer vector :D + DfVector geolayers = dm->readVector(geoblock_off + geolayer_geoblock_offset , 4); // let's hope + + // make sure we don't load crap + assert(geolayers.getSize() > 0 && geolayers.getSize() <= 16); + for(uint32_t j = 0;j< geolayers.getSize();j++) + { + int geol_offset; + + // read pointer to a layer + geolayers.read(j, (uint8_t *) & geol_offset); + + // read word at pointer + 2, store in our geology vectors + df_map->v_geology[i].push_back(MreadWord(geol_offset + 2)); + } + } + have_geology = true; + } + } + else + { + // crap, can't get the real layer materials + df_map->setRegionCoords(0,0,0); + } + + //read the memory from the map blocks + for(uint32_t x = 0; x < df_map->x_block_count; x++) + { + temp_locx = map_loc + ( 4 * x ); + temp_locy = MreadDWord(temp_locx); + for(uint32_t y = 0; y < df_map->y_block_count; y++) + { + temp_locz = MreadDWord(temp_locy); + temp_locy += 4; + for(uint32_t z = 0; z < df_map->z_block_count; z++) + { + temp_loc = MreadDWord(temp_locz); + temp_locz += 4; + if (temp_loc) + { + Block * b = df_map->allocBlock(x,y,z); + b->origin = temp_loc; // save place of block in DF's memory for later + + Mread( + /*Uint32 offset*/ temp_loc + tile_type_offset, + /*Uint32 size*/ sizeof(uint16_t)*BLOCK_SIZE*BLOCK_SIZE, + /*void *target*/ (uint8_t *)&b->tile_type + ); + Mread( + /*Uint32 offset*/ temp_loc + designation_offset, + /*Uint32 size*/ sizeof(uint32_t)*BLOCK_SIZE*BLOCK_SIZE, + /*void *target*/ (uint8_t *)&b->designation + ); + Mread( + /*Uint32 offset*/ temp_loc + occupancy_offset, + /*Uint32 size*/ sizeof(uint32_t)*BLOCK_SIZE*BLOCK_SIZE, + /*void *target*/ (uint8_t *)&b->occupancy + ); + + // set all materials to -1. + memset(b->material, -1, sizeof(int16_t) * 256); + if(biome_stuffs) // we got biome stuffs! we can try loading matgloss from here + { + Mread( + /*Uint32 offset*/ temp_loc + biome_stuffs, + /*Uint32 size*/ sizeof(uint8_t)*16, + /*void *target*/ (uint8_t *)&b->RegionOffsets + ); + // if we have geology, we can use the geolayers to determine materials + if(have_geology) + { + df_map->applyGeoMatgloss(b); + } + } + else + { + // can't load offsets, substitute local biome everywhere + memset(b->RegionOffsets,eHere,sizeof(b->RegionOffsets)); + } + // load veins from the game + if(veinvector && veinsize) + { + assert(sizeof(t_vein) == veinsize); + // veins are stored as a vector of pointers to veins .. at least in df 40d11 on linux + /*pointer is 4 bytes! we work with a 32bit program here, no matter what architecture we compile khazad for*/ + DfVector p_veins = dm->readVector(temp_loc + veinvector, 4); + // read all veins + for (uint32_t i = 0; i< p_veins.getSize();i++) + { + t_vein v; + uint32_t temp; + // read the vein pointer from the vector + p_veins.read((uint32_t)i,(uint8_t *)&temp); + // read the vein data (dereference pointer) + Mread(temp, veinsize, (uint8_t *)&v); + // store it in the block + b->veins.push_back(v); + } + b->collapseVeins(); // collapse *our* vein vector into vein matgloss data + } + } + } + } + } + // read constructions, apply immediately. + if(constructions) + { + // read the constructions vector + DfVector p_cons = dm->readVector(constructions,4); + // iterate + for (uint32_t i = 0; i< p_cons.getSize();i++) + { + uint32_t temp; + t_construction c; + t_construction_df40d c_40d; + // read pointer from vector at position + p_cons.read((uint32_t)i,(uint8_t *)&temp); + //read construction from memory + Mread(temp, sizeof(t_construction_df40d), (uint8_t *)&c_40d); + // stupid apply. this will probably be removed later + Block * b = df_map->getBlock(c_40d.x/16,c_40d.y/16,c_40d.z); + b->material[c_40d.x%16][c_40d.y%16] = c_40d.material; + //b->material[c_40d.x%16][c_40d.y%16].index = c_40d.material.index; + // transform + c.x = c_40d.x; + c.y = c_40d.y; + c.z = c_40d.z; + c.material = c_40d.material; + // store for save/load + df_map->v_constructions.push_back(c); + } + } + if(buildings) + { + // fill the building type vector first + offset_descriptor->copyBuildings(df_map->v_buildingtypes); + DfVector p_bld = dm->readVector(buildings,4); + for (uint32_t i = 0; i< p_bld.getSize();i++) + { + uint32_t temp; + t_building * bld = new t_building; + t_building_df40d bld_40d; + // read pointer from vector at position + p_bld.read((uint32_t)i,(uint8_t *)&temp); + //read construction from memory + Mread(temp, sizeof(t_building_df40d), (uint8_t *)&bld_40d); + // transform + int32_t type = -1; + offset_descriptor->resolveClassId(temp, type); + bld->vtable = bld_40d.vtable; + bld->type = type; + bld->x1 = bld_40d.x1; + bld->x2 = bld_40d.x2; + bld->y1 = bld_40d.y1; + bld->y2 = bld_40d.y2; + bld->z = bld_40d.z; + bld->material = bld_40d.material; + // store for save/load. will need more processing. + df_map->v_buildings.push_back(bld); + ///FIXME: delete created building structs + // save buildings in a block for later display + Block * b = df_map->getBlock(bld->x1/16,bld->y1/16,bld->z); + b->v_buildings.push_back(bld); + } + } + if(vegetation && tree_desc_offset) + { + DfVector p_tree = dm->readVector(vegetation,4); + for (uint32_t i = 0; i< p_tree.getSize();i++) + { + uint32_t temp; + t_tree_desc * tree = new t_tree_desc; + // read pointer from vector at position + p_tree.read((uint32_t)i,(uint8_t *)&temp); + //read construction from memory + Mread(temp + tree_desc_offset, sizeof(t_tree_desc), (uint8_t *)tree); + // fix bad stuff + if(tree->material.type == 2) tree->material.type = 3; + // store for save/load. will need more processing. + df_map->v_trees.push_back(tree); + // save buildings in a block for later display + Block * b = df_map->getBlock(tree->x/16,tree->y/16,tree->z); + b->v_trees.push_back(tree); + } + } + printf("Blocks read into memory: %d\n", blocks_read); + p->detach(); + return true; +} +// wrappers! +bool Extractor::loadMap(string FileName) +{ + if(df_map == NULL) + { + df_map = new DfMap(FileName); + } + else + { + df_map->load(FileName); + } + return df_map->isValid(); +} + +bool Extractor::writeMap(string FileName) +{ + if(df_map == NULL) + { + return false; + } + return df_map->write(FileName); +} + +bool Extractor::isMapLoaded() +{ + if(df_map != NULL) + { + if(df_map->isValid()) + { + return true; + } + } + return false; +} diff --git a/library/deprecated/Extract.h b/library/deprecated/Extract.h new file mode 100644 index 000000000..824de5f8f --- /dev/null +++ b/library/deprecated/Extract.h @@ -0,0 +1,51 @@ +/* +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. +*/ + +/* Memory research + http://dwarffortresswiki.net/index.php/User:Rick/memory.ini#A_table_of_available_settings + http://dwarffortresswiki.net/index.php/User:Rick/Memory_research#Tile_Block + http://dwarffortresswiki.net/index.php/User:AzureLightning/Memory_research + http://dwarffortresswiki.net/index.php/User:Iluxan/Memory_research + */ +#ifndef EXTRACT_HEADER +#define EXTRACT_HEADER + +class DfMap; + +class Extractor +{ +protected: + DfMap *df_map; // DF extracted map structure + +public: + bool Init(); + Extractor(); + ~Extractor(); + bool loadMap(string FileName); + bool writeMap(string FileName); + bool isMapLoaded(); + DfMap *getMap() {return df_map;}; + bool dumpMemory( string path_to_xml); +}; +#endif // EXTRACT_HEADER diff --git a/library/deprecated/LoadSave.cpp b/library/deprecated/LoadSave.cpp new file mode 100644 index 000000000..8f8873084 --- /dev/null +++ b/library/deprecated/LoadSave.cpp @@ -0,0 +1,188 @@ +/* +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 "DFCommon.h" +#include "DfMap.h" +#include "DfMapHeader.h" +#include "ZlibHelper.h" + + +bool DfMap::write(string FilePath) +{ + FILE *SaveFile; + SaveFile = fopen(FilePath.c_str(),"wb"); + DfMapHeader df_map_header; + + if(SaveFile == NULL) + { + printf("Can\'t create file for write.\n"); + return false; + } + else + { + // gather information to fill dfmapheader + strcpy(df_map_header.identifier, dmh_id); + df_map_header.version = dmh_ver; + df_map_header.reserved = 0/*sizeof(DfMapHeader)*/; + + // save map header + fwrite(&df_map_header, sizeof(DfMapHeader), 1, SaveFile); + + // save map + writeVersion1(SaveFile); + } + + // reopen file for reading + freopen (FilePath.c_str(),"rb",SaveFile); + if(SaveFile == NULL) + { + printf("Can\'t create file for read.\n"); + return false; + } + + FILE *SaveCompressedFile; + string CompressedFilePath = FilePath + ".comp"; + + SaveCompressedFile = fopen(CompressedFilePath.c_str(),"wb"); + if(SaveCompressedFile == NULL) + { + printf("Can\'t create a compressed file for write\n"); + return false; + } + + // compress + printf("Compressing... "); + int ret = def(SaveFile, SaveCompressedFile, Z_BEST_COMPRESSION); + if (ret != Z_OK) + { + zerr(ret); + } + printf("DONE\n"); + + fclose(SaveFile); + fclose(SaveCompressedFile); + remove(FilePath.c_str()); + rename(CompressedFilePath.c_str(), FilePath.c_str()); + return true; +} + +bool DfMap::load(string FilePath) +{ + string DecompressedFilePath = FilePath + ".decomp"; + FILE *ToDecompress; + FILE *Decompressed; + DfMapHeader df_map_header; + bool isok = false; + + // open file for writing decompressed data + Decompressed = fopen(DecompressedFilePath.c_str(), "wb"); + if (Decompressed == NULL) + { + printf("Can\'t open a decompressed file for write.\n"); + return false; + } + + // open compressed file + ToDecompress = fopen(FilePath.c_str(),"rb"); + if (ToDecompress == NULL) + { + printf("Can\'t open file for read.\n"); + return false; + } + + // decompress + printf("Decompressing... "); + int ret = inf(/*source*/ToDecompress,/*destination*/Decompressed); + + printf("DONE\n"); + + if (ret != Z_OK) + { + zerr(ret); + } + + // OK, close file with compressed data + fclose(ToDecompress); + + // reopen decompressed file for reading + freopen(DecompressedFilePath.c_str(), "rb", Decompressed); + if (Decompressed == NULL) + { + printf("Can\'t create decompressed file for read.\n"); + return false; + } + + // check, if the file is big enough to contain the header + fseek(Decompressed, 0, SEEK_END); + + if (ftell(Decompressed) < (int32_t)sizeof(DfMapHeader)) + { + printf("This Khazad map file is corrupted - file too small.\n"); + return false; + } + + // read the header + fseek(Decompressed, 0, SEEK_SET); + fread(&df_map_header, sizeof(DfMapHeader), 1, Decompressed); + + // check, if it's a Khazad map file + if (strcmp(df_map_header.identifier,dmh_id) != 0) + { + printf("This file is not a Khazad map file.\n"); + return false; + } + + // ALERT: flush all map data. This is very important. + clear(); + + switch(df_map_header.version) + { + /* + Basic format without matgloss. Kept for compatibility reasons. + Saved from version 0.0.5 + */ + case 1: + isok = loadVersion1(Decompressed, df_map_header); + break; + + /* + v2 format + Saved from some SVN version. this format is dead + */ + case 2: + //isok = loadVersion2(Decompressed, df_map_header); + isok = false; + break; + + default: + printf("Unknown Khazad map file version(%3d).\n", df_map_header.version); + isok = false; + break; + } + + // close decompressed file and delete it + fclose(Decompressed); + remove(DecompressedFilePath.c_str()); + return isok; +} diff --git a/library/deprecated/LoadSaveV1.cpp b/library/deprecated/LoadSaveV1.cpp new file mode 100644 index 000000000..84fe9bc67 --- /dev/null +++ b/library/deprecated/LoadSaveV1.cpp @@ -0,0 +1,106 @@ +/* +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 "DFCommon.h" +#include "DfMap.h" +#include "DfMapHeader.h" +#include "ZlibHelper.h" + +// LOAD v1 BLOCKS +bool DfMap::loadVersion1(FILE * Decompressed,DfMapHeader & h) +{ + uint32_t x, y, z; + uint32_t tile_block_count; // how many tile blocks to read from the data location + + // load new size information + fread(&tile_block_count, sizeof(uint32_t), 1, Decompressed); + fread(&x_block_count, sizeof(uint32_t), 1, Decompressed); + fread(&y_block_count, sizeof(uint32_t), 1, Decompressed); + fread(&z_block_count, sizeof(uint32_t), 1, Decompressed); + + // make sure those size variables are in sync + updateCellCount(); + + // alloc new space for our new size + allocBlockArray(x_block_count,y_block_count,z_block_count); + + // let's load the blocks + for (uint32_t tile_block = 0U; tile_block < tile_block_count; ++tile_block) + { + // block position - we can omit empty blocks this way + fread(&x, sizeof(uint32_t), 1, Decompressed); + fread(&y, sizeof(uint32_t), 1, Decompressed); + fread(&z, sizeof(uint32_t), 1, Decompressed); + + Block * b = allocBlock(x,y,z); + + // read data + fread(&b->tile_type, sizeof(b->tile_type), 1, Decompressed); + fread(&b->designation, sizeof(b->designation), 1, Decompressed); + fread(&b->occupancy, sizeof(b->occupancy), 1, Decompressed); + memset(b->material, -1, sizeof(b->material)); + + // can't load offsets, substitute local biome everywhere + memset(b->RegionOffsets,eHere,sizeof(b->RegionOffsets)); + } + + printf("Blocks read into memory: %d\n", tile_block_count); + return true; +} + +// SAVE v1 BLOCKS +bool DfMap::writeVersion1(FILE * SaveFile) +{ + uint32_t x, y, z; + // write size information - total blocks and map size + fwrite(&blocks_allocated, sizeof(uint32_t), 1, SaveFile); + fwrite(&x_block_count, sizeof(uint32_t), 1, SaveFile); + fwrite(&y_block_count, sizeof(uint32_t), 1, SaveFile); + fwrite(&z_block_count, sizeof(uint32_t), 1, SaveFile); + + // write each non-empty block + for (x = 0; x < x_block_count; x++ ) + { + for (y = 0; y < y_block_count; y++ ) + { + for (z = 0; z < z_block_count; z++ ) + { + Block *b = getBlock(x,y,z); + if(b != NULL) + { + // first goes block position + fwrite(&x, sizeof(uint32_t), 1, SaveFile); + fwrite(&y, sizeof(uint32_t), 1, SaveFile); + fwrite(&z, sizeof(uint32_t), 1, SaveFile); + // then block data + fwrite(&b->tile_type, sizeof(uint16_t), BLOCK_SIZE*BLOCK_SIZE, SaveFile); + fwrite(&b->designation, sizeof(uint32_t), BLOCK_SIZE*BLOCK_SIZE, SaveFile); + fwrite(&b->occupancy, sizeof(uint32_t), BLOCK_SIZE*BLOCK_SIZE, SaveFile); + } + } + } + } + return true; +} + diff --git a/library/deprecated/LoadSaveV2.cpp b/library/deprecated/LoadSaveV2.cpp new file mode 100644 index 000000000..69763ead7 --- /dev/null +++ b/library/deprecated/LoadSaveV2.cpp @@ -0,0 +1,196 @@ +/* +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 "DFCommon.h" +#include "DfMap.h" +#include "DfMapHeader.h" +#include "ZlibHelper.h" +/* +bool DfMap::loadMatgloss2(FILE * Decompressed) +{ + char buffer [256]; + uint32_t nummatgloss; + uint32_t temp; + fread(&nummatgloss, sizeof(uint32_t), 1, Decompressed); + ///FIXME: buffer overrun possible? probably not. but fix it anyway. + for(uint32_t i = 0; i< nummatgloss;i++) + { + fread(&temp, sizeof(uint32_t), 1, Decompressed); // string length + fread(&buffer, sizeof(char), temp, Decompressed); // string + buffer[temp] = 0; + v_matgloss[Mat_Stone].push_back(buffer); + } +} +bool DfMap::loadBlocks2(FILE * Decompressed,DfMapHeader & h) +{ + uint32_t x, y, z; + uint32_t numveins; + t_vein vein; + +// for (uint32_t tile_block = 0U; tile_block < h.tile_block_count; ++tile_block) + { + fread(&x, sizeof(uint32_t), 1, Decompressed); + fread(&y, sizeof(uint32_t), 1, Decompressed); + fread(&z, sizeof(uint32_t), 1, Decompressed); + + Block * b = allocBlock(x,y,z); + + fread(&b->tile_type, sizeof(b->tile_type), 1, Decompressed); + fread(&b->designation, sizeof(b->designation), 1, Decompressed); + fread(&b->occupancy, sizeof(b->occupancy), 1, Decompressed); + fread(&b->RegionOffsets,sizeof(b->RegionOffsets),1,Decompressed); + // load all veins of this block + fread(&numveins, sizeof(uint32_t), 1, Decompressed); + if(v_matgloss[Mat_Stone].size()) + { + applyGeoMatgloss(b); + } + for(uint32_t i = 0; i < numveins; i++) + { + fread(&vein,sizeof(t_vein),1,Decompressed); + b->veins.push_back(vein); + } + if(numveins) + b->collapseVeins(); + } +} +bool DfMap::loadRegion2(FILE * Decompressed) +{ + uint32_t temp, temp2; + for(uint32_t i = eNorthWest; i< eBiomeCount;i++) + { + fread(&temp, sizeof(uint32_t), 1, Decompressed); // layer vector length + for(uint32_t j = 0; j < temp;j++) // load all geolayers into vectors (just 16bit matgloss indices) + { + fread(&temp2, sizeof(uint16_t), 1, Decompressed); + v_geology[i].push_back(temp2); + } + } +} +bool DfMap::loadVersion2(FILE * Decompressed,DfMapHeader & h) +{ + return false; + + uint32_t tile_block_count; // how many tile blocks to read from the data location + + //uint32_t x_block_count, y_block_count, z_block_count; // DF's map block count + + fread(&tile_block_count, sizeof(uint32_t), 1, Decompressed); + fread(&x_block_count, sizeof(uint32_t), 1, Decompressed); + fread(&y_block_count, sizeof(uint32_t), 1, Decompressed); + fread(&z_block_count, sizeof(uint32_t), 1, Decompressed); + + // load new size information + //x_block_count = h.x_block_count; + //y_block_count = h.y_block_count; + //z_block_count = h.z_block_count; + // make sure those size variables are in sync + updateCellCount(); + // alloc new space for our new size + allocBlockArray(x_block_count,y_block_count,z_block_count); + +// fseek(Decompressed, h.map_data_location, SEEK_SET); + + // read matgloss vector + loadMatgloss2(Decompressed); + // read region data + loadRegion2(Decompressed); + // read blocks + loadBlocks2(Decompressed,h); + return true; +} + +void DfMap::writeMatgloss2(FILE * SaveFile) +{ + uint32_t nummatgloss = v_matgloss[Mat_Stone].size(); + fwrite(&nummatgloss, sizeof(uint32_t), 1, SaveFile); + for(uint32_t i = 0; i< nummatgloss;i++) + { + const char *saveme = v_matgloss[Mat_Stone][i].c_str(); + uint32_t length = v_matgloss[Mat_Stone][i].size(); + fwrite(&length, sizeof(uint32_t),1,SaveFile); + fwrite(saveme, sizeof(char), length, SaveFile); + } +} + +void DfMap::writeRegion2(FILE * SaveFile) +{ + uint32_t temp, temp2; + for(uint32_t i = eNorthWest; i< eBiomeCount;i++) + { + temp = v_geology[i].size(); + fwrite(&temp, sizeof(uint32_t), 1, SaveFile); // layer vector length + for(uint32_t j = 0; j < temp;j++) // write all geolayers (just 16bit matgloss indices) + { + temp2 = v_geology[i][j]; + fwrite(&temp2, sizeof(uint16_t), 1, SaveFile); + } + } +} + +void DfMap::writeBlocks2(FILE * SaveFile) +{ + uint32_t x, y, z, numveins; + for (x = 0; x < x_block_count; x++ ) + { + for (y = 0; y < y_block_count; y++ ) + { + for (z = 0; z < z_block_count; z++ ) + { + Block *b = getBlock(x,y,z); + if(b != NULL) + { + // which block it is + fwrite(&x, sizeof(uint32_t), 1, SaveFile); + fwrite(&y, sizeof(uint32_t), 1, SaveFile); + fwrite(&z, sizeof(uint32_t), 1, SaveFile); + // block data + fwrite(&b->tile_type, sizeof(uint16_t), BLOCK_SIZE*BLOCK_SIZE, SaveFile); + fwrite(&b->designation, sizeof(uint32_t), BLOCK_SIZE*BLOCK_SIZE, SaveFile); + fwrite(&b->occupancy, sizeof(uint32_t), BLOCK_SIZE*BLOCK_SIZE, SaveFile); + fwrite(&b->RegionOffsets, sizeof(b->RegionOffsets), 1, SaveFile); + // write all veins + numveins = b->veins.size(); + fwrite(&numveins, sizeof(uint32_t), 1, SaveFile); + for(uint32_t i = 0; i < numveins; i++) + { + fwrite(&b->veins[i],sizeof(t_vein),1,SaveFile); + } + } + } + } + } +} + +bool DfMap::writeVersion2(FILE * SaveFile) +{ + // write matgloss vector + writeMatgloss2(SaveFile); + // write region data + writeRegion2(SaveFile); + // write blocks + writeBlocks2(SaveFile); + return true; +} +*/ diff --git a/library/deprecated/ZlibHelper.h b/library/deprecated/ZlibHelper.h new file mode 100644 index 000000000..dc54d05fd --- /dev/null +++ b/library/deprecated/ZlibHelper.h @@ -0,0 +1,160 @@ +// SOURCE: http://www.zlib.net/zpipe.c + +#ifndef ZLIB_HELPER_HEADER +#define ZLIB_HELPER_HEADER + +#include +#include +#include +#include + +//#define CHUNK 16384 +#define CHUNK 262144 + +/* Compress from file source to file dest until EOF on source. + def() returns Z_OK on success, Z_MEM_ERROR if memory could not be + allocated for processing, Z_STREAM_ERROR if an invalid compression + level is supplied, Z_VERSION_ERROR if the version of zlib.h and the + version of the library linked do not match, or Z_ERRNO if there is + an error reading or writing the files. */ +inline int def(FILE *source, FILE *dest, int level) +{ + int ret, flush; + unsigned have; + z_stream strm; + unsigned char in[CHUNK]; + unsigned char out[CHUNK]; + + /* allocate deflate state */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + ret = deflateInit(&strm, level); + if (ret != Z_OK) + return ret; + + /* compress until end of file */ + do { + strm.avail_in = fread(in, 1, CHUNK, source); + if (ferror(source)) { + (void)deflateEnd(&strm); + return Z_ERRNO; + } + flush = feof(source) ? Z_FINISH : Z_NO_FLUSH; + strm.next_in = in; + + /* run deflate() on input until output buffer not full, finish + compression if all of source has been read in */ + do { + strm.avail_out = CHUNK; + strm.next_out = out; + ret = deflate(&strm, flush); /* no bad return value */ + assert(ret != Z_STREAM_ERROR); /* state not clobbered */ + have = CHUNK - strm.avail_out; + if (fwrite(out, 1, have, dest) != have || ferror(dest)) { + (void)deflateEnd(&strm); + return Z_ERRNO; + } + } while (strm.avail_out == 0); + assert(strm.avail_in == 0); /* all input will be used */ + + /* done when last data in file processed */ + } while (flush != Z_FINISH); + assert(ret == Z_STREAM_END); /* stream will be complete */ + + /* clean up and return */ + (void)deflateEnd(&strm); + return Z_OK; +} + +/* Decompress from file source to file dest until stream ends or EOF. + inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be + allocated for processing, Z_DATA_ERROR if the deflate data is + invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and + the version of the library linked do not match, or Z_ERRNO if there + is an error reading or writing the files. */ +inline int inf(FILE *source, FILE *dest) +{ + int ret; + unsigned have; + z_stream strm; + unsigned char in[CHUNK]; + unsigned char out[CHUNK]; + + /* allocate inflate state */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit(&strm); + if (ret != Z_OK) + return ret; + + /* decompress until deflate stream ends or end of file */ + do { + strm.avail_in = fread(in, 1, CHUNK, source); + if (ferror(source)) { + (void)inflateEnd(&strm); + return Z_ERRNO; + } + if (strm.avail_in == 0) + break; + strm.next_in = in; + + /* run inflate() on input until output buffer not full */ + do { + strm.avail_out = CHUNK; + strm.next_out = out; + ret = inflate(&strm, Z_NO_FLUSH); + assert(ret != Z_STREAM_ERROR); /* state not clobbered */ + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; /* and fall through */ + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void)inflateEnd(&strm); + return ret; + } + have = CHUNK - strm.avail_out; + if (fwrite(out, 1, have, dest) != have || ferror(dest)) { + (void)inflateEnd(&strm); + return Z_ERRNO; + } + } while (strm.avail_out == 0); + + /* done when inflate() says it's done */ + } while (ret != Z_STREAM_END); + + /* clean up and return */ + (void)inflateEnd(&strm); + return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; +} + +/* report a zlib or i/o error */ +inline void zerr(int ret) +{ + printf("zpipe: %d\n", ret); + switch (ret) { + case Z_ERRNO: + if (ferror(stdin)) + fprintf(stderr,"error reading stdin\n"); + if (ferror(stdout)) + fprintf(stderr,"error writing stdout\n"); + break; + case Z_STREAM_ERROR: + printf("invalid compression level\n"); + break; + case Z_DATA_ERROR: + printf("invalid or incomplete deflate data\n"); + break; + case Z_MEM_ERROR: + printf("out of memory\n"); + break; + case Z_VERSION_ERROR: + printf("zlib version mismatch!\n"); + } +} + + +#endif // ZLIB_HELPER_HEADER diff --git a/library/md5/md5.cpp b/library/md5/md5.cpp new file mode 100644 index 000000000..c791893a4 --- /dev/null +++ b/library/md5/md5.cpp @@ -0,0 +1,332 @@ +/* + * This is the C++ implementation of the MD5 Message-Digest + * Algorithm desrcipted in RFC 1321. + * I translated the C code from this RFC to C++. + * There is now warranty. + * + * Feb. 12. 2005 + * Benjamin Grüdelbach + */ + +/* + * Changed unsigned long int types into uint32_t to make this work on 64bit systems. + * Sep. 5. 2009 + * Petr Mrázek + */ + +/* + * 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. + */ + +//md5 class include +#include "md5.h" + +// Constants for MD5Transform routine. +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* +FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. +*/ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (unsigned long int)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (unsigned long int)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (unsigned long int)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (unsigned long int)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. */ +void MD5::MD5Init (MD5_CTX *context) +{ + context->count[0] = context->count[1] = 0; + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* + MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. +*/ +void MD5::MD5Update (MD5_CTX *context, unsigned char *input, unsigned int inputLen) +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ( (context->count[0] += ((unsigned long int)inputLen << 3)) + < ((unsigned long int)inputLen << 3)) + context->count[1]++; + + context->count[1] += ((unsigned long int)inputLen >> 29); + partLen = 64 - index; + + /* + * Transform as many times as possible. + */ + if (inputLen >= partLen) + { + MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy ((POINTER)&context->buffer[index], + (POINTER)&input[i], + inputLen-i); +} + +/* + * MD5 finalization. Ends an MD5 message-digest operation, writing the + * the message digest and zeroizing the context. + */ +void MD5::MD5Final (unsigned char digest[16], MD5_CTX *context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* + * Pad out to 56 mod 64. + */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* + * Zeroize sensitive information. + */ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* + * MD5 basic transformation. Transforms state based on block. + */ +void MD5::MD5Transform (uint32_t state[4], unsigned char block[64]) +{ + uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* + * Zeroize sensitive information. + */ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* + * Encodes input (unsigned long int) into output (unsigned char). Assumes len is + * a multiple of 4. + */ +void MD5::Encode (unsigned char *output, uint32_t *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* + * Decodes input (unsigned char) into output (unsigned long int). Assumes len is + * a multiple of 4. + */ +void MD5::Decode (uint32_t *output, unsigned char *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((uint32_t)input[j]) | + (((uint32_t)input[j+1]) << 8) | + (((uint32_t)input[j+2]) << 16) | + (((uint32_t)input[j+3]) << 24); +} + +/* + * Note: Replace "for loop" with standard memcpy if possible. + */ +void MD5::MD5_memcpy (POINTER output, POINTER input, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* + * Note: Replace "for loop" with standard memset if possible. + */ +void MD5::MD5_memset (POINTER output,int value,unsigned int len) +{ + unsigned int i; + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} + +/* + * EOF + */ diff --git a/library/md5/md5.h b/library/md5/md5.h new file mode 100644 index 000000000..1c4b59690 --- /dev/null +++ b/library/md5/md5.h @@ -0,0 +1,92 @@ +/* + * This is the C++ implementation of the MD5 Message-Digest + * Algorithm desrcipted in RFC 1321. + * I translated the C code from this RFC to C++. + * There is now warranty. + * + * Feb. 12. 2005 + * Benjamin Grüdelbach + */ + +/* + * Changed unsigned long int types into uint32_t to make this work on 64bit systems. + * Sep. 5. 2009 + * Petr Mrázek + */ + +/* + * 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. + */ + +//---------------------------------------------------------------------- +//include protection +#ifndef MD5_H +#define MD5_H + +//---------------------------------------------------------------------- +//STL includes +#include +#include +//---------------------------------------------------------------------- +//typedefs +typedef unsigned char *POINTER; + +/* + * MD5 context. + */ +typedef struct +{ + uint32_t state[4]; /* state (ABCD) */ + uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +/* + * MD5 class + */ +class MD5 +{ + + private: + + void MD5Transform (uint32_t state[4], unsigned char block[64]); + void Encode (unsigned char*, uint32_t*, unsigned int); + void Decode (uint32_t*, unsigned char*, unsigned int); + void MD5_memcpy (POINTER, POINTER, unsigned int); + void MD5_memset (POINTER, int, unsigned int); + + public: + + void MD5Init (MD5_CTX*); + void MD5Update (MD5_CTX*, unsigned char*, unsigned int); + void MD5Final (unsigned char [16], MD5_CTX*); + + MD5(){}; +}; + +//---------------------------------------------------------------------- +//End of include protection +#endif + +/* + * EOF + */ diff --git a/library/md5/md5wrapper.cpp b/library/md5/md5wrapper.cpp new file mode 100644 index 000000000..fbaf8ab4a --- /dev/null +++ b/library/md5/md5wrapper.cpp @@ -0,0 +1,140 @@ +/* + * This is part of 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 + */ + +/* + * Changed unsigned long int types into uint32_t to make this work on 64bit systems. + * Sep. 5. 2009 + * Petr Mrázek + */ + +//---------------------------------------------------------------------- +//basic includes +#include +#include + +//my includes +#include "md5wrapper.h" +#include "md5.h" + +//---------privates-------------------------- + +/* + * internal hash function, calling + * the basic methods from md5.h + */ +std::string md5wrapper::hashit(std::string text) +{ + MD5_CTX ctx; + + //init md5 + md5->MD5Init(&ctx); + //update with our string + md5->MD5Update(&ctx, + (unsigned char*)text.c_str(), + text.length()); + + //create the hash + unsigned char buff[16] = ""; + md5->MD5Final((unsigned char*)buff,&ctx); + + //converte the hash to a string and return it + return convToString(buff); +} + +/* + * converts the numeric hash to + * a valid std::string. + * (based on Jim Howard's code; + * http://www.codeproject.com/cpp/cmd5.asp) + */ +std::string md5wrapper::convToString(unsigned char *bytes) +{ + char asciihash[33]; + + int p = 0; + for(int i=0; i<16; i++) + { + ::sprintf(&asciihash[p],"%02x",bytes[i]); + p += 2; + } + asciihash[32] = '\0'; + return std::string(asciihash); +} + +//---------publics-------------------------- + +//constructor +md5wrapper::md5wrapper() +{ + md5 = new MD5(); +} + + +//destructor +md5wrapper::~md5wrapper() +{ + delete md5; +} + +/* + * creates a MD5 hash from + * "text" and returns it as + * string + */ +std::string md5wrapper::getHashFromString(std::string text) +{ + return this->hashit(text); +} + + +/* + * creates a MD5 hash from + * a file specified in "filename" and + * returns it as string + * (based on Ronald L. Rivest's code + * from RFC1321 "The MD5 Message-Digest Algorithm") + */ +std::string md5wrapper::getHashFromFile(std::string filename) +{ + FILE *file; + MD5_CTX context; + + int len; + unsigned char buffer[1024], digest[16]; + + //open file + if ((file = fopen (filename.c_str(), "rb")) == NULL) + { + return "-1"; + } + + //init md5 + md5->MD5Init (&context); + + //read the filecontent + while ( (len = fread (buffer, 1, 1024, file)) ) + { + md5->MD5Update (&context, buffer, len); + } + + /* + generate hash, close the file and return the + hash as std::string + */ + md5->MD5Final (digest, &context); + fclose (file); + return convToString(digest); + } + +/* + * EOF + */ diff --git a/library/md5/md5wrapper.h b/library/md5/md5wrapper.h new file mode 100644 index 000000000..65dd2564c --- /dev/null +++ b/library/md5/md5wrapper.h @@ -0,0 +1,73 @@ +/* + * 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 + */ + +/* + * Changed unsigned long int types into uint32_t to make this work on 64bit systems. + * Sep. 5. 2009 + * Petr Mrázek + */ + +//include protection +#ifndef MD5WRAPPER_H +#define MD5WRAPPER_H + +//basic includes +#include + +//forwards +class MD5; + +class md5wrapper +{ + private: + MD5 *md5; + + /* + * internal hash function, calling + * the basic methods from md5.h + */ + std::string hashit(std::string text); + + /* + * converts the numeric giets to + * a valid std::string + */ + std::string convToString(unsigned char *bytes); + public: + //constructor + md5wrapper(); + + //destructor + ~md5wrapper(); + + /* + * creates a MD5 hash from + * "text" and returns it as + * string + */ + std::string getHashFromString(std::string text); + + /* + * creates a MD5 hash from + * a file specified in "filename" and + * returns it as string + */ + std::string getHashFromFile(std::string filename); +}; + + +//include protection +#endif + +/* + * EOF + */ diff --git a/library/tinyxml/tinystr.cpp b/library/tinyxml/tinystr.cpp new file mode 100644 index 000000000..681250714 --- /dev/null +++ b/library/tinyxml/tinystr.cpp @@ -0,0 +1,116 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +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. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005. + */ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/library/tinyxml/tinystr.h b/library/tinyxml/tinystr.h new file mode 100644 index 000000000..3c2aa9d54 --- /dev/null +++ b/library/tinyxml/tinystr.h @@ -0,0 +1,319 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +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. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005. + * + * - completely rewritten. compact, clean, and fast implementation. + * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems) + * - fixed reserve() to work as per specification. + * - fixed buggy compares operator==(), operator<(), and operator>() + * - fixed operator+=() to take a const ref argument, following spec. + * - added "copy" constructor with length, and most compare operators. + * - added swap(), clear(), size(), capacity(), operator+(). + */ + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include +#include + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + // = operator + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + // = operator + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/library/tinyxml/tinyxml.cpp b/library/tinyxml/tinyxml.cpp new file mode 100644 index 000000000..5de21f6de --- /dev/null +++ b/library/tinyxml/tinyxml.cpp @@ -0,0 +1,1888 @@ +/* +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. +*/ + +#include + +#ifdef TIXML_USE_STL +#include +#include +#endif + +#include "tinyxml.h" + + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::DOCUMENT ) + { + delete node; + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return &node->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const char* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s ); + } + else { + *i = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const std::string* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s->c_str() ); + } + else { + *i = 0; + } + } + return s; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const char* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s ); + } + else { + *d = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const std::string* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s->c_str() ); + } + else { + *d = 0; + } + } + return s; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); + #else + sprintf( buf, "%d", val ); + #endif + SetAttribute( name, buf ); +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + std::ostringstream oss; + oss << val; + SetAttribute( name, oss.str() ); +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + #else + sprintf( buf, "%f", val ); + #endif + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING _name( cname ); + TIXML_STRING _value( cvalue ); + #else + const char* _name = cname; + const char* _value = cvalue; + #endif + + TiXmlAttribute* node = attributeSet.Find( _name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, const std::string& _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + //StringToBuffer buf( value ); + + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. +// StringToBuffer buf( value ); +// +// if ( buf.buffer && SaveFile( buf.buffer ) ) +// return true; +// +// return false; + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // Add an extra string to avoid the crash. + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + const char* lastPos = buf; + const char* p = buf; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + if ( *p == 0xa ) { + // Newline character. No special rules for this. Append all the characters + // since the last string, and include the newline. + data.append( lastPos, (p-lastPos+1) ); // append, include the newline + ++p; // move past the newline + lastPos = p; // and point to the new buffer (may be 0) + assert( p <= (buf+length) ); + } + else if ( *p == 0xd ) { + // Carriage return. Append what we have so far, then + // handle moving forward in the buffer. + if ( (p-lastPos) > 0 ) { + data.append( lastPos, p-lastPos ); // do not add the CR + } + data += (char)0xa; // a proper newline + + if ( *(p+1) == 0xa ) { + // Carriage return - new line sequence + p += 2; + lastPos = p; + assert( p <= (buf+length) ); + } + else { + // it was followed by something else...that is presumably characters again. + ++p; + lastPos = p; + assert( p <= (buf+length) ); + } + } + else { + ++p; + } + } + // Handle any left over characters. + if ( p-lastPos ) { + data.append( lastPos, p-lastPos ); + } + delete [] buf; + buf = 0; + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); + #else + sprintf (buf, "%lf", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} +*/ +#endif + + +const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} +*/ + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += ""; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += ""; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += ""; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/library/tinyxml/tinyxml.h b/library/tinyxml/tinyxml.h new file mode 100644 index 000000000..c6f40cc27 --- /dev/null +++ b/library/tinyxml/tinyxml.h @@ -0,0 +1,1802 @@ +/* +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. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include + #include + #include + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SNSCANF _snscanf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SNSCANF _snscanf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SNSCANF snscanf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 5; +const int TIXML_PATCH_VERSION = 3; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simple called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + const TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* Find( const char* _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + #ifdef TIXML_USE_STL + const TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* Find( const std::string& _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + + #endif + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + /* + This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string" + but template specialization is hard to get working cross-compiler. Leaving the bug for now. + + // The above will fail for std::string because the space character is used as a seperator. + // Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string + template<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + */ + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } + void operator=( const TiXmlText& base ) { base.CopyTo( this ); } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } + void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && LoadFile( f.buffer, encoding )); + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && SaveFile( f.buffer )); + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i +#include + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p ) + p += strlen( endTag ); + return p; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + +// int tabsize = 4; +// if ( document ) +// tabsize = document->TabSize(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = ""; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i + + + + _____________________________________________ + |\'-._( / | + | \ .'-._\ , ,| + |-.\' .-; .'\`-' | + | \ .' ( _.' \ | + |.--.\' _) ;-; \._| + | ` _\(_)/_ \ `'-,_,-'\ | + jgs____ /(O)\ _________________/____)_`-._\| + | | + | Old unsupported versions | + |-------------------------------------------> + + + + .,:rsr, + :2;,;r2A@@5 + @2::s5A#@@@ @r. . + sd;:riXA#@@ :@@@Gir;;AS9 + Bs::sS3A#@2 @@#AhXirsS#; + iHrLr5d#@@@ .@#95sr;;rie + i*' `*@3 @@A2sr;:;r#5 + :..:rll: @@A5sr::r3@ + @Hr;iZ#@@@@ `:rr;;;;: + S@r.;i2#@@@ @s. .. + @2::ri2A@@# B@G2ir:...5i + :@r,r3X##@@ @G5sr:..,:A + .@Ar;;rSB@@# H#2sr;,..,is + .' `* ,@ASs;:..,:B + ;rr;:,..,:. + `''' + W I N D O W S + and + W I N E + + + + 0x48C330DF + 2c686c26307dcccd7c36cc79737ebe4f + + +
0x015C4D58
+
0x0156F8B0
+
0x015838a0
+
0x01587A24
+ + +
0x015C4D70
+
0x015C4D74
+
0x015C4D78
+ + +
0x015C4D88
+
0x015C4D8C
+
0x015C4D90
+ + + 0x08 + 0x2C + 0x0062 + 0x0264 + 0x0664 + 0x1D64 + + + 0x70 + + +
0x015C6388
+ 0x84 + 0x86 + 0x684 + 0x6B4 + + + 0x5C + 0x58 + + + 0x4 + + +
0x015C6D70
+ 0x10 + + + 0x84 + 0x60 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 0x4967C2E0 + aea5a207b8b1cda942502f97a429f6c3 + + +
0x01531EC0
+
0x014da5e0
+
0x014ee978
+
0x014F4B4C
+ + 0x70 + + +
0x01531EE0
+
0x01531EE4
+
0x01531EE8
+ + +
0x01531EF8
+
0x01531EFC
+
0x01531F00
+ + + 0x10 + 0x2C + 0x0082 + 0x0284 + 0x0684 + 0x1D84 + + +
0x015334F8
+ 0x84 + 0x86 + 0x75C + 0x79C + + + 0x64 + 0x60 + + + 0xC + + +
0x01534030
+ 0x18 + + + + + + + + + +
+ + + 0x49C82D3F + 6f81231b845e9c9dc29aaf57705ccc7c + + + + + + 0x4A3CCB7F + 6ea1de36af8e1666bd6478736e298c4c + + +
0x015FACCC
+
0x015A33B8
+
0x015B7750
+
0x015BD924
+ + +
0x015FACEC
+
0x015FACF0
+
0x015FACF4
+ + +
0x015FAD04
+
0x015FAD08
+
0x015FAD0C
+ + +
0x015FC304
+ +
0x015FCE3C
+ + +
+ + + 0x4A51C26E + 04a8d8ce311d8ac75e4241bef68d3147 + + + + + + + 0x4A8623D2 + 781a2e51be4056a7320108f8f0df8a13 + + + + + + + 0x4A9A6090 + 12cc4a3dbb6e6dfd7bc7aee458b9471a + + + + + + + 0x4A9B1A72 + 59ab29021aca9f3c66b1ab102fb3ceea + + + + + .-"""-. + ' \ + |,. ,-. | + |()L( ()| | + |,' `".| | + |.___.',| ` + .j `--"' ` `. + / ' ' \ + / / ` `. + / / ` . + / / l | + . , L I N U X | | + ,"`. .| | + _.' ``. | `..-'l + | `.`, | `. + | `. __.j ) + |__ |--""___| ,-' + `"--...,+"""" `._,.-' + + + + + 992afd73855e787860277f53d18afcbb + + +
0x09372FC0
+
0x0931ED38
+ + +
0x09332B60
+ + + +
0x09335CB0
+ + 0x40 + + +
0x09372FD4
+
0x09372FD8
+
0x09372FDC
+ + +
0x09372FEC
+
0x09372FF0
+
0x09372FF4
+ + + 0x08 + 0x2C + 0x0052 + 0x0254 + 0x0654 + 0x1D54 + + +
0x093745EC
+ 0x54 + 0x56 + 0x5A4 + 0x5C8 + + + 0x58 + 0x54 + + + 0x4 + + +
0x9374E88
+ 0xC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + fb8ecac8a12af5d0d7b1707078985d0d + + + + + + + 4367c59934cbcf14f43fd3af6444c455 + + +
0x08F95BBC
+
0x08F41918
+
0x08F55740
+
0x08F58890
+ + +
0x08F95BD0
+
0x08F95BD4
+
0x08F95BD8
+ + +
0x08F95BE8
+
0x08F95BEC
+
0x08F95BF0
+ + +
0x08F971E8
+ + +
0x08F97A84
+ + +
+ + + 2f3cb9d720e9fe8844c02c72a2b20bbd + + + + + + + dab3ce6bc074529706a1e5fe1273108c + + + + + + + 4f55a1dcc326786271f221de23c425b5 + + + + + + + 022b933926e08da49c6df8649295f2b7 + + + +
+ +
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 000000000..28c150c7b --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,22 @@ +# don't use this file directly. use the one in the root folder of the project + +# this is required to ensure we use the right configuration for the system. +IF(UNIX) +add_definitions(-DLINUX_BUILD) +ENDIF(UNIX) + +# a benchmark program +ADD_EXECUTABLE(expbench expbench.cpp) +TARGET_LINK_LIBRARIES(expbench dfhack) + +# a reveal clone +ADD_EXECUTABLE(reveal reveal.cpp) +TARGET_LINK_LIBRARIES(reveal dfhack) + +# prospector - produces a list of available materials and their quantities +ADD_EXECUTABLE(prospector prospector.cpp) +TARGET_LINK_LIBRARIES(prospector dfhack) + +# cleanmap - removes mud, snow, blood and similar stuff from a map. farmers beware +ADD_EXECUTABLE(cleanmap cleanmap.cpp) +TARGET_LINK_LIBRARIES(cleanmap dfhack) \ No newline at end of file diff --git a/tools/cleanmap.cpp b/tools/cleanmap.cpp new file mode 100644 index 000000000..af35193fa --- /dev/null +++ b/tools/cleanmap.cpp @@ -0,0 +1,50 @@ +// Map cleaner. Removes all the snow, mud spills, blood and vomit from map tiles. + +#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; + t_occupancy occupancies[256]; + + DFHackAPI DF("Memory.xml"); + if(!DF.Attach()) + { + cerr << "DF not found" << endl; + return 1; + } + DF.InitMap(); + DF.getSize(x_max,y_max,z_max); + + // 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(DF.isValidBlock(x,y,z)) + { + // read block designations + DF.ReadOccupancy(x,y,z, (uint32_t *) occupancies); + // change the hidden flag to 0 + for (uint32_t i = 0; i < 256;i++) + { + occupancies[i].unibits.splatter = 0; + } + // write the designations back + DF.WriteOccupancy(x,y,z, (uint32_t *) occupancies); + } + } + } + } + return 0; +} \ No newline at end of file diff --git a/tools/expbench.cpp b/tools/expbench.cpp new file mode 100644 index 000000000..45a655872 --- /dev/null +++ b/tools/expbench.cpp @@ -0,0 +1,50 @@ +// This program exports the entire map from DF. Takes roughly 6.6 seconds for 1000 cycles on my Linux machine. ~px + +#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; + uint16_t tiletypes[16][16]; + t_designation designations[16][16]; + t_occupancy occupancies[16][16]; + + DFHackAPI DF("Memory.xml"); + if(!DF.Attach()) + { + cerr << "DF not found" << endl; + return 1; + } + DF.InitMap(); + DF.getSize(x_max,y_max,z_max); + + for(uint32_t i = 0; i< 1000;i++) + 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(DF.isValidBlock(x,y,z)) + { + DF.ReadTileTypes(x,y,z, (uint16_t *) tiletypes); + DF.ReadDesignations(x,y,z, (uint32_t *) designations); + DF.ReadOccupancy(x,y,z, (uint32_t *) occupancies); + num_blocks ++; + bytes_read += 256 * (4 + 4 + 2); + } + } + } + } + cout << num_blocks << " blocks read" << endl; + cout << bytes_read / (1024 * 1024) << " MB" << endl; + return 0; +} \ No newline at end of file diff --git a/tools/prospector.cpp b/tools/prospector.cpp new file mode 100644 index 000000000..f176449bb --- /dev/null +++ b/tools/prospector.cpp @@ -0,0 +1,168 @@ +// produces a list of vein materials available on the map. can be run with '-a' modifier to show even unrevealed minerals deep underground +// with -b modifier, it will show base layer materials too + +// TODO: use material colors to make the output prettier +// TODO: needs the tiletype filter! +// TODO: tile override materials +// TODO: material types, trees, ice, constructions +// TODO: GUI + +#include +#include +#include // for memset +#include +#include +using namespace std; + +#include +#include +#include + +int main (int argc, const char* argv[]) +{ + + bool showhidden = false; + bool showbaselayers = false; + for(int i = 0; i < argc; i++) + { + string test = argv[i]; + if(test == "-a") + { + showhidden = true; + } + else if(test == "-b") + { + showbaselayers = true; + } + else if(test == "-ab" || test == "-ba") + { + showhidden = true; + showbaselayers = true; + } + } + uint32_t x_max,y_max,z_max; + uint16_t tiletypes[16][16]; + t_designation designations[16][16]; + uint8_t regionoffsets[16]; + map materials; + materials.clear(); + vector stonetypes; + vector< vector > layerassign; + + // init the API + DFHackAPI DF("Memory.xml"); + + // attach + if(!DF.Attach()) + { + cerr << "DF not found" << endl; + return 1; + } + + // init the map + DF.InitMap(); + DF.getSize(x_max,y_max,z_max); + + // get stone matgloss mapping + if(!DF.ReadStoneMatgloss(stonetypes)) + { + //DF.DestroyMap(); + cerr << "Can't get the materials." << endl; + return 1; + } + + // get region geology + if(!DF.ReadGeology( layerassign )) + { + cerr << "Can't get region geology." << endl; + return 1; + } + + int16_t tempvein [16][16]; + vector veins; + // 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(!DF.isValidBlock(x,y,z)) + continue; + + // read data + DF.ReadTileTypes(x,y,z, (uint16_t *) tiletypes); + DF.ReadDesignations(x,y,z, (uint32_t *) designations); + + memset(tempvein, -1, sizeof(tempvein)); + veins.clear(); + DF.ReadVeins(x,y,z,veins); + + if(showbaselayers) + { + DF.ReadRegionOffsets(x,y,z, regionoffsets); + // get the layer materials + for(uint32_t xx = 0;xx<16;xx++) + { + for (uint32_t yy = 0; yy< 16;yy++) + { + tempvein[xx][yy] = + layerassign + [regionoffsets[designations[xx][yy].bits.biome]] + [designations[xx][yy].bits.geolayer_index]; + } + } + } + + // for each vein + for(int i = 0; i < veins.size();i++) + { + //iterate through vein rows + for(uint32_t j = 0;j<16;j++) + { + //iterate through the bits + for (uint32_t k = 0; k< 16;k++) + { + // and the bit array with a one-bit mask, check if the bit is set + bool set = ((1 << k) & veins[i].assignment[j]) >> k; + if(set) + { + // store matgloss + tempvein[k][j] = veins[i].type; + } + } + } + } + // count the material types + for(uint32_t xi = 0 ; xi< 16 ; xi++) + { + for(uint32_t yi = 0 ; yi< 16 ; yi++) + { + // hidden tiles are ignored unless '-a' is provided on the command line + // non-wall tiles are ignored + if(designations[xi][yi].bits.hidden && !showhidden || !isWallTerrain(tiletypes[xi][yi])) + continue; + if(tempvein[xi][yi] < 0) + continue; + + if(materials.count(tempvein[xi][yi])) + { + materials[tempvein[xi][yi]] += 1; + } + else + { + materials[tempvein[xi][yi]] = 1; + } + } + } + } + } + } + // print report + map::iterator p; + for(p = materials.begin(); p != materials.end(); p++) + { + cout << stonetypes[p->first].id << " : " << p->second << endl; + } + return 0; +} \ No newline at end of file diff --git a/tools/reveal.cpp b/tools/reveal.cpp new file mode 100644 index 000000000..45fec7325 --- /dev/null +++ b/tools/reveal.cpp @@ -0,0 +1,50 @@ +// This is a reveal program. It reveals the map. + +#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; + t_designation designations[256]; + + DFHackAPI DF("Memory.xml"); + if(!DF.Attach()) + { + cerr << "DF not found" << endl; + return 1; + } + DF.InitMap(); + DF.getSize(x_max,y_max,z_max); + + // 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(DF.isValidBlock(x,y,z)) + { + // read block designations + DF.ReadDesignations(x,y,z, (uint32_t *) designations); + // change the hidden flag to 0 + for (uint32_t i = 0; i < 256;i++) + { + designations[i].bits.hidden = 0; + } + // write the designations back + DF.WriteDesignations(x,y,z, (uint32_t *) designations); + } + } + } + } + return 0; +} \ No newline at end of file