From 9365796f40a4658f5ad86b0d271f244669aa6839 Mon Sep 17 00:00:00 2001 From: Christian Halaszovich Date: Thu, 30 Apr 2026 17:01:26 +0200 Subject: [PATCH 1/3] prepare hktoolslib for unbundled files --- hekatoolslib/DatFile.cpp | 35 +++++++++++++++++++++++++++++++++++ hekatoolslib/DatFile.h | 9 ++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/hekatoolslib/DatFile.cpp b/hekatoolslib/DatFile.cpp index ece092a..4bef67e 100644 --- a/hekatoolslib/DatFile.cpp +++ b/hekatoolslib/DatFile.cpp @@ -105,6 +105,41 @@ void DatFile::InitFromStream(std::istream& infile) if (!PgfTree.isValid())throw std::runtime_error("no valid Pgf in file"); } +void hkLib::DatFile::InitFromStream(std::istream& infile, std::istream& pulstream, std::uintmax_t pullength, std::istream& pgfstream, + std::uintmax_t pgflength, std::istream& ampstream, std::uintmax_t amplength) +{ + if (!infile) { + throw std::runtime_error("cannot access file"); + } + auto bh = std::make_unique(); + infile.read(reinterpret_cast(bh.get()), BundleHeaderSize); + if (!infile) { + throw std::runtime_error("cannot read file"); + } + + bool has_header = std::memcmp(bh->Signature, BundleSignatureInvalid, 8) == 0; + // Note: if it has a valid signature, it should not be handeled by this function + if (has_header) { + Version = bh->Version; + Time = bh->Time; + isSwapped = bool(bh->IsLittleEndian) != MachineIsLittleEndian(); + if (isSwapped) { + swapInPlace(Time); + } + } + if(!PulTree.InitFromStream(ExtPul, pulstream, 0, static_cast(pullength))){ + throw std::runtime_error("error processing pulse tree"); + } + if (!PgfTree.InitFromStream(ExtPgf, pgfstream, 0, static_cast(pgflength))) { + throw std::runtime_error("error processing pgf tree"); + } + if (0 != amplength) { + if (!AmpTree.InitFromStream(ExtAmp, ampstream, 0, static_cast(amplength))) { + throw std::runtime_error("error processing amp tree"); + } + } +} + std::string DatFile::getFileDate() const { #ifndef NDEBUG diff --git a/hekatoolslib/DatFile.h b/hekatoolslib/DatFile.h index e987990..7ad5936 100644 --- a/hekatoolslib/DatFile.h +++ b/hekatoolslib/DatFile.h @@ -78,7 +78,14 @@ namespace hkLib { PgfTree{}, AmpTree{} {}; DatFile(const DatFile&) = delete; DatFile operator=(const DatFile&) = delete; - void InitFromStream(std::istream& istream); + /// + /// initialize from bundle file stream, reads header and tree data, but not raw data + /// + /// input stream of the bundle file + void InitFromStream(std::istream& infile); + void InitFromStream(std::istream& infile, std::istream& pulstream, std::uintmax_t pullength, + std::istream& pgfstream, std::uintmax_t pgflength, + std::istream& ampstream, std::uintmax_t amplength); std::string getFileDate() const; // return formatted file creation date hkTree& GetPulTree() { return PulTree; }; hkTree& GetPgfTree() { return PgfTree; }; From d967c4dfcb82de283d74682fa31b7f214aa61649 Mon Sep 17 00:00:00 2001 From: Christian Halaszovich Date: Mon, 4 May 2026 09:18:30 +0200 Subject: [PATCH 2/3] use pointer for optional amp stream --- hekatoolslib/DatFile.cpp | 6 +++--- hekatoolslib/DatFile.h | 12 +++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/hekatoolslib/DatFile.cpp b/hekatoolslib/DatFile.cpp index 4bef67e..f389f78 100644 --- a/hekatoolslib/DatFile.cpp +++ b/hekatoolslib/DatFile.cpp @@ -106,7 +106,7 @@ void DatFile::InitFromStream(std::istream& infile) } void hkLib::DatFile::InitFromStream(std::istream& infile, std::istream& pulstream, std::uintmax_t pullength, std::istream& pgfstream, - std::uintmax_t pgflength, std::istream& ampstream, std::uintmax_t amplength) + std::uintmax_t pgflength, std::istream* ampstream, std::uintmax_t amplength) { if (!infile) { throw std::runtime_error("cannot access file"); @@ -133,8 +133,8 @@ void hkLib::DatFile::InitFromStream(std::istream& infile, std::istream& pulstrea if (!PgfTree.InitFromStream(ExtPgf, pgfstream, 0, static_cast(pgflength))) { throw std::runtime_error("error processing pgf tree"); } - if (0 != amplength) { - if (!AmpTree.InitFromStream(ExtAmp, ampstream, 0, static_cast(amplength))) { + if (ampstream && amplength) { + if (!AmpTree.InitFromStream(ExtAmp, *ampstream, 0, static_cast(amplength))) { throw std::runtime_error("error processing amp tree"); } } diff --git a/hekatoolslib/DatFile.h b/hekatoolslib/DatFile.h index 7ad5936..62c34cd 100644 --- a/hekatoolslib/DatFile.h +++ b/hekatoolslib/DatFile.h @@ -83,9 +83,19 @@ namespace hkLib { /// /// input stream of the bundle file void InitFromStream(std::istream& infile); + /// + /// initialized from unbundles dat file, requires separate streams for each tree, and their lengths + /// + /// dat file stream + /// pul file stream + /// length of pul file + /// pgf file stream + /// length of pgf file + /// pointer to optional amp file stream, can be nullptr + /// void InitFromStream(std::istream& infile, std::istream& pulstream, std::uintmax_t pullength, std::istream& pgfstream, std::uintmax_t pgflength, - std::istream& ampstream, std::uintmax_t amplength); + std::istream* ampstream, std::uintmax_t amplength); std::string getFileDate() const; // return formatted file creation date hkTree& GetPulTree() { return PulTree; }; hkTree& GetPgfTree() { return PgfTree; }; From 210ac6d51de45d21d84a755907ac98655d357030 Mon Sep 17 00:00:00 2001 From: Christian Halaszovich Date: Mon, 4 May 2026 14:16:55 +0200 Subject: [PATCH 3/3] finalize support for unbundled files --- QtPMbrowser/pmbrowserwindow.cpp | 43 ++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/QtPMbrowser/pmbrowserwindow.cpp b/QtPMbrowser/pmbrowserwindow.cpp index 843b68e..04f3fde 100644 --- a/QtPMbrowser/pmbrowserwindow.cpp +++ b/QtPMbrowser/pmbrowserwindow.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -306,10 +307,46 @@ void PMbrowserWindow::loadFile(QString filename) datfile->InitFromStream(infile); } catch (const std::exception& e) { - QMessageBox::warning(this, QString("File Error"), - QString("error while processing dat file:\n") + QString(e.what())); + //QMessageBox::warning(this, QString("File Error"), + // QString("error while processing dat file:\n") + QString(e.what())); + qDebug() << e.what(); datfile = nullptr; - infile.close(); + infile.seekg(0, std::ios_base::beg); + //infile.close(); + } + if (!datfile) { + try { + // we might habe an unbundled dat file + datfile = std::make_unique(); + std::filesystem::path path(QFile::encodeName(filename).constData()); + path.replace_extension(hkLib::ExtPul); + std::ifstream pulstream(path, std::ios_base::binary | std::ios_base::in); + if (!pulstream) { + throw std::runtime_error(std::string("could not open pul file, ") + ::strerror(errno)); + } + auto pullength = std::filesystem::file_size(path); + path.replace_extension(hkLib::ExtPgf); + std::ifstream pgfstream(path, std::ios_base::binary | std::ios_base::in); + if (!pgfstream) { + throw std::runtime_error(std::string("could not open pgf file, ") + ::strerror(errno)); + } + auto pgflength = std::filesystem::file_size(path); + path.replace_extension(hkLib::ExtAmp); + std::ifstream ampstream(path, std::ios_base::binary | std::ios_base::in); + if (!ampstream) { + datfile->InitFromStream(infile, pulstream, pullength, pgfstream, pgflength, nullptr, 0); + } + else { + datfile->InitFromStream(infile, pulstream, pullength, pgfstream, pgflength, &stream, std::filesystem::file_size(path)); + } + } + catch (const std::exception& e) { + qDebug() << e.what(); + QMessageBox::warning(this, QString("File Error"), + QString("error while processing unbundled dat file:\n") + QString(e.what())); + datfile = nullptr; + infile.close(); + } } if(datfile) { populateTreeView();