#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; }