Catch block data parsing errors (e.g. caused by a corrupt map block)
Corrupt blocks are reported, and map generation continues. Map generation aborts only after 100 or more corrupt map blocks.
This commit is contained in:
parent
721fbfac52
commit
6544d4de37
@ -54,9 +54,45 @@ static inline int unsignedToSigned(long i, long max_positive)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint16_t readU16(const unsigned char *data)
|
static inline void checkDataLimit(const char *type, size_t offset, size_t length, size_t dataLength)
|
||||||
{
|
{
|
||||||
return data[0] << 8 | data[1];
|
if (offset + length > dataLength)
|
||||||
|
throw TileGenerator::UnpackError(type, offset, length, dataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t readU8(const unsigned char *data, size_t offset, size_t dataLength)
|
||||||
|
{
|
||||||
|
checkDataLimit("uint8", offset, 1, dataLength);
|
||||||
|
return data[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t readU16(const unsigned char *data, size_t offset, size_t dataLength)
|
||||||
|
{
|
||||||
|
checkDataLimit("uint16", offset, 2, dataLength);
|
||||||
|
return data[offset] << 8 | data[offset + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void readString(string &str, const unsigned char *data, size_t offset, size_t length, size_t dataLength)
|
||||||
|
{
|
||||||
|
checkDataLimit("string", offset, length, dataLength);
|
||||||
|
str = string(reinterpret_cast<const char *>(data) + offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void checkBlockNodeDataLimit(int version, size_t dataLength)
|
||||||
|
{
|
||||||
|
int datapos = 16 * 16 * 16;
|
||||||
|
if (version >= 24) {
|
||||||
|
size_t index = datapos << 1;
|
||||||
|
checkDataLimit("node:24", index, 2, dataLength);
|
||||||
|
}
|
||||||
|
else if (version >= 20) {
|
||||||
|
checkDataLimit("node:20", datapos + 0x2000, 1, dataLength);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Unsupported map version " << version;
|
||||||
|
throw std::runtime_error(oss.str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int readBlockContent(const unsigned char *mapData, int version, int datapos)
|
static inline int readBlockContent(const unsigned char *mapData, int version, int datapos)
|
||||||
@ -892,8 +928,100 @@ void TileGenerator::createImage()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TileGenerator::processMapBlock(const DB::Block &block)
|
||||||
|
{
|
||||||
|
const BlockPos &pos = block.first;
|
||||||
|
const unsigned char *data = block.second.c_str();
|
||||||
|
size_t length = block.second.length();
|
||||||
|
|
||||||
|
uint8_t version = readU8(data, 0, length);
|
||||||
|
//uint8_t flags = readU8(data, 1, length);
|
||||||
|
|
||||||
|
size_t dataOffset = 0;
|
||||||
|
if (version >= 22) {
|
||||||
|
dataOffset = 4;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dataOffset = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zlib header: 2; Deflate header: >=1
|
||||||
|
checkDataLimit("zlib", dataOffset, 3, length);
|
||||||
|
ZlibDecompressor decompressor(data, length);
|
||||||
|
decompressor.setSeekPos(dataOffset);
|
||||||
|
ustring mapData = decompressor.decompress();
|
||||||
|
ustring mapMetadata = decompressor.decompress();
|
||||||
|
dataOffset = decompressor.seekPos();
|
||||||
|
|
||||||
|
// Skip unused data
|
||||||
|
if (version <= 21) {
|
||||||
|
dataOffset += 2;
|
||||||
|
}
|
||||||
|
if (version == 23) {
|
||||||
|
dataOffset += 1;
|
||||||
|
}
|
||||||
|
if (version == 24) {
|
||||||
|
uint8_t ver = readU8(data, dataOffset++, length);
|
||||||
|
if (ver == 1) {
|
||||||
|
uint16_t num = readU16(data, dataOffset, length);
|
||||||
|
dataOffset += 2;
|
||||||
|
dataOffset += 10 * num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip unused static objects
|
||||||
|
dataOffset++; // Skip static object version
|
||||||
|
int staticObjectCount = readU16(data, dataOffset, length);
|
||||||
|
dataOffset += 2;
|
||||||
|
for (int i = 0; i < staticObjectCount; ++i) {
|
||||||
|
dataOffset += 13;
|
||||||
|
uint16_t dataSize = readU16(data, dataOffset, length);
|
||||||
|
dataOffset += dataSize + 2;
|
||||||
|
}
|
||||||
|
dataOffset += 4; // Skip timestamp
|
||||||
|
|
||||||
|
m_blockAirId = -1;
|
||||||
|
m_blockIgnoreId = -1;
|
||||||
|
// Read mapping
|
||||||
|
if (version >= 22) {
|
||||||
|
dataOffset++; // mapping version
|
||||||
|
uint16_t numMappings = readU16(data, dataOffset, length);
|
||||||
|
dataOffset += 2;
|
||||||
|
for (int i = 0; i < numMappings; ++i) {
|
||||||
|
uint16_t nodeId = readU16(data, dataOffset, length);
|
||||||
|
dataOffset += 2;
|
||||||
|
uint16_t nameLen = readU16(data, dataOffset, length);
|
||||||
|
dataOffset += 2;
|
||||||
|
string name;
|
||||||
|
readString(name, data, dataOffset, nameLen, length);
|
||||||
|
name = name.c_str(); // Truncate any trailing NUL bytes
|
||||||
|
if (name == "air") {
|
||||||
|
m_blockAirId = nodeId;
|
||||||
|
}
|
||||||
|
else if (name == "ignore") {
|
||||||
|
m_blockIgnoreId = nodeId;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_nameMap[nodeId] = name;
|
||||||
|
}
|
||||||
|
dataOffset += nameLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node timers
|
||||||
|
if (version >= 25) {
|
||||||
|
dataOffset++;
|
||||||
|
uint16_t numTimers = readU16(data, dataOffset, length);
|
||||||
|
dataOffset += 2;
|
||||||
|
dataOffset += numTimers * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMapBlock(mapData, pos, version);
|
||||||
|
}
|
||||||
|
|
||||||
void TileGenerator::renderMap()
|
void TileGenerator::renderMap()
|
||||||
{
|
{
|
||||||
|
int unpackErrors = 0;
|
||||||
int blocks_rendered = 0;
|
int blocks_rendered = 0;
|
||||||
int area_rendered = 0;
|
int area_rendered = 0;
|
||||||
BlockPos currentPos;
|
BlockPos currentPos;
|
||||||
@ -922,102 +1050,44 @@ void TileGenerator::renderMap()
|
|||||||
}
|
}
|
||||||
DB::Block block = m_db->getBlockOnPos(pos);
|
DB::Block block = m_db->getBlockOnPos(pos);
|
||||||
if (!block.second.empty()) {
|
if (!block.second.empty()) {
|
||||||
const unsigned char *data = block.second.c_str();
|
try {
|
||||||
size_t length = block.second.length();
|
processMapBlock(block);
|
||||||
|
|
||||||
uint8_t version = data[0];
|
blocks_rendered++;
|
||||||
//uint8_t flags = data[1];
|
|
||||||
|
|
||||||
size_t dataOffset = 0;
|
allReaded = true;
|
||||||
if (version >= 22) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
dataOffset = 4;
|
if (m_readedPixels[i] != 0xffff) {
|
||||||
}
|
allReaded = false;
|
||||||
else {
|
}
|
||||||
dataOffset = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
ZlibDecompressor decompressor(data, length);
|
|
||||||
decompressor.setSeekPos(dataOffset);
|
|
||||||
ustring mapData = decompressor.decompress();
|
|
||||||
ustring mapMetadata = decompressor.decompress();
|
|
||||||
dataOffset = decompressor.seekPos();
|
|
||||||
|
|
||||||
// Skip unused data
|
|
||||||
if (version <= 21) {
|
|
||||||
dataOffset += 2;
|
|
||||||
}
|
|
||||||
if (version == 23) {
|
|
||||||
dataOffset += 1;
|
|
||||||
}
|
|
||||||
if (version == 24) {
|
|
||||||
uint8_t ver = data[dataOffset++];
|
|
||||||
if (ver == 1) {
|
|
||||||
uint16_t num = readU16(data + dataOffset);
|
|
||||||
dataOffset += 2;
|
|
||||||
dataOffset += 10 * num;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (UnpackError &e) {
|
||||||
// Skip unused static objects
|
std::cerr << "Failed to unpack map block " << pos.x << "," << pos.y << "," << pos.z
|
||||||
dataOffset++; // Skip static object version
|
<< " (id: " << pos.databasePos() << "). Block corrupt ?"
|
||||||
int staticObjectCount = readU16(data + dataOffset);
|
<< std::endl
|
||||||
dataOffset += 2;
|
<< "\tCoordinates: " << pos.x*16 << "," << pos.y*16 << "," << pos.z*16 << "+16+16+16"
|
||||||
for (int i = 0; i < staticObjectCount; ++i) {
|
<< "; Data: " << e.type << " at: " << e.offset << "(+" << e.length << ")/" << e.dataLength
|
||||||
dataOffset += 13;
|
<< std::endl;
|
||||||
uint16_t dataSize = readU16(data + dataOffset);
|
unpackErrors++;
|
||||||
dataOffset += dataSize + 2;
|
|
||||||
}
|
}
|
||||||
dataOffset += 4; // Skip timestamp
|
catch (ZlibDecompressor::DecompressError &e) {
|
||||||
|
std::cerr << "Failed to decompress data in map block " << pos.x << "," << pos.y << "," << pos.z
|
||||||
m_blockAirId = -1;
|
<< " (id: " << pos.databasePos() << "). Block corrupt ?"
|
||||||
m_blockIgnoreId = -1;
|
<< std::endl
|
||||||
// Read mapping
|
<< "\tCoordinates: " << pos.x*16 << "," << pos.y*16 << "," << pos.z*16 << "+16+16+16"
|
||||||
if (version >= 22) {
|
<< "; Cause: " << e.message
|
||||||
dataOffset++; // mapping version
|
<< std::endl;
|
||||||
uint16_t numMappings = readU16(data + dataOffset);
|
unpackErrors++;
|
||||||
dataOffset += 2;
|
|
||||||
for (int i = 0; i < numMappings; ++i) {
|
|
||||||
uint16_t nodeId = readU16(data + dataOffset);
|
|
||||||
dataOffset += 2;
|
|
||||||
uint16_t nameLen = readU16(data + dataOffset);
|
|
||||||
dataOffset += 2;
|
|
||||||
string name = string(reinterpret_cast<const char *>(data) + dataOffset, nameLen);
|
|
||||||
name = name.c_str(); // Truncate any trailing NUL bytes
|
|
||||||
if (name == "air") {
|
|
||||||
m_blockAirId = nodeId;
|
|
||||||
}
|
|
||||||
else if (name == "ignore") {
|
|
||||||
m_blockIgnoreId = nodeId;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_nameMap[nodeId] = name;
|
|
||||||
}
|
|
||||||
dataOffset += nameLen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Node timers
|
|
||||||
if (version >= 25) {
|
|
||||||
dataOffset++;
|
|
||||||
uint16_t numTimers = readU16(data + dataOffset);
|
|
||||||
dataOffset += 2;
|
|
||||||
dataOffset += numTimers * 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMapBlock(mapData, pos, version);
|
|
||||||
blocks_rendered++;
|
|
||||||
|
|
||||||
allReaded = true;
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
|
||||||
if (m_readedPixels[i] != 0xffff) {
|
|
||||||
allReaded = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (unpackErrors >= 100) {
|
||||||
|
throw(std::runtime_error("Too many block unpacking errors - bailing out"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (currentPos.z != INT_MIN)
|
if (currentPos.z != INT_MIN)
|
||||||
pushPixelRows(currentPos.z - 1);
|
pushPixelRows(currentPos.z - 1);
|
||||||
if (verboseStatistics)
|
if (verboseStatistics) {
|
||||||
cout << "Statistics"
|
cout << "Statistics"
|
||||||
<< ": blocks read: " << m_db->getBlocksReadCount()
|
<< ": blocks read: " << m_db->getBlocksReadCount()
|
||||||
<< " (" << m_db->getBlocksCachedCount() << " cached + "
|
<< " (" << m_db->getBlocksCachedCount() << " cached + "
|
||||||
@ -1025,14 +1095,18 @@ void TileGenerator::renderMap()
|
|||||||
<< "; blocks rendered: " << blocks_rendered
|
<< "; blocks rendered: " << blocks_rendered
|
||||||
<< "; area rendered: " << area_rendered
|
<< "; area rendered: " << area_rendered
|
||||||
<< "/" << (m_xMax-m_xMin+1) * (m_zMax-m_zMin+1)
|
<< "/" << (m_xMax-m_xMin+1) * (m_zMax-m_zMin+1)
|
||||||
<< " (" << (long long)area_rendered*16*16 << " nodes)"
|
<< " (" << (long long)area_rendered*16*16 << " nodes)";
|
||||||
<< std::endl;
|
if (unpackErrors)
|
||||||
|
cout << " (" << unpackErrors << " errors)";
|
||||||
|
cout << std::endl;
|
||||||
|
}
|
||||||
else if (progressIndicator)
|
else if (progressIndicator)
|
||||||
cout << std::setw(40) << "" << "\r";
|
cout << std::setw(40) << "" << "\r";
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void TileGenerator::renderMapBlock(const ustring &mapBlock, const BlockPos &pos, int version)
|
inline void TileGenerator::renderMapBlock(const ustring &mapBlock, const BlockPos &pos, int version)
|
||||||
{
|
{
|
||||||
|
checkBlockNodeDataLimit(version, mapBlock.length());
|
||||||
int xBegin = worldBlockX2StoredX(pos.x);
|
int xBegin = worldBlockX2StoredX(pos.x);
|
||||||
int zBegin = worldBlockZ2StoredY(pos.z);
|
int zBegin = worldBlockZ2StoredY(pos.z);
|
||||||
const unsigned char *mapData = mapBlock.c_str();
|
const unsigned char *mapData = mapBlock.c_str();
|
||||||
|
@ -44,6 +44,16 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
struct UnpackError
|
||||||
|
{
|
||||||
|
BlockPos pos;
|
||||||
|
const char *type;
|
||||||
|
size_t offset;
|
||||||
|
size_t length;
|
||||||
|
size_t dataLength;
|
||||||
|
UnpackError(const char *t, size_t o, size_t l, size_t dl) : type(t), offset(o), length(l), dataLength(dl) {}
|
||||||
|
};
|
||||||
|
|
||||||
TileGenerator();
|
TileGenerator();
|
||||||
~TileGenerator();
|
~TileGenerator();
|
||||||
void setBgColor(const Color &bgColor);
|
void setBgColor(const Color &bgColor);
|
||||||
@ -98,6 +108,7 @@ private:
|
|||||||
void renderMap();
|
void renderMap();
|
||||||
std::list<int> getZValueList() const;
|
std::list<int> getZValueList() const;
|
||||||
void pushPixelRows(int zPosLimit);
|
void pushPixelRows(int zPosLimit);
|
||||||
|
void processMapBlock(const DB::Block &block);
|
||||||
void renderMapBlock(const ustring &mapBlock, const BlockPos &pos, int version);
|
void renderMapBlock(const ustring &mapBlock, const BlockPos &pos, int version);
|
||||||
void renderScale();
|
void renderScale();
|
||||||
void renderOrigin();
|
void renderOrigin();
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include "TileGenerator.h"
|
#include "TileGenerator.h"
|
||||||
#include "ZlibDecompressor.h"
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -431,9 +430,6 @@ int main(int argc, char *argv[])
|
|||||||
} catch(std::runtime_error e) {
|
} catch(std::runtime_error e) {
|
||||||
std::cout<<"Exception: "<<e.what()<<std::endl;
|
std::cout<<"Exception: "<<e.what()<<std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
} catch(ZlibDecompressor::DecompressError e) {
|
|
||||||
std::cout<<"Block decompression failure: "<<e.message<<std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user