treexplore: TRE parser

This commit is contained in:
2015-04-17 15:38:34 +02:00
parent 17eee5fe68
commit 6726488dda
2 changed files with 282 additions and 1 deletions

View File

@@ -5,11 +5,18 @@ LDOPTS=
IFFEXPLORE_CXXSRCS=iffexplore.cc
IFFEXPLORE_OBJS=$(addprefix objs/,$(IFFEXPLORE_CXXSRCS:.cc=.o))
TREEXPLORE_CXXSRCS=treexplore.cc
TREEXPLORE_OBJS=$(addprefix objs/,$(TREEXPLORE_CXXSRCS:.cc=.o))
FONT2PNG_CXXSRCS=font2png.cc
FONT2PNG_OBJS=$(addprefix objs/,$(FONT2PNG_CXXSRCS:.cc=.o))
FONT2PNG_LIBS=-lpng
all: iffexplore font2png
PNTR2PNG_CXXSRCS=pntr2png.cc
PNTR2PNG_OBJS=$(addprefix objs/,$(PNTR2PNG_CXXSRCS:.cc=.o))
PNTR2PNG_LIBS=-lpng
all: iffexplore font2png pntr2png treexplore
objs/%.o: %.cc
$(CXX) $(CXXOPTS) -c -MMD -MP -o $@ $<
@@ -21,13 +28,25 @@ objs/%.o: %.cc
iffexplore: $(IFFEXPLORE_OBJS)
$(CXX) $(CXXOPTS) $(LDOPTS) -o $@ $(IFFEXPLORE_OBJS)
treexplore: $(TREEXPLORE_OBJS)
$(CXX) $(CXXOPTS) $(LDOPTS) -o $@ $(TREEXPLORE_OBJS)
font2png: $(FONT2PNG_OBJS)
$(CXX) $(CXXOPTS) $(LDOPTS) -o $@ $(FONT2PNG_OBJS) $(FONT2PNG_LIBS)
pntr2png: $(PNTR2PNG_OBJS)
$(CXX) $(CXXOPTS) $(LDOPTS) -o $@ $(PNTR2PNG_OBJS) $(PNTR2PNG_LIBS)
clean:
rm -f iffexplore $(IFFEXPLORE_OBJS) $(addprefix objs/,$(IFFEXPLORE_CXXSRCS:.cc=.P))
rm -f treexplore $(TREEXPLORE_OBJS) $(addprefix objs/,$(TREEXPLORE_CXXSRCS:.cc=.P))
rm -f font2png $(FONT2PNG_OBJS) $(addprefix objs/,$(FONT2PNG_CXXSRCS:.cc=.P))
rm -f pntr2png $(PNTR2PNG_OBJS) $(addprefix objs/,$(PNTR2PNG_CXXSRCS:.cc=.P))
.PHONY: clean all
-include $(addprefix objs/,$(IFFEXPLORE_CXXSRCS:.cc=.P))
-include $(addprefix objs/,$(TREEXPLORE_CXXSRCS:.cc=.P))
-include $(addprefix objs/,$(FONT2PNG_CXXSRCS:.cc=.P))
-include $(addprefix objs/,$(PNTR2PNG_CXXSRCS:.cc=.P))

262
treexplore.cc Normal file
View File

