lib: process data streams

This commit is contained in:
itsmattkc 2022-07-11 01:48:20 -07:00
parent 09384dbe46
commit 4465f26c38
11 changed files with 207 additions and 79 deletions

View file

@ -12,8 +12,6 @@ set(LIBWEAVER_SOURCES
object.h
sitypes.cpp
sitypes.h
stream.cpp
stream.h
types.h
)

View file

@ -27,6 +27,7 @@ public:
LIBWEAVER_EXPORT Core *RemoveChild(size_t index);
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(); }
protected:
void DeleteChildren();

View file

@ -1,5 +1,9 @@
#include "interleaf.h"
#include <iostream>
#include "object.h"
namespace si {
Interleaf::Interleaf()
@ -31,16 +35,66 @@ bool Interleaf::Parse(Chunk *riff)
return false;
}
const Data &offset_table = of->data("Offsets");
size_t offset_count = offset_table.size() / sizeof(uint32_t);
const Data &offset_data = of->data("Offsets");
const uint32_t *offset_table = reinterpret_cast<const uint32_t *>(offset_data.data());
size_t offset_count = offset_data.size() / sizeof(uint32_t);
DeleteChildren();
for (size_t i=0; i<offset_count; i++) {
Stream *o = new Stream();
if (offset_table[i]) {
Chunk *st = riff->FindChildWithOffset(offset_table[i]);
if (!o->Parse(st)) {
if (!ParseStream(st)) {
return false;
}
AppendChild(o);
} else {
//Object *nullobj = new Object();
//AppendChild(nullobj);
}
}
return true;
}
bool Interleaf::ParseStream(Chunk *chunk)
{
if (chunk->type() != Chunk::TYPE_MxSt) {
return false;
}
Chunk *obj_chunk = static_cast<Chunk*>(chunk->GetChildAt(0));
if (!obj_chunk) {
return false;
}
Object *obj = new Object();
if (!obj->Parse(obj_chunk)) {
return false;
}
AppendChild(obj);
Chunk *list = static_cast<Chunk*>(chunk->GetChildAt(1));
if (list) {
using ChunkMap = std::map<uint32_t, std::vector<bytearray> >;
ChunkMap data;
for (Core *chunk : list->GetChildren()) {
Chunk *mxch = static_cast<Chunk*>(chunk);
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"));
}
}
for (ChunkMap::const_iterator it=data.begin(); it!=data.end(); it++) {
Object *o = obj->FindSubObjectWithID(it->first);
if (o) {
o->ProcessData(it->second);
} else {
std::cout << "Failed to find object with ID " << it->first << std::endl;
}
}
}
return true;

View file

@ -3,7 +3,6 @@
#include "chunk.h"
#include "core.h"
#include "stream.h"
namespace si {
@ -15,6 +14,8 @@ public:
LIBWEAVER_EXPORT bool Parse(Chunk *riff);
private:
bool ParseStream(Chunk *chunk);
uint32_t version_;
uint32_t buffer_size_;
uint32_t buffer_count_;

View file

@ -26,12 +26,93 @@ bool Object::Parse(Chunk *chunk)
unknown26_ = chunk->data("Unknown26");
unknown27_ = chunk->data("Unknown27");
unknown28_ = chunk->data("Unknown28");
filetype_ = chunk->data("FileType");
filetype_ = static_cast<MxOb::FileType>(chunk->data("FileType").toU32());
unknown29_ = chunk->data("Unknown29");
unknown30_ = chunk->data("Unknown30");
unknown31_ = chunk->data("Unknown31");
if (chunk->HasChildren()) {
Chunk *child = static_cast<Chunk*>(chunk->GetChildAt(0));
if (child->id() == Chunk::TYPE_LIST) {
for (Core *entry : child->GetChildren()) {
Object *o = new Object();
if (!o->Parse(static_cast<Chunk*>(entry))) {
return false;
}
AppendChild(o);
}
}
}
return true;
}
void Object::ProcessData(const std::vector<bytearray> &chunks)
{
switch (filetype_) {
case MxOb::WAV:
{
// Make space for WAVE header
data_.resize(0x2C);
// Merge all chunks after the first one
for (size_t i=1; i<chunks.size(); i++) {
data_.append(chunks[i]);
}
// Copy boilerplate bytes for header
uint32_t *header = reinterpret_cast<uint32_t *>(data_.data());
header[0] = Chunk::TYPE_RIFF; // "RIFF"
header[1] = data_.size() - 8; // Size of total file
header[2] = 0x45564157; // "WAVE"
header[3] = 0x20746D66; // "fmt "
header[4] = 16; // Size of fmt chunk
header[9] = 0x61746164; // "data"
header[10] = data_.size() - 0x2C; // Size of data chunk
// Copy fmt header from chunk 1
memcpy(&header[5], chunks[0].data(), 16);
break;
}
case MxOb::STL:
{
// Make space for BMP header
data_.resize(14);
// Merge all chunks after the first one
for (size_t i=0; i<chunks.size(); i++) {
data_.append(chunks[i]);
}
// Set BM identifier
*(uint16_t *)(data_.data()) = 0x4D42;
// Set file size
*(uint32_t*)(data_.data()+2) = data_.size();
// Set reserved bytes
*(uint32_t*)(data_.data()+6) = 0;
// Set offset
*(uint32_t*)(data_.data()+10) = chunks.at(0).size() + 14;
break;
}
}
}
Object *Object::FindSubObjectWithID(uint32_t id)
{
if (this->id() == id) {
return this;
}
for (Core *child : GetChildren()) {
if (Object *o = static_cast<Object*>(child)->FindSubObjectWithID(id)) {
return o;
}
}
return NULL;
}
}

