Files
wc3re/IffFile.cc

136 lines
3.3 KiB
C++

#include <cstring>
#include "IffFile.hh"
#include "util.hh"
#include "common.hh"
struct ChunkHeader {
char typeID[4];
uint32_t length;
};
IffFile::IffFile(uint8_t const* base, size_t length)
: base_(base), length_(length)
{
size_t pos = 0;
while (pos < length) {
roots_.push_back(parseObject(base+pos, length-pos));
pos += roots_.back()->getSize() + 8;
}
}
IffFile::~IffFile()
{
}
static void _printStructure(IffFile::Object const& obj, unsigned level)
{
for (unsigned i = 0;i < level;++i)
putchar('\t');
printf("%s Length %lu (0x%.lx)", obj.getType().c_str(), obj.getSize(), obj.getSize());
if (obj.isForm()) {
auto& form = dynamic_cast<IffFile::Form const&>(obj);
printf(", Subtype %s\n", form.getSubtype().c_str());
for(auto it = form.childrenBegin();it != form.childrenEnd();++it)
_printStructure(*it, level+1);
} else {
try {
printf(" = \"%s\"\n", static_cast<std::string>(obj).c_str());
} catch(FormatException &ex) {
printf("\n");
}
}
}
void IffFile::printStructure(unsigned level) const
{
for (auto& root : *this)
_printStructure(root, level);
}
std::unique_ptr<IffFile::Object> IffFile::parseObject(uint8_t const* base, size_t length)
{
if (length < 8)
throw FormatException{"length < header size"};
ChunkHeader header;
memcpy(&header.typeID, base, 4);
header.length = readU32BE(base+4);
for (unsigned i = 0;i < 4;++i)
if (!isprint(header.typeID[i]))
throw FormatException{"Not an IFF chunk"};
if (header.length > length-8)
throw FormatException{"length < size in header"};
if(memcmp(header.typeID, "FORM", 4) == 0)
return std::make_unique<Form>("FORM", base+8, static_cast<size_t>(header.length));
else
return std::make_unique<Object>(std::string(header.typeID, 4), base+8, static_cast<size_t>(header.length));
}
IffFile::Object::Object(std::string type, uint8_t const* base, size_t length)
: base_(base), length_(length), type_(std::move(type))
{
}
IffFile::Object::operator std::string() const
{
// Check if BLOB is string
enum class State { STARTASCII, NULLS, ERROR };
State state = State::STARTASCII;
for (char const& c : *this) {
switch(state) {
case State::STARTASCII:
if (isprint(c))
continue;
if (c == '\0')
state = State::NULLS;
else
state = State::ERROR;
break;
case State::NULLS:
if (c != '\0')
state = State::ERROR;
break;
case State::ERROR:
break;
}
if (state == State::ERROR)
break;
}
if (state == State::NULLS)
return std::string(reinterpret_cast<char const*>(base_));
else if (state == State::STARTASCII)
return std::string(reinterpret_cast<char const*>(base_), length_);
else
throw FormatException{"BLOB not string"};
}
IffFile::Form::Form(std::string type, uint8_t const* base, size_t length)
: Object(std::move(type), base, length)
{
if (length < 4)
throw FormatException{"length < subtype id length"};
for (unsigned i = 0;i < 4;++i)
if (!isprint(base[i]))
throw FormatException{"Subtype not printable"};
subtype_ = std::string(reinterpret_cast<char const*>(base), 4);
size_t pos = 4;
while (pos+8 < length) {
children_.push_back(parseObject(base+pos, length-pos));
pos += 8 + children_.back()->getSize();
if (pos%2 != 0)
++pos;
}
}