mirror of
https://github.com/isledecomp/SIEdit.git
synced 2025-05-21 02:00:42 -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)
|
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Multimedia)
|
||||||
|
|
||||||
set(PROJECT_SOURCES
|
set(PROJECT_SOURCES
|
||||||
# siview/chunkmodel.cpp
|
siview/chunkmodel.cpp
|
||||||
# siview/chunkmodel.h
|
siview/chunkmodel.h
|
||||||
# siview/panels/mxch.cpp
|
siview/infopanel.cpp
|
||||||
# siview/panels/mxch.h
|
siview/infopanel.h
|
||||||
# siview/panels/mxhd.cpp
|
siview/siview.cpp
|
||||||
# siview/panels/mxhd.h
|
siview/siview.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
|
|
||||||
|
|
||||||
viewer/bitmappanel.cpp
|
viewer/bitmappanel.cpp
|
||||||
viewer/bitmappanel.h
|
viewer/bitmappanel.h
|
||||||
|
|
|
@ -6,8 +6,12 @@
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
|
|
||||||
|
#include "siview/siview.h"
|
||||||
|
|
||||||
using namespace si;
|
using namespace si;
|
||||||
|
|
||||||
|
const QString MainWindow::kFileFilter = tr("Interleaf Files (*.si)");
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent) :
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
QMainWindow{parent},
|
QMainWindow{parent},
|
||||||
last_set_data_(nullptr)
|
last_set_data_(nullptr)
|
||||||
|
@ -41,6 +45,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
|
||||||
auto replace_btn = new QPushButton(tr("Replace"));
|
auto replace_btn = new QPushButton(tr("Replace"));
|
||||||
action_layout->addWidget(replace_btn);
|
action_layout->addWidget(replace_btn);
|
||||||
|
connect(replace_btn, &QPushButton::clicked, this, &MainWindow::ReplaceClicked);
|
||||||
|
|
||||||
action_layout->addStretch();
|
action_layout->addStretch();
|
||||||
|
|
||||||
|
@ -67,18 +72,8 @@ void MainWindow::OpenFilename(const QString &s)
|
||||||
{
|
{
|
||||||
model_.SetCore(nullptr);
|
model_.SetCore(nullptr);
|
||||||
|
|
||||||
bool r =
|
if (OpenInterleafFileInternal(this, &interleaf_, s)) {
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
interleaf_.Read(s.toStdWString().c_str());
|
|
||||||
#else
|
|
||||||
interleaf_.Read(s.toUtf8());
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
|
|
||||||
if (r) {
|
|
||||||
model_.SetCore(&interleaf_);
|
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 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"));
|
file_menu->addAction(tr("&Save"), this, &MainWindow::SaveFile, tr("Ctrl+S"));
|
||||||
auto save_as_action = file_menu->addAction(tr("Save &As"));
|
|
||||||
|
file_menu->addAction(tr("Save &As"), this, &MainWindow::SaveFileAs, tr("Ctrl+Shift+S"));
|
||||||
|
|
||||||
file_menu->addSeparator();
|
file_menu->addSeparator();
|
||||||
|
|
||||||
auto export_action = file_menu->addAction(tr("&Export"));
|
file_menu->addAction(tr("&View SI File"), this, &MainWindow::ViewSIFile);
|
||||||
connect(export_action, &QAction::triggered, this, &MainWindow::ExportFile);
|
|
||||||
|
|
||||||
file_menu->addSeparator();
|
file_menu->addSeparator();
|
||||||
|
|
||||||
auto exit_action = file_menu->addAction(tr("E&xit"));
|
file_menu->addAction(tr("E&xit"), this, &MainWindow::close);
|
||||||
connect(exit_action, &QAction::triggered, this, &MainWindow::close);
|
|
||||||
|
|
||||||
setMenuBar(menubar);
|
setMenuBar(menubar);
|
||||||
}
|
}
|
||||||
|
@ -136,27 +130,102 @@ void MainWindow::ExtractObject(si::Object *obj)
|
||||||
|
|
||||||
QString s = QFileDialog::getSaveFileName(this, tr("Export Object"), filename);
|
QString s = QFileDialog::getSaveFileName(this, tr("Export Object"), filename);
|
||||||
if (!s.isEmpty()) {
|
if (!s.isEmpty()) {
|
||||||
QFile f(s);
|
if (!obj->ExtractToFile(
|
||||||
if (f.open(QFile::WriteOnly)) {
|
#ifdef Q_OS_WINDOWS
|
||||||
bytearray b = obj->GetNormalizedData();
|
s.toStdWString().c_str()
|
||||||
f.write(b.data(), b.size());
|
#else
|
||||||
f.close();
|
s.toUtf8()
|
||||||
} else {
|
#endif
|
||||||
|
)) {
|
||||||
QMessageBox::critical(this, QString(), tr("Failed to write to file \"%1\".").arg(s));
|
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()
|
void MainWindow::OpenFile()
|
||||||
{
|
{
|
||||||
QString s = QFileDialog::getOpenFileName(this, QString(), QString(), tr("Interleaf Files (*.si)"));
|
QString s = GetOpenFileName();
|
||||||
if (!s.isEmpty()) {
|
if (!s.isEmpty()) {
|
||||||
OpenFilename(s);
|
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)
|
void MainWindow::SelectionChanged(const QModelIndex &index)
|
||||||
|
@ -212,3 +281,22 @@ void MainWindow::ExtractClicked()
|
||||||
{
|
{
|
||||||
ExtractObject(last_set_data_);
|
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:
|
signals:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//bool CloseFile();
|
|
||||||
|
|
||||||
void InitializeMenuBar();
|
void InitializeMenuBar();
|
||||||
|
|
||||||
void SetPanel(Panel *panel, si::Object *chunk);
|
void SetPanel(Panel *panel, si::Object *chunk);
|
||||||
|
|
||||||
void ExtractObject(si::Object *obj);
|
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_;
|
QStackedWidget *config_stack_;
|
||||||
|
|
||||||
|
@ -47,21 +52,25 @@ private:
|
||||||
|
|
||||||
si::Object *last_set_data_;
|
si::Object *last_set_data_;
|
||||||
|
|
||||||
private slots:
|
QString current_filename_;
|
||||||
void OpenFile();
|
|
||||||
//bool SaveFile();
|
|
||||||
//bool SaveFileAs();
|
|
||||||
|
|
||||||
void ExportFile();
|
private slots:
|
||||||
|
void NewFile();
|
||||||
|
void OpenFile();
|
||||||
|
bool SaveFile();
|
||||||
|
bool SaveFileAs();
|
||||||
|
|
||||||
void SelectionChanged(const QModelIndex &index);
|
void SelectionChanged(const QModelIndex &index);
|
||||||
|
|
||||||
void ShowContextMenu(const QPoint &p);
|
void ShowContextMenu(const QPoint &p);
|
||||||
|
|
||||||
void ExtractSelectedItems();
|
void ExtractSelectedItems();
|
||||||
|
|
||||||
void ExtractClicked();
|
void ExtractClicked();
|
||||||
|
|
||||||
|
void ReplaceClicked();
|
||||||
|
|
||||||
|
void ViewSIFile();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "chunkmodel.h"
|
#include "chunkmodel.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sitypes.h>
|
||||||
|
|
||||||
#define super Model
|
#define super Model
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ int ChunkModel::columnCount(const QModelIndex &parent) const
|
||||||
|
|
||||||
QVariant ChunkModel::data(const QModelIndex &index, int role) 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) {
|
if (!c) {
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
@ -29,16 +30,15 @@ QVariant ChunkModel::data(const QModelIndex &index, int role) const
|
||||||
switch (index.column()) {
|
switch (index.column()) {
|
||||||
case kColType:
|
case kColType:
|
||||||
// Convert 4-byte ID to QString
|
// 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:
|
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:
|
case kColDesc:
|
||||||
return QString::fromUtf8(c->GetTypeDescription());
|
return QString::fromUtf8(RIFF::GetTypeDescription(static_cast<RIFF::Type>(c->GetType())));
|
||||||
case kColObjectID:
|
case kColObjectID:
|
||||||
if (c->type() == Chunk::TYPE_MxOb) {
|
uint32_t i = c->GetObjectID();
|
||||||
return c->data("ID").toU32();
|
if (i != Info::NULL_OBJECT_ID) {
|
||||||
} else if (c->type() == Chunk::TYPE_MxCh) {
|
return QString::number(i);
|
||||||
return c->data("Object").toU32();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef CHUNKMODEL_H
|
#ifndef CHUNKMODEL_H
|
||||||
#define CHUNKMODEL_H
|
#define CHUNKMODEL_H
|
||||||
|
|
||||||
#include <chunk.h>
|
#include <info.h>
|
||||||
|
|
||||||
#include "model.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;
|
using namespace si;
|
||||||
|
|
||||||
SIViewDialog::SIViewDialog(Mode mode, Chunk *riff, QWidget *parent) :
|
SIViewDialog::SIViewDialog(Info *riff, QWidget *parent) :
|
||||||
QDialog(parent),
|
QWidget(parent, Qt::Window),
|
||||||
root_(riff),
|
root_(riff),
|
||||||
last_set_data_(nullptr)
|
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);
|
auto layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
@ -31,95 +31,13 @@ SIViewDialog::SIViewDialog(Mode mode, Chunk *riff, QWidget *parent) :
|
||||||
config_stack_ = new QStackedWidget();
|
config_stack_ = new QStackedWidget();
|
||||||
splitter->addWidget(config_stack_);
|
splitter->addWidget(config_stack_);
|
||||||
|
|
||||||
panel_blank_ = new Panel();
|
panel_ = new InfoPanel();
|
||||||
config_stack_->addWidget(panel_blank_);
|
config_stack_->addWidget(panel_);
|
||||||
|
|
||||||
panel_mxhd_ = new MxHdPanel();
|
splitter->setSizes({99999, 99999});
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SIViewDialog::SelectionChanged(const QModelIndex &index)
|
void SIViewDialog::SelectionChanged(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
Panel *p = panel_blank_;
|
panel_->SetData(static_cast<Info*>(index.internalPointer()));
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +1,36 @@
|
||||||
#ifndef SIVIEW_H
|
#ifndef SIVIEW_H
|
||||||
#define SIVIEW_H
|
#define SIVIEW_H
|
||||||
|
|
||||||
|
#include <interleaf.h>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
|
|
||||||
#include "chunkmodel.h"
|
#include "chunkmodel.h"
|
||||||
#include "panels/mxch.h"
|
#include "infopanel.h"
|
||||||
#include "panels/mxhd.h"
|
|
||||||
#include "panels/mxob.h"
|
|
||||||
#include "panels/mxof.h"
|
|
||||||
#include "panels/riff.h"
|
|
||||||
#include "panel.h"
|
|
||||||
|
|
||||||
class SIViewDialog : public QDialog
|
class SIViewDialog : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum Mode {
|
SIViewDialog(si::Info *info, QWidget *parent = nullptr);
|
||||||
Import,
|
|
||||||
Export
|
|
||||||
};
|
|
||||||
|
|
||||||
SIViewDialog(Mode mode, si::Chunk *riff, QWidget *parent = nullptr);
|
std::unique_ptr<si::Interleaf> temp;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetPanel(Panel *panel, si::Chunk *chunk);
|
|
||||||
|
|
||||||
QStackedWidget *config_stack_;
|
QStackedWidget *config_stack_;
|
||||||
|
|
||||||
ChunkModel chunk_model_;
|
ChunkModel chunk_model_;
|
||||||
|
|
||||||
Panel *panel_blank_;
|
InfoPanel *panel_;
|
||||||
RIFFPanel *panel_riff_;
|
|
||||||
MxHdPanel *panel_mxhd_;
|
|
||||||
MxChPanel *panel_mxch_;
|
|
||||||
MxOfPanel *panel_mxof_;
|
|
||||||
MxObPanel *panel_mxob_;
|
|
||||||
|
|
||||||
si::Chunk *last_set_data_;
|
const si::Info *last_set_data_;
|
||||||
si::Chunk *root_;
|
const si::Info *root_;
|
||||||
|
|
||||||
|
std::unique_ptr<si::Interleaf> temp_interleaf_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void SelectionChanged(const QModelIndex &index);
|
void SelectionChanged(const QModelIndex &index);
|
||||||
|
|
||||||
void ImmediateReweave();
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SIVIEW_H
|
#endif // SIVIEW_H
|
||||||
|
|
|
@ -27,12 +27,11 @@ BitmapPanel::BitmapPanel(QWidget *parent)
|
||||||
void BitmapPanel::OnOpeningData(void *data)
|
void BitmapPanel::OnOpeningData(void *data)
|
||||||
{
|
{
|
||||||
si::Object *o = static_cast<si::Object*>(data);
|
si::Object *o = static_cast<si::Object*>(data);
|
||||||
|
si::bytearray d = o->ExtractToMemory();
|
||||||
si::bytearray processed = o->GetNormalizedData();
|
QByteArray b(d.data(), d.size());
|
||||||
QByteArray b(processed.data(), processed.size());
|
QBuffer read_buf(&b);
|
||||||
QBuffer buf(&b);
|
|
||||||
QImage img;
|
QImage img;
|
||||||
img.load(&buf, "BMP");
|
img.load(&read_buf, "BMP");
|
||||||
|
|
||||||
img_lbl_->setPixmap(QPixmap::fromImage(img));
|
img_lbl_->setPixmap(QPixmap::fromImage(img));
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
option(LIBWEAVER_BUILD_DOXYGEN "Build Doxygen documentation" OFF)
|
option(LIBWEAVER_BUILD_DOXYGEN "Build Doxygen documentation" OFF)
|
||||||
|
|
||||||
set(LIBWEAVER_SOURCES
|
set(LIBWEAVER_SOURCES
|
||||||
common.h
|
|
||||||
core.cpp
|
core.cpp
|
||||||
core.h
|
core.h
|
||||||
|
info.h
|
||||||
interleaf.cpp
|
interleaf.cpp
|
||||||
interleaf.h
|
interleaf.h
|
||||||
object.cpp
|
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 <vector>
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
namespace si {
|
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 <cmath>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "othertypes.h"
|
#include "othertypes.h"
|
||||||
|
@ -10,170 +11,220 @@
|
||||||
|
|
||||||
namespace si {
|
namespace si {
|
||||||
|
|
||||||
|
static const uint32_t kMinimumChunkSize = 8;
|
||||||
|
|
||||||
Interleaf::Interleaf()
|
Interleaf::Interleaf()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interleaf::Clear()
|
void Interleaf::Clear()
|
||||||
{
|
{
|
||||||
|
m_Info.clear();
|
||||||
m_BufferSize = 0;
|
m_BufferSize = 0;
|
||||||
m_OffsetTable.clear();
|
m_ObjectIDTable.clear();
|
||||||
m_ObjectIndexTable.clear();
|
m_ObjectList.clear();
|
||||||
DeleteChildren();
|
DeleteChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interleaf::Read(const char *f)
|
Interleaf::Error Interleaf::Read(const char *f)
|
||||||
{
|
{
|
||||||
std::ifstream is(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()) {
|
if (!is.is_open() || !is.good()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return Read(is);
|
return Read(is);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interleaf::Read(const wchar_t *f)
|
Interleaf::Error Interleaf::Write(const wchar_t *f) const
|
||||||
{
|
{
|
||||||
std::ifstream is(f);
|
std::ostream os(f);
|
||||||
if (!is.is_open() || !is.good()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return Read(is);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*bool Interleaf::Write(const char *f) const
|
|
||||||
{
|
|
||||||
std::ofstream os(f);
|
|
||||||
if (!os.is_open() || !os.good()) {
|
if (!os.is_open() || !os.good()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return Write(os);
|
return Write(os);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool Interleaf::Write(const wchar_t *f) const
|
Interleaf::Error Interleaf::ReadChunk(Core *parent, std::istream &is, Info *info)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
uint32_t offset = is.tellg();
|
uint32_t offset = is.tellg();
|
||||||
uint32_t id = ReadU32(is);
|
uint32_t id = ReadU32(is);
|
||||||
uint32_t size = ReadU32(is);
|
uint32_t size = ReadU32(is);
|
||||||
uint32_t end = uint32_t(is.tellg()) + size;
|
uint32_t end = uint32_t(is.tellg()) + size;
|
||||||
|
|
||||||
std::cout << PrintU32AsString(id)
|
info->SetType(id);
|
||||||
<< " Offset: 0x" << std::hex << offset
|
info->SetOffset(offset);
|
||||||
<< " Size: " << std::dec << size << std::endl;
|
info->SetSize(size);
|
||||||
|
|
||||||
switch (static_cast<SI::Type>(id)) {
|
std::stringstream desc;
|
||||||
case SI::RIFF:
|
|
||||||
|
switch (static_cast<RIFF::Type>(id)) {
|
||||||
|
case RIFF::RIFF_:
|
||||||
{
|
{
|
||||||
// Require RIFF type to be OMNI
|
// Require RIFF type to be OMNI
|
||||||
uint32_t riff_type = ReadU32(is);
|
uint32_t riff_type = ReadU32(is);
|
||||||
std::cout << " Type: " << PrintU32AsString(riff_type) << std::endl;
|
|
||||||
if (riff_type != RIFF::OMNI) {
|
if (riff_type != RIFF::OMNI) {
|
||||||
return false;
|
return ERROR_INVALID_INPUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
desc << "Type: " << RIFF::PrintU32AsString(riff_type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SI::MxHd:
|
case RIFF::MxHd:
|
||||||
{
|
{
|
||||||
m_Version = ReadU32(is);
|
m_Version = ReadU32(is);
|
||||||
std::cout << " Version: " << m_Version << std::endl;
|
desc << "Version: " << m_Version << std::endl;
|
||||||
|
|
||||||
m_BufferSize = ReadU32(is);
|
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);
|
m_BufferCount = ReadU32(is);
|
||||||
std::cout << " Buffer Count: " << m_BufferCount << std::endl;
|
desc << std::endl << "Buffer Count: " << std::dec << m_BufferCount << std::endl;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SI::pad_:
|
case RIFF::pad_:
|
||||||
is.seekg(size, std::ios::cur);
|
is.seekg(size, std::ios::cur);
|
||||||
break;
|
break;
|
||||||
case SI::MxOf:
|
case RIFF::MxOf:
|
||||||
{
|
{
|
||||||
m_OffsetCount = ReadU32(is);
|
uint32_t offset_count = ReadU32(is);
|
||||||
std::cout << " Count: " << m_OffsetCount << std::endl;
|
|
||||||
|
|
||||||
uint32_t i = 0;
|
desc << "Count: " << offset_count;
|
||||||
while (is.tellg() < end) {
|
|
||||||
uint32_t offset = ReadU32(is);
|
uint32_t real_count = (size - sizeof(uint32_t)) / sizeof(uint32_t);
|
||||||
std::cout << " " << i << ": 0x" << std::hex << offset << std::endl;
|
m_ObjectList.resize(real_count);
|
||||||
m_OffsetTable.push_back(offset);
|
for (uint32_t i = 0; i < real_count; i++) {
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
case SI::LIST:
|
case RIFF::LIST:
|
||||||
{
|
{
|
||||||
uint32_t list_type = ReadU32(is);
|
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;
|
uint32_t list_count = 0;
|
||||||
if (list_type == SI::MxCh) {
|
if (list_type == RIFF::MxCh) {
|
||||||
list_count = ReadU32(is);
|
list_count = ReadU32(is);
|
||||||
std::cout << " Count: " << list_count << std::endl;
|
desc << "Count: " << list_count << std::endl;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SI::MxSt:
|
case RIFF::MxSt:
|
||||||
case SI::MxDa:
|
case RIFF::MxDa:
|
||||||
|
case RIFF::WAVE:
|
||||||
|
case RIFF::fmt_:
|
||||||
|
case RIFF::data:
|
||||||
|
case RIFF::OMNI:
|
||||||
// Types with no data
|
// Types with no data
|
||||||
break;
|
break;
|
||||||
case SI::MxOb:
|
case RIFF::MxOb:
|
||||||
{
|
{
|
||||||
Object *o = ReadObject(is);
|
Object *o = NULL;
|
||||||
parent->AppendChild(o);
|
|
||||||
m_ObjectIndexTable[o->id()] = o;
|
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;
|
parent = o;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SI::MxCh:
|
case RIFF::MxCh:
|
||||||
{
|
{
|
||||||
uint16_t flags = ReadU16(is);
|
uint16_t flags = ReadU16(is);
|
||||||
|
desc << "Flags: 0x" << std::hex << flags << std::endl;
|
||||||
|
|
||||||
uint32_t object = ReadU32(is);
|
uint32_t object = ReadU32(is);
|
||||||
|
desc << "Object: " << std::dec << object << std::endl;
|
||||||
|
|
||||||
uint32_t time = ReadU32(is);
|
uint32_t time = ReadU32(is);
|
||||||
|
desc << "Time: " << time << std::endl;
|
||||||
|
|
||||||
uint32_t data_sz = ReadU32(is);
|
uint32_t data_sz = ReadU32(is);
|
||||||
|
desc << "Size: " << data_sz << std::endl;
|
||||||
|
|
||||||
bytearray data = ReadBytes(is, size - MxCh::HEADER_SIZE);
|
bytearray data = ReadBytes(is, size - MxCh::HEADER_SIZE);
|
||||||
|
|
||||||
Object *o = m_ObjectIndexTable.at(object);
|
info->SetObjectID(object);
|
||||||
if (!o) {
|
info->SetData(data);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
o->data_.push_back(data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// 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
|
// Check alignment, if there's not enough room to for another segment, skip ahead
|
||||||
if (m_BufferSize > 0) {
|
if (m_BufferSize > 0) {
|
||||||
uint32_t offset_in_buffer = is.tellg()%m_BufferSize;
|
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);
|
is.seekg(m_BufferSize-offset_in_buffer, std::ios::cur);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read next child
|
// Read next child
|
||||||
if (!ReadChunk(parent, is)) {
|
Info *subinfo = new Info();
|
||||||
return false;
|
info->AppendChild(subinfo);
|
||||||
|
Error e = ReadChunk(parent, is, subinfo);
|
||||||
|
if (e != ERROR_SUCCESS) {
|
||||||
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info->SetDescription(desc.str());
|
||||||
|
|
||||||
if (is.tellg() < end) {
|
if (is.tellg() < end) {
|
||||||
is.seekg(end, std::ios::beg);
|
is.seekg(end, std::ios::beg);
|
||||||
}
|
}
|
||||||
|
@ -182,315 +233,294 @@ bool Interleaf::ReadChunk(Core *parent, std::ifstream &is)
|
||||||
is.seekg(1, std::ios::cur);
|
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));
|
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);
|
o->presenter_ = ReadString(is);
|
||||||
std::cout << " Presenter: " << o->presenter_ << std::endl;
|
desc << "Presenter: " << o->presenter_ << std::endl;
|
||||||
o->unknown1_ = ReadU32(is);
|
o->unknown1_ = ReadU32(is);
|
||||||
std::cout << " Unknown1: " << o->unknown1_ << std::endl;
|
desc << "Unknown1: " << o->unknown1_ << std::endl;
|
||||||
o->name_ = ReadString(is);
|
o->name_ = ReadString(is);
|
||||||
std::cout << " Name: " << o->name_ << std::endl;
|
desc << "Name: " << o->name_ << std::endl;
|
||||||
o->id_ = ReadU32(is);
|
o->id_ = ReadU32(is);
|
||||||
std::cout << " ID: " << o->id_ << std::endl;
|
desc << "ID: " << o->id_ << std::endl;
|
||||||
o->flags_ = ReadU32(is);
|
o->flags_ = ReadU32(is);
|
||||||
std::cout << " Flags: " << o->flags_ << std::endl;
|
desc << "Flags: " << o->flags_ << std::endl;
|
||||||
o->unknown4_ = ReadU32(is);
|
o->unknown4_ = ReadU32(is);
|
||||||
std::cout << " Unknown4: " << o->unknown4_ << std::endl;
|
desc << "Unknown4: " << o->unknown4_ << std::endl;
|
||||||
o->duration_ = ReadU32(is);
|
o->duration_ = ReadU32(is);
|
||||||
std::cout << " Duration: " << o->duration_ << std::endl;
|
desc << "Duration: " << o->duration_ << std::endl;
|
||||||
o->loops_ = ReadU32(is);
|
o->loops_ = ReadU32(is);
|
||||||
std::cout << " Loops: " << o->loops_ << std::endl;
|
desc << "Loops: " << o->loops_ << std::endl;
|
||||||
o->position_ = ReadVector3(is);
|
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);
|
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);
|
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);
|
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);
|
o->extra_ = ReadBytes(is, extra_sz);
|
||||||
|
|
||||||
if (o->type_ != MxOb::Presenter && o->type_ != MxOb::World) {
|
if (o->type_ != MxOb::Presenter && o->type_ != MxOb::World) {
|
||||||
o->filename_ = ReadString(is);
|
o->filename_ = ReadString(is);
|
||||||
std::cout << " Filename: " << o->filename_ << std::endl;
|
desc << "Filename: " << o->filename_ << std::endl;
|
||||||
o->unknown26_ = ReadU32(is);
|
o->unknown26_ = ReadU32(is);
|
||||||
std::cout << " Unknown26: " << o->unknown26_ << std::endl;
|
desc << "Unknown26: " << o->unknown26_ << std::endl;
|
||||||
o->unknown27_ = ReadU32(is);
|
o->unknown27_ = ReadU32(is);
|
||||||
std::cout << " Unknown27: " << o->unknown27_ << std::endl;
|
desc << "Unknown27: " << o->unknown27_ << std::endl;
|
||||||
o->unknown28_ = ReadU32(is);
|
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));
|
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);
|
o->unknown29_ = ReadU32(is);
|
||||||
std::cout << " Unknown29: " << o->unknown29_ << std::endl;
|
desc << "Unknown29: " << o->unknown29_ << std::endl;
|
||||||
o->unknown30_ = ReadU32(is);
|
o->unknown30_ = ReadU32(is);
|
||||||
std::cout << " Unknown30: " << o->unknown30_ << std::endl;
|
desc << "Unknown30: " << o->unknown30_ << std::endl;
|
||||||
|
|
||||||
if (o->filetype_ == MxOb::WAV) {
|
if (o->filetype_ == MxOb::WAV) {
|
||||||
o->unknown31_ = ReadU32(is);
|
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;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interleaf::Read(std::ifstream &is)
|
Interleaf::Error Interleaf::Read(std::istream &is)
|
||||||
{
|
{
|
||||||
Clear();
|
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) {
|
if (m_BufferSize == 0) {
|
||||||
return false;
|
LogError() << "Buffer size must be set to write" << std::endl;
|
||||||
|
return ERROR_INVALID_BUFFER_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Chunk *hd = riff->FindChildWithType(Chunk::TYPE_MxHd);
|
RIFF::Chk riff = RIFF::BeginChunk(os, RIFF::RIFF_);
|
||||||
if (!hd) {
|
WriteU32(os, RIFF::OMNI);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
version_ = hd->data("Version");
|
std::ios::pos_type offset_table_pos;
|
||||||
if (version_ == 0) {
|
|
||||||
// Unknown version
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
WriteU32(os, m_Version);
|
||||||
if (!of) {
|
WriteU32(os, m_BufferSize);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Data &offset_data = of->data("Offsets");
|
if (m_Version == 0x00020002) {
|
||||||
const uint32_t *offset_table = reinterpret_cast<const uint32_t *>(offset_data.data());
|
WriteU32(os, m_BufferCount);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RIFF::EndChunk(os, mxhd);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
{
|
||||||
}
|
// MxOf
|
||||||
|
RIFF::Chk mxof = RIFF::BeginChunk(os, RIFF::MxOf);
|
||||||
|
|
||||||
Chunk *Interleaf::Export() const
|
WriteU32(os, GetChildCount());
|
||||||
{
|
|
||||||
Chunk *riff = new Chunk(Chunk::TYPE_RIFF);
|
|
||||||
riff->data("Format") = RIFF::OMNI;
|
|
||||||
|
|
||||||
Chunk *mxhd = new Chunk(Chunk::TYPE_MxHd);
|
offset_table_pos = os.tellp();
|
||||||
mxhd->data("Version") = version_;
|
|
||||||
mxhd->data("BufferSize") = buffer_size_;
|
|
||||||
mxhd->data("BufferCount") = buffer_count_;
|
|
||||||
riff->AppendChild(mxhd);
|
|
||||||
|
|
||||||
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
|
RIFF::EndChunk(os, mxof);
|
||||||
// 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)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
for (size_t i = 0; i < GetChildCount(); i++) {
|
||||||
{
|
Object *child = static_cast<Object*>(GetChildAt(i));
|
||||||
if (chunk->type() != Chunk::TYPE_MxSt) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Chunk *obj_chunk = static_cast<Chunk*>(chunk->GetChildAt(0));
|
uint32_t mxst_offset = os.tellp();
|
||||||
if (!obj_chunk) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object *obj = new Object();
|
os.seekp(size_t(offset_table_pos) + i * sizeof(uint32_t));
|
||||||
if (!obj->Parse(obj_chunk)) {
|
WriteU32(os, mxst_offset);
|
||||||
return false;
|
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) {
|
// MxOb
|
||||||
typedef std::map<uint32_t, Object::ChunkedData> ChunkMap;
|
WriteObject(os, child);
|
||||||
ChunkMap data;
|
}
|
||||||
|
|
||||||
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++) {
|
WriteU32(os, RIFF::MxDa);
|
||||||
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");
|
|
||||||
|
|
||||||
// For split chunks, join them together
|
// First, interleave headers
|
||||||
if (joining_chunk > 0) {
|
std::vector<Object*> objects;
|
||||||
data[obj_id].back().append(chunk_data);
|
objects.reserve(child->GetChildCount() + 1);
|
||||||
if (data[obj_id].back().size() == joining_chunk) {
|
objects.push_back(child);
|
||||||
joining_chunk = 0;
|
for (size_t j=0; j<child->GetChildCount(); j++) {
|
||||||
}
|
objects.push_back(static_cast<Object*>(child->GetChildAt(j)));
|
||||||
} else {
|
|
||||||
if (flags & MxCh::FLAG_SPLIT) {
|
|
||||||
joining_chunk = mxch->data("DataSize");
|
|
||||||
}
|
|
||||||
|
|
||||||
data[obj_id].push_back(chunk_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InterleaveObjects(os, objects);
|
||||||
|
|
||||||
|
RIFF::EndChunk(os, list_mxda);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RIFF::EndChunk(os, mxst);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ChunkMap::const_iterator it=data.begin(); it!=data.end(); it++) {
|
// Fill remainder with padding
|
||||||
Object *o = obj->FindSubObjectWithID(it->first);
|
if (os.tellp()%m_BufferSize != 0) {
|
||||||
if (o) {
|
uint32_t current_buf = os.tellp() / m_BufferSize;
|
||||||
o->SetChunkedData(it->second);
|
uint32_t target_sz = (current_buf + 1) * m_BufferSize;
|
||||||
} else {
|
|
||||||
std::cout << "Failed to find object with ID " << it->first << std::endl;
|
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
|
struct ChunkStatus
|
||||||
{
|
{
|
||||||
ChunkStatus()
|
|
||||||
{
|
|
||||||
object = NULL;
|
|
||||||
index = 0;
|
|
||||||
time = 0;
|
|
||||||
end_chunk = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object *object;
|
Object *object;
|
||||||
size_t index;
|
size_t index;
|
||||||
uint32_t time;
|
uint32_t time;
|
||||||
bool end_chunk;
|
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();
|
// Set up status vector
|
||||||
mxst->AppendChild(mxob);
|
for (size_t i=0; i<objects.size(); i++) {
|
||||||
|
status[i].object = objects.at(i);
|
||||||
Chunk *chunklst = new Chunk(Chunk::TYPE_LIST);
|
status[i].index = 0;
|
||||||
chunklst->data("Format") = Chunk::TYPE_MxDa;
|
status[i].time = 0;
|
||||||
mxst->AppendChild(chunklst);
|
status[i].end_chunk = false;
|
||||||
|
|
||||||
// Set up chunking status vector
|
|
||||||
std::vector<ChunkStatus> chunk_status(obj->GetChildCount() + 1);
|
|
||||||
chunk_status[0].object = obj;
|
|
||||||
for (size_t i=0; i<obj->GetChildCount(); i++) {
|
|
||||||
chunk_status[i+1].object = static_cast<Object*>(obj->GetChildAt(i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, interleave all headers (first chunk)
|
// First, interleave headers
|
||||||
for (std::vector<ChunkStatus>::iterator it=chunk_status.begin(); it!=chunk_status.end(); it++) {
|
for (size_t i=0; i<status.size(); i++) {
|
||||||
Object *working_obj = it->object;
|
ChunkStatus &s = status[i];
|
||||||
if (!working_obj->data().empty()) {
|
Object *o = s.object;
|
||||||
chunklst->AppendChild(ExportMxCh(0, working_obj->id(), 0, working_obj->data().front()));
|
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) {
|
while (true) {
|
||||||
// Find next chunk
|
// 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
|
// Check if we've already written all these chunks
|
||||||
if (it->index >= it->object->data().size()) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find earliest chunk to write
|
// Find earliest chunk to write
|
||||||
if (!status || it->time < status->time) {
|
if (!s || it->time < s->time) {
|
||||||
status = &(*it);
|
s = &(*it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!status) {
|
if (!s) {
|
||||||
// Assume chunks are all done
|
// Assume chunks are all done
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object *working_obj = status->object;
|
Object *obj = s->object;
|
||||||
const bytearray &data = working_obj->data().at(status->index);
|
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
|
// Increment time
|
||||||
switch (working_obj->filetype()) {
|
switch (obj->filetype()) {
|
||||||
case MxOb::WAV:
|
case MxOb::WAV:
|
||||||
{
|
{
|
||||||
const WAVFmt *fmt = working_obj->GetFileHeader().cast<WAVFmt>();
|
const WAVFmt *fmt = obj->GetFileHeader().cast<WAVFmt>();
|
||||||
status->time += (data.size() * 1000) / (fmt->BitsPerSample/8) / fmt->Channels / fmt->SampleRate;
|
s->time += round(double(data.size() * 1000) / (fmt->BitsPerSample/8) / fmt->Channels / fmt->SampleRate);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MxOb::SMK:
|
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;
|
int32_t fps;
|
||||||
if (frame_rate > 0) {
|
if (frame_rate > 0) {
|
||||||
fps = 1000/frame_rate;
|
fps = 1000/frame_rate;
|
||||||
|
@ -499,37 +529,64 @@ Chunk *Interleaf::ExportStream(Object *obj) const
|
||||||
} else {
|
} else {
|
||||||
fps = 10;
|
fps = 10;
|
||||||
}
|
}
|
||||||
status->time += 1000/fps;
|
s->time += 1000/fps;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MxOb::FLC:
|
case MxOb::FLC:
|
||||||
status->time += working_obj->GetFileHeader().cast<FLIC>()->speed;
|
s->time += obj->GetFileHeader().cast<FLIC>()->speed;
|
||||||
break;
|
break;
|
||||||
case MxOb::STL:
|
case MxOb::STL:
|
||||||
case MxOb::OBJ:
|
case MxOb::OBJ:
|
||||||
// Unaffected by time
|
// Unaffected by time
|
||||||
break;
|
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);
|
uint32_t total_sz = data.size() + MxCh::HEADER_SIZE + kMinimumChunkSize;
|
||||||
mxch->data("Flags") = flags;
|
|
||||||
mxch->data("Object") = object_id;
|
uint32_t start_buffer = os.tellp() / m_BufferSize;
|
||||||
mxch->data("Time") = time;
|
uint32_t stop_buffer = (uint32_t(os.tellp()) + total_sz) / m_BufferSize;
|
||||||
mxch->data("DataSize") = data.size();
|
|
||||||
mxch->data("Data") = data;
|
if (start_buffer != stop_buffer) {
|
||||||
return mxch;
|
// 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 <fstream>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
#include "info.h"
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
|
|
||||||
namespace si {
|
namespace si {
|
||||||
|
@ -11,32 +12,50 @@ namespace si {
|
||||||
class Interleaf : public Core
|
class Interleaf : public Core
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum Error
|
||||||
|
{
|
||||||
|
ERROR_SUCCESS,
|
||||||
|
ERROR_IO,
|
||||||
|
ERROR_INVALID_INPUT,
|
||||||
|
ERROR_INVALID_BUFFER_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
LIBWEAVER_EXPORT Interleaf();
|
LIBWEAVER_EXPORT Interleaf();
|
||||||
|
|
||||||
LIBWEAVER_EXPORT void Clear();
|
LIBWEAVER_EXPORT void Clear();
|
||||||
|
|
||||||
LIBWEAVER_EXPORT bool Read(const char *f);
|
LIBWEAVER_EXPORT Error Read(const char *f);
|
||||||
LIBWEAVER_EXPORT bool Read(const wchar_t *f);
|
LIBWEAVER_EXPORT Error Write(const char *f) const;
|
||||||
|
|
||||||
//LIBWEAVER_EXPORT bool Write(const char *f) const;
|
#ifdef _WIN32
|
||||||
//LIBWEAVER_EXPORT bool Write(const wchar_t *f) const;
|
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:
|
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_Version;
|
||||||
uint32_t m_BufferSize;
|
uint32_t m_BufferSize;
|
||||||
uint32_t m_BufferCount;
|
uint32_t m_BufferCount;
|
||||||
|
|
||||||
uint32_t m_OffsetCount;
|
std::vector<uint32_t> m_ObjectList;
|
||||||
std::vector<uint32_t> m_OffsetTable;
|
std::map<uint32_t, Object*> m_ObjectIDTable;
|
||||||
|
|
||||||
std::map<uint32_t, Object*> m_ObjectIndexTable;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
282
lib/object.cpp
282
lib/object.cpp
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "othertypes.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
namespace si {
|
namespace si {
|
||||||
|
@ -10,166 +11,213 @@ Object::Object()
|
||||||
{
|
{
|
||||||
type_ = MxOb::Null;
|
type_ = MxOb::Null;
|
||||||
id_ = 0;
|
id_ = 0;
|
||||||
|
last_chunk_split_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*bool Object::Read(std::ifstream &is)
|
bool Object::ReplaceWithFile(const char *f)
|
||||||
{
|
{
|
||||||
|
std::ifstream is(f);
|
||||||
|
if (!is.is_open() || !is.good()) {
|
||||||
if (chunk->HasChildren()) {
|
return false;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return ReplaceWithFile(is);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Object::Write(std::ofstream &os) const
|
bool Object::ExtractToFile(const char *f) const
|
||||||
{
|
{
|
||||||
WriteU16(os, type_);
|
std::ofstream os(f);
|
||||||
WriteString(os, presenter_);
|
if (!os.is_open() || !os.good()) {
|
||||||
WriteU32(os, unknown1_);
|
return false;
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return ExtractToFile(os);
|
||||||
return chunk;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
bytearray Object::GetNormalizedData() const
|
|
||||||
{
|
|
||||||
return ToPackedData(filetype(), data_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
switch (this->filetype()) {
|
||||||
{
|
|
||||||
bytearray data;
|
|
||||||
|
|
||||||
switch (filetype) {
|
|
||||||
case MxOb::WAV:
|
case MxOb::WAV:
|
||||||
{
|
{
|
||||||
// Make space for WAVE header
|
if (ReadU32(is) != RIFF::RIFF_) {
|
||||||
data.resize(0x2C);
|
return false;
|
||||||
|
|
||||||
// Merge all chunks after the first one
|
|
||||||
for (size_t i=1; i<chunks.size(); i++) {
|
|
||||||
data.append(chunks[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy boilerplate bytes for header
|
// Skip total size
|
||||||
uint32_t *header = reinterpret_cast<uint32_t *>(data.data());
|
ReadU32(is);
|
||||||
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
|
|
||||||
|
|
||||||
// Copy fmt header from chunk 1
|
if (ReadU32(is) != RIFF::WAVE) {
|
||||||
memcpy(&header[5], chunks[0].data(), 16);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case MxOb::STL:
|
case MxOb::STL:
|
||||||
{
|
{
|
||||||
// Make space for BMP header
|
static const uint32_t BMP_HDR_SZ = 14;
|
||||||
data.resize(14);
|
|
||||||
|
|
||||||
// Merge all chunks after the first one
|
// Write BMP header
|
||||||
for (size_t i=0; i<chunks.size(); i++) {
|
WriteU16(os, 0x4D42);
|
||||||
data.append(chunks[i]);
|
|
||||||
|
// 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
|
std::ios::pos_type len = os.tellp();
|
||||||
*(uint16_t *)(data.data()) = 0x4D42;
|
os.seekp(sz_loc);
|
||||||
|
WriteU32(os, len);
|
||||||
// Set file size
|
|
||||||
*(uint32_t*)(data.data()+2) = data.size();
|
|
||||||
|
|
||||||
// Set reserved bytes
|
|
||||||
*(uint32_t*)(data.data()+6) = 0;
|
|
||||||
|
|
||||||
// Set offset
|
|
||||||
*(uint32_t*)(data.data()+10) = chunks.at(0).size() + 14;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MxOb::FLC:
|
case MxOb::FLC:
|
||||||
{
|
{
|
||||||
// First chunk is a complete FLIC header, so add it as-is
|
// 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
|
// Subsequent chunks are FLIC frames with an additional 20 byte header that needs to be stripped
|
||||||
const int CUSTOM_HEADER_SZ = 20;
|
const int CUSTOM_HEADER_SZ = 20;
|
||||||
for (size_t i=1; i<chunks.size(); i++) {
|
for (size_t i=1; i<data_.size(); i++) {
|
||||||
data.append(chunks.at(i).data() + CUSTOM_HEADER_SZ, chunks.at(i).size() - CUSTOM_HEADER_SZ);
|
os.write(data_.at(i).data() + CUSTOM_HEADER_SZ, data_.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]);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
std::cout << "Didn't know how to extract type '" << std::string((const char *)&filetype, sizeof(filetype)) << "', merging..." << std::endl;
|
LogWarning() << "Didn't know how to extract type '" << RIFF::PrintU32AsString(filetype()) << "', merging..." << std::endl;
|
||||||
for (size_t i=0; i<chunks.size(); i++) {
|
/* fall-through */
|
||||||
data.append(chunks[i]);
|
case MxOb::SMK:
|
||||||
|
case MxOb::OBJ:
|
||||||
|
// Simply merge
|
||||||
|
for (size_t i=0; i<data_.size(); i++) {
|
||||||
|
WriteBytes(os, data_.at(i));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object::ChunkedData Object::ToChunkedData(MxOb::FileType filetype, const bytearray &chunks)
|
bytearray Object::ExtractToMemory() const
|
||||||
{
|
{
|
||||||
// FIXME: STUB
|
memorybuf buf;
|
||||||
return ChunkedData();
|
std::ostream os(&buf);
|
||||||
|
|
||||||
|
ExtractToFile(os);
|
||||||
|
|
||||||
|
return buf.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
const bytearray &Object::GetFileHeader() const
|
const bytearray &Object::GetFileHeader() const
|
||||||
|
|
19
lib/object.h
19
lib/object.h
|
@ -14,13 +14,18 @@ public:
|
||||||
|
|
||||||
Object();
|
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 bool ReplaceWithFile(const char *f);
|
||||||
LIBWEAVER_EXPORT void SetNormalizedData(const bytearray &d);
|
LIBWEAVER_EXPORT bool ExtractToFile(const char *f) const;
|
||||||
|
|
||||||
LIBWEAVER_EXPORT static bytearray ToPackedData(MxOb::FileType filetype, const ChunkedData &chunks);
|
LIBWEAVER_EXPORT bool ReplaceWithFile(std::istream &is);
|
||||||
LIBWEAVER_EXPORT static ChunkedData ToChunkedData(MxOb::FileType filetype, const bytearray &chunks);
|
LIBWEAVER_EXPORT bool ExtractToFile(std::ostream &os) const;
|
||||||
|
|
||||||
|
LIBWEAVER_EXPORT bytearray ExtractToMemory() const;
|
||||||
|
|
||||||
LIBWEAVER_EXPORT const bytearray &GetFileHeader() const;
|
LIBWEAVER_EXPORT const bytearray &GetFileHeader() const;
|
||||||
LIBWEAVER_EXPORT bytearray GetFileBody() const;
|
LIBWEAVER_EXPORT bytearray GetFileBody() const;
|
||||||
|
@ -59,6 +64,10 @@ public:
|
||||||
|
|
||||||
ChunkedData data_;
|
ChunkedData data_;
|
||||||
|
|
||||||
|
bool last_chunk_split_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,31 +25,31 @@ public:
|
||||||
class FLIC
|
class FLIC
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
uint32_t size; /* Size of FLIC including this header */
|
uint32_t size; // Size of FLIC including this headerdesc << "
|
||||||
uint16_t type; /* File type 0xAF11, 0xAF12, 0xAF30, 0xAF44, ... */
|
uint16_t type; // File type 0xAF11, 0xAF12, 0xAF30, 0xAF44, ...desc << "
|
||||||
uint16_t frames; /* Number of frames in first segment */
|
uint16_t frames; // Number of frames in first segmentdesc << "
|
||||||
uint16_t width; /* FLIC width in pixels */
|
uint16_t width; // FLIC width in pixelsdesc << "
|
||||||
uint16_t height; /* FLIC height in pixels */
|
uint16_t height; // FLIC height in pixelsdesc << "
|
||||||
uint16_t depth; /* Bits per pixel (usually 8) */
|
uint16_t depth; // Bits per pixel (usually 8)desc << "
|
||||||
uint16_t flags; /* Set to zero or to three */
|
uint16_t flags; // Set to zero or to threedesc << "
|
||||||
uint32_t speed; /* Delay between frames */
|
uint32_t speed; // Delay between framesdesc << "
|
||||||
uint16_t reserved1; /* Set to zero */
|
uint16_t reserved1; // Set to zerodesc << "
|
||||||
uint32_t created; /* Date of FLIC creation (FLC only) */
|
uint32_t created; // Date of FLIC creation (FLC only)desc << "
|
||||||
uint32_t creator; /* Serial number or compiler id (FLC only) */
|
uint32_t creator; // Serial number or compiler id (FLC only)desc << "
|
||||||
uint32_t updated; /* Date of FLIC update (FLC only) */
|
uint32_t updated; // Date of FLIC update (FLC only)desc << "
|
||||||
uint32_t updater; /* Serial number (FLC only), see creator */
|
uint32_t updater; // Serial number (FLC only), see creatordesc << "
|
||||||
uint16_t aspect_dx; /* Width of square rectangle (FLC only) */
|
uint16_t aspect_dx; // Width of square rectangle (FLC only)desc << "
|
||||||
uint16_t aspect_dy; /* Height of square rectangle (FLC only) */
|
uint16_t aspect_dy; // Height of square rectangle (FLC only)desc << "
|
||||||
uint16_t ext_flags; /* EGI: flags for specific EGI extensions */
|
uint16_t ext_flags; // EGI: flags for specific EGI extensionsdesc << "
|
||||||
uint16_t keyframes; /* EGI: key-image frequency */
|
uint16_t keyframes; // EGI: key-image frequencydesc << "
|
||||||
uint16_t totalframes; /* EGI: total number of frames (segments) */
|
uint16_t totalframes; // EGI: total number of frames (segments)desc << "
|
||||||
uint32_t req_memory; /* EGI: maximum chunk size (uncompressed) */
|
uint32_t req_memory; // EGI: maximum chunk size (uncompressed)desc << "
|
||||||
uint16_t max_regions; /* EGI: max. number of regions in a CHK_REGION chunk */
|
uint16_t max_regions; // EGI: max. number of regions in a CHK_REGION chunkdesc << "
|
||||||
uint16_t transp_num; /* EGI: number of transparent levels */
|
uint16_t transp_num; // EGI: number of transparent levelsdesc << "
|
||||||
uint8_t reserved2[24]; /* Set to zero */
|
uint8_t reserved2[24]; // Set to zerodesc << "
|
||||||
uint32_t oframe1; /* Offset to frame 1 (FLC only) */
|
uint32_t oframe1; // Offset to frame 1 (FLC only)desc << "
|
||||||
uint32_t oframe2; /* Offset to frame 2 (FLC only) */
|
uint32_t oframe2; // Offset to frame 2 (FLC only)desc << "
|
||||||
uint8_t reserved3[40]; /* Set to zero */
|
uint8_t reserved3[40]; // Set to zerodesc << "
|
||||||
};
|
};
|
||||||
|
|
||||||
// Copied from https://wiki.multimedia.cx/index.php/Smacker#Header
|
// 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 "sitypes.h"
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
namespace si {
|
namespace si {
|
||||||
|
|
||||||
const char *MxOb::GetTypeName(Type type)
|
const char *MxOb::GetTypeName(Type type)
|
||||||
|
@ -56,4 +58,67 @@ std::vector<const char*> MxOb::GetFlagsName(Flags flags)
|
||||||
return names;
|
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 <fstream>
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
namespace si {
|
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
|
* @brief RIFF chunk type
|
||||||
*
|
*
|
||||||
|
@ -35,9 +17,38 @@ public:
|
||||||
class RIFF
|
class RIFF
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum {
|
enum Type {
|
||||||
OMNI = 0x494e4d4f
|
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
|
class pad_ : public RIFF
|
||||||
{
|
{
|
||||||
public:
|
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
|
#ifndef TYPES_H
|
||||||
#define TYPES_H
|
#define TYPES_H
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <streambuf>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <ostream>
|
||||||
#include <vector>
|
#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)
|
#if defined(_MSC_VER) && (_MSC_VER < 1600)
|
||||||
// Declare types for MSVC versions less than 2010 (1600) which lacked a stdint.h
|
// Declare types for MSVC versions less than 2010 (1600) which lacked a stdint.h
|
||||||
typedef unsigned char uint8_t;
|
typedef unsigned char uint8_t;
|
||||||
|
@ -30,6 +53,11 @@ public:
|
||||||
{
|
{
|
||||||
resize(size);
|
resize(size);
|
||||||
}
|
}
|
||||||
|
bytearray(const char *data, size_t size)
|
||||||
|
{
|
||||||
|
resize(size);
|
||||||
|
memcpy(this->data(), data, size);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T *cast() { return reinterpret_cast<T*>(data()); }
|
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
|
class Vector3
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
40
lib/util.h
40
lib/util.h
|
@ -2,60 +2,61 @@
|
||||||
#define UTIL_H
|
#define UTIL_H
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
namespace si {
|
namespace si {
|
||||||
|
|
||||||
inline uint32_t ReadU32(std::ifstream &is)
|
inline uint32_t ReadU32(std::istream &is)
|
||||||
{
|
{
|
||||||
uint32_t u;
|
uint32_t u;
|
||||||
is.read((char *) &u, sizeof(u));
|
is.read((char *) &u, sizeof(u));
|
||||||
return 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));
|
os.write((const char *) &u, sizeof(u));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint16_t ReadU16(std::ifstream &is)
|
inline uint16_t ReadU16(std::istream &is)
|
||||||
{
|
{
|
||||||
uint16_t u;
|
uint16_t u;
|
||||||
is.read((char *) &u, sizeof(u));
|
is.read((char *) &u, sizeof(u));
|
||||||
return 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));
|
os.write((const char *) &u, sizeof(u));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint8_t ReadU8(std::ifstream &is)
|
inline uint8_t ReadU8(std::istream &is)
|
||||||
{
|
{
|
||||||
uint8_t u;
|
uint8_t u;
|
||||||
is.read((char *) &u, sizeof(u));
|
is.read((char *) &u, sizeof(u));
|
||||||
return 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));
|
os.write((const char *) &u, sizeof(u));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Vector3 ReadVector3(std::ifstream &is)
|
inline Vector3 ReadVector3(std::istream &is)
|
||||||
{
|
{
|
||||||
Vector3 u;
|
Vector3 u;
|
||||||
is.read((char *) &u, sizeof(u));
|
is.read((char *) &u, sizeof(u));
|
||||||
return 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));
|
os.write((const char *) &v, sizeof(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string ReadString(std::ifstream &is)
|
inline std::string ReadString(std::istream &is)
|
||||||
{
|
{
|
||||||
std::string d;
|
std::string d;
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ inline std::string ReadString(std::ifstream &is)
|
||||||
return d;
|
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());
|
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);
|
os.write(&nullterm, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bytearray ReadBytes(std::ifstream &is, size_t size)
|
inline bytearray ReadBytes(std::istream &is, size_t size)
|
||||||
{
|
{
|
||||||
bytearray d;
|
bytearray d;
|
||||||
|
|
||||||
|
@ -90,11 +91,26 @@ inline bytearray ReadBytes(std::ifstream &is, size_t size)
|
||||||
return d;
|
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());
|
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
|
#endif // UTIL_H
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue