SIEdit/lib/interleaf.cpp

696 lines
17 KiB
C++
Raw Normal View History

2022-07-11 00:16:20 -04:00
#include "interleaf.h"
#include <cmath>
2022-07-11 04:48:20 -04:00
#include <iostream>
2022-07-18 03:27:00 -04:00
#include <sstream>
2022-07-11 04:48:20 -04:00
#include "object.h"
#include "othertypes.h"
2022-07-17 21:51:16 -04:00
#include "sitypes.h"
#include "util.h"
2022-07-11 04:48:20 -04:00
2022-07-11 00:16:20 -04:00
namespace si {
2022-07-18 03:27:00 -04:00
static const uint32_t kMinimumChunkSize = 8;
2022-07-11 00:16:20 -04:00
Interleaf::Interleaf()
{
}
2022-07-17 21:51:16 -04:00
void Interleaf::Clear()
{
2022-07-18 03:27:00 -04:00
m_Info.clear();
2022-07-17 21:51:16 -04:00
m_BufferSize = 0;
m_JoiningProgress = 0;
m_JoiningSize = 0;
2022-07-18 03:27:00 -04:00
m_ObjectIDTable.clear();
m_ObjectList.clear();
2022-07-17 21:51:16 -04:00
DeleteChildren();
}
2022-07-18 03:27:00 -04:00
Interleaf::Error Interleaf::Read(const char *f)
2022-07-17 21:51:16 -04:00
{
2022-07-18 14:25:00 -04:00
File is;
if (!is.Open(f, File::Read)) {
2022-07-18 03:27:00 -04:00
return ERROR_IO;
2022-07-17 21:51:16 -04:00
}
2022-07-18 14:25:00 -04:00
return Read(&is);
2022-07-17 21:51:16 -04:00
}
2022-07-18 03:27:00 -04:00
Interleaf::Error Interleaf::Write(const char *f) const
2022-07-17 21:51:16 -04:00
{
2022-07-18 14:25:00 -04:00
File os;
if (!os.Open(f, File::Write)) {
2022-07-18 03:27:00 -04:00
return ERROR_IO;
2022-07-17 21:51:16 -04:00
}
2022-07-18 14:25:00 -04:00
return Write(&os);
2022-07-17 21:51:16 -04:00
}
2022-07-18 03:27:00 -04:00
#ifdef _WIN32
Interleaf::Error Interleaf::Read(const wchar_t *f)
2022-07-17 21:51:16 -04:00
{
2022-07-18 14:25:00 -04:00
File is;
if (!is.Open(f, File::Read)) {
2022-07-18 12:49:27 -04:00
return ERROR_IO;
2022-07-17 21:51:16 -04:00
}
2022-07-18 14:25:00 -04:00
return Read(&is);
2022-07-17 21:51:16 -04:00
}
2022-07-18 03:27:00 -04:00
Interleaf::Error Interleaf::Write(const wchar_t *f) const
2022-07-17 21:51:16 -04:00
{
2022-07-18 14:25:00 -04:00
File os;
if (!os.Open(f, File::Write)) {
2022-07-18 12:49:27 -04:00
return ERROR_IO;
2022-07-17 21:51:16 -04:00
}
2022-07-18 14:25:00 -04:00
return Write(&os);
2022-07-17 21:51:16 -04:00
}
2022-07-18 03:27:00 -04:00
#endif
2022-07-17 21:51:16 -04:00
2022-07-18 14:25:00 -04:00
Interleaf::Error Interleaf::ReadChunk(Core *parent, FileBase *f, Info *info)
2022-07-17 21:51:16 -04:00
{
2022-07-18 14:25:00 -04:00
uint32_t offset = f->pos();
uint32_t id = f->ReadU32();
uint32_t size = f->ReadU32();
uint32_t end = uint32_t(f->pos()) + size;
2022-07-17 21:51:16 -04:00
2022-07-18 03:27:00 -04:00
info->SetType(id);
info->SetOffset(offset);
info->SetSize(size);
2022-07-17 21:51:16 -04:00
2022-07-18 03:27:00 -04:00
std::stringstream desc;
switch (static_cast<RIFF::Type>(id)) {
case RIFF::RIFF_:
2022-07-17 21:51:16 -04:00
{
// Require RIFF type to be OMNI
2022-07-18 14:25:00 -04:00
uint32_t riff_type = f->ReadU32();
2022-07-17 21:51:16 -04:00
if (riff_type != RIFF::OMNI) {
2022-07-18 03:27:00 -04:00
return ERROR_INVALID_INPUT;
2022-07-17 21:51:16 -04:00
}
2022-07-18 03:27:00 -04:00
desc << "Type: " << RIFF::PrintU32AsString(riff_type);
2022-07-17 21:51:16 -04:00
break;
}
2022-07-18 03:27:00 -04:00
case RIFF::MxHd:
2022-07-17 21:51:16 -04:00
{
2022-07-18 14:25:00 -04:00
m_Version = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Version: " << m_Version << std::endl;
2022-07-17 21:51:16 -04:00
2022-07-18 14:25:00 -04:00
m_BufferSize = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Buffer Size: 0x" << std::hex << m_BufferSize;
2022-07-17 21:51:16 -04:00
2022-07-18 03:27:00 -04:00
if (m_Version == 0x00020002) {
2022-07-18 14:25:00 -04:00
m_BufferCount = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << std::endl << "Buffer Count: " << std::dec << m_BufferCount << std::endl;
2022-07-17 21:51:16 -04:00
}
break;
}
2022-07-18 03:27:00 -04:00
case RIFF::pad_:
2022-07-18 14:25:00 -04:00
f->seek(size, File::SeekCurrent);
2022-07-17 21:51:16 -04:00
break;
2022-07-18 03:27:00 -04:00
case RIFF::MxOf:
2022-07-17 21:51:16 -04:00
{
2022-07-18 14:25:00 -04:00
uint32_t offset_count = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Count: " << offset_count;
uint32_t real_count = (size - sizeof(uint32_t)) / sizeof(uint32_t);
m_ObjectList.resize(real_count);
for (uint32_t i = 0; i < real_count; i++) {
Object *o = new Object();
parent->AppendChild(o);
2022-07-18 14:25:00 -04:00
uint32_t choffset = f->ReadU32();
2022-07-18 03:27:00 -04:00
m_ObjectList[i] = choffset;
desc << std::endl << i << ": 0x" << std::hex << choffset;
2022-07-17 21:51:16 -04:00
}
break;
}
2022-07-18 03:27:00 -04:00
case RIFF::LIST:
2022-07-17 21:51:16 -04:00
{
2022-07-18 14:25:00 -04:00
uint32_t list_type = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Type: " << RIFF::PrintU32AsString(list_type) << std::endl;
2022-07-17 21:51:16 -04:00
uint32_t list_count = 0;
2022-07-18 03:27:00 -04:00
if (list_type == RIFF::MxCh) {
2022-07-18 14:25:00 -04:00
list_count = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Count: " << list_count << std::endl;
2022-07-17 21:51:16 -04:00
}
break;
}
2022-07-18 03:27:00 -04:00
case RIFF::MxSt:
case RIFF::MxDa:
case RIFF::WAVE:
case RIFF::fmt_:
case RIFF::data:
case RIFF::OMNI:
2022-07-17 21:51:16 -04:00
// Types with no data
break;
2022-07-18 03:27:00 -04:00
case RIFF::MxOb:
2022-07-17 21:51:16 -04:00
{
2022-07-18 03:27:00 -04:00
Object *o = NULL;
for (size_t i=0; i<m_ObjectList.size(); i++) {
if (m_ObjectList[i] == offset-kMinimumChunkSize) {
o = static_cast<Object*>(GetChildAt(i));
break;
}
}
if (!o) {
o = new Object();
parent->AppendChild(o);
}
2022-07-18 14:25:00 -04:00
ReadObject(f, o, desc);
2022-07-18 03:27:00 -04:00
info->SetObjectID(o->id());
m_ObjectIDTable[o->id()] = o;
2022-07-17 21:51:16 -04:00
parent = o;
break;
}
2022-07-18 03:27:00 -04:00
case RIFF::MxCh:
2022-07-17 21:51:16 -04:00
{
2022-07-18 14:25:00 -04:00
uint16_t flags = f->ReadU16();
2022-07-18 03:27:00 -04:00
desc << "Flags: 0x" << std::hex << flags << std::endl;
2022-07-18 14:25:00 -04:00
uint32_t object = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Object: " << std::dec << object << std::endl;
2022-07-18 14:25:00 -04:00
uint32_t time = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Time: " << time << std::endl;
2022-07-18 14:25:00 -04:00
uint32_t data_sz = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Size: " << data_sz << std::endl;
2022-07-18 14:25:00 -04:00
bytearray data = f->ReadBytes(size - MxCh::HEADER_SIZE);
2022-07-17 21:51:16 -04:00
2022-07-18 03:27:00 -04:00
info->SetObjectID(object);
info->SetData(data);
if (!(flags & MxCh::FLAG_END)) {
Object *o = m_ObjectIDTable.at(object);
if (!o) {
return ERROR_INVALID_INPUT;
}
if (flags & MxCh::FLAG_SPLIT && m_JoiningSize > 0) {
2022-07-18 03:27:00 -04:00
o->data_.back().append(data);
m_JoiningProgress += data.size();
if (m_JoiningProgress == m_JoiningSize) {
m_JoiningProgress = 0;
m_JoiningSize = 0;
}
2022-07-18 03:27:00 -04:00
} else {
o->data_.push_back(data);
2022-07-21 20:06:52 -04:00
if (o->data_.size() == 2) {
o->time_offset_ = time;
}
if (flags & MxCh::FLAG_SPLIT) {
m_JoiningProgress = data.size();
m_JoiningSize = data_sz;
}
2022-07-18 03:27:00 -04:00
}
break;
2022-07-17 21:51:16 -04:00
}
}
}
// Assume any remaining data is this chunk's children
2022-07-18 14:25:00 -04:00
while (!f->atEnd() && (f->pos() + kMinimumChunkSize) < end) {
2022-07-17 21:51:16 -04:00
// Check alignment, if there's not enough room to for another segment, skip ahead
if (m_BufferSize > 0) {
2022-07-18 14:25:00 -04:00
uint32_t offset_in_buffer = f->pos()%m_BufferSize;
2022-07-18 03:27:00 -04:00
if (offset_in_buffer + kMinimumChunkSize > m_BufferSize) {
2022-07-18 14:25:00 -04:00
f->seek(m_BufferSize-offset_in_buffer, File::SeekCurrent);
2022-07-17 21:51:16 -04:00
}
}
// Read next child
2022-07-18 03:27:00 -04:00
Info *subinfo = new Info();
info->AppendChild(subinfo);
2022-07-18 14:25:00 -04:00
Error e = ReadChunk(parent, f, subinfo);
2022-07-18 03:27:00 -04:00
if (e != ERROR_SUCCESS) {
return e;
2022-07-17 21:51:16 -04:00
}
}
2022-07-18 03:27:00 -04:00
info->SetDescription(desc.str());
2022-07-18 14:25:00 -04:00
if (f->pos() < end) {
f->seek(end, File::SeekStart);
2022-07-17 21:51:16 -04:00
}
if (size%2 == 1) {
2022-07-18 14:25:00 -04:00
f->seek(1, File::SeekCurrent);
2022-07-17 21:51:16 -04:00
}
2022-07-18 03:27:00 -04:00
return ERROR_SUCCESS;
2022-07-17 21:51:16 -04:00
}
2022-07-18 14:25:00 -04:00
Object *Interleaf::ReadObject(FileBase *f, Object *o, std::stringstream &desc)
2022-07-17 21:51:16 -04:00
{
2022-07-18 14:25:00 -04:00
o->type_ = static_cast<MxOb::Type>(f->ReadU16());
2022-07-18 03:27:00 -04:00
desc << "Type: " << o->type_ << std::endl;
2022-07-18 14:25:00 -04:00
o->presenter_ = f->ReadString();
2022-07-18 03:27:00 -04:00
desc << "Presenter: " << o->presenter_ << std::endl;
2022-07-18 14:25:00 -04:00
o->unknown1_ = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Unknown1: " << o->unknown1_ << std::endl;
2022-07-18 14:25:00 -04:00
o->name_ = f->ReadString();
2022-07-18 03:27:00 -04:00
desc << "Name: " << o->name_ << std::endl;
2022-07-18 14:25:00 -04:00
o->id_ = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "ID: " << o->id_ << std::endl;
2022-07-18 14:25:00 -04:00
o->flags_ = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Flags: " << o->flags_ << std::endl;
2022-07-18 14:25:00 -04:00
o->unknown4_ = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Unknown4: " << o->unknown4_ << std::endl;
2022-07-18 14:25:00 -04:00
o->duration_ = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Duration: " << o->duration_ << std::endl;
2022-07-18 14:25:00 -04:00
o->loops_ = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Loops: " << o->loops_ << std::endl;
o->location_ = f->ReadVector3();
desc << "Location: " << o->location_.x << " " << o->location_.y << " " << o->location_.z << std::endl;
2022-07-18 14:25:00 -04:00
o->direction_ = f->ReadVector3();
2022-07-18 03:27:00 -04:00
desc << "Direction: " << o->direction_.x << " " << o->direction_.y << " " << o->direction_.z << std::endl;
2022-07-18 14:25:00 -04:00
o->up_ = f->ReadVector3();
2022-07-18 03:27:00 -04:00
desc << "Up: " << o->up_.x << " " << o->up_.y << " " << o->up_.z << std::endl;
2022-07-17 21:51:16 -04:00
2022-07-18 14:25:00 -04:00
uint16_t extra_sz = f->ReadU16();
2022-07-18 03:27:00 -04:00
desc << "Extra Size: " << extra_sz << std::endl;
2022-07-18 14:25:00 -04:00
o->extra_ = f->ReadBytes(extra_sz);
2022-07-17 21:51:16 -04:00
if (o->type_ != MxOb::Presenter && o->type_ != MxOb::World) {
2022-07-18 14:25:00 -04:00
o->filename_ = f->ReadString();
2022-07-18 03:27:00 -04:00
desc << "Filename: " << o->filename_ << std::endl;
2022-07-18 14:25:00 -04:00
o->unknown26_ = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Unknown26: " << o->unknown26_ << std::endl;
2022-07-18 14:25:00 -04:00
o->unknown27_ = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Unknown27: " << o->unknown27_ << std::endl;
2022-07-18 14:25:00 -04:00
o->unknown28_ = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Unknown28: " << o->unknown28_ << std::endl;
2022-07-18 14:25:00 -04:00
o->filetype_ = static_cast<MxOb::FileType>(f->ReadU32());
2022-07-18 03:27:00 -04:00
desc << "File Type: " << RIFF::PrintU32AsString(o->filetype_) << std::endl;
2022-07-18 14:25:00 -04:00
o->unknown29_ = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Unknown29: " << o->unknown29_ << std::endl;
2022-07-18 14:25:00 -04:00
o->unknown30_ = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Unknown30: " << o->unknown30_ << std::endl;
2022-07-17 21:51:16 -04:00
if (o->filetype_ == MxOb::WAV) {
2022-07-18 14:25:00 -04:00
o->unknown31_ = f->ReadU32();
2022-07-18 03:27:00 -04:00
desc << "Unknown31: " << o->unknown31_ << std::endl;
2022-07-17 21:51:16 -04:00
}
}
return o;
}
2022-07-18 14:25:00 -04:00
Interleaf::Error Interleaf::Read(FileBase *f)
2022-07-17 21:51:16 -04:00
{
Clear();
2022-07-18 14:25:00 -04:00
return ReadChunk(this, f, &m_Info);
2022-07-17 21:51:16 -04:00
}
2022-07-28 21:22:13 -04:00
void RecursivelyAddObjectToList(std::vector<Object*> *list, Object *o)
{
list->push_back(o);
for (size_t j=0; j<o->GetChildCount(); j++) {
RecursivelyAddObjectToList(list, static_cast<Object*>(o->GetChildAt(j)));
}
}
2022-07-18 14:25:00 -04:00
Interleaf::Error Interleaf::Write(FileBase *f) const
2022-07-11 00:16:20 -04:00
{
2022-07-18 03:27:00 -04:00
if (m_BufferSize == 0) {
LogError() << "Buffer size must be set to write" << std::endl;
return ERROR_INVALID_BUFFER_SIZE;
2022-07-11 00:16:20 -04:00
}
2022-07-18 14:25:00 -04:00
RIFF::Chk riff = RIFF::BeginChunk(f, RIFF::RIFF_);
f->WriteU32(RIFF::OMNI);
2022-07-11 00:16:20 -04:00
2022-07-18 14:25:00 -04:00
size_t offset_table_pos;
2022-07-11 00:16:20 -04:00
2022-07-18 03:27:00 -04:00
{
// MxHd
2022-07-18 14:25:00 -04:00
RIFF::Chk mxhd = RIFF::BeginChunk(f, RIFF::MxHd);
2022-07-11 00:16:20 -04:00
2022-07-18 14:25:00 -04:00
f->WriteU32(m_Version);
f->WriteU32(m_BufferSize);
2022-07-11 00:16:20 -04:00
2022-07-18 03:27:00 -04:00
if (m_Version == 0x00020002) {
2022-07-18 14:25:00 -04:00
f->WriteU32(m_BufferCount);
2022-07-11 04:48:20 -04:00
}
2022-07-18 03:27:00 -04:00
2022-07-18 14:25:00 -04:00
RIFF::EndChunk(f, mxhd);
2022-07-11 04:48:20 -04:00
}
2022-07-18 03:27:00 -04:00
{
// MxOf
2022-07-18 14:25:00 -04:00
RIFF::Chk mxof = RIFF::BeginChunk(f, RIFF::MxOf);
2022-07-11 04:48:20 -04:00
2022-07-18 14:25:00 -04:00
f->WriteU32(GetChildCount());
2022-07-18 03:27:00 -04:00
2022-07-18 14:25:00 -04:00
offset_table_pos = f->pos();
2022-07-18 03:27:00 -04:00
for (size_t i = 0; i < GetChildCount(); i++) {
2022-07-18 14:25:00 -04:00
f->WriteU32(0);
2022-07-18 03:27:00 -04:00
}
2022-07-18 14:25:00 -04:00
RIFF::EndChunk(f, mxof);
2022-07-11 17:19:36 -04:00
}
2022-07-18 03:27:00 -04:00
{
// LIST
2022-07-18 14:25:00 -04:00
RIFF::Chk list_mxst = RIFF::BeginChunk(f, RIFF::LIST);
2022-07-11 17:19:36 -04:00
2022-07-18 14:25:00 -04:00
f->WriteU32(RIFF::MxSt);
2022-07-11 17:19:36 -04:00
2022-07-18 03:27:00 -04:00
for (size_t i = 0; i < GetChildCount(); i++) {
Object *child = static_cast<Object*>(GetChildAt(i));
2022-07-28 21:22:13 -04:00
if (child->type() == MxOb::Null) {
continue;
}
2022-07-11 17:19:36 -04:00
2022-07-18 14:25:00 -04:00
uint32_t mxst_offset = f->pos();
2022-07-11 17:19:36 -04:00
2022-07-18 14:25:00 -04:00
f->seek(size_t(offset_table_pos) + i * sizeof(uint32_t));
f->WriteU32(mxst_offset);
f->seek(mxst_offset);
2022-07-11 17:19:36 -04:00
2022-07-18 03:27:00 -04:00
// MxSt
2022-07-18 14:25:00 -04:00
RIFF::Chk mxst = RIFF::BeginChunk(f, RIFF::MxSt);
2022-07-11 17:19:36 -04:00
2022-07-18 03:27:00 -04:00
{
// MxOb
2022-07-18 14:25:00 -04:00
WriteObject(f, child);
2022-07-18 03:27:00 -04:00
}
2022-07-11 04:48:20 -04:00
2022-07-18 03:27:00 -04:00
{
// LIST
2022-07-18 14:25:00 -04:00
RIFF::Chk list_mxda = RIFF::BeginChunk(f, RIFF::LIST);
2022-07-11 04:48:20 -04:00
2022-07-18 14:25:00 -04:00
f->WriteU32(RIFF::MxDa);
2022-07-11 04:48:20 -04:00
2022-07-18 03:27:00 -04:00
// First, interleave headers
std::vector<Object*> objects;
objects.reserve(child->GetChildCount() + 1);
2022-07-28 21:22:13 -04:00
RecursivelyAddObjectToList(&objects, child);
2022-07-18 03:27:00 -04:00
2022-07-18 14:25:00 -04:00
InterleaveObjects(f, objects);
2022-07-18 03:27:00 -04:00
2022-07-18 14:25:00 -04:00
RIFF::EndChunk(f, list_mxda);
2022-07-11 04:48:20 -04:00
}
2022-07-18 03:27:00 -04:00
2022-07-18 14:25:00 -04:00
RIFF::EndChunk(f, mxst);
2022-07-11 04:48:20 -04:00
}
2022-07-18 03:27:00 -04:00
// Fill remainder with padding
2022-07-18 14:25:00 -04:00
if (f->pos()%m_BufferSize != 0) {
uint32_t current_buf = f->pos() / m_BufferSize;
2022-07-18 03:27:00 -04:00
uint32_t target_sz = (current_buf + 1) * m_BufferSize;
2022-07-18 14:25:00 -04:00
WritePadding(f, target_sz - f->pos());
2022-07-11 00:16:20 -04:00
}
2022-07-18 03:27:00 -04:00
2022-07-18 14:25:00 -04:00
RIFF::EndChunk(f, list_mxst);
2022-07-11 00:16:20 -04:00
}
2022-07-18 14:25:00 -04:00
RIFF::EndChunk(f, riff);
2022-07-18 03:27:00 -04:00
return ERROR_SUCCESS;
2022-07-11 00:16:20 -04:00
}
2022-07-18 14:25:00 -04:00
void Interleaf::WriteObject(FileBase *f, const Object *o) const
{
2022-07-18 14:25:00 -04:00
RIFF::Chk mxob = RIFF::BeginChunk(f, RIFF::MxOb);
f->WriteU16(o->type_);
f->WriteString(o->presenter_);
f->WriteU32(o->unknown1_);
f->WriteString(o->name_);
f->WriteU32(o->id_);
f->WriteU32(o->flags_);
f->WriteU32(o->unknown4_);
f->WriteU32(o->duration_);
f->WriteU32(o->loops_);
f->WriteVector3(o->location_);
2022-07-18 14:25:00 -04:00
f->WriteVector3(o->direction_);
f->WriteVector3(o->up_);
f->WriteU16(o->extra_.size());
f->WriteBytes(o->extra_);
2022-07-18 03:27:00 -04:00
if (o->type_ != MxOb::Presenter && o->type_ != MxOb::World) {
2022-07-18 14:25:00 -04:00
f->WriteString(o->filename_);
f->WriteU32(o->unknown26_);
f->WriteU32(o->unknown27_);
f->WriteU32(o->unknown28_);
f->WriteU32(o->filetype_);
f->WriteU32(o->unknown29_);
f->WriteU32(o->unknown30_);
2022-07-18 03:27:00 -04:00
if (o->filetype_ == MxOb::WAV) {
2022-07-18 14:25:00 -04:00
f->WriteU32(o->unknown31_);
2022-07-18 03:27:00 -04:00
}
}
if (o->HasChildren()) {
// Child list
2022-07-18 14:25:00 -04:00
RIFF::Chk list_mxch = RIFF::BeginChunk(f, RIFF::LIST);
2022-07-18 03:27:00 -04:00
2022-07-18 14:25:00 -04:00
f->WriteU32(RIFF::MxCh);
f->WriteU32(o->GetChildCount());
2022-07-18 03:27:00 -04:00
for (size_t i = 0; i < o->GetChildCount(); i++) {
2022-07-18 14:25:00 -04:00
WriteObject(f, static_cast<Object*>(o->GetChildAt(i)));
2022-07-18 03:27:00 -04:00
}
2022-07-18 14:25:00 -04:00
RIFF::EndChunk(f, list_mxch);
}
2022-07-18 14:25:00 -04:00
RIFF::EndChunk(f, mxob);
2022-07-18 03:27:00 -04:00
}
struct ChunkStatus
{
Object *object;
size_t index;
uint32_t time;
};
bool HasChildrenThatNeedPriority(Object *parent, uint32_t parent_time, const std::vector<ChunkStatus> &other_jobs)
{
for (size_t i=0; i<other_jobs.size(); i++) {
2022-07-28 21:22:13 -04:00
Object *other_obj = other_jobs.at(i).object;
if (parent->ContainsChild(other_obj) && other_jobs.at(i).time <= parent_time) {
return true;
}
}
return false;
}
2022-07-18 14:25:00 -04:00
void Interleaf::InterleaveObjects(FileBase *f, const std::vector<Object *> &objects) const
{
2022-07-18 03:27:00 -04:00
std::vector<ChunkStatus> status(objects.size());
// Set up status vector
for (size_t i=0; i<objects.size(); i++) {
status[i].object = objects.at(i);
status[i].index = 0;
2022-07-21 20:06:52 -04:00
status[i].time = status[i].object->time_offset_;
}
2022-07-18 03:27:00 -04:00
// First, interleave headers
for (size_t i=0; i<status.size(); i++) {
ChunkStatus &s = status[i];
Object *o = s.object;
if (!o->data().empty()) {
2022-07-18 14:25:00 -04:00
WriteSubChunk(f, 0, o->id(), 0xFFFFFFFF, o->data().front());
2022-07-18 03:27:00 -04:00
s.index++;
}
}
2022-07-18 03:27:00 -04:00
// Next, interleave the rest based on time
while (true) {
// Find next chunk
std::vector<ChunkStatus>::iterator s = status.begin();
if (s == status.end()) {
break;
}
while (HasChildrenThatNeedPriority(s->object, s->time, status)) {
s++;
}
if (s == status.end()) {
break;
}
std::vector<ChunkStatus>::iterator it = s;
it++;
for (; it!=status.end(); it++) {
// Find earliest chunk to write
if (it->time < s->time && !HasChildrenThatNeedPriority(it->object, it->time, status)) {
s = it;
}
}
if (s->index == s->object->data_.size()) {
2022-07-18 14:25:00 -04:00
WriteSubChunk(f, MxCh::FLAG_END, s->object->id(), s->time);
status.erase(s);
continue;
}
2022-07-18 03:27:00 -04:00
Object *obj = s->object;
const bytearray &data = obj->data().at(s->index);
2022-07-18 14:25:00 -04:00
WriteSubChunk(f, 0, obj->id(), s->time, data);
2022-07-18 03:27:00 -04:00
s->index++;
// Increment time
2022-07-18 03:27:00 -04:00
switch (obj->filetype()) {
case MxOb::WAV:
{
2022-07-18 03:27:00 -04:00
const WAVFmt *fmt = obj->GetFileHeader().cast<WAVFmt>();
s->time += round(double(data.size() * 1000) / (fmt->BitsPerSample/8) / fmt->Channels / fmt->SampleRate);
break;
}
case MxOb::SMK:
{
2022-07-18 03:27:00 -04:00
int32_t frame_rate = obj->GetFileHeader().cast<SMK2>()->FrameRate;
int32_t fps;
if (frame_rate > 0) {
fps = 1000/frame_rate;
} else if (frame_rate < 0) {
fps = 100000/-frame_rate;
} else {
fps = 10;
}
2022-07-18 03:27:00 -04:00
s->time += 1000/fps;
break;
}
case MxOb::FLC:
2022-07-18 03:27:00 -04:00
s->time += obj->GetFileHeader().cast<FLIC>()->speed;
break;
case MxOb::STL:
case MxOb::OBJ:
// Unaffected by time
break;
}
2022-07-18 03:27:00 -04:00
// Update parent time too
2022-07-28 21:22:13 -04:00
bool done = false;
while (!done) {
done = true;
for (size_t i=0; i<status.size(); i++) {
ChunkStatus &p = status.at(i);
if (p.object != obj) {
if (p.object->ContainsChild(obj)) {
if (p.time < s->time) {
p.time = s->time;
done = false;
}
}
}
}
}
2022-07-18 03:27:00 -04:00
}
}
2022-07-18 14:25:00 -04:00
void Interleaf::WriteSubChunk(FileBase *f, uint16_t flags, uint32_t object, uint32_t time, const bytearray &data) const
2022-07-18 03:27:00 -04:00
{
static const uint32_t total_hdr = MxCh::HEADER_SIZE + kMinimumChunkSize;
uint32_t data_offset = 0;
while (data_offset < data.size() || data.size() == 0) {
uint32_t data_sz = data.size() - data_offset;
2022-07-18 03:27:00 -04:00
// Calculate whether this chunk will overrun the buffer
2022-07-18 14:25:00 -04:00
uint32_t start_buffer = f->pos() / m_BufferSize;
uint32_t stop_buffer = (uint32_t(f->pos()) - 1 + data_sz + total_hdr) / m_BufferSize;
size_t max_chunk = data_sz;
if (start_buffer != stop_buffer) {
2022-07-18 14:25:00 -04:00
size_t remaining = ((start_buffer + 1) * m_BufferSize) - f->pos();
if (remaining < total_hdr) {
if (remaining < kMinimumChunkSize) {
// There isn't enough space for another chunk, just jump ahead
f->seek(remaining, File::SeekCurrent);
} else {
// This chunk won't fit in our buffer alignment. We must make a decision to either insert
// padding or split the clip.
WritePadding(f, remaining);
}
continue;
}
max_chunk = remaining - total_hdr;
if (!(flags & MxCh::FLAG_SPLIT)) {
// FIXME: Not sure exactly what this value is yet, likely to be smaller than this
static const uint32_t MAX_PADDING = 9882;
if (remaining < MAX_PADDING) {
// This chunk won't fit in our buffer alignment. We must make a decision to either insert
// padding or split the clip.
2022-07-18 14:25:00 -04:00
WritePadding(f, remaining);
// Do loop over again
continue;
} else {
flags |= MxCh::FLAG_SPLIT;
}
}
}
bytearray chunk = data.mid(data_offset, max_chunk);
2022-07-18 14:25:00 -04:00
WriteSubChunkInternal(f, flags, object, time, data_sz, chunk);
data_offset += chunk.size();
if (data.size() == 0) {
break;
}
}
}
2022-07-18 14:25:00 -04:00
void Interleaf::WriteSubChunkInternal(FileBase *f, uint16_t flags, uint32_t object, uint32_t time, uint32_t data_sz, const bytearray &data) const
{
2022-07-18 14:25:00 -04:00
RIFF::Chk mxch = RIFF::BeginChunk(f, RIFF::MxCh);
2022-07-18 03:27:00 -04:00
2022-07-18 14:25:00 -04:00
f->WriteU16(flags);
f->WriteU32(object);
f->WriteU32(time);
f->WriteU32(data_sz);
f->WriteBytes(data);
2022-07-18 03:27:00 -04:00
2022-07-18 14:25:00 -04:00
RIFF::EndChunk(f, mxch);
}
2022-07-18 14:25:00 -04:00
void Interleaf::WritePadding(FileBase *f, uint32_t size) const
{
2022-07-18 03:27:00 -04:00
if (size < kMinimumChunkSize) {
return;
}
size -= kMinimumChunkSize;
2022-07-18 14:25:00 -04:00
f->WriteU32(RIFF::pad_);
f->WriteU32(size);
2022-07-18 03:27:00 -04:00
bytearray b(size);
b.fill(0xCD);
2022-07-18 14:25:00 -04:00
f->WriteBytes(b);
2022-07-18 03:27:00 -04:00
}
2022-07-11 00:16:20 -04:00
}