mirror of
https://github.com/isledecomp/SIEdit.git
synced 2025-02-17 00:40:42 -05:00
app/lib: implemented partial re-weave functionality
This commit is contained in:
parent
172aef0c9d
commit
a976634681
17 changed files with 334 additions and 149 deletions
|
@ -52,6 +52,7 @@ endif()
|
|||
|
||||
target_link_libraries(si-edit PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Multimedia libweaver)
|
||||
target_include_directories(si-edit PRIVATE "${CMAKE_SOURCE_DIR}/lib")
|
||||
target_compile_options(si-edit PRIVATE -Werror)
|
||||
|
||||
set_target_properties(si-edit PROPERTIES
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER com.mattkc.SIEdit
|
||||
|
|
|
@ -181,6 +181,10 @@ void MainWindow::SelectionChanged(const QModelIndex &index)
|
|||
case MxOb::STL:
|
||||
p = panel_bmp_;
|
||||
break;
|
||||
case MxOb::SMK:
|
||||
case MxOb::FLC:
|
||||
case MxOb::OBJ:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "siview.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QPushButton>
|
||||
#include <QSplitter>
|
||||
#include <QTreeView>
|
||||
|
@ -9,6 +10,7 @@ using namespace si;
|
|||
|
||||
SIViewDialog::SIViewDialog(Mode mode, Chunk *riff, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
root_(riff),
|
||||
last_set_data_(nullptr)
|
||||
{
|
||||
setWindowTitle(mode == Import ? tr("Import SI File") : tr("Export SI File"));
|
||||
|
@ -61,6 +63,12 @@ SIViewDialog::SIViewDialog(Mode mode, Chunk *riff, QWidget *parent) :
|
|||
connect(reject_btn, &QPushButton::clicked, this, &SIViewDialog::reject);
|
||||
btn_layout->addWidget(reject_btn);
|
||||
|
||||
if (mode == Import) {
|
||||
auto imm_re_btn = new QPushButton(tr("Immediate Re-Weave"));
|
||||
connect(imm_re_btn, &QPushButton::clicked, this, &SIViewDialog::ImmediateReweave);
|
||||
btn_layout->addWidget(imm_re_btn);
|
||||
}
|
||||
|
||||
btn_layout->addStretch();
|
||||
}
|
||||
|
||||
|
@ -107,3 +115,11 @@ void SIViewDialog::SelectionChanged(const QModelIndex &index)
|
|||
SetPanel(p, c);
|
||||
}
|
||||
}
|
||||
|
||||
void SIViewDialog::ImmediateReweave()
|
||||
{
|
||||
QString s = QFileDialog::getSaveFileName(this);
|
||||
if (!s.isEmpty()) {
|
||||
root_->Write(s.toStdString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,10 +38,13 @@ private:
|
|||
MxObPanel *panel_mxob_;
|
||||
|
||||
si::Chunk *last_set_data_;
|
||||
si::Chunk *root_;
|
||||
|
||||
private slots:
|
||||
void SelectionChanged(const QModelIndex &index);
|
||||
|
||||
void ImmediateReweave();
|
||||
|
||||
};
|
||||
|
||||
#endif // SIVIEW_H
|
||||
|
|
|
@ -40,7 +40,7 @@ void WavPanel::OnOpeningData(void *data)
|
|||
si::Object *o = static_cast<si::Object*>(data);
|
||||
|
||||
// Find fmt and data
|
||||
header_ = *o->GetFileHeader().cast<si::WAVFormatHeader>();
|
||||
header_ = *o->GetFileHeader().cast<si::WAVFmt>();
|
||||
playhead_slider_->setMaximum(o->GetFileBodySize()/GetSampleSize());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef WAVPANEL_H
|
||||
#define WAVPANEL_H
|
||||
|
||||
#include <sitypes.h>
|
||||
#include <othertypes.h>
|
||||
#include <QAudioOutput>
|
||||
#include <QBuffer>
|
||||
#include <QByteArray>
|
||||
|
@ -31,7 +31,7 @@ private:
|
|||
QBuffer buffer_;
|
||||
QByteArray array_;
|
||||
QTimer *playback_timer_;
|
||||
si::WAVFormatHeader header_;
|
||||
si::WAVFmt header_;
|
||||
QByteArray play_buffer_;
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -20,6 +20,7 @@ add_library(libweaver SHARED
|
|||
)
|
||||
|
||||
target_compile_definitions(libweaver PRIVATE LIBWEAVER_LIBRARY)
|
||||
target_compile_options(libweaver PRIVATE -Werror -Wall -Wextra -Wno-unused-parameter)
|
||||
set_target_properties(libweaver PROPERTIES
|
||||
CXX_STANDARD 98
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
|
|
|
@ -90,8 +90,33 @@ bool Chunk::Read(std::ifstream &f, uint32_t &version, uint32_t &alignment)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Chunk::Write(std::ofstream &f, const uint32_t &version) const
|
||||
bool Chunk::Write(std::ofstream &f, uint32_t &version, uint32_t &alignment) const
|
||||
{
|
||||
RIFF *writer = GetReaderFromType(type());
|
||||
|
||||
if (alignment != 0) {
|
||||
// Determine if we have enough space left to write this chunk without the alignment
|
||||
size_t expected_write = 8;
|
||||
for (DataMap::const_iterator it=data_.begin(); it!=data_.end(); it++) {
|
||||
expected_write += it->second.size();
|
||||
}
|
||||
|
||||
size_t start_chunk = f.tellp()/alignment;
|
||||
size_t end_chunk = (size_t(f.tellp())+expected_write)/alignment;
|
||||
if (start_chunk != end_chunk) {
|
||||
// This chunk is going to cross a boundary. We could write padding or split the chunk.
|
||||
// I'm not exactly sure how Weaver decides which, but I suppose it doesn't matter.
|
||||
size_t diff = (end_chunk * alignment) - f.tellp();
|
||||
pad_::WriteArbitraryPadding(f, diff - 8);
|
||||
/*if (id_ != Chunk::TYPE_MxCh || diff < 0x200) {
|
||||
// Make padding
|
||||
pad_::WriteArbitraryPadding(f, diff - 8);
|
||||
} else {
|
||||
// Attempt to split chunk
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
// Write 4-byte ID
|
||||
f.write((const char *) &id_, sizeof(id_));
|
||||
|
||||
|
@ -100,13 +125,19 @@ bool Chunk::Write(std::ofstream &f, const uint32_t &version) const
|
|||
std::ios::pos_type size_pos = f.tellp();
|
||||
f.write((const char *) &chunk_size, sizeof(chunk_size));
|
||||
|
||||
if (RIFF *writer = GetReaderFromType(type())) {
|
||||
if (writer) {
|
||||
writer->Write(f, data_, version);
|
||||
|
||||
if (type() == TYPE_MxHd) {
|
||||
version = data_.at("Version");
|
||||
alignment = data_.at("BufferSize");
|
||||
}
|
||||
|
||||
delete writer;
|
||||
}
|
||||
|
||||
for (Children::const_iterator it=GetChildren().begin(); it!=GetChildren().end(); it++) {
|
||||
static_cast<Chunk*>(*it)->Write(f, version);
|
||||
static_cast<Chunk*>(*it)->Write(f, version, alignment);
|
||||
}
|
||||
|
||||
// Backtrack and write chunk size
|
||||
|
@ -171,9 +202,9 @@ bool Chunk::Write(const char *f)
|
|||
return false;
|
||||
}
|
||||
|
||||
uint32_t version = 0;
|
||||
uint32_t version = 0, alignment = 0;
|
||||
|
||||
return Write(file, version);
|
||||
return Write(file, version, alignment);
|
||||
}
|
||||
|
||||
const char *Chunk::GetTypeDescription(Type type)
|
||||
|
@ -204,8 +235,8 @@ const char *Chunk::GetTypeDescription(Type type)
|
|||
|
||||
Chunk *Chunk::FindChildWithType(Type type) const
|
||||
{
|
||||
for (Core *child : GetChildren()) {
|
||||
Chunk *chunk = static_cast<Chunk*>(child);
|
||||
for (Children::const_iterator it=GetChildren().begin(); it!=GetChildren().end(); it++) {
|
||||
Chunk *chunk = static_cast<Chunk*>(*it);
|
||||
if (chunk->type() == type) {
|
||||
return chunk;
|
||||
} else if (Chunk *grandchild = chunk->FindChildWithType(type)) {
|
||||
|
@ -218,8 +249,8 @@ Chunk *Chunk::FindChildWithType(Type type) const
|
|||
|
||||
Chunk *Chunk::FindChildWithOffset(uint32_t offset) const
|
||||
{
|
||||
for (Core *child : GetChildren()) {
|
||||
Chunk *chunk = static_cast<Chunk*>(child);
|
||||
for (Children::const_iterator it=GetChildren().begin(); it!=GetChildren().end(); it++) {
|
||||
Chunk *chunk = static_cast<Chunk*>(*it);
|
||||
if (chunk->offset() == offset) {
|
||||
return chunk;
|
||||
} else if (Chunk *grandchild = chunk->FindChildWithOffset(offset)) {
|
||||
|
|
|
@ -43,7 +43,7 @@ public:
|
|||
LIBWEAVER_EXPORT const uint32_t &offset() const { return offset_; }
|
||||
|
||||
LIBWEAVER_EXPORT Data &data(const std::string &key) { return data_[key]; }
|
||||
LIBWEAVER_EXPORT const Data &data(const std::string &key) const { return data_.at(key); }
|
||||
LIBWEAVER_EXPORT Data data(const std::string &key) const { return data_.at(key); }
|
||||
|
||||
LIBWEAVER_EXPORT static const char *GetTypeDescription(Type type);
|
||||
LIBWEAVER_EXPORT const char *GetTypeDescription() const
|
||||
|
@ -56,13 +56,13 @@ public:
|
|||
|
||||
private:
|
||||
bool Read(std::ifstream &f, uint32_t &version, uint32_t &alignment);
|
||||
bool Write(std::ofstream &f, const uint32_t &version) const;
|
||||
bool Write(std::ofstream &f, uint32_t &version, uint32_t &alignment) const;
|
||||
|
||||
static RIFF *GetReaderFromType(Type type);
|
||||
|
||||
uint32_t id_;
|
||||
uint32_t offset_;
|
||||
std::map<std::string, Data> data_;
|
||||
DataMap data_;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#include "interleaf.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
#include "object.h"
|
||||
#include "othertypes.h"
|
||||
|
||||
namespace si {
|
||||
|
||||
|
@ -54,17 +56,6 @@ bool Interleaf::Parse(Chunk *riff)
|
|||
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);
|
||||
|
@ -97,80 +88,7 @@ Chunk *Interleaf::Export() const
|
|||
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<Object*>(*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; i<ssize_t(obj->GetChildCount()); i++) {
|
||||
Object *working_obj = static_cast<Object*>((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<ChunkStatus> chunk_status(obj->GetChildCount() + 1);
|
||||
bool all_done;
|
||||
do {
|
||||
all_done = true;
|
||||
for (ssize_t i=-1; i<ssize_t(obj->GetChildCount()); i++) {
|
||||
Object *working_obj = static_cast<Object*>((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<Object*>((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);
|
||||
}
|
||||
list->AppendChild(ExportStream(static_cast<Object*>(*it)));
|
||||
}
|
||||
|
||||
// FIXME: Fill in MxOf table
|
||||
|
@ -244,4 +162,132 @@ bool Interleaf::ParseStream(Chunk *chunk)
|
|||
return true;
|
||||
}
|
||||
|
||||
struct ChunkStatus
|
||||
{
|
||||
ChunkStatus()
|
||||
{
|
||||
object = NULL;
|
||||
index = 0;
|
||||
time = 0;
|
||||
end_chunk = false;
|
||||
}
|
||||
|
||||
Object *object;
|
||||
size_t index;
|
||||
uint32_t time;
|
||||
bool end_chunk;
|
||||
};
|
||||
|
||||
Chunk *Interleaf::ExportStream(Object *obj) const
|
||||
{
|
||||
Chunk *mxst = new Chunk(Chunk::TYPE_MxSt);
|
||||
|
||||
Chunk *mxob = obj->Export();
|
||||
mxst->AppendChild(mxob);
|
||||
|
||||
Chunk *chunklst = new Chunk(Chunk::TYPE_LIST);
|
||||
chunklst->data("Format") = Chunk::TYPE_MxDa;
|
||||
mxst->AppendChild(chunklst);
|
||||
|
||||
// Set up chunking status vector
|
||||
std::vector<ChunkStatus> chunk_status(obj->GetChildCount() + 1);
|
||||
chunk_status[0].object = obj;
|
||||
for (size_t i=0; i<obj->GetChildCount(); i++) {
|
||||
chunk_status[i+1].object = static_cast<Object*>(obj->GetChildAt(i));
|
||||
}
|
||||
|
||||
// First, interleave all headers (first chunk)
|
||||
for (std::vector<ChunkStatus>::iterator it=chunk_status.begin(); it!=chunk_status.end(); it++) {
|
||||
Object *working_obj = it->object;
|
||||
if (!working_obj->data().empty()) {
|
||||
chunklst->AppendChild(ExportMxCh(0, working_obj->id(), 0, working_obj->data().front()));
|
||||
}
|
||||
it->index++;
|
||||
}
|
||||
|
||||
// Next, interleave everything by time
|
||||
while (true) {
|
||||
// Find next chunk
|
||||
ChunkStatus *status = NULL;
|
||||
|
||||
for (std::vector<ChunkStatus>::iterator it=chunk_status.begin(); it!=chunk_status.end(); it++) {
|
||||
// Check if we've already written all these chunks
|
||||
if (it->index >= it->object->data().size()) {
|
||||
/*if (!it->end_chunk) {
|
||||
chunklst->AppendChild(ExportMxCh(MxCh::FLAG_END, it->object->id(), it->time));
|
||||
it->end_chunk = true;
|
||||
}*/
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find earliest chunk to write
|
||||
if (!status || it->time < status->time) {
|
||||
status = it.base();
|
||||
}
|
||||
}
|
||||
|
||||
if (!status) {
|
||||
// Assume chunks are all done
|
||||
break;
|
||||
}
|
||||
|
||||
Object *working_obj = status->object;
|
||||
const bytearray &data = working_obj->data().at(status->index);
|
||||
|
||||
chunklst->AppendChild(ExportMxCh(0, working_obj->id(), status->time, data));
|
||||
|
||||
status->index++;
|
||||
|
||||
// Increment time
|
||||
switch (working_obj->filetype()) {
|
||||
case MxOb::WAV:
|
||||
{
|
||||
const WAVFmt *fmt = working_obj->GetFileHeader().cast<WAVFmt>();
|
||||
status->time += (data.size() * 1000) / (fmt->BitsPerSample/8) / fmt->Channels / fmt->SampleRate;
|
||||
break;
|
||||
}
|
||||
case MxOb::SMK:
|
||||
{
|
||||
int32_t frame_rate = working_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;
|
||||
}
|
||||
status->time += 1000/fps;
|
||||
break;
|
||||
}
|
||||
case MxOb::FLC:
|
||||
status->time += working_obj->GetFileHeader().cast<FLIC>()->speed;
|
||||
break;
|
||||
case MxOb::STL:
|
||||
case MxOb::OBJ:
|
||||
// Unaffected by time
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
for (size_t i=1; i<chunk_status.size(); i++) {
|
||||
const ChunkStatus &s = chunk_status.at(i);
|
||||
chunklst->AppendChild(ExportMxCh(MxCh::FLAG_END, s.object->id(), s.time));
|
||||
}
|
||||
chunklst->AppendChild(ExportMxCh(MxCh::FLAG_END, chunk_status.front().object->id(), chunk_status.front().time));
|
||||
|
||||
return mxst;
|
||||
}
|
||||
|
||||
Chunk *Interleaf::ExportMxCh(uint16_t flags, uint32_t object_id, uint32_t time, const bytearray &data) const
|
||||
{
|
||||
Chunk *mxch = new Chunk(Chunk::TYPE_MxCh);
|
||||
mxch->data("Flags") = flags;
|
||||
mxch->data("Object") = object_id;
|
||||
mxch->data("Time") = time;
|
||||
mxch->data("DataSize") = data.size();
|
||||
mxch->data("Data") = data;
|
||||
return mxch;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "chunk.h"
|
||||
#include "core.h"
|
||||
#include "object.h"
|
||||
|
||||
namespace si {
|
||||
|
||||
|
@ -16,6 +17,8 @@ public:
|
|||
|
||||
private:
|
||||
bool ParseStream(Chunk *chunk);
|
||||
Chunk *ExportStream(Object *obj) const;
|
||||
Chunk *ExportMxCh(uint16_t flags, uint32_t object_id, uint32_t time, const bytearray &data = bytearray()) const;
|
||||
|
||||
uint32_t version_;
|
||||
uint32_t buffer_size_;
|
||||
|
|
|
@ -37,9 +37,9 @@ bool Object::Parse(Chunk *chunk)
|
|||
if (chunk->HasChildren()) {
|
||||
Chunk *child = static_cast<Chunk*>(chunk->GetChildAt(0));
|
||||
if (child->id() == Chunk::TYPE_LIST) {
|
||||
for (Core *entry : child->GetChildren()) {
|
||||
for (Children::const_iterator it=child->GetChildren().begin(); it!=child->GetChildren().end(); it++) {
|
||||
Object *o = new Object();
|
||||
if (!o->Parse(static_cast<Chunk*>(entry))) {
|
||||
if (!o->Parse(static_cast<Chunk*>(*it))) {
|
||||
return false;
|
||||
}
|
||||
AppendChild(o);
|
||||
|
@ -79,6 +79,7 @@ Chunk *Object::Export() const
|
|||
if (HasChildren()) {
|
||||
Chunk *list = new Chunk(Chunk::TYPE_LIST);
|
||||
list->data("Format") = Chunk::TYPE_MxCh;
|
||||
list->data("Count") = list->GetChildCount();
|
||||
chunk->AppendChild(list);
|
||||
|
||||
for (Children::const_iterator it=GetChildren().begin(); it!=GetChildren().end(); it++) {
|
||||
|
@ -164,6 +165,15 @@ bytearray Object::ToPackedData(MxOb::FileType filetype, const ChunkedData &chunk
|
|||
}
|
||||
break;
|
||||
}
|
||||
case MxOb::SMK:
|
||||
case MxOb::OBJ:
|
||||
{
|
||||
// Simply merge
|
||||
for (size_t i=0; i<chunks.size(); i++) {
|
||||
data.append(chunks[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
std::cout << "Didn't know how to extract type '" << std::string((const char *)&filetype, sizeof(filetype)) << "', merging..." << std::endl;
|
||||
for (size_t i=0; i<chunks.size(); i++) {
|
||||
|
@ -181,28 +191,17 @@ Object::ChunkedData Object::ToChunkedData(MxOb::FileType filetype, const bytearr
|
|||
return ChunkedData();
|
||||
}
|
||||
|
||||
bytearray Object::GetFileHeader() const
|
||||
const bytearray &Object::GetFileHeader() const
|
||||
{
|
||||
switch (filetype()) {
|
||||
case MxOb::WAV:
|
||||
case MxOb::STL:
|
||||
return data_.at(0);
|
||||
}
|
||||
|
||||
return bytearray();
|
||||
return data_.at(0);
|
||||
}
|
||||
|
||||
bytearray Object::GetFileBody() const
|
||||
{
|
||||
bytearray b;
|
||||
|
||||
switch (filetype()) {
|
||||
case MxOb::WAV:
|
||||
case MxOb::STL:
|
||||
for (size_t i=1; i<data_.size(); i++) {
|
||||
b.append(data_.at(i));
|
||||
}
|
||||
break;
|
||||
for (size_t i=1; i<data_.size(); i++) {
|
||||
b.append(data_.at(i));
|
||||
}
|
||||
|
||||
return b;
|
||||
|
@ -211,13 +210,9 @@ bytearray Object::GetFileBody() const
|
|||
size_t Object::GetFileBodySize() const
|
||||
{
|
||||
size_t s = 0;
|
||||
switch (filetype()) {
|
||||
case MxOb::WAV:
|
||||
case MxOb::STL:
|
||||
for (size_t i=1; i<data_.size(); i++) {
|
||||
s += data_.at(i).size();
|
||||
}
|
||||
break;
|
||||
|
||||
for (size_t i=1; i<data_.size(); i++) {
|
||||
s += data_.at(i).size();
|
||||
}
|
||||
|
||||
return s;
|
||||
|
@ -229,8 +224,8 @@ Object *Object::FindSubObjectWithID(uint32_t id)
|
|||
return this;
|
||||
}
|
||||
|
||||
for (Core *child : GetChildren()) {
|
||||
if (Object *o = static_cast<Object*>(child)->FindSubObjectWithID(id)) {
|
||||
for (Children::const_iterator it=GetChildren().begin(); it!=GetChildren().end(); it++) {
|
||||
if (Object *o = static_cast<Object*>(*it)->FindSubObjectWithID(id)) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ public:
|
|||
LIBWEAVER_EXPORT static bytearray ToPackedData(MxOb::FileType filetype, const ChunkedData &chunks);
|
||||
LIBWEAVER_EXPORT static ChunkedData ToChunkedData(MxOb::FileType filetype, const bytearray &chunks);
|
||||
|
||||
LIBWEAVER_EXPORT bytearray GetFileHeader() const;
|
||||
LIBWEAVER_EXPORT const bytearray &GetFileHeader() const;
|
||||
LIBWEAVER_EXPORT bytearray GetFileBody() const;
|
||||
LIBWEAVER_EXPORT size_t GetFileBodySize() const;
|
||||
|
||||
|
|
77
lib/othertypes.h
Normal file
77
lib/othertypes.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
#ifndef OTHERTYPES_H
|
||||
#define OTHERTYPES_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace si {
|
||||
|
||||
class WAVFmt
|
||||
{
|
||||
public:
|
||||
// Standard WAV header
|
||||
uint16_t Format;
|
||||
uint16_t Channels;
|
||||
uint32_t SampleRate;
|
||||
uint32_t ByteRate;
|
||||
uint16_t BlockAlign;
|
||||
uint16_t BitsPerSample;
|
||||
|
||||
// Mindscape extensions (not confirmed yet)
|
||||
uint32_t DataSize;
|
||||
uint32_t Flags;
|
||||
};
|
||||
|
||||
// Copied from https://www.compuphase.com/flic.htm#FLICHEADER
|
||||
class FLIC
|
||||
{
|
||||
public:
|
||||
uint32_t size; /* Size of FLIC including this header */
|
||||
uint16_t type; /* File type 0xAF11, 0xAF12, 0xAF30, 0xAF44, ... */
|
||||
uint16_t frames; /* Number of frames in first segment */
|
||||
uint16_t width; /* FLIC width in pixels */
|
||||
uint16_t height; /* FLIC height in pixels */
|
||||
uint16_t depth; /* Bits per pixel (usually 8) */
|
||||
uint16_t flags; /* Set to zero or to three */
|
||||
uint32_t speed; /* Delay between frames */
|
||||
uint16_t reserved1; /* Set to zero */
|
||||
uint32_t created; /* Date of FLIC creation (FLC only) */
|
||||
uint32_t creator; /* Serial number or compiler id (FLC only) */
|
||||
uint32_t updated; /* Date of FLIC update (FLC only) */
|
||||
uint32_t updater; /* Serial number (FLC only), see creator */
|
||||
uint16_t aspect_dx; /* Width of square rectangle (FLC only) */
|
||||
uint16_t aspect_dy; /* Height of square rectangle (FLC only) */
|
||||
uint16_t ext_flags; /* EGI: flags for specific EGI extensions */
|
||||
uint16_t keyframes; /* EGI: key-image frequency */
|
||||
uint16_t totalframes; /* EGI: total number of frames (segments) */
|
||||
uint32_t req_memory; /* EGI: maximum chunk size (uncompressed) */
|
||||
uint16_t max_regions; /* EGI: max. number of regions in a CHK_REGION chunk */
|
||||
uint16_t transp_num; /* EGI: number of transparent levels */
|
||||
uint8_t reserved2[24]; /* Set to zero */
|
||||
uint32_t oframe1; /* Offset to frame 1 (FLC only) */
|
||||
uint32_t oframe2; /* Offset to frame 2 (FLC only) */
|
||||
uint8_t reserved3[40]; /* Set to zero */
|
||||
};
|
||||
|
||||
// Copied from https://wiki.multimedia.cx/index.php/Smacker#Header
|
||||
class SMK2
|
||||
{
|
||||
public:
|
||||
uint32_t Signature;
|
||||
uint32_t Width;
|
||||
uint32_t Height;
|
||||
uint32_t Frames;
|
||||
uint32_t FrameRate;
|
||||
uint32_t Flags;
|
||||
uint32_t AudioSize[7];
|
||||
uint32_t TreesSize;
|
||||
uint32_t MMap_Size;
|
||||
uint32_t MClr_Size;
|
||||
uint32_t Full_Size;
|
||||
uint32_t Type_Size;
|
||||
uint32_t AudioRate[7];
|
||||
uint32_t Dummy;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OTHERTYPES_H
|
|
@ -202,6 +202,16 @@ void pad_::Write(std::ofstream &os, const DataMap &data, uint32_t version)
|
|||
}
|
||||
}
|
||||
|
||||
void pad_::WriteArbitraryPadding(std::ofstream &os, uint32_t size)
|
||||
{
|
||||
uint32_t pad_id = Chunk::TYPE_pad_;
|
||||
os.write((const char *) &pad_id, sizeof(uint32_t));
|
||||
os.write((const char *) &size, sizeof(uint32_t));
|
||||
bytearray b(size);
|
||||
b.fill(0xCD);
|
||||
WriteBytes(os, b);
|
||||
}
|
||||
|
||||
const char *MxOb::GetTypeName(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
|
@ -211,9 +221,9 @@ const char *MxOb::GetTypeName(Type type)
|
|||
return "WAV";
|
||||
case Presenter:
|
||||
return "MxPresenter";
|
||||
case BMP:
|
||||
case Bitmap:
|
||||
return "BMP";
|
||||
case OBJ:
|
||||
case Object:
|
||||
return "3D Object";
|
||||
case World:
|
||||
return "World";
|
||||
|
@ -221,6 +231,7 @@ const char *MxOb::GetTypeName(Type type)
|
|||
return "Event";
|
||||
case Animation:
|
||||
return "Animation";
|
||||
case Null:
|
||||
case TYPE_COUNT:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
|
||||
virtual void Read(std::ifstream &is, DataMap &data, uint32_t version, uint32_t size);
|
||||
virtual void Write(std::ofstream &os, const DataMap &data, uint32_t version);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -122,6 +123,8 @@ class pad_ : public RIFF
|
|||
public:
|
||||
virtual void Read(std::ifstream &is, DataMap &data, uint32_t version, uint32_t size);
|
||||
virtual void Write(std::ofstream &os, const DataMap &data, uint32_t version);
|
||||
|
||||
static void WriteArbitraryPadding(std::ofstream &os, uint32_t size);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -180,10 +183,10 @@ public:
|
|||
Animation = 0x09,
|
||||
|
||||
/// Bitmap image
|
||||
BMP = 0x0A,
|
||||
Bitmap = 0x0A,
|
||||
|
||||
/// 3D Object
|
||||
OBJ = 0x0B,
|
||||
Object = 0x0B,
|
||||
|
||||
/// Total number of types (not a real type)
|
||||
TYPE_COUNT
|
||||
|
@ -220,6 +223,12 @@ public:
|
|||
|
||||
/// FLIC animation
|
||||
FLC = 0x434c4620,
|
||||
|
||||
/// SMK video
|
||||
SMK = 0x4b4d5320,
|
||||
|
||||
/// 3D Object
|
||||
OBJ = 0x4a424f20,
|
||||
};
|
||||
|
||||
// FIXME: sitypes.h probably won't be part of the public API, so this should
|
||||
|
@ -231,22 +240,6 @@ public:
|
|||
virtual void Write(std::ofstream &os, const DataMap &data, uint32_t version);
|
||||
};
|
||||
|
||||
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
|
||||
|
|
|
@ -109,8 +109,12 @@ public:
|
|||
inline size_t size() const { return data_.size(); }
|
||||
inline const std::string toString() const
|
||||
{
|
||||
// Subtract 1 from size, assuming the last character is a null terminator
|
||||
return std::string(data_.data(), std::max(size_t(0), data_.size()-1));
|
||||
if (data_.empty()) {
|
||||
return std::string();
|
||||
} else {
|
||||
// Subtract 1 from size, assuming the last character is a null terminator
|
||||
return std::string(data_.data(), std::max(size_t(0), data_.size()-1));
|
||||
}
|
||||
}
|
||||
|
||||
inline bool operator==(int u) const
|
||||
|
|
Loading…
Reference in a new issue