View file

@ -13,11 +13,15 @@ public:
Object();
bool Parse(Chunk *chunk);
void ProcessData(const std::vector<bytearray> &chunks);
bytearray &data()
{
return data_;
}
const MxOb::FileType &filetype() const { return filetype_; }
const uint32_t &id() const { return id_; }
const std::string &name() const { return name_; }
const std::string &filename() const { return filename_; }
bytearray &data() { return data_; }
Object *FindSubObjectWithID(uint32_t id);
private:
MxOb::Type type_;
@ -37,7 +41,7 @@ private:
uint32_t unknown26_;
uint32_t unknown27_;
uint32_t unknown28_;
uint32_t filetype_;
MxOb::FileType filetype_;
uint32_t unknown29_;
uint32_t unknown30_;
uint32_t unknown31_;

View file

@ -111,9 +111,9 @@ void pad_::Read(std::ifstream &is, DataMap &data, uint32_t version, uint32_t siz
const char *MxOb::GetTypeName(Type type)
{
switch (type) {
case SMK:
case Video:
return "SMK";
case WAV:
case Sound:
return "WAV";
case Presenter:
return "MxPresenter";
@ -193,7 +193,7 @@ void MxOb::Read(std::ifstream &is, DataMap &data, uint32_t version, uint32_t siz
data["Unknown29"] = ReadU32(is);
data["Unknown30"] = ReadU32(is);
if (obj_type == MxOb::WAV) {
if (obj_type == MxOb::Sound) {
data["Unknown31"] = ReadU32(is);
}
}

View file

@ -139,11 +139,11 @@ class MxOb : public RIFF
public:
enum Type
{
/// Smacker video
SMK = 0x03,
/// Video
Video = 0x03,
/// WAVE audio
WAV = 0x04,
/// Audio
Sound = 0x04,
/// World object for LegoWorldPresenter
World = 0x06,
@ -185,7 +185,16 @@ public:
Unknown = 0x20,
/// Total number of flags (not a real type)
FLAGS_COUNT
FLAGS_COUNT,
};
enum FileType
{
/// WAVE audio
WAV = 0x56415720,
/// Bitmap image
STL = 0x4C545320,
};
// FIXME: sitypes.h probably won't be part of the public API, so this should
@ -196,6 +205,22 @@ public:
virtual void Read(std::ifstream &is, DataMap &data, uint32_t version, uint32_t size);
};
class WAVFormatHeader
{
public:
// Standard WAV header
uint16_t Format;
uint16_t Channels;
uint32_t SampleRate;
uint32_t ByteRate;
uint16_t BlockAlign;
uint16_t BitsPerSample;
// Mx extensions (not confirmed yet)
uint32_t DataSize;
uint32_t Flags;
};
}
#endif // SI_H

View file

@ -1,33 +0,0 @@
#include "stream.h"
#include "object.h"
namespace si {
Stream::Stream()
{
}
bool Stream::Parse(Chunk *chunk)
{
if (chunk->type() != Chunk::TYPE_MxSt) {
return false;
}
Chunk *obj_chunk = chunk->FindChildWithType(Chunk::TYPE_MxOb);
if (!obj_chunk) {
return false;
}
Object *obj = new Object();
if (!obj->Parse(obj_chunk)) {
return false;
}
// FIXME: Read MxCh into Objects
return true;
}
}

View file

@ -1,22 +0,0 @@
#ifndef STREAM_H
#define STREAM_H
#include "chunk.h"
#include "core.h"
namespace si {
class Stream : public Core
{
public:
Stream();
bool Parse(Chunk *chunk);
private:
};
}
#endif // STREAM_H

View file

@ -32,6 +32,25 @@ public:
template <typename T>
const T *cast() const { return reinterpret_cast<const T*>(data()); }
void append(const char *data, size_t size)
{
size_t current = this->size();
this->resize(current + size);
memcpy(this->data() + current, data, size);
}
void append(const bytearray &other)
{
size_t current = this->size();
this->resize(current + other.size());
memcpy(this->data() + current, other.data(), other.size());
}
void fill(char c)
{
memset(this->data(), c, this->size());
}
};
class Vector3