@@ -0,0 +1,262 @@
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <cstdint>
#include <vector>
#include <map>
#include <utility>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "common.hh"
struct TreHeader { // little endian
char magic[4]; // == "XTRE"
uint32_t unknown;
uint32_t table1Ofs;
uint32_t table2Ofs;
uint32_t table3Ofs;
uint32_t dataOfs;
} __attribute__((__packed__));
struct Table1Entry { // little endian
uint32_t crc;
uint32_t table3Ptr;
} __attribute__((__packed__));
struct Table2EntryHead {
uint8_t len;
} __attribute__((__packed__));
struct Table2EntryTail { // little endian
uint32_t table3Ptr;
} __attribute__((__packed__));
struct Table3Entry { // little endian
unsigned dataPtr:32;
// unsigned unknown1:8;
unsigned length:24;
unsigned unknown2:8;
} __attribute__((__packed__));
class TreFile {
public:
TreFile(std::string const& filename) {
FILEUPtr file{fopen(filename.c_str(), "rb")};
if (!file)
throw POSIXException{errno, "Could not open "s + filename};
struct stat statBuf;
if (fstat(fileno(file.get()), &statBuf) != 0)
throw POSIXException(errno, "Could not stat");
construct_(file.get(), 0, statBuf.st_size);
}
TreFile(FILE* file, off_t start, off_t length) {
construct_(file, start, length);
}
void printStructure() {
printf("Table 1: %lu entries\n", table1_.size());
printf("Table 2: %lu entries\n", table2_.size());
printf("Table 3: %lu entries\n", table3_.size());
printf("Files by CRC:\n");
for(auto const& ent : table1_) {
printf("\t%.8x -> (ofs %.8x, len %.8x)\n", ent.first,
std::get<0>(table3_[ent.second]),
std::get<1>(table3_[ent.second]));
}
printf("Files by Name:\n");
for(auto const& ent : table2_) {
printf("\t%s -> (ofs %.8x, len %.8x)\n", ent.first.c_str(),
std::get<0>(table3_[ent.second]),
std::get<1>(table3_[ent.second]));
}
}
private:
void construct_(FILE* file, off_t start, size_t length) {
if (length < sizeof(TreHeader))
throw FormatException{"Size < header size"};
if (fseeko(file, start, SEEK_SET) != 0) {
throw POSIXException(errno, "Could not seek");
}
TreHeader header;
if (fread(&header, sizeof(TreHeader), 1, file) != 1)
throw POSIXException{errno, "Could not read header"};
if (memcmp(header.magic, "XTRE", 4) != 0)
throw FormatException{"Wrong magic, not a TRE?"};
if ((header.table1Ofs > length) ||
(header.table2Ofs > length) ||
(header.table3Ofs > length) ||
(header.dataOfs > length))
throw FormatException{"Table offset exceeds length"};
if ((header.table1Ofs > header.table2Ofs) ||
(header.table2Ofs > header.table3Ofs) ||
(header.table3Ofs > header.dataOfs))
throw FormatException{"Table offsets not in ascending order"};
const size_t numTable1Entries = (header.table2Ofs-header.table1Ofs)/sizeof(Table1Entry);
const size_t numTable3Entries = (header.dataOfs-header.table3Ofs)/sizeof(Table3Entry);
printf("Table 1: start %u, length %u (%lu entries)\n",
header.table1Ofs, header.table2Ofs-header.table1Ofs,
numTable1Entries);
printf("Table 2: start %u, length %u\n",
header.table2Ofs, header.table3Ofs-header.table2Ofs);
printf("Table 3: start %u, length %u (%lu entries)\n",
header.table3Ofs, header.dataOfs-header.table3Ofs,
numTable3Entries);
printf("Data: start %u, length %lu\n",
header.dataOfs, length - header.dataOfs);
// Read Table 3
if (fseeko(file, start+header.table3Ofs, SEEK_SET) != 0) {
throw POSIXException{errno, "Could not seek"};
}
table3_.reserve(numTable3Entries);
for (size_t i = 0;i < numTable3Entries;++i) {
Table3Entry entry;
if (fread(&entry, sizeof(Table3Entry), 1, file) != 1)
throw POSIXException{errno, "Could not read"};
if ((entry.dataPtr > length) ||
(entry.dataPtr < header.dataOfs))
throw FormatException{"Data pointer out of range"};
if ((entry.dataPtr + entry.length) > length) {
fprintf(stderr, "Data length exceeds file length: %.8x %.8x\n", entry.dataPtr, entry.length);
//throw FormatException{"Data length exceeds file length"};
}
table3_.push_back(std::make_tuple(static_cast<uint32_t>(entry.dataPtr),
static_cast<uint32_t>(entry.length)));
}
// Read Table 2
if (fseeko(file, start+header.table2Ofs, SEEK_SET) != 0) {
throw POSIXException{errno, "Could not seek"};
}
std::map<size_t, uint32_t> table2Pos;
size_t pos = header.table2Ofs;
while(pos < header.table3Ofs) {
uint8_t nameLen;
if (fread(&nameLen, 1, 1, file) != 1)
throw POSIXException{errno, "Could not read"};
if (pos+nameLen+4 > header.table3Ofs)
throw FormatException{"Table 2 entry exceeds table "s + std::to_string(nameLen)};
std::vector<char> nameData(nameLen);
if (fread(nameData.data(), nameLen, 1, file) != 1)
throw POSIXException{errno, "Could not read"};
uint32_t table3Ptr;
if (fread(&table3Ptr, 4, 1, file) != 1)
throw POSIXException{errno, "Could not read"};
if ((table3Ptr < header.table3Ofs) ||
(table3Ptr >= header.dataOfs)) {
throw FormatException{"Table3 pointer out of range"};
}
std::string nameStr{nameData.data(), nameLen};
size_t table3Index = (table3Ptr - header.table3Ofs)/sizeof(Table3Entry);
auto ins = table2_.emplace(nameStr, table3Index);
if (!ins.second) {
if (ins.first->second == table3Index)
printf("Duplicate entry for name %s\n", nameStr.c_str());
else
printf("Collision for name %s: prev %lu, new %lu\n", nameStr.c_str(),
ins.first->second, table3Index);
}
// Store an position-indexed copy of Table2 to resolve Table1->Table2 ptr's
table2Pos.emplace(pos, table3Ptr);
pos += 1+nameLen+4;
}
// Read Table 1
if (fseeko(file, start+header.table1Ofs, SEEK_SET) != 0) {
throw POSIXException{errno, "Could not seek"};
}
for (size_t i = 0;i < numTable1Entries;++i) {
Table1Entry entry;
if (fread(&entry, sizeof(Table1Entry), 1, file) != 1)
throw POSIXException{errno, "Could not read"};
if ((entry.crc == 0x00000000) &&
(entry.table3Ptr == 0xffffffff))
continue; // Unused entry
if ((entry.table3Ptr < header.table2Ofs) ||
(entry.table3Ptr >= header.dataOfs)) {
throw FormatException{"Table3 pointer out of range"};
}
if (entry.table3Ptr < header.table3Ofs) {
auto it = table2Pos.find(entry.table3Ptr);
if (it == table2Pos.end()) {
fprintf(stderr, "Table1->Table2 pointer: Table 2 entry not found: crc %.8x ptr %.8x\n",
entry.crc, entry.table3Ptr);
continue;
}
printf("Table1->Table2 pointer resolved: Table 2 %.8x -> Table 3 %.8x\n",
entry.table3Ptr, it->second);
entry.table3Ptr = it->second;
}
size_t table3Index = (entry.table3Ptr - header.table3Ofs)/sizeof(Table3Entry);
auto ins = table1_.emplace(static_cast<uint32_t>(entry.crc), table3Index);
if (!ins.second) {
if (ins.first->second == table3Index)
printf("Duplicate entry for CRC %.8x\n", entry.crc);
else
printf("Collision for CRC %.8x: prev %lu, new %lu\n", entry.crc, ins.first->second, table3Index);
}
}
}
std::map<uint32_t, size_t> table1_;
std::map<std::string, size_t> table2_;
std::vector<std::tuple<uint32_t, uint32_t> > table3_;
};
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s tre-file\n", argv[0]);
return 1;
}
try {
TreFile file{argv[1]};
file.printStructure();
} catch (POSIXException &ex) {
fprintf(stderr, "%s\n", ex.toString().c_str());
return 2;
} catch (FormatException &ex) {
fprintf(stderr, "%s\n", ex.toString().c_str());
return 3;
}
return 0;
}