434 lines
16 KiB
C++
434 lines
16 KiB
C++
/*
|
|
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;
|
|
}
|