#include #include "IffFile.hh" #include "util.hh" #include "common.hh" struct ChunkHeader { char typeID[4]; uint32_t length; }; IffFile::IffFile(Resource const& res) : res_(res) { size_t pos = 0; while (pos < res_.size()) { roots_.push_back(parseObject(res_.data()+pos, res_.size()-pos)); pos += roots_.back()->size() + 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.size(), obj.size()); if (obj.isForm()) { auto& form = dynamic_cast(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(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::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", base+8, static_cast(header.length)); else return std::make_unique(std::string(header.typeID, 4), base+8, static_cast(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(base_)); else if (state == State::STARTASCII) return std::string(reinterpret_cast(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 ((i > 0) || (base[i] == '\0')) break; if (!isprint(base[i])) throw FormatException{"Subtype not printable"}; } subtype_ = std::string(reinterpret_cast(base), 4); size_t pos = 4; while (pos+8 < length) { children_.push_back(parseObject(base+pos, length-pos)); pos += 8 + children_.back()->size(); if (pos%2 != 0) ++pos; } }