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)
|
||||
@ -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()
|
||||
{
|
||||
int unpackErrors = 0;
|
||||
int blocks_rendered = 0;
|
||||
int area_rendered = 0;
|
||||
BlockPos currentPos;
|
||||
@ -922,89 +1050,9 @@ void TileGenerator::renderMap()
|
||||
}
|
||||
DB::Block block = m_db->getBlockOnPos(pos);
|
||||
if (!block.second.empty()) {
|
||||
const unsigned char *data = block.second.c_str();
|
||||
size_t length = block.second.length();
|
||||
try {
|
||||
processMapBlock(block);
|
||||
|
||||
uint8_t version = data[0];
|
||||
//uint8_t flags = data[1];
|
||||
|
||||
size_t dataOffset = 0;
|
||||
if (version >= 22) {
|
||||
dataOffset = 4;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip unused static objects
|
||||
dataOffset++; // Skip static object version
|
||||
int staticObjectCount = readU16(data + dataOffset);
|
||||
dataOffset += 2;
|
||||
for (int i = 0; i < staticObjectCount; ++i) {
|
||||
dataOffset += 13;
|
||||
uint16_t dataSize = readU16(data + dataOffset);
|
||||
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);
|
||||
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;
|
||||
@ -1014,10 +1062,32 @@ void TileGenerator::renderMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (UnpackError &e) {
|
||||
std::cerr << "Failed to unpack map block " << pos.x << "," << pos.y << "," << pos.z
|
||||
<< " (id: " << pos.databasePos() << "). Block corrupt ?"
|
||||
<< std::endl
|
||||
<< "\tCoordinates: " << pos.x*16 << "," << pos.y*16 << "," << pos.z*16 << "+16+16+16"
|
||||
<< "; Data: " << e.type << " at: " << e.offset << "(+" << e.length << ")/" << e.dataLength
|
||||
<< std::endl;
|
||||
unpackErrors++;
|
||||
}
|
||||
catch (ZlibDecompressor::DecompressError &e) {
|
||||
std::cerr << "Failed to decompress data in map block " << pos.x << "," << pos.y << "," << pos.z
|
||||
<< " (id: " << pos.databasePos() << "). Block corrupt ?"
|
||||
<< std::endl
|
||||
<< "\tCoordinates: " << pos.x*16 << "," << pos.y*16 << "," << pos.z*16 << "+16+16+16"
|
||||
<< "; Cause: " << e.message
|
||||
<< std::endl;
|
||||
unpackErrors++;
|
||||
}
|
||||
}
|
||||
if (unpackErrors >= 100) {
|
||||
throw(std::runtime_error("Too many block unpacking errors - bailing out"));
|
||||
}
|
||||
}
|
||||
if (currentPos.z != INT_MIN)
|
||||
pushPixelRows(currentPos.z - 1);
|
||||
if (verboseStatistics)
|
||||
if (verboseStatistics) {
|
||||
cout << "Statistics"
|
||||
<< ": blocks read: " << m_db->getBlocksReadCount()
|
||||
<< " (" << m_db->getBlocksCachedCount() << " cached + "
|
||||
@ -1025,14 +1095,18 @@ void TileGenerator::renderMap()
|
||||
<< "; blocks rendered: " << blocks_rendered
|
||||
<< "; area rendered: " << area_rendered
|
||||
<< "/" << (m_xMax-m_xMin+1) * (m_zMax-m_zMin+1)
|
||||
<< " (" << (long long)area_rendered*16*16 << " nodes)"
|
||||
<< std::endl;
|
||||
<< " (" << (long long)area_rendered*16*16 << " nodes)";
|
||||
if (unpackErrors)
|
||||
cout << " (" << unpackErrors << " errors)";
|
||||
cout << std::endl;
|
||||
}
|
||||
else if (progressIndicator)
|
||||
cout << std::setw(40) << "" << "\r";
|
||||
}
|
||||
|
||||
inline void TileGenerator::renderMapBlock(const ustring &mapBlock, const BlockPos &pos, int version)
|
||||
{
|
||||
checkBlockNodeDataLimit(version, mapBlock.length());
|
||||
int xBegin = worldBlockX2StoredX(pos.x);
|
||||
int zBegin = worldBlockZ2StoredY(pos.z);
|
||||
const unsigned char *mapData = mapBlock.c_str();
|
||||
|
@ -44,6 +44,16 @@ private:
|
||||
#endif
|
||||
|
||||
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();
|
||||
void setBgColor(const Color &bgColor);
|
||||
@ -98,6 +108,7 @@ private:
|
||||
void renderMap();
|
||||
std::list<int> getZValueList() const;
|
||||
void pushPixelRows(int zPosLimit);
|
||||
void processMapBlock(const DB::Block &block);
|
||||
void renderMapBlock(const ustring &mapBlock, const BlockPos &pos, int version);
|
||||
void renderScale();
|
||||
void renderOrigin();
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include "TileGenerator.h"
|
||||
#include "ZlibDecompressor.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -431,9 +430,6 @@ int main(int argc, char *argv[])
|
||||
} catch(std::runtime_error e) {
|
||||
std::cout<<"Exception: "<<e.what()<<std::endl;
|
||||
return 1;
|
||||
} catch(ZlibDecompressor::DecompressError e) {
|
||||
std::cout<<"Block decompression failure: "<<e.message<<std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user