From 6726488ddafb01fdb0bf25abc41238fa452a164f Mon Sep 17 00:00:00 2001 From: Matthias Blankertz Date: Fri, 17 Apr 2015 15:38:34 +0200 Subject: [PATCH] treexplore: TRE parser --- Makefile | 21 +++- treexplore.cc | 262 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 treexplore.cc diff --git a/Makefile b/Makefile index 650e810..c7b273f 100644 --- a/Makefile +++ b/Makefile @@ -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)) diff --git a/treexplore.cc b/treexplore.cc new file mode 100644 index 0000000..5021487 --- /dev/null +++ b/treexplore.cc @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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(entry.dataPtr), + static_cast(entry.length))); + } + + // Read Table 2 + if (fseeko(file, start+header.table2Ofs, SEEK_SET) != 0) { + throw POSIXException{errno, "Could not seek"}; + } + + std::map 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 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(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 table1_; + std::map table2_; + std::vector > 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; +}