diff --git a/lib/core.cpp b/lib/core.cpp index 14cf2d0..60aa36d 100644 --- a/lib/core.cpp +++ b/lib/core.cpp @@ -65,7 +65,7 @@ void Core::DeleteChildren() } } -size_t Core::IndexOfChild(Core *chunk) +size_t Core::IndexOfChild(Core *chunk) const { return std::find(children_.begin(), children_.end(), chunk) - children_.begin(); } diff --git a/lib/core.h b/lib/core.h index de11c41..dcf0fb0 100644 --- a/lib/core.h +++ b/lib/core.h @@ -21,11 +21,12 @@ public: bool FindParent(Core *p) const; - LIBWEAVER_EXPORT void AppendChild(Core *Core); - LIBWEAVER_EXPORT bool RemoveChild(Core *Core); - LIBWEAVER_EXPORT size_t IndexOfChild(Core *Core); - LIBWEAVER_EXPORT void InsertChild(size_t index, Core *Core); - LIBWEAVER_EXPORT Core *RemoveChild(size_t index); + void AppendChild(Core *Core); + bool RemoveChild(Core *Core); + void InsertChild(size_t index, Core *Core); + Core *RemoveChild(size_t index); + + LIBWEAVER_EXPORT size_t IndexOfChild(Core *Core) const; LIBWEAVER_EXPORT Core *GetChildAt(size_t index) const { return children_.at(index); } LIBWEAVER_EXPORT size_t GetChildCount() const { return children_.size(); } LIBWEAVER_EXPORT bool HasChildren() const { return !children_.empty(); } diff --git a/lib/interleaf.cpp b/lib/interleaf.cpp index b3cfffb..00dd7b1 100644 --- a/lib/interleaf.cpp +++ b/lib/interleaf.cpp @@ -46,14 +46,139 @@ bool Interleaf::Parse(Chunk *riff) return false; } } else { - //Object *nullobj = new Object(); - //AppendChild(nullobj); + Object *nullobj = new Object(); + AppendChild(nullobj); } } return true; } +struct ChunkStatus { + ChunkStatus() + { + index = 0; + time = 0; + } + + size_t index; + uint32_t time; +}; + +Chunk *Interleaf::Export() const +{ + Chunk *riff = new Chunk(Chunk::TYPE_RIFF); + riff->data("Format") = RIFF::OMNI; + + Chunk *mxhd = new Chunk(Chunk::TYPE_MxHd); + mxhd->data("Version") = version_; + mxhd->data("BufferSize") = buffer_size_; + mxhd->data("BufferCount") = buffer_count_; + riff->AppendChild(mxhd); + + Chunk *mxof = new Chunk(Chunk::TYPE_MxOf); + + // FIXME: This appears to not always be correct, sometimes an MxOf with only one entry will have + // a count of 3, seemingly due to embedded objects (e.g. a movie with an SMK + WAV)? + uint32_t obj_count = this->GetChildCount(); + for (Children::const_iterator it=GetChildren().begin(); it!=GetChildren().end(); it++) { + Object *obj = static_cast(*it); + obj_count += obj->GetChildCount(); + } + mxof->data("Count") = obj_count; + + // This however is correct. + mxof->data("Offsets") = bytearray(this->GetChildCount() * sizeof(uint32_t)); + + riff->AppendChild(mxof); + + Chunk *list = new Chunk(Chunk::TYPE_LIST); + list->data("Format") = Chunk::TYPE_MxSt; + riff->AppendChild(list); + + for (Children::const_iterator it=GetChildren().begin(); it!=GetChildren().end(); it++) { + Chunk *mxst = new Chunk(Chunk::TYPE_MxSt); + list->AppendChild(mxst); + + Object *obj = static_cast(*it); + Chunk *mxob = obj->Export(); + mxst->AppendChild(mxob); + + Chunk *chunklst = new Chunk(Chunk::TYPE_LIST); + chunklst->data("Format") = Chunk::TYPE_MxDa; + mxst->AppendChild(chunklst); + + // First, interleave all headers (first chunk) + for (ssize_t i=-1; iGetChildCount()); i++) { + Object *working_obj = static_cast((i == -1) ? obj : obj->GetChildAt(i)); + if (!working_obj->data().empty()) { + const bytearray &data = working_obj->data().front(); + Chunk *mxch = new Chunk(Chunk::TYPE_MxCh); + mxch->data("Flags") = 0; + mxch->data("Object") = working_obj->id(); + mxch->data("Time") = 0; + mxch->data("DataSize") = data.size(); + mxch->data("Data") = data; + chunklst->AppendChild(mxch); + } + } + + // Next, interleave everything by time + std::vector chunk_status(obj->GetChildCount() + 1); + bool all_done; + do { + all_done = true; + for (ssize_t i=-1; iGetChildCount()); i++) { + Object *working_obj = static_cast((i == -1) ? obj : obj->GetChildAt(i)); + ChunkStatus &status = chunk_status[i+1]; + + if (status.index < working_obj->data().size()) { + const bytearray &data = working_obj->data().at(status.index); + + all_done = false; + + Chunk *mxch = new Chunk(Chunk::TYPE_MxCh); + mxch->data("Flags") = 0; + mxch->data("Object") = working_obj->id(); + mxch->data("Time") = status.time; + mxch->data("DataSize") = data.size(); + mxch->data("Data") = data; + chunklst->AppendChild(mxch); + + status.index++; + + // Increment time + switch (working_obj->filetype()) { + case MxOb::WAV: + status.time += 1000; + break; + case MxOb::STL: + // Unaffected by time + break; + } + } + } + } while (!all_done); + + // Finally interleave end chunks + for (ssize_t i=obj->GetChildCount()-1; i>=-1; i--) { + Object *working_obj = static_cast((i == -1) ? obj : obj->GetChildAt(i)); + ChunkStatus &status = chunk_status[i+1]; + Chunk *mxch = new Chunk(Chunk::TYPE_MxCh); + mxch->data("Flags") = MxCh::FLAG_END; + mxch->data("Object") = working_obj->id(); + mxch->data("Time") = status.time; + mxch->data("DataSize") = 0; + chunklst->AppendChild(mxch); + } + } + + // FIXME: Fill in MxOf table + // FIXME: Split MxCh chunks over alignment + + return riff; +} + bool Interleaf::ParseStream(Chunk *chunk) { if (chunk->type() != Chunk::TYPE_MxSt) { @@ -77,13 +202,32 @@ bool Interleaf::ParseStream(Chunk *chunk) typedef std::map ChunkMap; ChunkMap data; + uint32_t joining_chunk = 0; + for (Children::const_iterator it=list->GetChildren().begin(); it!=list->GetChildren().end(); it++) { Chunk *mxch = static_cast(*it); if (mxch->id() == Chunk::TYPE_pad_) { // Ignore this chunk } else if (mxch->id() == Chunk::TYPE_MxCh) { - uint32_t obj_id = mxch->data("Object"); - data[obj_id].push_back(mxch->data("Data")); + uint16_t flags = mxch->data("Flags"); + if (!(flags & MxCh::FLAG_END)) { + uint32_t obj_id = mxch->data("Object"); + const Data &chunk_data = mxch->data("Data"); + + // For split chunks, join them together + if (joining_chunk > 0) { + data[obj_id].back().append(chunk_data); + if (data[obj_id].back().size() == joining_chunk) { + joining_chunk = 0; + } + } else { + if (flags & MxCh::FLAG_SPLIT) { + joining_chunk = mxch->data("DataSize"); + } + + data[obj_id].push_back(chunk_data); + } + } } } diff --git a/lib/interleaf.h b/lib/interleaf.h index 0e0ba98..0b9ec6a 100644 --- a/lib/interleaf.h +++ b/lib/interleaf.h @@ -12,6 +12,7 @@ public: LIBWEAVER_EXPORT Interleaf(); LIBWEAVER_EXPORT bool Parse(Chunk *riff); + LIBWEAVER_EXPORT Chunk *Export() const; private: bool ParseStream(Chunk *chunk); diff --git a/lib/object.cpp b/lib/object.cpp index 2b5f49e..de584c3 100644 --- a/lib/object.cpp +++ b/lib/object.cpp @@ -1,10 +1,13 @@ #include "object.h" +#include + namespace si { Object::Object() { - + type_ = MxOb::Null; + id_ = 0; } bool Object::Parse(Chunk *chunk) @@ -47,6 +50,46 @@ bool Object::Parse(Chunk *chunk) return true; } +Chunk *Object::Export() const +{ + Chunk *chunk = new Chunk(Chunk::TYPE_MxOb); + + chunk->data("Type") = type_; + chunk->data("Presenter") = presenter_; + chunk->data("Unknown1") = unknown1_; + chunk->data("Name") = name_; + chunk->data("ID") = id_; + chunk->data("Flags") = flags_; + chunk->data("Unknown4") = unknown4_; + chunk->data("Duration") = duration_; + chunk->data("Loops") = loops_; + chunk->data("Position") = position_; + chunk->data("Direction") = direction_; + chunk->data("Up") = up_; + chunk->data("ExtraData") = extra_; + chunk->data("FileName") = filename_; + chunk->data("Unknown26") = unknown26_; + chunk->data("Unknown27") = unknown27_; + chunk->data("Unknown28") = unknown28_; + chunk->data("FileType") = filetype_; + chunk->data("Unknown29") = unknown29_; + chunk->data("Unknown30") = unknown30_; + chunk->data("Unknown31") = unknown31_; + + if (HasChildren()) { + Chunk *list = new Chunk(Chunk::TYPE_LIST); + list->data("Format") = Chunk::TYPE_MxCh; + chunk->AppendChild(list); + + for (Children::const_iterator it=GetChildren().begin(); it!=GetChildren().end(); it++) { + Object *child = static_cast(*it); + list->AppendChild(child->Export()); + } + } + + return chunk; +} + bytearray Object::GetNormalizedData() const { return ToPackedData(filetype(), data_); diff --git a/lib/object.h b/lib/object.h index 6f0fe62..d4bf2c4 100644 --- a/lib/object.h +++ b/lib/object.h @@ -15,6 +15,8 @@ public: Object(); bool Parse(Chunk *chunk); + Chunk *Export() const; + void SetChunkedData(const ChunkedData &cd) { data_ = cd; } LIBWEAVER_EXPORT bytearray GetNormalizedData() const; @@ -27,6 +29,7 @@ public: LIBWEAVER_EXPORT bytearray GetFileBody() const; LIBWEAVER_EXPORT size_t GetFileBodySize() const; + const MxOb::Type &type() const { return type_; } const MxOb::FileType &filetype() const { return filetype_; } const uint32_t &id() const { return id_; } const std::string &name() const { return name_; }