mirror of
https://github.com/isledecomp/SIEdit.git
synced 2025-05-11 21:30:36 -04:00
code: major refactoring
This commit is contained in:
parent
0a76066185
commit
6cf3d80047
34 changed files with 1064 additions and 1425 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
56
app/siview/infopanel.cpp
Normal 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
32
app/siview/infopanel.h
Normal 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
|
|
@ -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());
|
||||
}
|
|
@ -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
|
|
@ -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();*/
|
||||
}
|
|
@ -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
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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();*/
|
||||
}
|
|
@ -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
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
24
lib/common.h
24
lib/common.h
|
@ -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
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace si {
|
||||
|
|
54
lib/info.h
Normal file
54
lib/info.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
282
lib/object.cpp
282
lib/object.cpp
|
@ -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
|
||||
|
|
19
lib/object.h
19
lib/object.h
|
@ -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:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
59
lib/ptr.h
59
lib/ptr.h
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
49
lib/types.h
49
lib/types.h
|
@ -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:
|
||||
|
|
40
lib/util.h
40
lib/util.h
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue