Seperate exceptions; Use proper endian-aware int parsing routines
This commit is contained in:
20
IffFile.cc
20
IffFile.cc
@@ -1,12 +1,13 @@
|
|||||||
#include <arpa/inet.h>
|
#include <cstring>
|
||||||
|
|
||||||
#include "IffFile.hh"
|
#include "IffFile.hh"
|
||||||
|
#include "util.hh"
|
||||||
#include "common.hh"
|
#include "common.hh"
|
||||||
|
|
||||||
struct ChunkHeader {
|
struct ChunkHeader {
|
||||||
char typeID[4];
|
char typeID[4];
|
||||||
uint32_t length; // big endian!!
|
uint32_t length;
|
||||||
} __attribute__((__packed__));
|
};
|
||||||
|
|
||||||
IffFile::IffFile(char const* base, size_t length)
|
IffFile::IffFile(char const* base, size_t length)
|
||||||
: base_(base), length_(length), root_(nullptr)
|
: base_(base), length_(length), root_(nullptr)
|
||||||
@@ -51,18 +52,13 @@ void IffFile::printStructure(unsigned level) const
|
|||||||
|
|
||||||
|
|
||||||
std::unique_ptr<IffFile::Object> IffFile::parseObject(char const* base, size_t length)
|
std::unique_ptr<IffFile::Object> IffFile::parseObject(char const* base, size_t length)
|
||||||
{
|
{
|
||||||
// if (reinterpret_cast<uintptr_t>(base)%2 != 0) {
|
if (length < 8)
|
||||||
// ++base;
|
|
||||||
// --length;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (length < sizeof(ChunkHeader))
|
|
||||||
throw FormatException{"length < header size"};
|
throw FormatException{"length < header size"};
|
||||||
|
|
||||||
ChunkHeader header;
|
ChunkHeader header;
|
||||||
memcpy(&header, base, sizeof(ChunkHeader));
|
memcpy(&header.typeID, base, 4);
|
||||||
header.length = ntohl(header.length);
|
header.length = readU32BE(base+4);
|
||||||
|
|
||||||
for (unsigned i = 0;i < 4;++i)
|
for (unsigned i = 0;i < 4;++i)
|
||||||
if (!isprint(header.typeID[i]))
|
if (!isprint(header.typeID[i]))
|
||||||
|
|||||||
8
Makefile
8
Makefile
@@ -2,19 +2,19 @@ CXX=g++
|
|||||||
CXXOPTS=-Og -ggdb -Wall -Wextra -pedantic -std=c++14 -flto
|
CXXOPTS=-Og -ggdb -Wall -Wextra -pedantic -std=c++14 -flto
|
||||||
LDOPTS=
|
LDOPTS=
|
||||||
|
|
||||||
iffexplore_CXXSRCS ::= iffexplore.cc IffFile.cc util.cc
|
iffexplore_CXXSRCS ::= iffexplore.cc IffFile.cc util.cc exceptions.cc
|
||||||
iffexplore_LIBS ::=
|
iffexplore_LIBS ::=
|
||||||
|
|
||||||
treexplore_CXXSRCS ::= treexplore.cc TreFile.cc IffFile.cc util.cc
|
treexplore_CXXSRCS ::= treexplore.cc TreFile.cc IffFile.cc util.cc exceptions.cc
|
||||||
treexplore_LIBS ::=
|
treexplore_LIBS ::=
|
||||||
|
|
||||||
font2png_CXXSRCS ::= font2png.cc
|
font2png_CXXSRCS ::= font2png.cc
|
||||||
font2png_LIBS ::= -lpng
|
font2png_LIBS ::= -lpng
|
||||||
|
|
||||||
mvedecode_CXXSRCS ::= mvedecode.cc TreFile.cc IffFile.cc util.cc MveDecoder.cc
|
mvedecode_CXXSRCS ::= mvedecode.cc TreFile.cc IffFile.cc util.cc MveDecoder.cc exceptions.cc
|
||||||
mvedecode_LIBS ::=
|
mvedecode_LIBS ::=
|
||||||
|
|
||||||
progs ::= iffexplore font2png treexplore mvedecode
|
progs ::= iffexplore treexplore mvedecode
|
||||||
|
|
||||||
all: $(progs)
|
all: $(progs)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "common.hh"
|
#include "common.hh"
|
||||||
|
#include "util.hh"
|
||||||
#include "IffFile.hh"
|
#include "IffFile.hh"
|
||||||
#include "MveDecoder.hh"
|
#include "MveDecoder.hh"
|
||||||
|
|
||||||
@@ -30,9 +32,9 @@ MveDecoder::MveDecoder(char const* base, size_t length)
|
|||||||
(PC.getSize() != 12))
|
(PC.getSize() != 12))
|
||||||
throw FormatException{"_PC_ chunk missing or wrong size"};
|
throw FormatException{"_PC_ chunk missing or wrong size"};
|
||||||
|
|
||||||
PCChunk const* PCc = reinterpret_cast<PCChunk const*>(PC.begin());
|
const uint32_t PC_PALTcount = readU32LE(PC.begin()+8);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
printf("_PC_: %u PALTs\n", PCc->PALTcount);
|
printf("_PC_: %u PALTs\n", PC_PALTcount);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Parse SOND chunk
|
// Parse SOND chunk
|
||||||
@@ -41,8 +43,8 @@ MveDecoder::MveDecoder(char const* base, size_t length)
|
|||||||
(SOND.getSize() != 4))
|
(SOND.getSize() != 4))
|
||||||
throw FormatException{"SOND chunk missing or wrong size"};
|
throw FormatException{"SOND chunk missing or wrong size"};
|
||||||
|
|
||||||
uint32_t const* SONDc = reinterpret_cast<uint32_t const*>(SOND.begin());
|
const uint32_t SONDc = readU32LE(SOND.begin());
|
||||||
if (*SONDc != 3)
|
if (SONDc != 3)
|
||||||
throw FormatException{"Unexpected SOND value"};
|
throw FormatException{"Unexpected SOND value"};
|
||||||
|
|
||||||
// Parse data
|
// Parse data
|
||||||
@@ -52,7 +54,7 @@ MveDecoder::MveDecoder(char const* base, size_t length)
|
|||||||
|
|
||||||
for (;it != rootForm.childrenEnd();++it) {
|
for (;it != rootForm.childrenEnd();++it) {
|
||||||
if (it->getType() == "PALT") {
|
if (it->getType() == "PALT") {
|
||||||
if (curPALT > PCc->PALTcount)
|
if (curPALT > PC_PALTcount)
|
||||||
throw FormatException{"Number of PALT chunks exceeds amount specified in _PC_"};
|
throw FormatException{"Number of PALT chunks exceeds amount specified in _PC_"};
|
||||||
if (it->getSize() != 768)
|
if (it->getSize() != 768)
|
||||||
throw FormatException{"Unexpected PALT size"};
|
throw FormatException{"Unexpected PALT size"};
|
||||||
@@ -62,11 +64,11 @@ MveDecoder::MveDecoder(char const* base, size_t length)
|
|||||||
[](const char& in) -> uint8_t {return (in << 2) | ((in >> 6)&0x3);});
|
[](const char& in) -> uint8_t {return (in << 2) | ((in >> 6)&0x3);});
|
||||||
++curPALT;
|
++curPALT;
|
||||||
} else if (it->getType() == "SHOT") {
|
} else if (it->getType() == "SHOT") {
|
||||||
uint32_t const* SHOTc = reinterpret_cast<uint32_t const*>(it->begin());
|
const uint32_t SHOTc = readU32LE(it->begin());
|
||||||
if (*SHOTc > PCc->PALTcount)
|
if (SHOTc > PC_PALTcount)
|
||||||
throw FormatException{"SHOT refers to PALT outside amount specified in _PC_"};
|
throw FormatException{"SHOT refers to PALT outside amount specified in _PC_"};
|
||||||
++curSHOT;
|
++curSHOT;
|
||||||
Shot shot{palts_.at(*SHOTc), {}, {}};
|
Shot shot{palts_.at(SHOTc), {}, {}};
|
||||||
shots_.push_back(shot);
|
shots_.push_back(shot);
|
||||||
} else if (it->getType() == "VGA ") {
|
} else if (it->getType() == "VGA ") {
|
||||||
if (curSHOT < 0)
|
if (curSHOT < 0)
|
||||||
@@ -239,7 +241,7 @@ std::vector<uint8_t> parsePixels_(char const* data, size_t len)
|
|||||||
ret.push_back(*(data+pos++));
|
ret.push_back(*(data+pos++));
|
||||||
|
|
||||||
for (unsigned i = 0;i < replSize;++i)
|
for (unsigned i = 0;i < replSize;++i)
|
||||||
ret.push_back(ret[replOfs+i]);
|
ret.push_back(ret.at(replOfs+i));
|
||||||
} else {
|
} else {
|
||||||
//printf("Code: Copy %.2hhx\n", b);
|
//printf("Code: Copy %.2hhx\n", b);
|
||||||
unsigned size = (b&0x1f)*4+4;
|
unsigned size = (b&0x1f)*4+4;
|
||||||
@@ -265,8 +267,8 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
|
|||||||
throw FormatException{"VGA chunk smaller than VGA header"};
|
throw FormatException{"VGA chunk smaller than VGA header"};
|
||||||
|
|
||||||
std::array<uint16_t, 4> segOfs;
|
std::array<uint16_t, 4> segOfs;
|
||||||
memcpy(segOfs.data(), vga.begin(), 8);
|
|
||||||
for(unsigned i = 0;i < segOfs.size();++i) {
|
for(unsigned i = 0;i < segOfs.size();++i) {
|
||||||
|
segOfs[i] = readU16LE(vga.begin()+i*2);
|
||||||
if (segOfs[i] >= vga.getSize())
|
if (segOfs[i] >= vga.getSize())
|
||||||
throw FormatException{"Segment offset exceeds chunk"};
|
throw FormatException{"Segment offset exceeds chunk"};
|
||||||
}
|
}
|
||||||
|
|||||||
171
TreFile.cc
171
TreFile.cc
@@ -1,94 +1,107 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "common.hh"
|
#include "common.hh"
|
||||||
|
#include "util.hh"
|
||||||
#include "TreFile.hh"
|
#include "TreFile.hh"
|
||||||
|
|
||||||
struct TreHeader { // little endian
|
struct TreHeader { // little endian
|
||||||
char magic[4]; // == "XTRE"
|
char magic[4]; // == "XTRE"
|
||||||
uint32_t unknown;
|
//uint32_t unknown;
|
||||||
uint32_t table1Ofs;
|
uint32_t table1Ofs;
|
||||||
uint32_t table2Ofs;
|
uint32_t table2Ofs;
|
||||||
uint32_t table3Ofs;
|
uint32_t table3Ofs;
|
||||||
uint32_t dataOfs;
|
uint32_t dataOfs;
|
||||||
} __attribute__((__packed__));
|
};
|
||||||
|
|
||||||
struct Table1Entry { // little endian
|
static const size_t headerSize = 24;
|
||||||
uint32_t crc;
|
static const size_t table1EntrySize = 8;
|
||||||
uint32_t table3Ptr;
|
static const size_t table3EntrySize = 8;
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
TreFile::TreFile(char const* base, size_t length)
|
TreFile::TreFile(char const* base, size_t length)
|
||||||
: base_(base), length_(length)
|
: base_(base), length_(length)
|
||||||
{
|
{
|
||||||
if (length_ < sizeof(TreHeader))
|
if (length_ < 6*4)
|
||||||
throw FormatException{"Size < header size"};
|
throw FormatException{"Size < header size"};
|
||||||
|
|
||||||
TreHeader const* header = reinterpret_cast<TreHeader const*>(base);
|
TreHeader header;
|
||||||
|
memcpy(header.magic, base, 4);
|
||||||
|
|
||||||
if (memcmp(header->magic, "XTRE", 4) != 0)
|
if (memcmp(header.magic, "XTRE", 4) != 0)
|
||||||
throw FormatException{"Wrong magic, not a TRE?"};
|
throw FormatException{"Wrong magic, not a TRE?"};
|
||||||
|
|
||||||
if ((header->table1Ofs > length_) ||
|
header.table1Ofs = readU32LE(base_+8);
|
||||||
(header->table2Ofs > length_) ||
|
header.table2Ofs = readU32LE(base_+12);
|
||||||
(header->table3Ofs > length_) ||
|
header.table3Ofs = readU32LE(base_+16);
|
||||||
(header->dataOfs > length_))
|
header.dataOfs = readU32LE(base_+20);
|
||||||
|
|
||||||
|
if ((header.table1Ofs > length_) ||
|
||||||
|
(header.table2Ofs > length_) ||
|
||||||
|
(header.table3Ofs > length_) ||
|
||||||
|
(header.dataOfs > length_))
|
||||||
throw FormatException{"Table offset exceeds length"};
|
throw FormatException{"Table offset exceeds length"};
|
||||||
|
|
||||||
if ((header->table1Ofs > header->table2Ofs) ||
|
if ((header.table1Ofs > header.table2Ofs) ||
|
||||||
(header->table2Ofs > header->table3Ofs) ||
|
(header.table2Ofs > header.table3Ofs) ||
|
||||||
(header->table3Ofs > header->dataOfs))
|
(header.table3Ofs > header.dataOfs))
|
||||||
throw FormatException{"Table offsets not in ascending order"};
|
throw FormatException{"Table offsets not in ascending order"};
|
||||||
|
|
||||||
const size_t numTable1Entries = (header->table2Ofs-header->table1Ofs)/sizeof(Table1Entry);
|
const size_t numTable1Entries = (header.table2Ofs-header.table1Ofs)/table1EntrySize;
|
||||||
table3Size_ = (header->dataOfs-header->table3Ofs)/sizeof(Table3Entry);
|
const size_t numTable3Entries = (header.dataOfs-header.table3Ofs)/table3EntrySize;
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
printf("Table 1: start %u, length %u (%lu entries)\n",
|
printf("Table 1: start %u, length %u (%lu entries)\n",
|
||||||
header->table1Ofs, header->table2Ofs-header->table1Ofs,
|
header.table1Ofs, header.table2Ofs-header.table1Ofs,
|
||||||
numTable1Entries);
|
numTable1Entries);
|
||||||
printf("Table 2: start %u, length %u\n",
|
printf("Table 2: start %u, length %u\n",
|
||||||
header->table2Ofs, header->table3Ofs-header->table2Ofs);
|
header.table2Ofs, header.table3Ofs-header.table2Ofs);
|
||||||
printf("Table 3: start %u, length %u (%lu entries)\n",
|
printf("Table 3: start %u, length %u (%lu entries)\n",
|
||||||
header->table3Ofs, header->dataOfs-header->table3Ofs,
|
header.table3Ofs, header.dataOfs-header.table3Ofs,
|
||||||
table3Size_);
|
numTable3Entries);
|
||||||
printf("Data: start %u, length %lu\n",
|
printf("Data: start %u, length %lu\n",
|
||||||
header->dataOfs, length_ - header->dataOfs);
|
header.dataOfs, length_ - header.dataOfs);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Check Table 3
|
// Read table 3
|
||||||
table3_ = reinterpret_cast<Table3Entry const*>(base+header->table3Ofs);
|
for (size_t i = 0;i < numTable3Entries;++i) {
|
||||||
for (size_t i = 0;i < table3Size_;++i) {
|
char const* const entryBase = base_+header.table3Ofs+i*table3EntrySize;
|
||||||
if ((table3_[i].dataPtr > length_) ||
|
|
||||||
(table3_[i].dataPtr < header->dataOfs))
|
const uint32_t dataPtr = readU32LE(entryBase);
|
||||||
|
const uint32_t length = readU24LE(entryBase+4);
|
||||||
|
const uint8_t flags = *(entryBase+7);
|
||||||
|
|
||||||
|
if ((dataPtr > length_) ||
|
||||||
|
(dataPtr < header.dataOfs))
|
||||||
throw FormatException{"Data pointer out of range"};
|
throw FormatException{"Data pointer out of range"};
|
||||||
|
|
||||||
if ((table3_[i].dataPtr + table3_[i].length) > length_) {
|
if ((dataPtr + length) > length_) {
|
||||||
fprintf(stderr, "Data length exceeds file length: %.8x %.8x\n", table3_[i].dataPtr, table3_[i].length);
|
fprintf(stderr, "Data length exceeds file length: %.8x %.8x\n", dataPtr, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table3_.emplace_back(dataPtr, length, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read Table 2
|
// Read Table 2
|
||||||
std::map<size_t, uint32_t> table2Pos;
|
std::map<size_t, uint32_t> table2Pos;
|
||||||
|
|
||||||
size_t pos = header->table2Ofs;
|
size_t pos = header.table2Ofs;
|
||||||
while((pos+5) < header->table3Ofs) {
|
while((pos+5) < header.table3Ofs) {
|
||||||
uint8_t const* nameLen = reinterpret_cast<uint8_t const*>(base+pos);
|
uint8_t nameLen = *(base+pos);
|
||||||
|
|
||||||
if (pos+*nameLen+5 > header->table3Ofs)
|
if (pos+nameLen+5 > header.table3Ofs)
|
||||||
throw FormatException{"Table 2 entry exceeds table "s + std::to_string(*nameLen)};
|
throw FormatException{"Table 2 entry exceeds table " + std::to_string(nameLen)};
|
||||||
|
|
||||||
std::string nameStr(base+pos+1, *nameLen);
|
std::string nameStr(base+pos+1, nameLen);
|
||||||
|
|
||||||
uint32_t const* table3Ptr = reinterpret_cast<uint32_t const*>(base+pos+*nameLen+1);
|
const uint32_t table3Ptr = readU32LE(base+pos+nameLen+1);
|
||||||
|
|
||||||
if ((*table3Ptr < header->table3Ofs) ||
|
if ((table3Ptr < header.table3Ofs) ||
|
||||||
(*table3Ptr >= header->dataOfs)) {
|
(table3Ptr >= header.dataOfs)) {
|
||||||
throw FormatException{"Table3 pointer out of range"};
|
throw FormatException{"Table3 pointer out of range"};
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t table3Index = (*table3Ptr - header->table3Ofs)/sizeof(Table3Entry);
|
const size_t table3Index = (table3Ptr - header.table3Ofs)/table3EntrySize;
|
||||||
|
|
||||||
auto ins = table2_.emplace(nameStr, table3Index);
|
auto ins = table2_.emplace(nameStr, table3Index);
|
||||||
if (!ins.second) {
|
if (!ins.second) {
|
||||||
@@ -99,29 +112,32 @@ TreFile::TreFile(char const* base, size_t length)
|
|||||||
ins.first->second, table3Index);
|
ins.first->second, table3Index);
|
||||||
}
|
}
|
||||||
// Store an position-indexed copy of Table2 to resolve Table1->Table2 ptr's
|
// Store an position-indexed copy of Table2 to resolve Table1->Table2 ptr's
|
||||||
table2Pos.emplace(pos, *table3Ptr);
|
table2Pos.emplace(pos, table3Ptr);
|
||||||
|
|
||||||
pos += 1+*nameLen+4;
|
pos += 1+nameLen+4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read Table 1
|
// Read Table 1
|
||||||
Table1Entry const* table1 = reinterpret_cast<Table1Entry const*>(base+header->table1Ofs);
|
|
||||||
for (size_t i = 0;i < numTable1Entries;++i) {
|
for (size_t i = 0;i < numTable1Entries;++i) {
|
||||||
if ((table1[i].crc == 0x00000000) &&
|
char const* const entryBase = base+header.table1Ofs+i*table1EntrySize;
|
||||||
(table1[i].table3Ptr == 0xffffffff))
|
|
||||||
|
const uint32_t crc = readU32LE(entryBase);
|
||||||
|
uint32_t table3Ptr = readU32LE(entryBase+4);
|
||||||
|
|
||||||
|
if ((crc == 0x00000000) &&
|
||||||
|
(table3Ptr == 0xffffffff))
|
||||||
continue; // Unused entry
|
continue; // Unused entry
|
||||||
|
|
||||||
if ((table1[i].table3Ptr < header->table2Ofs) ||
|
if ((table3Ptr < header.table2Ofs) ||
|
||||||
(table1[i].table3Ptr >= header->dataOfs)) {
|
(table3Ptr >= header.dataOfs)) {
|
||||||
throw FormatException{"Table3 pointer out of range"};
|
throw FormatException{"Table3 pointer out of range"};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t table3Ptr = table1[i].table3Ptr;
|
if (table3Ptr < header.table3Ofs) {
|
||||||
if (table3Ptr < header->table3Ofs) {
|
|
||||||
auto it = table2Pos.find(table3Ptr);
|
auto it = table2Pos.find(table3Ptr);
|
||||||
if (it == table2Pos.end()) {
|
if (it == table2Pos.end()) {
|
||||||
fprintf(stderr, "Table1->Table2 pointer: Table 2 entry not found: crc %.8x ptr %.8x\n",
|
fprintf(stderr, "Table1->Table2 pointer: Table 2 entry not found: crc %.8x ptr %.8x\n",
|
||||||
table1[i].crc, table3Ptr);
|
crc, table3Ptr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
printf("Table1->Table2 pointer resolved: Table 2 %.8x -> Table 3 %.8x\n",
|
printf("Table1->Table2 pointer resolved: Table 2 %.8x -> Table 3 %.8x\n",
|
||||||
@@ -129,13 +145,13 @@ TreFile::TreFile(char const* base, size_t length)
|
|||||||
table3Ptr = it->second;
|
table3Ptr = it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t table3Index = (table3Ptr - header->table3Ofs)/sizeof(Table3Entry);
|
const size_t table3Index = (table3Ptr - header.table3Ofs)/table3EntrySize;
|
||||||
auto ins = table1_.emplace(static_cast<uint32_t>(table1[i].crc), table3Index);
|
auto ins = table1_.emplace(crc, table3Index);
|
||||||
if (!ins.second) {
|
if (!ins.second) {
|
||||||
if (ins.first->second == table3Index)
|
if (ins.first->second == table3Index)
|
||||||
printf("Duplicate entry for CRC %.8x\n", table1[i].crc);
|
printf("Duplicate entry for CRC %.8x\n", crc);
|
||||||
else
|
else
|
||||||
printf("Collision for CRC %.8x: prev %lu, new %lu\n", table1[i].crc, ins.first->second, table3Index);
|
printf("Collision for CRC %.8x: prev %lu, new %lu\n", crc, ins.first->second, table3Index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,22 +251,22 @@ void TreFile::printStructure()
|
|||||||
{
|
{
|
||||||
printf("Table 1: %lu entries\n", table1_.size());
|
printf("Table 1: %lu entries\n", table1_.size());
|
||||||
printf("Table 2: %lu entries\n", table2_.size());
|
printf("Table 2: %lu entries\n", table2_.size());
|
||||||
printf("Table 3: %lu entries\n", table3Size_);
|
printf("Table 3: %lu entries\n", table3_.size());
|
||||||
|
|
||||||
printf("Files by CRC:\n");
|
printf("Files by CRC:\n");
|
||||||
for(auto const& ent : table1_) {
|
for(auto const& ent : table1_) {
|
||||||
printf("\t%.8x -> (ofs %.8x, len %.8x, flags %.2hhx)\n", ent.first,
|
printf("\t%.8x -> (ofs %.8x, len %.8x, flags %.2hhx)\n", ent.first,
|
||||||
table3_[ent.second].dataPtr,
|
std::get<0>(table3_[ent.second]),
|
||||||
table3_[ent.second].length,
|
std::get<1>(table3_[ent.second]),
|
||||||
table3_[ent.second].flags);
|
std::get<2>(table3_[ent.second]));
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Files by Name:\n");
|
printf("Files by Name:\n");
|
||||||
for(auto const& ent : table2_) {
|
for(auto const& ent : table2_) {
|
||||||
printf("\t%s -> (ofs %.8x, len %.8x, flags %.2hhx)\n", ent.first.c_str(),
|
printf("\t%s -> (ofs %.8x, len %.8x, flags %.2hhx)\n", ent.first.c_str(),
|
||||||
table3_[ent.second].dataPtr,
|
std::get<0>(table3_[ent.second]),
|
||||||
table3_[ent.second].length,
|
std::get<1>(table3_[ent.second]),
|
||||||
table3_[ent.second].flags);
|
std::get<2>(table3_[ent.second]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +285,7 @@ size_t TreFile::findCRC_(uint32_t crc) const
|
|||||||
if (it == table1_.end()) {
|
if (it == table1_.end()) {
|
||||||
char crcStr[9];
|
char crcStr[9];
|
||||||
snprintf(crcStr, 9, "%.8X", crc);
|
snprintf(crcStr, 9, "%.8X", crc);
|
||||||
throw Exception{"CRC "s + crcStr + " not found"s};
|
throw Exception{std::string("CRC ") + crcStr + " not found"};
|
||||||
}
|
}
|
||||||
|
|
||||||
return it->second;
|
return it->second;
|
||||||
@@ -277,35 +293,44 @@ size_t TreFile::findCRC_(uint32_t crc) const
|
|||||||
|
|
||||||
void TreFile::dumpIdx_(std::string const& name, size_t table3Idx) const
|
void TreFile::dumpIdx_(std::string const& name, size_t table3Idx) const
|
||||||
{
|
{
|
||||||
if(table3_[table3Idx].flags&0x80)
|
uint32_t dataPtr, length;
|
||||||
|
uint8_t flags;
|
||||||
|
std::tie(dataPtr, length, flags) = table3_[table3Idx];
|
||||||
|
|
||||||
|
if(flags&0x80)
|
||||||
throw Exception{"Compressed TRE objects NYI"};
|
throw Exception{"Compressed TRE objects NYI"};
|
||||||
|
|
||||||
|
if ((dataPtr + length) > length_)
|
||||||
|
throw FormatException{"length exceeds file size"};
|
||||||
|
|
||||||
FILEUPtr outFile{fopen(name.c_str(), "wb")};
|
FILEUPtr outFile{fopen(name.c_str(), "wb")};
|
||||||
if (!outFile)
|
if (!outFile)
|
||||||
throw POSIXException{errno, "Could not open " + name};
|
throw POSIXException{errno, "Could not open " + name};
|
||||||
|
|
||||||
if (fwrite(base_+table3_[table3Idx].dataPtr, table3_[table3Idx].length, 1, outFile.get()) != 1)
|
if (fwrite(base_+dataPtr, length, 1, outFile.get()) != 1)
|
||||||
throw POSIXException{errno, "Could not write"};
|
throw POSIXException{errno, "Could not write"};
|
||||||
}
|
}
|
||||||
|
|
||||||
TreFile::Object TreFile::openIdx_(size_t table3Idx) const
|
TreFile::Object TreFile::openIdx_(size_t table3Idx) const
|
||||||
{
|
{
|
||||||
if(table3_[table3Idx].flags&0x80)
|
uint32_t dataPtr, length;
|
||||||
|
uint8_t flags;
|
||||||
|
std::tie(dataPtr, length, flags) = table3_[table3Idx];
|
||||||
|
|
||||||
|
if(flags&0x80)
|
||||||
throw Exception{"Compressed TRE objects NYI"};
|
throw Exception{"Compressed TRE objects NYI"};
|
||||||
|
|
||||||
if ((table3_[table3Idx].dataPtr + table3_[table3Idx].length) > length_)
|
if ((dataPtr + length) > length_)
|
||||||
throw FormatException{"length exceeds file size"};
|
throw FormatException{"length exceeds file size"};
|
||||||
|
|
||||||
return Object(base_+table3_[table3Idx].dataPtr,
|
return Object(base_+dataPtr,
|
||||||
table3_[table3Idx].length);
|
length);
|
||||||
}
|
}
|
||||||
|
|
||||||
TreFile::Stat TreFile::statIdx_(size_t table3Idx) const
|
TreFile::Stat TreFile::statIdx_(size_t table3Idx) const
|
||||||
{
|
{
|
||||||
Stat ret;
|
Stat ret;
|
||||||
ret.size = table3_[table3Idx].length;
|
std::tie(std::ignore, ret.size, ret.flags) = table3_[table3Idx];
|
||||||
ret.flags = table3_[table3Idx].flags;
|
|
||||||
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
12
TreFile.hh
12
TreFile.hh
@@ -89,16 +89,8 @@ private:
|
|||||||
|
|
||||||
std::map<uint32_t, size_t> table1_;
|
std::map<uint32_t, size_t> table1_;
|
||||||
std::map<std::string, size_t> table2_;
|
std::map<std::string, size_t> table2_;
|
||||||
|
std::vector<std::tuple<uint32_t, uint32_t, uint8_t> > table3_;
|
||||||
struct Table3Entry { // little endian
|
|
||||||
unsigned dataPtr:32;
|
|
||||||
unsigned length:24;
|
|
||||||
unsigned flags:8;
|
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
Table3Entry const* table3_;
|
|
||||||
size_t table3Size_;
|
|
||||||
|
|
||||||
char const* base_;
|
char const* base_;
|
||||||
size_t length_;
|
size_t length_;
|
||||||
};
|
};
|
||||||
|
|||||||
49
common.hh
49
common.hh
@@ -1,56 +1,9 @@
|
|||||||
#ifndef WC3RE__COMMON_HH__
|
#ifndef WC3RE__COMMON_HH__
|
||||||
#define WC3RE__COMMON_HH__
|
#define WC3RE__COMMON_HH__
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cerrno>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using namespace std::string_literals;
|
|
||||||
|
|
||||||
class Exception {
|
|
||||||
public:
|
|
||||||
Exception() : msg_() {
|
|
||||||
}
|
|
||||||
|
|
||||||
Exception(std::string msg) : msg_(std::move(msg)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~Exception() {}
|
|
||||||
|
|
||||||
virtual std::string toString() const {
|
|
||||||
return "Exception: "s + msg_;
|
|
||||||
}
|
|
||||||
protected:
|
|
||||||
std::string msg_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FormatException : public Exception {
|
|
||||||
public:
|
|
||||||
FormatException(std::string msg) : Exception(std::move(msg)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string toString() const override {
|
|
||||||
return "FormatException: "s + msg_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class POSIXException : public Exception {
|
|
||||||
public:
|
|
||||||
POSIXException(int err, std::string msg = ""): Exception(std::move(msg)), err_(err) {
|
|
||||||
}
|
|
||||||
|
|
||||||
int getErr() const {return err_;}
|
|
||||||
|
|
||||||
std::string toString() const override {
|
|
||||||
return "POSIXException: "s + msg_ + ": "s + std::strerror(err_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
int err_;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FILEDeleter {
|
struct FILEDeleter {
|
||||||
void operator()(FILE* file) const {
|
void operator()(FILE* file) const {
|
||||||
fclose(file);
|
fclose(file);
|
||||||
@@ -59,4 +12,6 @@ struct FILEDeleter {
|
|||||||
|
|
||||||
using FILEUPtr = std::unique_ptr<FILE, FILEDeleter>;
|
using FILEUPtr = std::unique_ptr<FILE, FILEDeleter>;
|
||||||
|
|
||||||
|
#include "exceptions.hh"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
47
exceptions.cc
Normal file
47
exceptions.cc
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#include <cstring>
|
||||||
|
#include <cerrno>
|
||||||
|
|
||||||
|
#include "exceptions.hh"
|
||||||
|
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
Exception::Exception(std::string msg)
|
||||||
|
: msg_(std::move(msg))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Exception::~Exception()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Exception::toString() const
|
||||||
|
{
|
||||||
|
return "Exception: " + msg_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FormatException::FormatException(std::string msg)
|
||||||
|
: Exception(std::move(msg))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FormatException::toString() const
|
||||||
|
{
|
||||||
|
return "FormatException: " + msg_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
POSIXException::POSIXException(int err, std::string msg)
|
||||||
|
: Exception(std::move(msg)), err_(err)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int POSIXException::getErr() const
|
||||||
|
{
|
||||||
|
return err_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string POSIXException::toString() const
|
||||||
|
{
|
||||||
|
return "POSIXException: " + msg_ + ": " + std::strerror(err_);
|
||||||
|
}
|
||||||
36
exceptions.hh
Normal file
36
exceptions.hh
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef WC3RE_EXCEPTIONS_HH__
|
||||||
|
#define WC3RE_EXCEPTIONS_HH__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Exception {
|
||||||
|
public:
|
||||||
|
Exception(std::string msg = "");
|
||||||
|
|
||||||
|
virtual ~Exception();
|
||||||
|
|
||||||
|
virtual std::string toString() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string msg_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormatException : public Exception {
|
||||||
|
public:
|
||||||
|
FormatException(std::string msg);
|
||||||
|
|
||||||
|
std::string toString() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class POSIXException : public Exception {
|
||||||
|
public:
|
||||||
|
POSIXException(int err, std::string msg = "");
|
||||||
|
|
||||||
|
int getErr() const;
|
||||||
|
|
||||||
|
std::string toString() const override;
|
||||||
|
private:
|
||||||
|
int err_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -111,7 +111,7 @@ void saveImage(std::string const& name, Image const& img)
|
|||||||
{
|
{
|
||||||
FILEUPtr file{fopen(name.c_str(), "wb")};
|
FILEUPtr file{fopen(name.c_str(), "wb")};
|
||||||
if (!file)
|
if (!file)
|
||||||
throw POSIXException{errno, "Could not open "s + name};
|
throw POSIXException{errno, "Could not open " + name};
|
||||||
|
|
||||||
png_image pngImage;
|
png_image pngImage;
|
||||||
pngImage.version = PNG_IMAGE_VERSION;
|
pngImage.version = PNG_IMAGE_VERSION;
|
||||||
@@ -126,7 +126,7 @@ void saveImage(std::string const& name, Image const& img)
|
|||||||
|
|
||||||
if (!png_image_write_to_stdio(&pngImage, file.get(), false, img.data.data(),
|
if (!png_image_write_to_stdio(&pngImage, file.get(), false, img.data.data(),
|
||||||
img.width, nullptr))
|
img.width, nullptr))
|
||||||
throw Exception{"PNG write failed: "s + pngImage.message};
|
throw Exception{"PNG write failed: " + pngImage.message};
|
||||||
}
|
}
|
||||||
|
|
||||||
Image makeFontImage(Font const& font)
|
Image makeFontImage(Font const& font)
|
||||||
@@ -172,7 +172,7 @@ int main(int argc, char *argv[])
|
|||||||
try {
|
try {
|
||||||
FILEUPtr fontFile{fopen(argv[1], "rb")};
|
FILEUPtr fontFile{fopen(argv[1], "rb")};
|
||||||
if (!fontFile) {
|
if (!fontFile) {
|
||||||
throw POSIXException{errno, "Could not open "s + argv[1]};
|
throw POSIXException{errno, "Could not open "s+ argv[1]};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct stat statBuf;
|
struct stat statBuf;
|
||||||
@@ -182,7 +182,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto font = parseFont(fontFile.get(), statBuf.st_size);
|
auto font = parseFont(fontFile.get(), statBuf.st_size);
|
||||||
saveImage(argv[1] + ".png"s, makeFontImage(font));
|
saveImage(argv[1] + ".png", makeFontImage(font));
|
||||||
|
|
||||||
} catch (POSIXException &ex) {
|
} catch (POSIXException &ex) {
|
||||||
fprintf(stderr, "%s\n", ex.toString().c_str());
|
fprintf(stderr, "%s\n", ex.toString().c_str());
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ void iffDumper(IffFile::Object const& obj, bool dumpBlobs, std::string dumpPath,
|
|||||||
} catch(FormatException &ex) {
|
} catch(FormatException &ex) {
|
||||||
if (dumpBlobs) {
|
if (dumpBlobs) {
|
||||||
std::string filename{dumpPath};
|
std::string filename{dumpPath};
|
||||||
filename += obj.getType() + "-"s + std::to_string(blobCount++);
|
filename += obj.getType() + "-" + std::to_string(blobCount++);
|
||||||
printf(" dump to %s\n", filename.c_str());
|
printf(" dump to %s\n", filename.c_str());
|
||||||
blobDump(obj, filename);
|
blobDump(obj, filename);
|
||||||
} else
|
} else
|
||||||
|
|||||||
117
util.cc
117
util.cc
@@ -1,5 +1,5 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <cstring>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@@ -12,7 +12,7 @@ MmapFile::MmapFile(std::string fileName)
|
|||||||
: name_(std::move(fileName)), fd_(fopen(name_.c_str(), "rb")), size_(0), base_(nullptr)
|
: name_(std::move(fileName)), fd_(fopen(name_.c_str(), "rb")), size_(0), base_(nullptr)
|
||||||
{
|
{
|
||||||
if (!fd_)
|
if (!fd_)
|
||||||
throw POSIXException{errno, "Could not open "s + name_};
|
throw POSIXException{errno, "Could not open " + name_};
|
||||||
|
|
||||||
struct stat statBuf;
|
struct stat statBuf;
|
||||||
|
|
||||||
@@ -85,3 +85,116 @@ MmapFile::operator bool() const
|
|||||||
{
|
{
|
||||||
return base_;
|
return base_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t readU16BE(char const* data)
|
||||||
|
{
|
||||||
|
if(!data)
|
||||||
|
throw std::logic_error("readU16BE() called with nullptr");
|
||||||
|
|
||||||
|
uint16_t ret;
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
ret = ((static_cast<uint16_t>(*data)&0xff)<<8) | static_cast<uint8_t>(*(data+1));
|
||||||
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
memcpy(&ret, data, 2);
|
||||||
|
#else
|
||||||
|
#error Unknown endianess
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t readU24BE(char const* data)
|
||||||
|
{
|
||||||
|
if(!data)
|
||||||
|
throw std::logic_error("readU24BE() called with nullptr");
|
||||||
|
|
||||||
|
uint32_t ret = 0;
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
ret = ((static_cast<uint32_t>(*data)&0xff)<<16) |
|
||||||
|
((static_cast<uint32_t>(*(data+1))&0xff)<<8) |
|
||||||
|
static_cast<uint8_t>(*(data+2));
|
||||||
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
memcpy(static_cast<char*>(&ret)+1, data, 3);
|
||||||
|
#else
|
||||||
|
#error Unknown endianess
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t readU32BE(char const* data)
|
||||||
|
{
|
||||||
|
if(!data)
|
||||||
|
throw std::logic_error("readU32BE() called with nullptr");
|
||||||
|
|
||||||
|
uint32_t ret;
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
ret = ((static_cast<uint32_t>(*data)&0xff)<<24) |
|
||||||
|
((static_cast<uint32_t>(*(data+1))&0xff)<<16) |
|
||||||
|
((static_cast<uint32_t>(*(data+2))&0xff)<<8) |
|
||||||
|
static_cast<uint8_t>(*(data+3));
|
||||||
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
memcpy(&ret, data, 4);
|
||||||
|
#else
|
||||||
|
#error Unknown endianess
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t readU16LE(char const* data)
|
||||||
|
{
|
||||||
|
if(!data)
|
||||||
|
throw std::logic_error("readU16LE() called with nullptr");
|
||||||
|
|
||||||
|
uint16_t ret;
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
memcpy(&ret, data, 2);
|
||||||
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
ret = ((static_cast<uint16_t>(*(data+1))&0xff)<<8) | static_cast<uint8_t>(*data);
|
||||||
|
#else
|
||||||
|
#error Unknown endianess
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t readU24LE(char const* data)
|
||||||
|
{
|
||||||
|
if(!data)
|
||||||
|
throw std::logic_error("readU24LE() called with nullptr");
|
||||||
|
|
||||||
|
uint32_t ret = 0;
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
memcpy(&ret, data, 3);
|
||||||
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
ret = ((static_cast<uint32_t>(*(data+2))&0xff)<<16) |
|
||||||
|
((static_cast<uint32_t>(*(data+1))&0xff)<<8) |
|
||||||
|
static_cast<uint8_t>(*data);
|
||||||
|
#else
|
||||||
|
#error Unknown endianess
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t readU32LE(char const* data)
|
||||||
|
{
|
||||||
|
if(!data)
|
||||||
|
throw std::logic_error("readU32LE() called with nullptr");
|
||||||
|
|
||||||
|
uint32_t ret;
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
memcpy(&ret, data, 4);
|
||||||
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
ret = ((static_cast<uint32_t>(*(data+3))&0xff)<<24) |
|
||||||
|
((static_cast<uint32_t>(*(data+2))&0xff)<<16) |
|
||||||
|
((static_cast<uint32_t>(*(data+1))&0xff)<<8) |
|
||||||
|
static_cast<uint8_t>(*data);
|
||||||
|
#else
|
||||||
|
#error Unknown endianess
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
10
util.hh
10
util.hh
@@ -29,4 +29,14 @@ private:
|
|||||||
void *base_;
|
void *base_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Helper functions to read big endian numbers
|
||||||
|
uint16_t readU16BE(char const* data);
|
||||||
|
uint32_t readU24BE(char const* data);
|
||||||
|
uint32_t readU32BE(char const* data);
|
||||||
|
|
||||||
|
// Helper functions to read little endian numbers
|
||||||
|
uint16_t readU16LE(char const* data);
|
||||||
|
uint32_t readU24LE(char const* data);
|
||||||
|
uint32_t readU32LE(char const* data);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user