code: major refactoring

This commit is contained in:
itsmattkc 2022-07-18 00:27:00 -07:00
parent 0a76066185
commit 6cf3d80047
34 changed files with 1064 additions and 1425 deletions

View file

@ -4,20 +4,12 @@ find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Multimedia)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Multimedia)
set(PROJECT_SOURCES
# siview/chunkmodel.cpp
# siview/chunkmodel.h
# siview/panels/mxch.cpp
# siview/panels/mxch.h
# siview/panels/mxhd.cpp
# siview/panels/mxhd.h
# siview/panels/mxob.cpp
# siview/panels/mxob.h
# siview/panels/mxof.cpp
# siview/panels/mxof.h
# siview/panels/riff.cpp
# siview/panels/riff.h
# siview/siview.cpp
# siview/siview.h
siview/chunkmodel.cpp
siview/chunkmodel.h
siview/infopanel.cpp
siview/infopanel.h
siview/siview.cpp
siview/siview.h
viewer/bitmappanel.cpp
viewer/bitmappanel.h

View file

@ -6,8 +6,12 @@
#include <QMessageBox>
#include <QSplitter>
#include "siview/siview.h"
using namespace si;
const QString MainWindow::kFileFilter = tr("Interleaf Files (*.si)");
MainWindow::MainWindow(QWidget *parent) :
QMainWindow{parent},
last_set_data_(nullptr)
@ -41,6 +45,7 @@ MainWindow::MainWindow(QWidget *parent) :
auto replace_btn = new QPushButton(tr("Replace"));
action_layout->addWidget(replace_btn);
connect(replace_btn, &QPushButton::clicked, this, &MainWindow::ReplaceClicked);
action_layout->addStretch();
@ -67,18 +72,8 @@ void MainWindow::OpenFilename(const QString &s)
{
model_.SetCore(nullptr);
bool r =
#ifdef Q_OS_WINDOWS
interleaf_.Read(s.toStdWString().c_str());
#else
interleaf_.Read(s.toUtf8());
#endif
;
if (r) {
if (OpenInterleafFileInternal(this, &interleaf_, s)) {
model_.SetCore(&interleaf_);
} else {
QMessageBox::critical(this, QString(), tr("Failed to load Interleaf file."));
}
}
@ -88,22 +83,21 @@ void MainWindow::InitializeMenuBar()
auto file_menu = menubar->addMenu(tr("&File"));
auto new_action = file_menu->addAction(tr("&New"));
file_menu->addAction(tr("&New"), this, &MainWindow::NewFile, tr("Ctrl+N"));
auto open_action = file_menu->addAction(tr("&Open"), this, &MainWindow::OpenFile, tr("Ctrl+O"));
file_menu->addAction(tr("&Open"), this, &MainWindow::OpenFile, tr("Ctrl+O"));
auto save_action = file_menu->addAction(tr("&Save"));
auto save_as_action = file_menu->addAction(tr("Save &As"));
file_menu->addAction(tr("&Save"), this, &MainWindow::SaveFile, tr("Ctrl+S"));
file_menu->addAction(tr("Save &As"), this, &MainWindow::SaveFileAs, tr("Ctrl+Shift+S"));
file_menu->addSeparator();
auto export_action = file_menu->addAction(tr("&Export"));
connect(export_action, &QAction::triggered, this, &MainWindow::ExportFile);
file_menu->addAction(tr("&View SI File"), this, &MainWindow::ViewSIFile);
file_menu->addSeparator();
auto exit_action = file_menu->addAction(tr("E&xit"));
connect(exit_action, &QAction::triggered, this, &MainWindow::close);
file_menu->addAction(tr("E&xit"), this, &MainWindow::close);
setMenuBar(menubar);
}
@ -136,27 +130,102 @@ void MainWindow::ExtractObject(si::Object *obj)
QString s = QFileDialog::getSaveFileName(this, tr("Export Object"), filename);
if (!s.isEmpty()) {
QFile f(s);
if (f.open(QFile::WriteOnly)) {
bytearray b = obj->GetNormalizedData();
f.write(b.data(), b.size());
f.close();
} else {
if (!obj->ExtractToFile(
#ifdef Q_OS_WINDOWS
s.toStdWString().c_str()
#else
s.toUtf8()
#endif
)) {
QMessageBox::critical(this, QString(), tr("Failed to write to file \"%1\".").arg(s));
}
}
}
void MainWindow::ReplaceObject(si::Object *obj)
{
QString s = QFileDialog::getOpenFileName(this, tr("Replace Object"));
if (!s.isEmpty()) {
if (!obj->ReplaceWithFile(
#ifdef Q_OS_WINDOWS
s.toStdWString().c_str()
#else
s.toUtf8()
#endif
)) {
QMessageBox::critical(this, QString(), tr("Failed to open to file \"%1\".").arg(s));
}
}
}
bool MainWindow::OpenInterleafFileInternal(QWidget *parent, si::Interleaf *interleaf, const QString &s)
{
Interleaf::Error r = interleaf->Read(
#ifdef Q_OS_WINDOWS
s.toStdWString().c_str()
#else
s.toUtf8()
#endif
);
if (r == Interleaf::ERROR_SUCCESS) {
return true;
} else {
QMessageBox::critical(parent, QString(), tr("Failed to load Interleaf file: %1").arg(r));
return false;
}
}
QString MainWindow::GetOpenFileName()
{
return QFileDialog::getOpenFileName(this, QString(), QString(), kFileFilter);
}
void MainWindow::NewFile()
{
model_.SetCore(nullptr);
interleaf_.Clear();
model_.SetCore(&interleaf_);
}
void MainWindow::OpenFile()
{
QString s = QFileDialog::getOpenFileName(this, QString(), QString(), tr("Interleaf Files (*.si)"));
QString s = GetOpenFileName();
if (!s.isEmpty()) {
OpenFilename(s);
}
}
void MainWindow::ExportFile()
bool MainWindow::SaveFile()
{
if (current_filename_.isEmpty()) {
return SaveFileAs();
} else {
Interleaf::Error r = interleaf_.Write(
#ifdef Q_OS_WINDOWS
current_filename_.toStdWString().c_str()
#else
current_filename_.toUtf8()
#endif
);
if (r == Interleaf::ERROR_SUCCESS) {
return true;
} else {
QMessageBox::critical(this, QString(), tr("Failed to write SI file: %1").arg(r));
return false;
}
}
}
bool MainWindow::SaveFileAs()
{
current_filename_ = QFileDialog::getSaveFileName(this, QString(), QString(), kFileFilter);
if (!current_filename_.isEmpty()) {
return SaveFile();
}
return false;
}
void MainWindow::SelectionChanged(const QModelIndex &index)
@ -212,3 +281,22 @@ void MainWindow::ExtractClicked()
{
ExtractObject(last_set_data_);
}
void MainWindow::ReplaceClicked()
{
ReplaceObject(last_set_data_);
}
void MainWindow::ViewSIFile()
{
QString s = GetOpenFileName();
if (!s.isEmpty()) {
std::unique_ptr<Interleaf> temp = std::make_unique<Interleaf>();
if (OpenInterleafFileInternal(this, temp.get(), s)) {
SIViewDialog *v = new SIViewDialog(temp->GetInformation(), this);
v->temp = std::move(temp);
v->setAttribute(Qt::WA_DeleteOnClose);
v->show();
}
}
}

View file

@ -24,13 +24,18 @@ public:
signals:
private:
//bool CloseFile();
void InitializeMenuBar();
void SetPanel(Panel *panel, si::Object *chunk);
void ExtractObject(si::Object *obj);
void ReplaceObject(si::Object *obj);
static bool OpenInterleafFileInternal(QWidget *parent, si::Interleaf *interleaf, const QString &s);
QString GetOpenFileName();
static const QString kFileFilter;
QStackedWidget *config_stack_;
@ -47,21 +52,25 @@ private:
si::Object *last_set_data_;
private slots:
void OpenFile();
//bool SaveFile();
//bool SaveFileAs();
QString current_filename_;
void ExportFile();
private slots:
void NewFile();
void OpenFile();
bool SaveFile();
bool SaveFileAs();
void SelectionChanged(const QModelIndex &index);
void ShowContextMenu(const QPoint &p);
void ExtractSelectedItems();
void ExtractClicked();
void ReplaceClicked();
void ViewSIFile();
};
#endif // MAINWINDOW_H

View file

@ -1,6 +1,7 @@
#include "chunkmodel.h"
#include <iostream>
#include <sitypes.h>
#define super Model
@ -18,7 +19,7 @@ int ChunkModel::columnCount(const QModelIndex &parent) const
QVariant ChunkModel::data(const QModelIndex &index, int role) const
{
Chunk *c = static_cast<Chunk*>(GetCoreFromIndex(index));
Info *c = static_cast<Info*>(GetCoreFromIndex(index));
if (!c) {
return QVariant();
}
@ -29,16 +30,15 @@ QVariant ChunkModel::data(const QModelIndex &index, int role) const
switch (index.column()) {
case kColType:
// Convert 4-byte ID to QString
return QString::fromLatin1(reinterpret_cast<const char *>(&c->id()), sizeof(uint32_t));
return QString::fromLatin1(reinterpret_cast<const char *>(&c->GetType()), sizeof(uint32_t));
case kColOffset:
return QStringLiteral("0x%1").arg(QString::number(c->offset(), 16).toUpper());
return QStringLiteral("0x%1").arg(QString::number(c->GetOffset(), 16).toUpper());
case kColDesc:
return QString::fromUtf8(c->GetTypeDescription());
return QString::fromUtf8(RIFF::GetTypeDescription(static_cast<RIFF::Type>(c->GetType())));
case kColObjectID:
if (c->type() == Chunk::TYPE_MxOb) {
return c->data("ID").toU32();
} else if (c->type() == Chunk::TYPE_MxCh) {
return c->data("Object").toU32();
uint32_t i = c->GetObjectID();
if (i != Info::NULL_OBJECT_ID) {
return QString::number(i);
}
break;
}

View file

@ -1,7 +1,7 @@
#ifndef CHUNKMODEL_H
#define CHUNKMODEL_H
#include <chunk.h>
#include <info.h>
#include "model.h"

56
app/siview/infopanel.cpp Normal file
View file

@ -0,0 +1,56 @@
#include "infopanel.h"
#include <info.h>
#include <QDebug>
InfoPanel::InfoPanel(QWidget *parent) :
Panel(parent)
{
int row = 0;
m_Lbl = new QLabel();
layout()->addWidget(m_Lbl, row, 0);
row++;
m_ShowDataBtn = new QPushButton(tr("Show Data"));
connect(m_ShowDataBtn, &QPushButton::clicked, this, &InfoPanel::ShowData);
layout()->addWidget(m_ShowDataBtn, row, 0);
m_ShowDataBtn->hide();
row++;
m_DataView = new QPlainTextEdit();
m_DataView->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
layout()->addWidget(m_DataView, row, 0);
m_DataView->hide();
FinishLayout();
}
void InfoPanel::OnOpeningData(void *data)
{
auto info = static_cast<si::Info*>(data);
m_Lbl->setText(QString::fromStdString(info->GetDescription()));
if (!info->GetData().empty()) {
m_ShowDataBtn->show();
}
}
void InfoPanel::OnClosingData(void *data)
{
m_Lbl->setText(QString());
m_DataView->hide();
m_DataView->clear();
m_ShowDataBtn->hide();
}
void InfoPanel::ShowData()
{
const si::bytearray &s = static_cast<si::Info*>(this->GetData())->GetData();
m_ShowDataBtn->hide();
m_DataView->setPlainText(QByteArray(s.data(), s.size()).toHex());
m_DataView->show();
}

32
app/siview/infopanel.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef INFOPANEL_H
#define INFOPANEL_H
#include <QLabel>
#include <QPlainTextEdit>
#include <QPushButton>
#include "panel.h"
class InfoPanel : public Panel
{
Q_OBJECT
public:
InfoPanel(QWidget *parent = nullptr);
protected:
virtual void OnOpeningData(void *data) override;
virtual void OnClosingData(void *data) override;
private:
QLabel *m_Lbl;
QPushButton *m_ShowDataBtn;
QPlainTextEdit *m_DataView;
private slots:
void ShowData();
};
#endif // INFOPANEL_H

View file

@ -1,145 +0,0 @@
#include "mxch.h"
#include <chunk.h>
#include <QFontDatabase>
#include <QGroupBox>
#include <QLabel>
using namespace si;
MxChPanel::MxChPanel(QWidget *parent) :
Panel(parent)
{
int row = 0;
auto flag_group = new QGroupBox(tr("Flags"));
layout()->addWidget(flag_group, row, 0, 1, 2);
{
auto flag_layout = new QGridLayout(flag_group);
int flag_row = 0;
flag_layout->addWidget(new QLabel(tr("Value")), flag_row, 0);
flag_edit_ = new QLineEdit();
flag_layout->addWidget(flag_edit_, flag_row, 1);
flag_row++;
auto split_chunk = new QCheckBox(tr("Split Chunk"));
split_chunk->setProperty("flag", MxCh::FLAG_SPLIT);
connect(split_chunk, &QCheckBox::clicked, this, &MxChPanel::FlagCheckBoxClicked);
flag_checkboxes_.append(split_chunk);
flag_layout->addWidget(split_chunk, flag_row, 0, 1, 2);
flag_row++;
auto end_chunk = new QCheckBox(tr("End Chunk"));
end_chunk->setProperty("flag", MxCh::FLAG_END);
connect(end_chunk, &QCheckBox::clicked, this, &MxChPanel::FlagCheckBoxClicked);
flag_checkboxes_.append(end_chunk);
flag_layout->addWidget(end_chunk, flag_row, 0, 1, 2);
flag_row++;
}
row++;
layout()->addWidget(new QLabel(tr("Object ID")), row, 0);
obj_edit_ = new QSpinBox();
obj_edit_->setMinimum(0);
obj_edit_->setMaximum(INT_MAX);
layout()->addWidget(obj_edit_, row, 1);
row++;
layout()->addWidget(new QLabel(tr("Millisecond Offset")), row, 0);
ms_offset_edit_ = new QSpinBox();
ms_offset_edit_->setMinimum(0);
ms_offset_edit_->setMaximum(INT_MAX);
layout()->addWidget(ms_offset_edit_, row, 1);
row++;
layout()->addWidget(new QLabel(tr("Data Size")), row, 0);
data_sz_edit_ = new QSpinBox();
data_sz_edit_->setMinimum(0);
data_sz_edit_->setMaximum(INT_MAX);
layout()->addWidget(data_sz_edit_, row, 1);
row++;
show_data_btn_ = new QPushButton(tr("Show Data"));
connect(show_data_btn_, &QPushButton::clicked, this, &MxChPanel::ShowDataField);
layout()->addWidget(show_data_btn_, row, 0, 1, 2);
data_edit_ = new QTextEdit();
data_edit_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
layout()->addWidget(data_edit_, row, 0, 1, 2);
data_edit_->hide();
FinishLayout();
}
void MxChPanel::OnOpeningData(void *d)
{
Chunk *chunk = static_cast<Chunk*>(d);
flag_edit_->setText(QString::number(chunk->data("Flags"), 16));
obj_edit_->setValue(chunk->data("Object"));
ms_offset_edit_->setValue(chunk->data("Time"));
data_sz_edit_->setValue(chunk->data("DataSize"));
for (QCheckBox *cb : qAsConst(flag_checkboxes_)) {
cb->setChecked(cb->property("flag").toUInt() & chunk->data("Flags"));
}
}
void MxChPanel::OnClosingData(void *data)
{
/*bool ok;
u16 flags = flag_edit_->text().toUShort(&ok, 16);
if (ok) {
chunk->data("Flags") = flags;
}
chunk->data("Object") = u32(obj_edit_->value());
chunk->data("Time") = u32(ms_offset_edit_->value());
chunk->data("DataSize") = u32(data_sz_edit_->value());*/
show_data_btn_->show();
data_edit_->clear();
data_edit_->hide();
}
void MxChPanel::FlagCheckBoxClicked(bool e)
{
uint16_t flag = sender()->property("flag").toUInt();
bool ok;
uint16_t current = flag_edit_->text().toUShort(&ok, 16);
if (ok) {
if (e) {
current |= flag;
} else {
current &= ~flag;
}
flag_edit_->setText(QString::number(current, 16));
}
}
void MxChPanel::ShowDataField()
{
show_data_btn_->hide();
data_edit_->show();
Chunk *chunk = static_cast<Chunk*>(GetData());
const Data &data = chunk->data("Data");
QByteArray ba(data.data(), int(data.size()));
data_edit_->setPlainText(ba.toHex());
}

View file

@ -1,39 +0,0 @@
#ifndef MXCHPANEL_H
#define MXCHPANEL_H
#include <QCheckBox>
#include <QLineEdit>
#include <QPushButton>
#include <QSpinBox>
#include <QTextEdit>
#include "panel.h"
class MxChPanel : public Panel
{
Q_OBJECT
public:
explicit MxChPanel(QWidget *parent = nullptr);
protected:
virtual void OnOpeningData(void *data) override;
virtual void OnClosingData(void *data) override;
private:
QLineEdit *flag_edit_;
QSpinBox *obj_edit_;
QSpinBox *ms_offset_edit_;
QSpinBox *data_sz_edit_;
QTextEdit *data_edit_;
QPushButton *show_data_btn_;
QVector<QCheckBox*> flag_checkboxes_;
private slots:
void FlagCheckBoxClicked(bool e);
void ShowDataField();
};
#endif // MXCHPANEL_H

View file

@ -1,69 +0,0 @@
#include "mxhd.h"
#include <chunk.h>
#include <QLabel>
using namespace si;
MxHdPanel::MxHdPanel(QWidget *parent) :
Panel{parent}
{
int row = 0;
auto version_layout = new QHBoxLayout();
layout()->addLayout(version_layout, row, 0, 1, 2);
version_layout->addWidget(new QLabel(tr("Major Version")));
major_version_edit_ = new QSpinBox();
major_version_edit_->setMinimum(0);
major_version_edit_->setMaximum(UINT16_MAX);
version_layout->addWidget(major_version_edit_);
version_layout->addWidget(new QLabel(tr("Minor Version")));
minor_version_edit_ = new QSpinBox();
minor_version_edit_->setMinimum(0);
minor_version_edit_->setMaximum(UINT16_MAX);
version_layout->addWidget(minor_version_edit_);
row++;
layout()->addWidget(new QLabel(tr("Buffer Alignment")), row, 0);
buffer_alignment_edit_ = new QSpinBox();
buffer_alignment_edit_->setMinimum(0);
buffer_alignment_edit_->setMaximum(INT_MAX); // Technically this is UINT32_MAX but QSpinBox only supports int
layout()->addWidget(buffer_alignment_edit_, row, 1);
row++;
layout()->addWidget(new QLabel(tr("Buffer Count")), row, 0);
buffer_count_edit_ = new QSpinBox();
buffer_count_edit_->setMinimum(0);
buffer_count_edit_->setMaximum(INT_MAX); // Technically this is UINT32_MAX but QSpinBox only supports int
layout()->addWidget(buffer_count_edit_, row, 1);
FinishLayout();
}
void MxHdPanel::OnOpeningData(void *data)
{
Chunk *chunk = static_cast<Chunk*>(data);
uint32_t version = chunk->data("Version");
uint16_t major_ver = version >> 16;
uint16_t minor_ver = version;
major_version_edit_->setValue(major_ver);
minor_version_edit_->setValue(minor_ver);
buffer_alignment_edit_->setValue(chunk->data("BufferSize"));
buffer_count_edit_->setValue(chunk->data("BufferCount"));
}
void MxHdPanel::OnClosingData(void *data)
{
/*chunk->data("Version") = (major_version_edit_->value() << 16 | (minor_version_edit_->value() & 0xFFFF));
chunk->data("BufferSize") = buffer_alignment_edit_->value();
chunk->data("BufferCount") = buffer_count_edit_->value();*/
}

View file

@ -1,29 +0,0 @@
#ifndef MXHDPANEL_H
#define MXHDPANEL_H
#include <QLineEdit>
#include <QSpinBox>
#include "panel.h"
class MxHdPanel : public Panel
{
Q_OBJECT
public:
explicit MxHdPanel(QWidget *parent = nullptr);
signals:
protected:
virtual void OnOpeningData(void *data) override;
virtual void OnClosingData(void *data) override;
private:
QSpinBox *major_version_edit_;
QSpinBox *minor_version_edit_;
QSpinBox *buffer_alignment_edit_;
QSpinBox *buffer_count_edit_;
};
#endif // MXHDPANEL_H

View file

@ -1,217 +0,0 @@
#include "mxob.h"
#include <chunk.h>
#include <sitypes.h>
#include <QGroupBox>
#include <QLabel>
using namespace si;
MxObPanel::MxObPanel(QWidget *parent) :
Panel(parent)
{
int row = 0;
layout()->addWidget(new QLabel(tr("Type")), row, 0);
type_combo_ = new QComboBox();
for (int i=0; i<MxOb::TYPE_COUNT; i++) {
type_combo_->addItem(MxOb::GetTypeName(MxOb::Type(i)));
}
layout()->addWidget(type_combo_, row, 1);
row++;
layout()->addWidget(new QLabel(tr("Name")), row, 0);
name_edit_ = new QLineEdit();
layout()->addWidget(name_edit_, row, 1);
row++;
layout()->addWidget(new QLabel(tr("Filename")), row, 0);
filename_edit_ = new QLineEdit();
layout()->addWidget(filename_edit_, row, 1);
row++;
layout()->addWidget(new QLabel(tr("Object ID")), row, 0);
obj_id_edit_ = new QSpinBox();
obj_id_edit_->setMinimum(0);
obj_id_edit_->setMaximum(INT_MAX);
layout()->addWidget(obj_id_edit_, row, 1);
row++;
layout()->addWidget(new QLabel(tr("Presenter")), row, 0);
presenter_edit_ = new QLineEdit();
layout()->addWidget(presenter_edit_, row, 1);
row++;
auto flag_group = new QGroupBox(tr("Flags"));
layout()->addWidget(flag_group, row, 0, 1, 2);
row++;
{
auto flag_layout = new QGridLayout(flag_group);
int flag_row = 0;
flag_layout->addWidget(new QLabel(tr("Value")), flag_row, 0);
flag_edit_ = new QLineEdit();
flag_layout->addWidget(flag_edit_, flag_row, 1);
flag_row++;
auto loop_cache = new QCheckBox(tr("Loop from Cache"));
loop_cache->setProperty("flag", 0x01);
connect(loop_cache, &QCheckBox::clicked, this, &MxObPanel::FlagCheckBoxClicked);
flag_checkboxes_.append(loop_cache);
flag_layout->addWidget(loop_cache, flag_row, 0, 1, 2);
flag_row++;
auto no_loop = new QCheckBox(tr("No Loop"));
no_loop->setProperty("flag", 0x02);
connect(no_loop, &QCheckBox::clicked, this, &MxObPanel::FlagCheckBoxClicked);
flag_checkboxes_.append(no_loop);
flag_layout->addWidget(no_loop, flag_row, 0, 1, 2);
flag_row++;
auto loop_stream = new QCheckBox(tr("Loop from Stream"));
loop_stream->setProperty("flag", 0x04);
connect(loop_stream, &QCheckBox::clicked, this, &MxObPanel::FlagCheckBoxClicked);
flag_checkboxes_.append(loop_stream);
flag_layout->addWidget(loop_stream, flag_row, 0, 1, 2);
flag_row++;
auto transparent = new QCheckBox(tr("Transparent"));
transparent->setProperty("flag", 0x08);
connect(transparent, &QCheckBox::clicked, this, &MxObPanel::FlagCheckBoxClicked);
flag_checkboxes_.append(transparent);
flag_layout->addWidget(transparent, flag_row, 0, 1, 2);
flag_row++;
auto unknown = new QCheckBox(tr("Unknown"));
unknown->setProperty("flag", 0x20);
connect(unknown, &QCheckBox::clicked, this, &MxObPanel::FlagCheckBoxClicked);
flag_checkboxes_.append(unknown);
flag_layout->addWidget(unknown, flag_row, 0, 1, 2);
flag_row++;
}
layout()->addWidget(new QLabel(tr("Duration")), row, 0);
duration_edit_ = new QSpinBox();
duration_edit_->setMinimum(0);
duration_edit_->setMaximum(INT_MAX);
layout()->addWidget(duration_edit_, row, 1);
row++;
layout()->addWidget(new QLabel(tr("Loops")), row, 0);
loops_edit_ = new QSpinBox();
loops_edit_->setMinimum(0);
loops_edit_->setMaximum(INT_MAX);
layout()->addWidget(loops_edit_, row, 1);
row++;
auto pos_grp = new QGroupBox(tr("Position"));
auto pos_lyt = new QVBoxLayout(pos_grp);
pos_lyt->setMargin(0);
pos_edit_ = new Vector3Edit();
pos_lyt->addWidget(pos_edit_);
layout()->addWidget(pos_grp, row, 0, 1, 2);
row++;
auto dir_grp = new QGroupBox(tr("Direction"));
auto dir_lyt = new QVBoxLayout(dir_grp);
dir_lyt->setMargin(0);
dir_edit_ = new Vector3Edit();
dir_lyt->addWidget(dir_edit_);
layout()->addWidget(dir_grp, row, 0, 1, 2);
row++;
auto up_grp = new QGroupBox(tr("Up"));
auto up_lyt = new QVBoxLayout(up_grp);
up_lyt->setMargin(0);
up_edit_ = new Vector3Edit();
up_lyt->addWidget(up_edit_);
layout()->addWidget(up_grp, row, 0, 1, 2);
FinishLayout();
}
void MxObPanel::OnOpeningData(void *data)
{
Chunk *chunk = static_cast<Chunk*>(data);
type_combo_->setCurrentIndex(chunk->data("Type"));
name_edit_->setText(QString(chunk->data("Name")));
filename_edit_->setText(QString(chunk->data("FileName")));
presenter_edit_->setText(QString(chunk->data("Presenter")));
obj_id_edit_->setValue(chunk->data("ID"));
flag_edit_->setText(QString::number(chunk->data("Flags"), 16));
for (QCheckBox* cb : qAsConst(flag_checkboxes_)) {
cb->setChecked(cb->property("flag").toUInt() & chunk->data("Flags"));
}
duration_edit_->setValue(chunk->data("Duration"));
loops_edit_->setValue(chunk->data("Loops"));
pos_edit_->SetValue(chunk->data("Position"));
dir_edit_->SetValue(chunk->data("Direction"));
up_edit_->SetValue(chunk->data("Up"));
}
void MxObPanel::OnClosingData(void *data)
{
/*chunk->data("Type") = type_combo_->currentIndex();
bool ok;
u32 flags = flag_edit_->text().toUInt(&ok, 16);
if (ok) {
chunk->data("Flags") = flags;
}
chunk->data("Duration") = duration_edit_->value();
chunk->data("Loops") = loops_edit_->value();
chunk->data("Position") = pos_edit_->GetValue();
chunk->data("Direction") = dir_edit_->GetValue();
chunk->data("Up") = up_edit_->GetValue();*/
}
void MxObPanel::FlagCheckBoxClicked(bool e)
{
uint32_t flag = sender()->property("flag").toUInt();
bool ok;
uint32_t current = flag_edit_->text().toUInt(&ok, 16);
if (ok) {
if (e) {
current |= flag;
}
else {
current &= ~flag;
}
flag_edit_->setText(QString::number(current, 16));
}
}

View file

@ -1,45 +0,0 @@
#ifndef MXOBPANEL_H
#define MXOBPANEL_H
#include <QCheckBox>
#include <QComboBox>
#include <QLineEdit>
#include <QSpinBox>
#include "panel.h"
#include "vector3edit.h"
class MxObPanel : public Panel
{
Q_OBJECT
public:
explicit MxObPanel(QWidget *parent = nullptr);
protected:
virtual void OnOpeningData(void *data) override;
virtual void OnClosingData(void *data) override;
private:
QComboBox *type_combo_;
QLineEdit *name_edit_;
QLineEdit *presenter_edit_;
QLineEdit *filename_edit_;
QSpinBox *obj_id_edit_;
QLineEdit* flag_edit_;
QSpinBox *duration_edit_;
QSpinBox *loops_edit_;
Vector3Edit *pos_edit_;
Vector3Edit *dir_edit_;
Vector3Edit *up_edit_;
QVector<QCheckBox*> flag_checkboxes_;
private slots:
void FlagCheckBoxClicked(bool e);
};
#endif // MXOBPANEL_H

View file

@ -1,47 +0,0 @@
#include "mxof.h"
#include <chunk.h>
#include <QLabel>
using namespace si;
MxOfPanel::MxOfPanel(QWidget *parent) :
Panel(parent)
{
int row = 0;
layout()->addWidget(new QLabel(tr("Object Count")), row, 0);
obj_count_edit_ = new QSpinBox();
obj_count_edit_->setMinimum(0);
obj_count_edit_->setMaximum(INT_MAX);
layout()->addWidget(obj_count_edit_, row, 1);
row++;
list_ = new QListWidget();
layout()->addWidget(list_, row, 0, 1, 2);
FinishLayout();
}
void MxOfPanel::OnOpeningData(void *data)
{
Chunk *chunk = static_cast<Chunk*>(data);
const Data &offsets_bytes = chunk->data("Offsets");
const uint32_t *offsets = reinterpret_cast<const uint32_t*>(offsets_bytes.data());
size_t offset_count = offsets_bytes.size() / sizeof(uint32_t);
obj_count_edit_->setValue(chunk->data("Count"));
for (size_t i=0; i<offset_count; i++) {
QString addr = QStringLiteral("0x%1").arg(offsets[i], 8, 16, QChar('0'));
list_->addItem(QStringLiteral("%1: %2").arg(QString::number(i), addr));
}
}
void MxOfPanel::OnClosingData(void *data)
{
list_->clear();
}

View file

@ -1,27 +0,0 @@
#ifndef MXOFPANEL_H
#define MXOFPANEL_H
#include <QListWidget>
#include <QLineEdit>
#include <QSpinBox>
#include "panel.h"
class MxOfPanel : public Panel
{
Q_OBJECT
public:
explicit MxOfPanel(QWidget *parent = nullptr);
protected:
virtual void OnOpeningData(void *data) override;
virtual void OnClosingData(void *data) override;
private:
QSpinBox *obj_count_edit_;
QListWidget *list_;
};
#endif // MXOFPANEL_H

View file

@ -1,44 +0,0 @@
#include "riff.h"
#include <chunk.h>
#include <QLabel>
using namespace si;
RIFFPanel::RIFFPanel(QWidget *parent) :
Panel(parent)
{
int row = 0;
layout()->addWidget(new QLabel(tr("ID")), row, 0);
id_edit_ = new QLineEdit();
id_edit_->setMaxLength(sizeof(uint32_t));
layout()->addWidget(id_edit_, row, 1);
FinishLayout();
}
void RIFFPanel::OnOpeningData(void *data)
{
Chunk *chunk = static_cast<Chunk*>(data);
QString s = QString::fromLatin1(chunk->data("Format").c_str(), sizeof(uint32_t));
id_edit_->setText(s);
}
void RIFFPanel::OnClosingData(void *data)
{
/*QByteArray d = id_edit_->text().toLatin1();
const int target_sz = sizeof(u32);
if (d.size() != target_sz) {
int old_sz = d.size();
d.resize(target_sz);
for (int i=old_sz; i<target_sz; i++) {
// Fill any empty space with spaces
d[i] = ' ';
}
}
chunk->data("Format") = *(u32*)d.data();*/
}

View file

@ -1,23 +0,0 @@
#ifndef RIFFPANEL_H
#define RIFFPANEL_H
#include <QLineEdit>
#include "panel.h"
class RIFFPanel : public Panel
{
Q_OBJECT
public:
explicit RIFFPanel(QWidget *parent = nullptr);
protected:
virtual void OnOpeningData(void *data) override;
virtual void OnClosingData(void *data) override;
private:
QLineEdit *id_edit_;
};
#endif // RIFFPANEL_H

View file

@ -8,12 +8,12 @@
using namespace si;
SIViewDialog::SIViewDialog(Mode mode, Chunk *riff, QWidget *parent) :
QDialog(parent),
SIViewDialog::SIViewDialog(Info *riff, QWidget *parent) :
QWidget(parent, Qt::Window),
root_(riff),
last_set_data_(nullptr)
{
setWindowTitle(mode == Import ? tr("Import SI File") : tr("Export SI File"));
setWindowTitle(tr("View SI File"));
auto layout = new QVBoxLayout(this);
@ -31,95 +31,13 @@ SIViewDialog::SIViewDialog(Mode mode, Chunk *riff, QWidget *parent) :
config_stack_ = new QStackedWidget();
splitter->addWidget(config_stack_);
panel_blank_ = new Panel();
config_stack_->addWidget(panel_blank_);
panel_ = new InfoPanel();
config_stack_->addWidget(panel_);
panel_mxhd_ = new MxHdPanel();
config_stack_->addWidget(panel_mxhd_);
panel_riff_ = new RIFFPanel();
config_stack_->addWidget(panel_riff_);
panel_mxch_ = new MxChPanel();
config_stack_->addWidget(panel_mxch_);
panel_mxof_ = new MxOfPanel();
config_stack_->addWidget(panel_mxof_);
panel_mxob_ = new MxObPanel();
config_stack_->addWidget(panel_mxob_);
auto btn_layout = new QHBoxLayout();
layout->addLayout(btn_layout);
btn_layout->addStretch();
auto accept_btn = new QPushButton(mode == Import ? tr("De-Weave") : tr("Weave"));
accept_btn->setDefault(true);
connect(accept_btn, &QPushButton::clicked, this, &SIViewDialog::accept);
btn_layout->addWidget(accept_btn);
auto reject_btn = new QPushButton(tr("Cancel"));
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();
}
void SIViewDialog::SetPanel(Panel *panel, si::Chunk *chunk)
{
auto current = static_cast<Panel*>(config_stack_->currentWidget());
current->SetData(nullptr);
config_stack_->setCurrentWidget(panel);
panel->SetData(chunk);
last_set_data_ = chunk;
splitter->setSizes({99999, 99999});
}
void SIViewDialog::SelectionChanged(const QModelIndex &index)
{
Panel *p = panel_blank_;
Chunk *c = static_cast<Chunk*>(index.internalPointer());
if (c) {
switch (c->type()) {
case Chunk::TYPE_MxHd:
p = panel_mxhd_;
break;
case Chunk::TYPE_RIFF:
case Chunk::TYPE_LIST:
p = panel_riff_;
break;
case Chunk::TYPE_MxCh:
p = panel_mxch_;
break;
case Chunk::TYPE_MxOf:
p = panel_mxof_;
break;
case Chunk::TYPE_MxOb:
p = panel_mxob_;
break;
case Chunk::TYPE_MxSt:
case Chunk::TYPE_pad_:
break;
}
}
if (p != config_stack_->currentWidget() || c != last_set_data_) {
SetPanel(p, c);
}
}
void SIViewDialog::ImmediateReweave()
{
QString s = QFileDialog::getSaveFileName(this);
if (!s.isEmpty()) {
root_->Write(s.toStdString());
}
panel_->SetData(static_cast<Info*>(index.internalPointer()));
}

View file

@ -1,50 +1,36 @@
#ifndef SIVIEW_H
#define SIVIEW_H
#include <interleaf.h>
#include <QDialog>
#include <QStackedWidget>
#include "chunkmodel.h"
#include "panels/mxch.h"
#include "panels/mxhd.h"
#include "panels/mxob.h"
#include "panels/mxof.h"
#include "panels/riff.h"
#include "panel.h"
#include "infopanel.h"
class SIViewDialog : public QDialog
class SIViewDialog : public QWidget
{
Q_OBJECT
public:
enum Mode {
Import,
Export
};
SIViewDialog(si::Info *info, QWidget *parent = nullptr);
SIViewDialog(Mode mode, si::Chunk *riff, QWidget *parent = nullptr);
std::unique_ptr<si::Interleaf> temp;
private:
void SetPanel(Panel *panel, si::Chunk *chunk);
QStackedWidget *config_stack_;
ChunkModel chunk_model_;
Panel *panel_blank_;
RIFFPanel *panel_riff_;
MxHdPanel *panel_mxhd_;
MxChPanel *panel_mxch_;
MxOfPanel *panel_mxof_;
MxObPanel *panel_mxob_;
InfoPanel *panel_;
si::Chunk *last_set_data_;
si::Chunk *root_;
const si::Info *last_set_data_;
const si::Info *root_;
std::unique_ptr<si::Interleaf> temp_interleaf_;
private slots:
void SelectionChanged(const QModelIndex &index);
void ImmediateReweave();
};
#endif // SIVIEW_H

View file

@ -27,12 +27,11 @@ BitmapPanel::BitmapPanel(QWidget *parent)
void BitmapPanel::OnOpeningData(void *data)
{
si::Object *o = static_cast<si::Object*>(data);
si::bytearray processed = o->GetNormalizedData();
QByteArray b(processed.data(), processed.size());
QBuffer buf(&b);
si::bytearray d = o->ExtractToMemory();
QByteArray b(d.data(), d.size());
QBuffer read_buf(&b);
QImage img;
img.load(&buf, "BMP");
img.load(&read_buf, "BMP");
img_lbl_->setPixmap(QPixmap::fromImage(img));

View file

@ -1,9 +1,9 @@
option(LIBWEAVER_BUILD_DOXYGEN "Build Doxygen documentation" OFF)
set(LIBWEAVER_SOURCES
common.h
core.cpp
core.h
info.h
interleaf.cpp
interleaf.h
object.cpp

View file

@ -1,24 +0,0 @@
#ifndef LIBWEAVER_GLOBAL_H
#define LIBWEAVER_GLOBAL_H
#if defined(__GNUC__)
#define LIBWEAVER_PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
#elif defined(_MSC_VER)
#define LIBWEAVER_PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop))
#endif
#ifdef _MSC_VER
#define LIBWEAVER_EXPORT __declspec(dllexport)
#else
#define LIBWEAVER_EXPORT
#endif
#if defined(_WIN32)
#define LIBWEAVER_OS_WINDOWS
#elif defined(__APPLE__)
#define LIBWEAVER_OS_MACOS
#elif defined(__linux__)
#define LIBWEAVER_OS_LINUX
#endif
#endif // LIBWEAVER_GLOBAL_H

View file

@ -3,7 +3,6 @@
#include <vector>
#include "common.h"
#include "types.h"
namespace si {

54
lib/info.h Normal file
View file

@ -0,0 +1,54 @@
#ifndef INFO_H
#define INFO_H
#include "core.h"
namespace si {
class Info : public Core
{
public:
static const uint32_t NULL_OBJECT_ID = 0xFFFFFFFF;
Info()
{
m_ObjectID = NULL_OBJECT_ID;
}
void clear()
{
m_Desc.clear();
DeleteChildren();
}
const uint32_t &GetType() const { return m_Type; }
void SetType(const uint32_t &t) { m_Type = t; }
const uint32_t &GetOffset() const { return m_Offset; }
void SetOffset(const uint32_t &t) { m_Offset = t; }
const uint32_t &GetObjectID() const { return m_ObjectID; }
void SetObjectID(const uint32_t &t) { m_ObjectID = t; }
const uint32_t &GetSize() const { return m_Size; }
void SetSize(const uint32_t &t) { m_Size = t; }
const std::string &GetDescription() const { return m_Desc; }
void SetDescription(const std::string &d) { m_Desc = d; }
const bytearray &GetData() const { return m_Data; }
void SetData(const bytearray &d) { m_Data = d; }
private:
uint32_t m_Type;
uint32_t m_Offset;
uint32_t m_Size;
uint32_t m_ObjectID;
std::string m_Desc;
bytearray m_Data;
};
}
#endif // INFO_H

View file

@ -2,6 +2,7 @@
#include <cmath>
#include <iostream>
#include <sstream>
#include "object.h"
#include "othertypes.h"
@ -10,170 +11,220 @@
namespace si {
static const uint32_t kMinimumChunkSize = 8;
Interleaf::Interleaf()
{
}
void Interleaf::Clear()
{
m_Info.clear();
m_BufferSize = 0;
m_OffsetTable.clear();
m_ObjectIndexTable.clear();
m_ObjectIDTable.clear();
m_ObjectList.clear();
DeleteChildren();
}
bool Interleaf::Read(const char *f)
Interleaf::Error Interleaf::Read(const char *f)
{
std::ifstream is(f);
if (!is.is_open() || !is.good()) {
return ERROR_IO;
}
return Read(is);
}
Interleaf::Error Interleaf::Write(const char *f) const
{
std::ofstream os(f);
if (!os.is_open() || !os.good()) {
return ERROR_IO;
}
return Write(os);
}
#ifdef _WIN32
Interleaf::Error Interleaf::Read(const wchar_t *f)
{
std::istream is(f);
if (!is.is_open() || !is.good()) {
return false;
}
return Read(is);
}
bool Interleaf::Read(const wchar_t *f)
Interleaf::Error Interleaf::Write(const wchar_t *f) const
{
std::ifstream is(f);
if (!is.is_open() || !is.good()) {
return false;
}
return Read(is);
}
/*bool Interleaf::Write(const char *f) const
{
std::ofstream os(f);
std::ostream os(f);
if (!os.is_open() || !os.good()) {
return false;
}
return Write(os);
}
#endif
bool Interleaf::Write(const wchar_t *f) const
{
std::ofstream os(f);
if (!os.is_open() || !os.good()) {
return false;
}
return Write(os);
}*/
inline std::string PrintU32AsString(uint32_t u)
{
return std::string((const char *) &u, sizeof(u));
}
bool Interleaf::ReadChunk(Core *parent, std::ifstream &is)
Interleaf::Error Interleaf::ReadChunk(Core *parent, std::istream &is, Info *info)
{
uint32_t offset = is.tellg();
uint32_t id = ReadU32(is);
uint32_t size = ReadU32(is);
uint32_t end = uint32_t(is.tellg()) + size;
std::cout << PrintU32AsString(id)
<< " Offset: 0x" << std::hex << offset
<< " Size: " << std::dec << size << std::endl;
info->SetType(id);
info->SetOffset(offset);
info->SetSize(size);
switch (static_cast<SI::Type>(id)) {
case SI::RIFF:
std::stringstream desc;
switch (static_cast<RIFF::Type>(id)) {
case RIFF::RIFF_:
{
// Require RIFF type to be OMNI
uint32_t riff_type = ReadU32(is);
std::cout << " Type: " << PrintU32AsString(riff_type) << std::endl;
if (riff_type != RIFF::OMNI) {
return false;
return ERROR_INVALID_INPUT;
}
desc << "Type: " << RIFF::PrintU32AsString(riff_type);
break;
}
case SI::MxHd:
case RIFF::MxHd:
{
m_Version = ReadU32(is);
std::cout << " Version: " << m_Version << std::endl;
desc << "Version: " << m_Version << std::endl;
m_BufferSize = ReadU32(is);
std::cout << " Buffer Size: " << m_BufferSize << std::endl;
desc << "Buffer Size: 0x" << std::hex << m_BufferSize;
if (m_Version < 0x00020002) {
if (m_Version == 0x00020002) {
m_BufferCount = ReadU32(is);
std::cout << " Buffer Count: " << m_BufferCount << std::endl;
desc << std::endl << "Buffer Count: " << std::dec << m_BufferCount << std::endl;
}
break;
}
case SI::pad_:
case RIFF::pad_:
is.seekg(size, std::ios::cur);
break;
case SI::MxOf:
case RIFF::MxOf:
{
m_OffsetCount = ReadU32(is);
std::cout << " Count: " << m_OffsetCount << std::endl;
uint32_t offset_count = ReadU32(is);
uint32_t i = 0;
while (is.tellg() < end) {
uint32_t offset = ReadU32(is);
std::cout << " " << i << ": 0x" << std::hex << offset << std::endl;
m_OffsetTable.push_back(offset);
i++;
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);
uint32_t choffset = ReadU32(is);
m_ObjectList[i] = choffset;
desc << std::endl << i << ": 0x" << std::hex << choffset;
}
break;
}
case SI::LIST:
case RIFF::LIST:
{
uint32_t list_type = ReadU32(is);
std::cout << " Type: " << PrintU32AsString(list_type) << std::endl;
desc << "Type: " << RIFF::PrintU32AsString(list_type) << std::endl;
uint32_t list_count = 0;
if (list_type == SI::MxCh) {
if (list_type == RIFF::MxCh) {
list_count = ReadU32(is);
std::cout << " Count: " << list_count << std::endl;
desc << "Count: " << list_count << std::endl;
}
break;
}
case SI::MxSt:
case SI::MxDa:
case RIFF::MxSt:
case RIFF::MxDa:
case RIFF::WAVE:
case RIFF::fmt_:
case RIFF::data:
case RIFF::OMNI:
// Types with no data
break;
case SI::MxOb:
case RIFF::MxOb:
{
Object *o = ReadObject(is);
parent->AppendChild(o);
m_ObjectIndexTable[o->id()] = o;
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);
}
ReadObject(is, o, desc);
info->SetObjectID(o->id());
m_ObjectIDTable[o->id()] = o;
parent = o;
break;
}
case SI::MxCh:
case RIFF::MxCh:
{
uint16_t flags = ReadU16(is);
desc << "Flags: 0x" << std::hex << flags << std::endl;
uint32_t object = ReadU32(is);
desc << "Object: " << std::dec << object << std::endl;
uint32_t time = ReadU32(is);
desc << "Time: " << time << std::endl;
uint32_t data_sz = ReadU32(is);
desc << "Size: " << data_sz << std::endl;
bytearray data = ReadBytes(is, size - MxCh::HEADER_SIZE);
Object *o = m_ObjectIndexTable.at(object);
if (!o) {
return false;
}
o->data_.push_back(data);
break;
}
}
info->SetObjectID(object);
info->SetData(data);
std::cout << "Reading children at 0x" << std::hex << is.tellg() << std::endl;
if (!(flags & MxCh::FLAG_END)) {
Object *o = m_ObjectIDTable.at(object);
if (!o) {
return ERROR_INVALID_INPUT;
}
if (flags & MxCh::FLAG_SPLIT && o->last_chunk_split_) {
o->data_.back().append(data);
} else {
o->data_.push_back(data);
o->last_chunk_split_ = (flags & MxCh::FLAG_SPLIT);
}
break;
}
}
}
// Assume any remaining data is this chunk's children
while (is.good() && (size_t(is.tellg()) + 4) < end) {
while (is.good() && (size_t(is.tellg()) + kMinimumChunkSize) < end) {
// Check alignment, if there's not enough room to for another segment, skip ahead
if (m_BufferSize > 0) {
uint32_t offset_in_buffer = is.tellg()%m_BufferSize;
if (offset_in_buffer + sizeof(uint32_t)*2 > m_BufferSize) {
if (offset_in_buffer + kMinimumChunkSize > m_BufferSize) {
is.seekg(m_BufferSize-offset_in_buffer, std::ios::cur);
}
}
// Read next child
if (!ReadChunk(parent, is)) {
return false;
Info *subinfo = new Info();
info->AppendChild(subinfo);
Error e = ReadChunk(parent, is, subinfo);
if (e != ERROR_SUCCESS) {
return e;
}
}
info->SetDescription(desc.str());
if (is.tellg() < end) {
is.seekg(end, std::ios::beg);
}
@ -182,315 +233,294 @@ bool Interleaf::ReadChunk(Core *parent, std::ifstream &is)
is.seekg(1, std::ios::cur);
}
return true;
return ERROR_SUCCESS;
}
Object *Interleaf::ReadObject(std::ifstream &is)
Object *Interleaf::ReadObject(std::istream &is, Object *o, std::stringstream &desc)
{
Object *o = new Object();
o->type_ = static_cast<MxOb::Type>(ReadU16(is));
std::cout << " Type: " << o->type_ << std::endl;
desc << "Type: " << o->type_ << std::endl;
o->presenter_ = ReadString(is);
std::cout << " Presenter: " << o->presenter_ << std::endl;
desc << "Presenter: " << o->presenter_ << std::endl;
o->unknown1_ = ReadU32(is);
std::cout << " Unknown1: " << o->unknown1_ << std::endl;
desc << "Unknown1: " << o->unknown1_ << std::endl;
o->name_ = ReadString(is);
std::cout << " Name: " << o->name_ << std::endl;
desc << "Name: " << o->name_ << std::endl;
o->id_ = ReadU32(is);
std::cout << " ID: " << o->id_ << std::endl;
desc << "ID: " << o->id_ << std::endl;
o->flags_ = ReadU32(is);
std::cout << " Flags: " << o->flags_ << std::endl;
desc << "Flags: " << o->flags_ << std::endl;
o->unknown4_ = ReadU32(is);
std::cout << " Unknown4: " << o->unknown4_ << std::endl;
desc << "Unknown4: " << o->unknown4_ << std::endl;
o->duration_ = ReadU32(is);
std::cout << " Duration: " << o->duration_ << std::endl;
desc << "Duration: " << o->duration_ << std::endl;
o->loops_ = ReadU32(is);
std::cout << " Loops: " << o->loops_ << std::endl;
desc << "Loops: " << o->loops_ << std::endl;
o->position_ = ReadVector3(is);
std::cout << " Position: " << o->position_.x << " " << o->position_.y << " " << o->position_.z << std::endl;
desc << "Position: " << o->position_.x << " " << o->position_.y << " " << o->position_.z << std::endl;
o->direction_ = ReadVector3(is);
std::cout << " Direction: " << o->direction_.x << " " << o->direction_.y << " " << o->direction_.z << std::endl;
desc << "Direction: " << o->direction_.x << " " << o->direction_.y << " " << o->direction_.z << std::endl;
o->up_ = ReadVector3(is);
std::cout << " Up: " << o->up_.x << " " << o->up_.y << " " << o->up_.z << std::endl;
desc << "Up: " << o->up_.x << " " << o->up_.y << " " << o->up_.z << std::endl;
uint16_t extra_sz = ReadU16(is);
std::cout << " Extra Size: " << extra_sz << std::endl;
desc << "Extra Size: " << extra_sz << std::endl;
o->extra_ = ReadBytes(is, extra_sz);
if (o->type_ != MxOb::Presenter && o->type_ != MxOb::World) {
o->filename_ = ReadString(is);
std::cout << " Filename: " << o->filename_ << std::endl;
desc << "Filename: " << o->filename_ << std::endl;
o->unknown26_ = ReadU32(is);
std::cout << " Unknown26: " << o->unknown26_ << std::endl;
desc << "Unknown26: " << o->unknown26_ << std::endl;
o->unknown27_ = ReadU32(is);
std::cout << " Unknown27: " << o->unknown27_ << std::endl;
desc << "Unknown27: " << o->unknown27_ << std::endl;
o->unknown28_ = ReadU32(is);
std::cout << " Unknown28: " << o->unknown28_ << std::endl;
desc << "Unknown28: " << o->unknown28_ << std::endl;
o->filetype_ = static_cast<MxOb::FileType>(ReadU32(is));
std::cout << " File Type: " << PrintU32AsString(o->filetype_) << std::endl;
desc << "File Type: " << RIFF::PrintU32AsString(o->filetype_) << std::endl;
o->unknown29_ = ReadU32(is);
std::cout << " Unknown29: " << o->unknown29_ << std::endl;
desc << "Unknown29: " << o->unknown29_ << std::endl;
o->unknown30_ = ReadU32(is);
std::cout << " Unknown30: " << o->unknown30_ << std::endl;
desc << "Unknown30: " << o->unknown30_ << std::endl;
if (o->filetype_ == MxOb::WAV) {
o->unknown31_ = ReadU32(is);
std::cout << " Unknown31: " << o->unknown31_ << std::endl;
desc << "Unknown31: " << o->unknown31_ << std::endl;
}
}
//std::cout << "Next ID: 0x" << std::hex << is.tellg() << " ";
//std::cout << PrintU32AsString(ReadU32(is));
//std::cout << " After: 0x" << std::hex << is.tellg() << std::endl;
//is.seekg(-4, std::ios::cur);
return o;
}
bool Interleaf::Read(std::ifstream &is)
Interleaf::Error Interleaf::Read(std::istream &is)
{
Clear();
return ReadChunk(this, is);
return ReadChunk(this, is, &m_Info);
}
/*bool Interleaf::Parse(Chunk *riff)
Interleaf::Error Interleaf::Write(std::ostream &os) const
{
if (riff->id() != Chunk::TYPE_RIFF) {
return false;
if (m_BufferSize == 0) {
LogError() << "Buffer size must be set to write" << std::endl;
return ERROR_INVALID_BUFFER_SIZE;
}
Chunk *hd = riff->FindChildWithType(Chunk::TYPE_MxHd);
if (!hd) {
return false;
}
RIFF::Chk riff = RIFF::BeginChunk(os, RIFF::RIFF_);
WriteU32(os, RIFF::OMNI);
version_ = hd->data("Version");
if (version_ == 0) {
// Unknown version
return false;
}
std::ios::pos_type offset_table_pos;
buffer_size_ = hd->data("BufferSize");
buffer_count_ = hd->data("BufferCount");
{
// MxHd
RIFF::Chk mxhd = RIFF::BeginChunk(os, RIFF::MxHd);
Chunk *of = riff->FindChildWithType(Chunk::TYPE_MxOf);
if (!of) {
return false;
}
WriteU32(os, m_Version);
WriteU32(os, m_BufferSize);
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++) {
if (offset_table[i]) {
Chunk *st = riff->FindChildWithOffset(offset_table[i]);
if (!ParseStream(st)) {
return false;
}
} else {
Object *nullobj = new Object();
AppendChild(nullobj);
if (m_Version == 0x00020002) {
WriteU32(os, m_BufferCount);
}
RIFF::EndChunk(os, mxhd);
}
return true;
}
{
// MxOf
RIFF::Chk mxof = RIFF::BeginChunk(os, RIFF::MxOf);
Chunk *Interleaf::Export() const
{
Chunk *riff = new Chunk(Chunk::TYPE_RIFF);
riff->data("Format") = RIFF::OMNI;
WriteU32(os, GetChildCount());
Chunk *mxhd = new Chunk(Chunk::TYPE_MxHd);
mxhd->data("Version") = version_;
mxhd->data("BufferSize") = buffer_size_;
mxhd->data("BufferCount") = buffer_count_;
riff->AppendChild(mxhd);
offset_table_pos = os.tellp();
Chunk *mxof = new Chunk(Chunk::TYPE_MxOf);
for (size_t i = 0; i < GetChildCount(); i++) {
WriteU32(os, 0);
}
// 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<Object*>(*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++) {
list->AppendChild(ExportStream(static_cast<Object*>(*it)));
RIFF::EndChunk(os, mxof);
}
// FIXME: Fill in MxOf table
// FIXME: Split MxCh chunks over alignment
{
// LIST
RIFF::Chk list_mxst = RIFF::BeginChunk(os, RIFF::LIST);
return riff;
}
WriteU32(os, RIFF::MxSt);
bool Interleaf::ParseStream(Chunk *chunk)
{
if (chunk->type() != Chunk::TYPE_MxSt) {
return false;
}
for (size_t i = 0; i < GetChildCount(); i++) {
Object *child = static_cast<Object*>(GetChildAt(i));
Chunk *obj_chunk = static_cast<Chunk*>(chunk->GetChildAt(0));
if (!obj_chunk) {
return false;
}
uint32_t mxst_offset = os.tellp();
Object *obj = new Object();
if (!obj->Parse(obj_chunk)) {
return false;
}
os.seekp(size_t(offset_table_pos) + i * sizeof(uint32_t));
WriteU32(os, mxst_offset);
os.seekp(mxst_offset);
AppendChild(obj);
// MxSt
RIFF::Chk mxst = RIFF::BeginChunk(os, RIFF::MxSt);
Chunk *list = static_cast<Chunk*>(chunk->GetChildAt(1));
if (list) {
typedef std::map<uint32_t, Object::ChunkedData> ChunkMap;
ChunkMap data;
{
// MxOb
WriteObject(os, child);
}
uint32_t joining_chunk = 0;
{
// LIST
RIFF::Chk list_mxda = RIFF::BeginChunk(os, RIFF::LIST);
for (Children::const_iterator it=list->GetChildren().begin(); it!=list->GetChildren().end(); it++) {
Chunk *mxch = static_cast<Chunk*>(*it);
if (mxch->id() == Chunk::TYPE_pad_) {
// Ignore this chunk
} else if (mxch->id() == Chunk::TYPE_MxCh) {
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");
WriteU32(os, RIFF::MxDa);
// 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);
}
// First, interleave headers
std::vector<Object*> objects;
objects.reserve(child->GetChildCount() + 1);
objects.push_back(child);
for (size_t j=0; j<child->GetChildCount(); j++) {
objects.push_back(static_cast<Object*>(child->GetChildAt(j)));
}
InterleaveObjects(os, objects);
RIFF::EndChunk(os, list_mxda);
}
RIFF::EndChunk(os, mxst);
}
for (ChunkMap::const_iterator it=data.begin(); it!=data.end(); it++) {
Object *o = obj->FindSubObjectWithID(it->first);
if (o) {
o->SetChunkedData(it->second);
} else {
std::cout << "Failed to find object with ID " << it->first << std::endl;
}
// Fill remainder with padding
if (os.tellp()%m_BufferSize != 0) {
uint32_t current_buf = os.tellp() / m_BufferSize;
uint32_t target_sz = (current_buf + 1) * m_BufferSize;
WritePadding(os, target_sz - os.tellp());
}
RIFF::EndChunk(os, list_mxst);
}
RIFF::EndChunk(os, riff);
return ERROR_SUCCESS;
}
void Interleaf::WriteObject(std::ostream &os, const Object *o) const
{
RIFF::Chk mxob = RIFF::BeginChunk(os, RIFF::MxOb);
WriteU16(os, o->type_);
WriteString(os, o->presenter_);
WriteU32(os, o->unknown1_);
WriteString(os, o->name_);
WriteU32(os, o->id_);
WriteU32(os, o->flags_);
WriteU32(os, o->unknown4_);
WriteU32(os, o->duration_);
WriteU32(os, o->loops_);
WriteVector3(os, o->position_);
WriteVector3(os, o->direction_);
WriteVector3(os, o->up_);
WriteU16(os, o->extra_.size());
WriteBytes(os, o->extra_);
if (o->type_ != MxOb::Presenter && o->type_ != MxOb::World) {
WriteString(os, o->filename_);
WriteU32(os, o->unknown26_);
WriteU32(os, o->unknown27_);
WriteU32(os, o->unknown28_);
WriteU32(os, o->filetype_);
WriteU32(os, o->unknown29_);
WriteU32(os, o->unknown30_);
if (o->filetype_ == MxOb::WAV) {
WriteU32(os, o->unknown31_);
}
}
return true;
if (o->HasChildren()) {
// Child list
RIFF::Chk list_mxch = RIFF::BeginChunk(os, RIFF::LIST);
WriteU32(os, RIFF::MxCh);
WriteU32(os, o->GetChildCount());
for (size_t i = 0; i < o->GetChildCount(); i++) {
WriteObject(os, static_cast<Object*>(o->GetChildAt(i)));
}
RIFF::EndChunk(os, list_mxch);
}
RIFF::EndChunk(os, mxob);
}
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
void Interleaf::InterleaveObjects(std::ostream &os, const std::vector<Object *> &objects) const
{
Chunk *mxst = new Chunk(Chunk::TYPE_MxSt);
std::vector<ChunkStatus> status(objects.size());
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));
// Set up status vector
for (size_t i=0; i<objects.size(); i++) {
status[i].object = objects.at(i);
status[i].index = 0;
status[i].time = 0;
status[i].end_chunk = false;
}
// 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()));
// First, interleave headers
for (size_t i=0; i<status.size(); i++) {
ChunkStatus &s = status[i];
Object *o = s.object;
if (!o->data().empty()) {
WriteSubChunk(os, 0, o->id(), 0xFFFFFFFF, o->data().front());
s.index++;
}
it->index++;
}
// Next, interleave everything by time
// Next, interleave the rest based on time
while (true) {
// Find next chunk
ChunkStatus *status = NULL;
ChunkStatus *s = NULL;
for (std::vector<ChunkStatus>::iterator it=chunk_status.begin(); it!=chunk_status.end(); it++) {
for (std::vector<ChunkStatus>::iterator it=status.begin(); it!=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);
if (!s || it->time < s->time) {
s = &(*it);
}
}
if (!status) {
if (!s) {
// Assume chunks are all done
break;
}
Object *working_obj = status->object;
const bytearray &data = working_obj->data().at(status->index);
Object *obj = s->object;
const bytearray &data = obj->data().at(s->index);
chunklst->AppendChild(ExportMxCh(0, working_obj->id(), status->time, data));
WriteSubChunk(os, 0, obj->id(), s->time, data);
status->index++;
s->index++;
// Increment time
switch (working_obj->filetype()) {
switch (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;
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:
{
int32_t frame_rate = working_obj->GetFileHeader().cast<SMK2>()->FrameRate;
int32_t frame_rate = obj->GetFileHeader().cast<SMK2>()->FrameRate;
int32_t fps;
if (frame_rate > 0) {
fps = 1000/frame_rate;
@ -499,37 +529,64 @@ Chunk *Interleaf::ExportStream(Object *obj) const
} else {
fps = 10;
}
status->time += 1000/fps;
s->time += 1000/fps;
break;
}
case MxOb::FLC:
status->time += working_obj->GetFileHeader().cast<FLIC>()->speed;
s->time += 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;
for (size_t i=1; i<status.size(); i++) {
const ChunkStatus &s = status.at(i);
WriteSubChunk(os, MxCh::FLAG_END, s.object->id(), s.time);
}
WriteSubChunk(os, MxCh::FLAG_END, status.front().object->id(), status.front().time);
}
Chunk *Interleaf::ExportMxCh(uint16_t flags, uint32_t object_id, uint32_t time, const bytearray &data) const
void Interleaf::WriteSubChunk(std::ostream &os, uint16_t flags, uint32_t object, 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;
}*/
uint32_t total_sz = data.size() + MxCh::HEADER_SIZE + kMinimumChunkSize;
uint32_t start_buffer = os.tellp() / m_BufferSize;
uint32_t stop_buffer = (uint32_t(os.tellp()) + total_sz) / m_BufferSize;
if (start_buffer != stop_buffer) {
// This chunk won't fit in our buffer alignment. We must make a decision to either insert
// padding or split the clip.
WritePadding(os, (stop_buffer * m_BufferSize) - os.tellp());
}
RIFF::Chk mxch = RIFF::BeginChunk(os, RIFF::MxCh);
WriteU16(os, flags);
WriteU32(os, object);
WriteU32(os, time);
WriteU32(os, data.size());
WriteBytes(os, data);
RIFF::EndChunk(os, mxch);
}
void Interleaf::WritePadding(std::ostream &os, uint32_t size) const
{
if (size < kMinimumChunkSize) {
return;
}
size -= kMinimumChunkSize;
WriteU32(os, RIFF::pad_);
WriteU32(os, size);
bytearray b(size);
b.fill(0xCD);
WriteBytes(os, b);
}
}

View file

@ -4,6 +4,7 @@
#include <fstream>
#include "core.h"
#include "info.h"
#include "object.h"
namespace si {
@ -11,32 +12,50 @@ namespace si {
class Interleaf : public Core
{
public:
enum Error
{
ERROR_SUCCESS,
ERROR_IO,
ERROR_INVALID_INPUT,
ERROR_INVALID_BUFFER_SIZE
};
LIBWEAVER_EXPORT Interleaf();
LIBWEAVER_EXPORT void Clear();
LIBWEAVER_EXPORT bool Read(const char *f);
LIBWEAVER_EXPORT bool Read(const wchar_t *f);
LIBWEAVER_EXPORT Error Read(const char *f);
LIBWEAVER_EXPORT Error Write(const char *f) const;
//LIBWEAVER_EXPORT bool Write(const char *f) const;
//LIBWEAVER_EXPORT bool Write(const wchar_t *f) const;
#ifdef _WIN32
LIBWEAVER_EXPORT Error Read(const wchar_t *f);
LIBWEAVER_EXPORT Error Write(const wchar_t *f) const;
#endif
Error Read(std::istream &is);
Error Write(std::ostream &os) const;
Info *GetInformation() { return &m_Info; }
private:
bool Read(std::ifstream &is);
//bool Write(std::ofstream &os) const;
bool ReadChunk(Core *parent, std::ifstream &is);
Error ReadChunk(Core *parent, std::istream &is, Info *info);
Object *ReadObject(std::ifstream &is);
Object *ReadObject(std::istream &is, Object *o, std::stringstream &desc);
void WriteObject(std::ostream &os, const Object *o) const;
void InterleaveObjects(std::ostream &os, const std::vector<Object*> &objects) const;
void WriteSubChunk(std::ostream &os, uint16_t flags, uint32_t object, uint32_t time, const bytearray &data = bytearray()) const;
void WritePadding(std::ostream &os, uint32_t size) const;
Info m_Info;
uint32_t m_Version;
uint32_t m_BufferSize;
uint32_t m_BufferCount;
uint32_t m_OffsetCount;
std::vector<uint32_t> m_OffsetTable;
std::map<uint32_t, Object*> m_ObjectIndexTable;
std::vector<uint32_t> m_ObjectList;
std::map<uint32_t, Object*> m_ObjectIDTable;
};

View file

@ -2,6 +2,7 @@
#include <iostream>
#include "othertypes.h"
#include "util.h"
namespace si {
@ -10,166 +11,213 @@ Object::Object()
{
type_ = MxOb::Null;
id_ = 0;
last_chunk_split_ = false;
}
/*bool Object::Read(std::ifstream &is)
bool Object::ReplaceWithFile(const char *f)
{
if (chunk->HasChildren()) {
Chunk *child = static_cast<Chunk*>(chunk->GetChildAt(0));
if (child->id() == Chunk::TYPE_LIST) {
for (Children::const_iterator it=child->GetChildren().begin(); it!=child->GetChildren().end(); it++) {
Object *o = new Object();
if (!o->Parse(static_cast<Chunk*>(*it))) {
return false;
}
AppendChild(o);
}
}
std::ifstream is(f);
if (!is.is_open() || !is.good()) {
return false;
}
return true;
return ReplaceWithFile(is);
}
void Object::Write(std::ofstream &os) const
bool Object::ExtractToFile(const char *f) const
{
WriteU16(os, type_);
WriteString(os, presenter_);
WriteU32(os, unknown1_);
WriteString(os, name_);
WriteU32(os, id_);
WriteU32(os, flags_);
WriteU32(os, unknown4_);
WriteU32(os, duration_);
WriteU32(os, loops_);
WriteVector3(os, position_);
WriteVector3(os, direction_);
WriteVector3(os, up_);
WriteU16(os, extra_.size());
WriteBytes(os, extra_);
WriteString(os, filename_);
WriteU32(os, unknown26_);
WriteU32(os, unknown27_);
WriteU32(os, unknown28_);
WriteU32(os, filetype_);
WriteU32(os, unknown29_);
WriteU32(os, unknown30_);
WriteU32(os, unknown31_);
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++) {
Object *child = static_cast<Object*>(*it);
list->AppendChild(child->Export());
}
std::ofstream os(f);
if (!os.is_open() || !os.good()) {
return false;
}
return chunk;
}*/
bytearray Object::GetNormalizedData() const
{
return ToPackedData(filetype(), data_);
return ExtractToFile(os);
}
void Object::SetNormalizedData(const bytearray &d)
bool Object::ReplaceWithFile(std::istream &is)
{
SetChunkedData(ToChunkedData(filetype(), d));
}
data_.clear();
bytearray Object::ToPackedData(MxOb::FileType filetype, const ChunkedData &chunks)
{
bytearray data;
switch (filetype) {
switch (this->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]);
if (ReadU32(is) != RIFF::RIFF_) {
return false;
}
// Copy boilerplate bytes for header
uint32_t *header = reinterpret_cast<uint32_t *>(data.data());
header[0] = SI::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
// Skip total size
ReadU32(is);
// Copy fmt header from chunk 1
memcpy(&header[5], chunks[0].data(), 16);
if (ReadU32(is) != RIFF::WAVE) {
return false;
}
bytearray fmt;
bytearray data;
while (is.good()) {
uint32_t id = ReadU32(is);
uint32_t sz = ReadU32(is);
if (id == RIFF::fmt_) {
fmt.resize(sz);
is.read(fmt.data(), fmt.size());
} else if (id == RIFF::data) {
data.resize(sz);
is.read(data.data(), data.size());
} else {
is.seekg(sz, std::ios::cur);
}
}
if (fmt.empty() || data.empty()) {
return false;
}
data_.push_back(fmt);
WAVFmt *fmt_info = fmt.cast<WAVFmt>();
size_t second_in_bytes = fmt_info->Channels * fmt_info->SampleRate * (fmt_info->BitsPerSample/8);
size_t max;
for (size_t i=0; i<data.size(); i+=max) {
max = std::min(data.size() - i, second_in_bytes);
data_.push_back(bytearray(data.data() + i, max));
}
return true;
}
case MxOb::SMK:
{
// Read header
bytearray hdr(sizeof(SMK2));
is.read(hdr.data(), hdr.size());
// Read frame sizes
SMK2 smk = *hdr.cast<SMK2>();
bytearray frame_sizes(smk.Frames * sizeof(uint32_t));
is.read(frame_sizes.data(), frame_sizes.size());
hdr.append(frame_sizes);
// Read frame types
bytearray frame_types(smk.Frames);
is.read(frame_types.data(), frame_types.size());
hdr.append(frame_types);
// Read Huffman trees
bytearray huffman(smk.TreesSize);
is.read(huffman.data(), huffman.size());
hdr.append(huffman);
// Place header into data vector
data_.resize(smk.Frames + 1);
data_[0] = hdr;
uint32_t *real_sizes = frame_sizes.cast<uint32_t>();
for (uint32_t i=0; i<smk.Frames; i++) {
uint32_t sz = real_sizes[i];
if (sz > 0) {
bytearray &d = data_[i+1];
d.resize(sz);
is.read(d.data(), d.size());
}
}
return true;
}
default:
LogWarning() << "Don't yet know how to chunk type " << RIFF::PrintU32AsString(this->filetype()) << std::endl;
break;
}
return false;
}
bool Object::ExtractToFile(std::ostream &os) const
{
switch (this->filetype()) {
case MxOb::WAV:
{
// Write RIFF header
RIFF::Chk riff = RIFF::BeginChunk(os, RIFF::RIFF_);
WriteU32(os, RIFF::WAVE);
{
RIFF::Chk fmt = RIFF::BeginChunk(os, RIFF::fmt_);
WriteBytes(os, data_.at(0));
RIFF::EndChunk(os, fmt);
}
{
RIFF::Chk data = RIFF::BeginChunk(os, RIFF::data);
// Merge all chunks after the first one
for (size_t i=1; i<data_.size(); i++) {
WriteBytes(os, data_.at(i));
}
RIFF::EndChunk(os, data);
}
RIFF::EndChunk(os, riff);
break;
}
case MxOb::STL:
{
// Make space for BMP header
data.resize(14);
static const uint32_t BMP_HDR_SZ = 14;
// Merge all chunks after the first one
for (size_t i=0; i<chunks.size(); i++) {
data.append(chunks[i]);
// Write BMP header
WriteU16(os, 0x4D42);
// Write placeholder for size
std::ios::pos_type sz_loc = os.tellp();
WriteU32(os, 0);
// Write "reserved" bytes
WriteU32(os, 0);
// Write data offset
WriteU32(os, data_.at(0).size() + BMP_HDR_SZ);
for (size_t i=0; i<data_.size(); i++) {
WriteBytes(os, data_.at(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;
std::ios::pos_type len = os.tellp();
os.seekp(sz_loc);
WriteU32(os, len);
break;
}
case MxOb::FLC:
{
// First chunk is a complete FLIC header, so add it as-is
data.append(chunks[0]);
WriteBytes(os, data_.at(0));
// Subsequent chunks are FLIC frames with an additional 20 byte header that needs to be stripped
const int CUSTOM_HEADER_SZ = 20;
for (size_t i=1; i<chunks.size(); i++) {
data.append(chunks.at(i).data() + CUSTOM_HEADER_SZ, chunks.at(i).size() - CUSTOM_HEADER_SZ);
}
break;
}
case MxOb::SMK:
case MxOb::OBJ:
{
// Simply merge
for (size_t i=0; i<chunks.size(); i++) {
data.append(chunks[i]);
for (size_t i=1; i<data_.size(); i++) {
os.write(data_.at(i).data() + CUSTOM_HEADER_SZ, data_.at(i).size() - CUSTOM_HEADER_SZ);
}
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++) {
data.append(chunks[i]);
LogWarning() << "Didn't know how to extract type '" << RIFF::PrintU32AsString(filetype()) << "', merging..." << std::endl;
/* fall-through */
case MxOb::SMK:
case MxOb::OBJ:
// Simply merge
for (size_t i=0; i<data_.size(); i++) {
WriteBytes(os, data_.at(i));
}
break;
}
return data;
return true;
}
Object::ChunkedData Object::ToChunkedData(MxOb::FileType filetype, const bytearray &chunks)
bytearray Object::ExtractToMemory() const
{
// FIXME: STUB
return ChunkedData();
memorybuf buf;
std::ostream os(&buf);
ExtractToFile(os);
return buf.data();
}
const bytearray &Object::GetFileHeader() const

View file

@ -14,13 +14,18 @@ public:
Object();
void SetChunkedData(const ChunkedData &cd) { data_ = cd; }
#if defined(_WIN32)
LIBWEAVER_EXPORT bool ReplaceWithFile(const wchar_t *f);
LIBWEAVER_EXPORT bool ExtractToFile(const wchar_t *f) const;
#endif
LIBWEAVER_EXPORT bytearray GetNormalizedData() const;
LIBWEAVER_EXPORT void SetNormalizedData(const bytearray &d);
LIBWEAVER_EXPORT bool ReplaceWithFile(const char *f);
LIBWEAVER_EXPORT bool ExtractToFile(const char *f) const;
LIBWEAVER_EXPORT static bytearray ToPackedData(MxOb::FileType filetype, const ChunkedData &chunks);
LIBWEAVER_EXPORT static ChunkedData ToChunkedData(MxOb::FileType filetype, const bytearray &chunks);
LIBWEAVER_EXPORT bool ReplaceWithFile(std::istream &is);
LIBWEAVER_EXPORT bool ExtractToFile(std::ostream &os) const;
LIBWEAVER_EXPORT bytearray ExtractToMemory() const;
LIBWEAVER_EXPORT const bytearray &GetFileHeader() const;
LIBWEAVER_EXPORT bytearray GetFileBody() const;
@ -59,6 +64,10 @@ public:
ChunkedData data_;
bool last_chunk_split_;
private:
};
}

View file

@ -25,31 +25,31 @@ public:
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 */
uint32_t size; // Size of FLIC including this headerdesc << "
uint16_t type; // File type 0xAF11, 0xAF12, 0xAF30, 0xAF44, ...desc << "
uint16_t frames; // Number of frames in first segmentdesc << "
uint16_t width; // FLIC width in pixelsdesc << "
uint16_t height; // FLIC height in pixelsdesc << "
uint16_t depth; // Bits per pixel (usually 8)desc << "
uint16_t flags; // Set to zero or to threedesc << "
uint32_t speed; // Delay between framesdesc << "
uint16_t reserved1; // Set to zerodesc << "
uint32_t created; // Date of FLIC creation (FLC only)desc << "
uint32_t creator; // Serial number or compiler id (FLC only)desc << "
uint32_t updated; // Date of FLIC update (FLC only)desc << "
uint32_t updater; // Serial number (FLC only), see creatordesc << "
uint16_t aspect_dx; // Width of square rectangle (FLC only)desc << "
uint16_t aspect_dy; // Height of square rectangle (FLC only)desc << "
uint16_t ext_flags; // EGI: flags for specific EGI extensionsdesc << "
uint16_t keyframes; // EGI: key-image frequencydesc << "
uint16_t totalframes; // EGI: total number of frames (segments)desc << "
uint32_t req_memory; // EGI: maximum chunk size (uncompressed)desc << "
uint16_t max_regions; // EGI: max. number of regions in a CHK_REGION chunkdesc << "
uint16_t transp_num; // EGI: number of transparent levelsdesc << "
uint8_t reserved2[24]; // Set to zerodesc << "
uint32_t oframe1; // Offset to frame 1 (FLC only)desc << "
uint32_t oframe2; // Offset to frame 2 (FLC only)desc << "
uint8_t reserved3[40]; // Set to zerodesc << "
};
// Copied from https://wiki.multimedia.cx/index.php/Smacker#Header

View file

@ -1,59 +0,0 @@
#ifndef PTR_H
#define PTR_H
namespace si {
/**
* @brief Smart pointer implementation for versions of C++ < 11
*/
template <typename T>
class Ptr
{
public:
Ptr(T *ptr = 0)
{
data_ = ptr;
ref_count_ = new size_t;
*ref_count_ = 1;
}
~Ptr()
{
*ref_count_--;
if (*ref_count_ == 0) {
delete data_;
delete ref_count_;
}
}
Ptr(const Ptr<T> &other)
{
data_ = other.data_;
ref_count_ = other.ref_count_;
*ref_count_++;
}
Ptr<T> &operator=(const Ptr<T> &other)
{
if (this != other) {
*ref_count_--;
if (*ref_count_ == 0) {
delete data_;
delete ref_count_;
}
data_ = other.data_;
ref_count_ = other.ref_count_;
*ref_count_++;
}
}
private:
T *data_;
size_t *ref_count_;
};
}
#endif // PTR_H

View file

@ -1,5 +1,7 @@
#include "sitypes.h"
#include "util.h"
namespace si {
const char *MxOb::GetTypeName(Type type)
@ -56,4 +58,67 @@ std::vector<const char*> MxOb::GetFlagsName(Flags flags)
return names;
}
const char *RIFF::GetTypeDescription(Type t)
{
switch (t) {
case RIFF_:
return "Resource Interchange File Format";
case LIST:
return "List of sub-elements";
case MxSt:
return "Stream";
case MxHd:
return "Interleaf Header";
case MxCh:
return "Data Chunk";
case MxOf:
return "Offset Table";
case pad_:
return "Padding";
case MxOb:
return "Streamable Object";
case MxDa:
return "Data";
case WAVE:
return "WAVE";
case fmt_:
return "WAVE Format";
case OMNI:
return "OMNI";
case data:
return "WAVE Data";
}
return "Unknown";
}
RIFF::Chk RIFF::BeginChunk(std::ostream &os, uint32_t type)
{
Chk stat;
WriteU32(os, type);
stat.size_position = os.tellp();
WriteU32(os, 0);
stat.data_start = os.tellp();
return stat;
}
void RIFF::EndChunk(std::ostream &os, const Chk &stat)
{
std::ios::pos_type now = os.tellp();
uint32_t sz = now - stat.data_start;
os.seekp(stat.size_position);
WriteU32(os, sz);
os.seekp(now);
if (sz%2 == 1) {
char nullterm = 0;
os.write(&nullterm, 1);
}
}
}

View file

@ -3,28 +3,10 @@
#include <fstream>
#include "common.h"
#include "types.h"
namespace si {
class SI
{
public:
enum Type
{
RIFF = 0x46464952,
LIST = 0x5453494c,
MxSt = 0x7453784d,
MxHd = 0x6448784d,
MxCh = 0x6843784d,
MxOf = 0x664f784d,
MxOb = 0x624f784d,
MxDa = 0x6144784d,
pad_ = 0x20646170
};
};
/**
* @brief RIFF chunk type
*
@ -35,9 +17,38 @@ public:
class RIFF
{
public:
enum {
OMNI = 0x494e4d4f
enum Type {
RIFF_ = 0x46464952,
LIST = 0x5453494c,
MxSt = 0x7453784d,
MxHd = 0x6448784d,
MxCh = 0x6843784d,
MxOf = 0x664f784d,
MxOb = 0x624f784d,
MxDa = 0x6144784d,
pad_ = 0x20646170,
OMNI = 0x494e4d4f,
WAVE = 0x45564157,
fmt_ = 0x20746D66,
data = 0x61746164
};
struct Chk
{
std::ios::pos_type size_position;
std::ios::pos_type data_start;
};
static Chk BeginChunk(std::ostream &os, uint32_t type);
static void EndChunk(std::ostream &os, const Chk &stat);
static inline std::string PrintU32AsString(uint32_t u)
{
return std::string((const char *) &u, sizeof(u));
}
static const char *GetTypeDescription(Type t);
};
/**
@ -121,7 +132,7 @@ public:
class pad_ : public RIFF
{
public:
static void WriteArbitraryPadding(std::ofstream &os, uint32_t size);
static void WriteArbitraryPadding(std::ostream &os, uint32_t size);
};
/**

View file

@ -1,11 +1,34 @@
#ifndef TYPES_H
#define TYPES_H
#include <cstdio>
#include <cstring>
#include <map>
#include <streambuf>
#include <string>
#include <ostream>
#include <vector>
#if defined(__GNUC__)
#define LIBWEAVER_PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
#elif defined(_MSC_VER)
#define LIBWEAVER_PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop))
#endif
#ifdef _MSC_VER
#define LIBWEAVER_EXPORT __declspec(dllexport)
#else
#define LIBWEAVER_EXPORT
#endif
#if defined(_WIN32)
#define LIBWEAVER_OS_WINDOWS
#elif defined(__APPLE__)
#define LIBWEAVER_OS_MACOS
#elif defined(__linux__)
#define LIBWEAVER_OS_LINUX
#endif
#if defined(_MSC_VER) && (_MSC_VER < 1600)
// Declare types for MSVC versions less than 2010 (1600) which lacked a stdint.h
typedef unsigned char uint8_t;
@ -30,6 +53,11 @@ public:
{
resize(size);
}
bytearray(const char *data, size_t size)
{
resize(size);
memcpy(this->data(), data, size);
}
template <typename T>
T *cast() { return reinterpret_cast<T*>(data()); }
@ -58,6 +86,27 @@ public:
};
LIBWEAVER_EXPORT class memorybuf : public std::streambuf
{
public:
memorybuf(){}
virtual int_type overflow(int_type c)
{
if (c != EOF) {
char c2 = c;
m_Internal.append(&c2, 1);
}
return c;
}
const bytearray &data() const { return m_Internal; }
private:
bytearray m_Internal;
};
class Vector3
{
public:

View file

@ -2,60 +2,61 @@
#define UTIL_H
#include <fstream>
#include <iostream>
#include "types.h"
namespace si {
inline uint32_t ReadU32(std::ifstream &is)
inline uint32_t ReadU32(std::istream &is)
{
uint32_t u;
is.read((char *) &u, sizeof(u));
return u;
}
inline void WriteU32(std::ofstream &os, uint32_t u)
inline void WriteU32(std::ostream &os, uint32_t u)
{
os.write((const char *) &u, sizeof(u));
}
inline uint16_t ReadU16(std::ifstream &is)
inline uint16_t ReadU16(std::istream &is)
{
uint16_t u;
is.read((char *) &u, sizeof(u));
return u;
}
inline void WriteU16(std::ofstream &os, uint16_t u)
inline void WriteU16(std::ostream &os, uint16_t u)
{
os.write((const char *) &u, sizeof(u));
}
inline uint8_t ReadU8(std::ifstream &is)
inline uint8_t ReadU8(std::istream &is)
{
uint8_t u;
is.read((char *) &u, sizeof(u));
return u;
}
inline void WriteU8(std::ofstream &os, uint8_t u)
inline void WriteU8(std::ostream &os, uint8_t u)
{
os.write((const char *) &u, sizeof(u));
}
inline Vector3 ReadVector3(std::ifstream &is)
inline Vector3 ReadVector3(std::istream &is)
{
Vector3 u;
is.read((char *) &u, sizeof(u));
return u;
}
inline void WriteVector3(std::ofstream &os, Vector3 v)
inline void WriteVector3(std::ostream &os, Vector3 v)
{
os.write((const char *) &v, sizeof(v));
}
inline std::string ReadString(std::ifstream &is)
inline std::string ReadString(std::istream &is)
{
std::string d;
@ -71,7 +72,7 @@ inline std::string ReadString(std::ifstream &is)
return d;
}
inline void WriteString(std::ofstream &os, const std::string &d)
inline void WriteString(std::ostream &os, const std::string &d)
{
os.write(d.c_str(), d.size());
@ -80,7 +81,7 @@ inline void WriteString(std::ofstream &os, const std::string &d)
os.write(&nullterm, 1);
}
inline bytearray ReadBytes(std::ifstream &is, size_t size)
inline bytearray ReadBytes(std::istream &is, size_t size)
{
bytearray d;
@ -90,11 +91,26 @@ inline bytearray ReadBytes(std::ifstream &is, size_t size)
return d;
}
inline void WriteBytes(std::ofstream &os, const bytearray &ba)
inline void WriteBytes(std::ostream &os, const bytearray &ba)
{
os.write(ba.data(), ba.size());
}
inline std::ostream &LogDebug()
{
return std::cout << "[DEBUG] ";
}
inline std::ostream &LogWarning()
{
return std::cerr << "[WARNING] ";
}
inline std::ostream &LogError()
{
return std::cerr << "[ERROR] ";
}
}
#endif // UTIL_H