2025-01-09 17:54:44 -05:00
|
|
|
#include "mainwindow.h"
|
2025-01-17 00:23:50 -05:00
|
|
|
#include "qheaderview.h"
|
2025-01-09 17:54:44 -05:00
|
|
|
#include "ui_mainwindow.h"
|
|
|
|
|
|
|
|
|
|
MainWindow::MainWindow(QWidget *parent)
|
|
|
|
|
: QMainWindow(parent)
|
|
|
|
|
, ui(new Ui::MainWindow) {
|
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
|
|
|
|
|
mTypeMap = QMap<QString, int>();
|
|
|
|
|
mTypeOrder = QStringList();
|
|
|
|
|
mRawFileMap = QMap<QString, QString>();
|
|
|
|
|
mTreeMap = QMap<QString, QTreeWidgetItem*>();
|
|
|
|
|
mStrTableMap = QMap<QString, QVector<QPair<QString, QString>>>();
|
|
|
|
|
mBSPVersion = 0;
|
|
|
|
|
mDiskLumpCount = 0;
|
|
|
|
|
mDiskLumpOrder = QVector<quint32>();
|
|
|
|
|
mLumps = QMap<quint32, Lump>();
|
2025-01-17 00:23:50 -05:00
|
|
|
mTreeWidget = new QTreeWidget();
|
|
|
|
|
mRootItem = mTreeWidget->invisibleRootItem();
|
|
|
|
|
mScriptEditor = new QPlainTextEdit();
|
|
|
|
|
mModelViewer = new ModelViewer();
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Connect Help > About dialog
|
|
|
|
|
connect(ui->actionAbout, &QAction::triggered, this, [this](bool checked) {
|
|
|
|
|
Q_UNUSED(checked);
|
|
|
|
|
|
|
|
|
|
AboutDialog *aboutDialog = new AboutDialog(this);
|
|
|
|
|
aboutDialog->exec();
|
|
|
|
|
|
|
|
|
|
delete aboutDialog;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(ui->actionOpen_Fast_File, &QAction::triggered, this, [this](bool checked) {
|
|
|
|
|
Q_UNUSED(checked);
|
|
|
|
|
OpenFastFile();
|
|
|
|
|
});
|
|
|
|
|
connect(ui->actionOpen_Zone_File, &QAction::triggered, this, [this](bool checked) {
|
|
|
|
|
Q_UNUSED(checked);
|
|
|
|
|
OpenZoneFile();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QDockWidget *treeDockWidget = new QDockWidget(this);
|
|
|
|
|
mTreeWidget->header()->hide();
|
|
|
|
|
treeDockWidget->setWidget(mTreeWidget);
|
|
|
|
|
addDockWidget(Qt::LeftDockWidgetArea, treeDockWidget);
|
|
|
|
|
|
|
|
|
|
setCentralWidget(mScriptEditor);
|
2025-01-09 17:54:44 -05:00
|
|
|
|
|
|
|
|
LoadFile_D3DBSP(":/d3dbsp/data/d3dbsp/barebones.d3dbsp");
|
|
|
|
|
|
|
|
|
|
Reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MainWindow::~MainWindow() {
|
|
|
|
|
delete ui;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::Reset() {
|
2025-01-17 00:23:50 -05:00
|
|
|
// Clear data tree
|
|
|
|
|
mTreeWidget->clear();
|
2025-01-09 17:54:44 -05:00
|
|
|
|
|
|
|
|
// Reset class vars
|
|
|
|
|
mTypeMap.clear();
|
|
|
|
|
mTypeOrder.clear();
|
|
|
|
|
mTagCount = 0;
|
|
|
|
|
mRecordCount = 0;
|
|
|
|
|
mRawFileMap.clear();
|
|
|
|
|
mTreeMap.clear();
|
|
|
|
|
mStrTableMap.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
OpenFastFile()
|
|
|
|
|
|
|
|
|
|
Opens a file dialog in the steam folder,
|
|
|
|
|
and opens the selected file.
|
|
|
|
|
*/
|
2025-01-17 00:23:50 -05:00
|
|
|
bool MainWindow::OpenFastFile(const QString aFastFilePath) {
|
|
|
|
|
if (aFastFilePath.isEmpty()) { return false; }
|
2025-01-09 17:54:44 -05:00
|
|
|
|
|
|
|
|
// Reset dialog before opening new file
|
|
|
|
|
Reset();
|
|
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Add fast file as tree widget root
|
|
|
|
|
const QString fastFileStem = aFastFilePath.split('/').last();
|
|
|
|
|
QTreeWidgetItem *fastFileItem = new QTreeWidgetItem(mRootItem);
|
|
|
|
|
fastFileItem->setText(0, fastFileStem);
|
|
|
|
|
mRootItem = fastFileItem;
|
2025-01-09 17:54:44 -05:00
|
|
|
|
|
|
|
|
// Check fastfile can be read
|
2025-01-17 00:23:50 -05:00
|
|
|
QFile *fastFileObj = new QFile(aFastFilePath);
|
|
|
|
|
if (!fastFileObj->open(QIODevice::ReadOnly)) {
|
|
|
|
|
QMessageBox::warning(this, "Warning!", QString("Failed to open FastFile: %1!")
|
|
|
|
|
.arg(aFastFilePath));
|
|
|
|
|
return false;
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
|
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Decompress fastfile and close
|
|
|
|
|
const QByteArray fastFileData = fastFileObj->readAll();
|
|
|
|
|
const QByteArray decompressedData = Compressor::DecompressZLIB(fastFileData);
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Open zone file as little endian stream
|
|
|
|
|
QDataStream fastFileStream(fastFileData);
|
|
|
|
|
fastFileStream.setByteOrder(QDataStream::LittleEndian);
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Parse data from fast file header
|
|
|
|
|
FastFile fastFile = FastFileParser::ParseFFHeader(&fastFileStream);
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
QTreeWidgetItem *metaDataItem = new QTreeWidgetItem(mRootItem);
|
|
|
|
|
metaDataItem->setText(0, "Metadata");
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
QTreeWidgetItem *companyItem = new QTreeWidgetItem(metaDataItem);
|
|
|
|
|
companyItem->setText(0, "Company: " + Utils::CompanyEnumToStr(fastFile.company));
|
|
|
|
|
QTreeWidgetItem *fileTypeItem = new QTreeWidgetItem(metaDataItem);
|
|
|
|
|
fileTypeItem->setText(0, "File Type: " + Utils::FileTypeEnumToStr(fastFile.fileType));
|
|
|
|
|
QTreeWidgetItem *signageItem = new QTreeWidgetItem(metaDataItem);
|
|
|
|
|
signageItem->setText(0, "Signage: " + Utils::SignageEnumToStr(fastFile.signage));
|
|
|
|
|
QTreeWidgetItem *magicItem = new QTreeWidgetItem(metaDataItem);
|
|
|
|
|
magicItem->setText(0, "Magic: " + fastFile.magic);
|
|
|
|
|
QTreeWidgetItem *versionItem = new QTreeWidgetItem(metaDataItem);
|
|
|
|
|
versionItem->setText(0, "Version: " + QString::number(fastFile.version));
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
const QString zoneFilePath = fastFileObj->fileName().replace(".ff", ".zone");
|
|
|
|
|
fastFileObj->close();
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Check zone file is writeable
|
|
|
|
|
QFile *zoneFile = new QFile(zoneFilePath);
|
|
|
|
|
if (!zoneFile->open(QIODevice::ReadWrite)) {
|
|
|
|
|
qDebug() << QString("Zone file could not be written to: '%1'").arg(zoneFilePath);
|
|
|
|
|
return false;
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
|
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Write zone data
|
|
|
|
|
zoneFile->write(decompressedData);
|
|
|
|
|
zoneFile->close();
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Open zone file after decompressing ff and writing
|
|
|
|
|
return OpenZoneFile(zoneFilePath);
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2025-01-17 00:23:50 -05:00
|
|
|
OpenFastFile()
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
Opens a file dialog in the steam folder,
|
|
|
|
|
and opens the selected file.
|
2025-01-09 17:54:44 -05:00
|
|
|
*/
|
2025-01-17 00:23:50 -05:00
|
|
|
bool MainWindow::OpenFastFile() {
|
|
|
|
|
const QString fastFileName = Utils::GetOpenFastFileName();
|
|
|
|
|
if (!OpenFastFile(fastFileName)) {
|
|
|
|
|
qDebug() << "Failed to open Fast file!";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2025-01-17 00:23:50 -05:00
|
|
|
OpenZoneFile()
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
Opens a file dialog in the steam folder,
|
|
|
|
|
and opens the selected file.
|
2025-01-09 17:54:44 -05:00
|
|
|
*/
|
2025-01-17 00:23:50 -05:00
|
|
|
bool MainWindow::OpenZoneFile(const QString aZoneFilePath) {
|
|
|
|
|
if (aZoneFilePath.isEmpty()) { return false; }
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Reset dialog before opening new file
|
|
|
|
|
//Reset();
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
//ui->lineEdit_ZoneFile->setText(zoneFilePath);
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
const QString zoneFileStem = aZoneFilePath.split('/').last();
|
|
|
|
|
QTreeWidgetItem *zoneItem = new QTreeWidgetItem(mRootItem);
|
|
|
|
|
zoneItem->setText(0, zoneFileStem);
|
|
|
|
|
mRootItem = zoneItem;
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
//setWindowTitle(QString("FastFile Wizard - %1").arg(zoneFileStem));
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Check zone file can be read
|
|
|
|
|
QFile *zoneFileObj = new QFile(aZoneFilePath);
|
|
|
|
|
if (!zoneFileObj->open(QIODevice::ReadOnly)) {
|
|
|
|
|
QMessageBox::warning(this, "Warning!", QString("%1 could not be read!.").arg(aZoneFilePath));
|
|
|
|
|
return false;
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
2025-01-17 00:23:50 -05:00
|
|
|
const QByteArray decompressedData = zoneFileObj->readAll();
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Open zone file as little endian stream
|
|
|
|
|
QDataStream zoneFileStream(decompressedData);
|
|
|
|
|
zoneFileStream.setByteOrder(QDataStream::LittleEndian);
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Parse data from zone file header
|
|
|
|
|
ZoneFile zoneFile = ZoneFileParser::ParseZoneHeader(&zoneFileStream);
|
|
|
|
|
|
|
|
|
|
if (zoneFile.tagCount) {
|
|
|
|
|
QTreeWidgetItem *tagsItem = new QTreeWidgetItem(mRootItem);
|
|
|
|
|
tagsItem->setText(0, QString("Tags [%1]")
|
|
|
|
|
.arg(zoneFile.tags.length()));
|
|
|
|
|
foreach (const QString tag, zoneFile.tags) {
|
|
|
|
|
QTreeWidgetItem *tagItem = new QTreeWidgetItem(tagsItem);
|
|
|
|
|
tagItem->setText(0, tag);
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
2025-01-17 00:23:50 -05:00
|
|
|
}
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
QTreeWidgetItem *indexRecordsItem = new QTreeWidgetItem(mRootItem);
|
|
|
|
|
QTreeWidgetItem *assetsItem = new QTreeWidgetItem(mRootItem);
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
mRootItem = indexRecordsItem;
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
int consecutiveCount = 1;
|
|
|
|
|
QString lastRecord = "";
|
|
|
|
|
zoneFile.records = ZoneFileParser::ParseZoneIndex(&zoneFileStream, zoneFile.recordCount);
|
|
|
|
|
indexRecordsItem->setText(0, QString("Index Records [%1]")
|
|
|
|
|
.arg(zoneFile.records.length()));
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
foreach (const QString record, zoneFile.records) {
|
|
|
|
|
if (lastRecord.isEmpty()) {
|
|
|
|
|
lastRecord = record;
|
|
|
|
|
continue;
|
|
|
|
|
} else if (lastRecord == record) {
|
2025-01-09 17:54:44 -05:00
|
|
|
consecutiveCount++;
|
2025-01-17 00:23:50 -05:00
|
|
|
continue;
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
2025-01-17 00:23:50 -05:00
|
|
|
QTreeWidgetItem *recordItem = new QTreeWidgetItem(mRootItem);
|
|
|
|
|
recordItem->setText(0, QString("%1 [%2]")
|
|
|
|
|
.arg(Utils::AssetTypeToString(lastRecord))
|
|
|
|
|
.arg(consecutiveCount));
|
|
|
|
|
lastRecord = record;
|
|
|
|
|
consecutiveCount = 1;
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
|
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
mRootItem = assetsItem;
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Parse current and consecutive assets
|
|
|
|
|
AssetMap assetMap = ZoneFileParser::ParseAssets(&zoneFileStream, zoneFile.records);
|
|
|
|
|
assetsItem->setText(0, QString("Assets"));
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
if (!assetMap.rawFiles.isEmpty()) {
|
|
|
|
|
QTreeWidgetItem *rawFilesItem = new QTreeWidgetItem(mRootItem);
|
|
|
|
|
foreach (const RawFile rawFile, assetMap.rawFiles) {
|
|
|
|
|
QTreeWidgetItem *rawFileItem = new QTreeWidgetItem(rawFilesItem);
|
|
|
|
|
rawFileItem->setText(0, rawFile.path);
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
2025-01-17 00:23:50 -05:00
|
|
|
rawFilesItem->setText(0, QString("Raw Files [%1]")
|
|
|
|
|
.arg(assetMap.rawFiles.length()));
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
|
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
if (!assetMap.localStrings.isEmpty()) {
|
|
|
|
|
QTreeWidgetItem *localStrsItem = new QTreeWidgetItem(mRootItem);
|
|
|
|
|
foreach (const LocalString localString, assetMap.localStrings) {
|
|
|
|
|
QTreeWidgetItem *localStrItem = new QTreeWidgetItem(localStrsItem);
|
|
|
|
|
localStrItem->setText(0, localString.string);
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
2025-01-17 00:23:50 -05:00
|
|
|
localStrsItem->setText(0, QString("Local Strings [%1]")
|
|
|
|
|
.arg(assetMap.localStrings.length()));
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
|
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
if (!assetMap.stringTables.isEmpty()) {
|
|
|
|
|
QTreeWidgetItem *strTablesItem = new QTreeWidgetItem(mRootItem);
|
|
|
|
|
foreach (const StringTable stringTable, assetMap.stringTables) {
|
|
|
|
|
QTreeWidgetItem *strTableItem = new QTreeWidgetItem(strTablesItem);
|
|
|
|
|
strTableItem->setText(0, stringTable.name);
|
|
|
|
|
}
|
|
|
|
|
strTablesItem->setText(0, QString("String Tables [%1]")
|
|
|
|
|
.arg(assetMap.stringTables.length()));
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
|
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
if (!assetMap.techSets.isEmpty()) {
|
|
|
|
|
QTreeWidgetItem *techSetsItem = new QTreeWidgetItem(mRootItem);
|
|
|
|
|
foreach (const TechSet techSet, assetMap.techSets) {
|
|
|
|
|
QTreeWidgetItem *techSetItem = new QTreeWidgetItem(techSetsItem);
|
|
|
|
|
techSetItem->setText(0, techSet.name);
|
|
|
|
|
}
|
|
|
|
|
techSetsItem->setText(0, QString("Tech Sets [%1]")
|
|
|
|
|
.arg(assetMap.techSets.length()));
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
|
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
if (!assetMap.animations.isEmpty()) {
|
|
|
|
|
QTreeWidgetItem *animationsItem = new QTreeWidgetItem(mRootItem);
|
|
|
|
|
animationsItem->setText(0, "Animations");
|
|
|
|
|
foreach (const Animation animation, assetMap.animations) {
|
|
|
|
|
QTreeWidgetItem *animationItem = new QTreeWidgetItem(animationsItem);
|
|
|
|
|
animationItem->setText(0, animation.name);
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
2025-01-17 00:23:50 -05:00
|
|
|
animationsItem->setText(0, QString("Animations [%1]")
|
|
|
|
|
.arg(assetMap.animations.length()));
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
|
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
// Clean up zone file
|
|
|
|
|
zoneFileObj->close();
|
|
|
|
|
delete zoneFileObj;
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
return true;
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
|
|
|
|
|
2025-01-17 00:23:50 -05:00
|
|
|
bool MainWindow::OpenZoneFile() {
|
|
|
|
|
const QString zoneFileName = Utils::GetOpenZoneFileName();
|
|
|
|
|
if (!OpenZoneFile(zoneFileName)) {
|
|
|
|
|
qDebug() << "Failed to open Zone file!";
|
|
|
|
|
return false;
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
2025-01-17 00:23:50 -05:00
|
|
|
mRootItem = mTreeWidget->invisibleRootItem();
|
|
|
|
|
return true;
|
2025-01-09 17:54:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int MainWindow::LoadFile_D3DBSP(const QString aFilePath) {
|
|
|
|
|
QFile file(aFilePath);
|
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
|
|
|
|
qDebug() << "Error: Unable to open file" << aFilePath;
|
|
|
|
|
return 1; // ERR_FILE_NOT_FOUND
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDataStream stream(&file);
|
|
|
|
|
stream.setByteOrder(QDataStream::LittleEndian);
|
|
|
|
|
|
|
|
|
|
// Read Magic Value
|
|
|
|
|
quint32 magicValue;
|
|
|
|
|
stream >> magicValue;
|
|
|
|
|
if (magicValue != 'PSBI') {
|
|
|
|
|
qDebug() << "Error: Not a valid D3DBSP file";
|
|
|
|
|
return 2; // ERR_NOT_A_D3DBSP_FILE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read BSP Version and Lump Count
|
|
|
|
|
stream >> mBSPVersion;
|
|
|
|
|
stream >> mDiskLumpCount;
|
|
|
|
|
|
|
|
|
|
// Assign diskLumpOrderSize
|
|
|
|
|
mDiskLumpOrder.resize(mDiskLumpCount);
|
|
|
|
|
|
|
|
|
|
// Read Lump Index Entries
|
|
|
|
|
quint32 lumpOffset = sizeof(quint32) * 3 + sizeof(LumpIndexEntry) * mDiskLumpCount;
|
|
|
|
|
|
|
|
|
|
for (quint32 i = 0; i < mDiskLumpCount; i++) {
|
|
|
|
|
LumpIndexEntry indexEntry;
|
|
|
|
|
stream >> indexEntry.type >> indexEntry.length;
|
|
|
|
|
|
|
|
|
|
mDiskLumpOrder[i] = indexEntry.type;
|
|
|
|
|
|
|
|
|
|
Lump &lump = mLumps[indexEntry.type];
|
|
|
|
|
lump.size = indexEntry.length;
|
|
|
|
|
lump.content.resize(indexEntry.length);
|
|
|
|
|
lump.isEmpty = false;
|
|
|
|
|
|
|
|
|
|
qDebug() << "Lump Type:" << Utils::LumpTypeToString((LUMP_TYPE)indexEntry.type) << "Lump Size:" << indexEntry.length;
|
|
|
|
|
|
|
|
|
|
// Handle offsets and padding
|
|
|
|
|
qint64 currentPos = file.pos();
|
|
|
|
|
file.seek(lumpOffset);
|
|
|
|
|
file.read(lump.content.data(), lump.size);
|
|
|
|
|
file.seek(currentPos);
|
|
|
|
|
lumpOffset += Utils::PadInt4(indexEntry.length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file.close();
|
|
|
|
|
return 0; // Success
|
|
|
|
|
}
|
|
|
|
|
|