Added ResourceManager; WIP: Decoder for SHAP chunks

This commit is contained in:
2015-06-10 17:42:32 +02:00
parent 70439a10c8
commit 818c0821fe
26 changed files with 960 additions and 220 deletions

View File

@@ -1,6 +1,7 @@
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <limits>
#include "common.hh"
#include "util.hh"
@@ -20,27 +21,27 @@ static const size_t headerSize = 24;
static const size_t table1EntrySize = 8;
static const size_t table3EntrySize = 8;
TreFile::TreFile(uint8_t const* base, size_t length)
: base_(base), length_(length)
TreFile::TreFile(Resource::Handle res)
: res_(std::move(res)), footprint_(sizeof(TreFile))
{
if (length_ < 6*4)
if (res_->size() < 6*4)
throw FormatException{"Size < header size"};
TreHeader header;
memcpy(header.magic, base, 4);
memcpy(header.magic, res_->data(), 4);
if (memcmp(header.magic, "XTRE", 4) != 0)
throw FormatException{"Wrong magic, not a TRE?"};
header.table1Ofs = readU32LE(base_+8);
header.table2Ofs = readU32LE(base_+12);
header.table3Ofs = readU32LE(base_+16);
header.dataOfs = readU32LE(base_+20);
header.table1Ofs = readU32LE(res_->data()+8);
header.table2Ofs = readU32LE(res_->data()+12);
header.table3Ofs = readU32LE(res_->data()+16);
header.dataOfs = readU32LE(res_->data()+20);
if ((header.table1Ofs > length_) ||
(header.table2Ofs > length_) ||
(header.table3Ofs > length_) ||
(header.dataOfs > length_))
if ((header.table1Ofs > res_->size()) ||
(header.table2Ofs > res_->size()) ||
(header.table3Ofs > res_->size()) ||
(header.dataOfs > res_->size()))
throw FormatException{"Table offset exceeds length"};
if ((header.table1Ofs > header.table2Ofs) ||
@@ -61,22 +62,22 @@ TreFile::TreFile(uint8_t const* base, size_t length)
header.table3Ofs, header.dataOfs-header.table3Ofs,
numTable3Entries);
printf("Data: start %u, length %lu\n",
header.dataOfs, length_ - header.dataOfs);
header.dataOfs, res_->size() - header.dataOfs);
#endif
// Read table 3
for (size_t i = 0;i < numTable3Entries;++i) {
uint8_t const* const entryBase = base_+header.table3Ofs+i*table3EntrySize;
uint8_t const* const entryBase = res_->data()+header.table3Ofs+i*table3EntrySize;
const uint32_t dataPtr = readU32LE(entryBase);
const uint32_t length = readU32LE(entryBase+4)&0x0fffffffu;
const uint8_t flags = *(entryBase+7)&0xf0;
if ((dataPtr > length_) ||
if ((dataPtr > res_->size()) ||
(dataPtr < header.dataOfs))
throw FormatException{"Data pointer out of range"};
if ((dataPtr + length) > length_) {
if ((dataPtr + length) > res_->size()) {
fprintf(stderr, "Data length exceeds file length: %.8x %.8x\n", dataPtr, length);
}
@@ -100,11 +101,11 @@ TreFile::TreFile(uint8_t const* base, size_t length)
uint32_t lastPtr, lastLen;
uint8_t lastFlags;
std::tie(lastPtr, std::ignore, lastLen, lastFlags) = table3_.back();
if (lastPtr+lastLen > length_) {
if (lastPtr+lastLen > res_->size()) {
if (lastFlags&0x80)
std::get<2>(table3_.back()) = length_ - lastPtr;
std::get<2>(table3_.back()) = res_->size() - lastPtr;
else
fprintf(stderr, "Overrun? %u %u (%hhu) -> %lu\n",lastPtr, lastLen, lastFlags, length_);
fprintf(stderr, "Overrun? %u %u (%hhu) -> %lu\n",lastPtr, lastLen, lastFlags, res_->size());
}
}
@@ -114,18 +115,18 @@ TreFile::TreFile(uint8_t const* base, size_t length)
size_t pos = header.table2Ofs;
while((pos+5) < header.table3Ofs) {
uint8_t nameLen = *(base+pos);
uint8_t nameLen = *(res_->data()+pos);
if (pos+nameLen+5 > header.table3Ofs)
throw FormatException{"Table 2 entry exceeds table " + std::to_string(nameLen)};
for (unsigned i = 0;i < nameLen;++i)
if (!isprint(base[pos+1+i]))
if (!isprint(res_->data()[pos+1+i]))
throw FormatException{"Filename not printable"};
std::string nameStr(reinterpret_cast<char const*>(base)+pos+1, nameLen);
std::string nameStr(reinterpret_cast<char const*>(res_->data())+pos+1, nameLen);
const uint32_t table3Ptr = readU32LE(base+pos+nameLen+1);
const uint32_t table3Ptr = readU32LE(res_->data()+pos+nameLen+1);
if ((table3Ptr < header.table3Ofs) ||
(table3Ptr >= header.dataOfs)) {
@@ -150,7 +151,7 @@ TreFile::TreFile(uint8_t const* base, size_t length)
// Read Table 1
for (size_t i = 0;i < numTable1Entries;++i) {
uint8_t const* const entryBase = base+header.table1Ofs+i*table1EntrySize;
uint8_t const* const entryBase = res_->data()+header.table1Ofs+i*table1EntrySize;
const uint32_t crc = readU32LE(entryBase);
uint32_t table3Ptr = readU32LE(entryBase+4);
@@ -185,6 +186,10 @@ TreFile::TreFile(uint8_t const* base, size_t length)
printf("Collision for CRC %.8x: prev %lu, new %lu\n", crc, ins.first->second, table3Index);
}
}
footprint_ += sizeof(std::map<uint32_t, size_t>::value_type)*table1_.size()+
sizeof(std::map<std::string, size_t>::value_type)*table2_.size()+
sizeof(std::tuple<uint32_t, uint32_t, uint32_t, uint8_t>)*table3_.capacity();
}
@@ -212,16 +217,40 @@ std::vector<uint32_t> TreFile::getCRCs() const
return ret;
}
TreFile::Object TreFile::openName(std::string const& name) const
std::unique_ptr<TreFile::Object> TreFile::openName(std::string const& name) const
{
return openIdx_(findName_(name));
}
TreFile::Object TreFile::openCRC(uint32_t crc) const
std::unique_ptr<TreFile::Object> TreFile::openCRC(uint32_t crc) const
{
return openIdx_(findCRC_(crc));
}
std::unique_ptr<TreFile::Object> TreFile::getObject(std::string const& spec) const
{
uint64_t crc;
try {
size_t pos;
crc = std::stoul(spec, &pos, 16);
if (pos < spec.size())
crc = std::numeric_limits<uint64_t>::max();
} catch (std::invalid_argument &ex) {
crc = std::numeric_limits<uint64_t>::max();
} catch (std::out_of_range &ex) {
crc = std::numeric_limits<uint64_t>::max();
}
if (crc <= std::numeric_limits<uint32_t>::max()) {
try {
return openCRC(crc);
} catch (Exception &ex) {
}
}
return openName(spec);
}
TreFile::Stat TreFile::statName(std::string const& name) const
{
return statIdx_(findName_(name));
@@ -325,7 +354,41 @@ uint32_t TreFile::calcCRC(std::string const& path)
return sum;
}
std::string TreFile::normalizeName(std::string const& name) const
{
// Is name already a CRC?
if(name.size() == 8) {
uint64_t crc;
try {
size_t pos;
crc = std::stoul(name, &pos, 16);
if (pos < name.size())
crc = std::numeric_limits<uint64_t>::max();
} catch (std::invalid_argument &ex) {
crc = std::numeric_limits<uint64_t>::max();
} catch (std::out_of_range &ex) {
crc = std::numeric_limits<uint64_t>::max();
}
if (crc <= std::numeric_limits<uint32_t>::max()) {
// yes it is a CRC, keep name
return name;
}
}
// Is name a named file?
auto it = table2_.find(name);
if (it == table2_.end()) {
// no, reduce to CRC
char crcStr[9];
snprintf(crcStr, 9, "%.8X", calcCRC(name));
return crcStr;
} else
// yes it is a named file, keep name
return name;
}
size_t TreFile::findName_(std::string const& name) const
{
auto it = table2_.find(name);
@@ -360,37 +423,37 @@ void TreFile::dumpIdx_(std::string const& name, size_t table3Idx) const
if (!outFile)
throw POSIXException{errno, "Could not open " + name};
if (fwrite(obj.data(), obj.size(), 1, outFile.get()) != 1)
if (fwrite(obj->data(), obj->size(), 1, outFile.get()) != 1)
throw POSIXException{errno, "Could not write"};
}
TreFile::Object TreFile::openIdx_(size_t table3Idx) const
std::unique_ptr<TreFile::Object> TreFile::openIdx_(size_t table3Idx) const
{
uint32_t dataPtr, length, clength;
uint8_t flags;
std::tie(dataPtr, length, clength, flags) = table3_[table3Idx];
if ((dataPtr + clength) > length_)
if ((dataPtr + clength) > res_->size())
throw FormatException{"length exceeds file size"};
if (flags&0x80) {
if (flags&0x40) {
auto dec = decompressLZ(base_+dataPtr, clength, length);
auto dec = decompressLZ(res_->data()+dataPtr, clength, length);
#ifndef NDEBUG
if (dec.size() != length)
printf("WARNING: Decompressed size != expected (%lu, %u)\n", dec.size(), length);
#endif
return Object(std::move(dec));
return std::make_unique<Object>(std::move(dec));
} else {
auto dec = decompressLZW(base_+dataPtr, clength, length);
auto dec = decompressLZW(res_->data()+dataPtr, clength, length);
#ifndef NDEBUG
if (dec.size() != length)
printf("WARNING: Decompressed size != expected (%lu, %u)\n", dec.size(), length);
#endif
return Object(std::move(dec));
return std::make_unique<Object>(std::move(dec));
}
} else {
return Object(base_+dataPtr, clength);
return std::make_unique<Object>(res_->data()+dataPtr, clength, res_);
}
}