133 lines
3.1 KiB
C++
133 lines
3.1 KiB
C++
#include <cstring>
|
|
|
|
#include "IffFile.hh"
|
|
#include "util.hh"
|
|
#include "common.hh"
|
|
|
|
struct ChunkHeader {
|
|
char typeID[4];
|
|
uint32_t length;
|
|
};
|
|
|
|
IffFile::IffFile(char 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(char 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, char 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(base_);
|
|
else if (state == State::STARTASCII)
|
|
return std::string(base_, length_);
|
|
else
|
|
throw FormatException{"BLOB not string"};
|
|
}
|
|
|
|
IffFile::Form::Form(std::string type, char const* base, size_t length)
|
|
: Object(std::move(type), base, length)
|
|
{
|
|
if (length < 4)
|
|
throw FormatException{"length < subtype id length"};
|
|
|
|
subtype_ = std::string(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;
|
|
}
|
|
}
|
|
|