From b7dfe5fe192389f6840fab31844b8af60cce5cc6 Mon Sep 17 00:00:00 2001 From: erysdren Date: Sun, 23 Feb 2025 04:23:40 -0600 Subject: [PATCH 1/2] vpkpp: Volition HOG support --- include/vpkpp/format/HOG.h | 36 +++++++++++++++++ include/vpkpp/vpkpp.h | 1 + src/vpkpp/_vpkpp.cmake | 2 + src/vpkpp/format/HOG.cpp | 80 ++++++++++++++++++++++++++++++++++++++ test/vpkpp.cpp | 6 +++ 5 files changed, 125 insertions(+) create mode 100644 include/vpkpp/format/HOG.h create mode 100644 src/vpkpp/format/HOG.cpp diff --git a/include/vpkpp/format/HOG.h b/include/vpkpp/format/HOG.h new file mode 100644 index 000000000..e5ff4bfc3 --- /dev/null +++ b/include/vpkpp/format/HOG.h @@ -0,0 +1,36 @@ +#pragma once + +#include "../PackFile.h" + +namespace vpkpp { + +constexpr std::string_view HOG_SIGNATURE = "DHF"; +constexpr std::string_view HOG_EXTENSION = ".hog"; + +class HOG : public PackFileReadOnly { +public: + /// Open a HOG file + [[nodiscard]] static std::unique_ptr open(const std::string& path, const EntryCallback& callback = nullptr); + + static constexpr inline std::string_view GUID = "FDE9941424FF4EC1BC4C90A7DA52AF87"; + + [[nodiscard]] constexpr std::string_view getGUID() const override { + return HOG::GUID; + } + + [[nodiscard]] constexpr bool isCaseSensitive() const override { + return true; + } + + [[nodiscard]] std::optional> readEntry(const std::string& path_) const override; + + [[nodiscard]] Attribute getSupportedEntryAttributes() const override; + +protected: + using PackFileReadOnly::PackFileReadOnly; + +private: + VPKPP_REGISTER_PACKFILE_OPEN(HOG_EXTENSION, &HOG::open); +}; + +} // namespace vpkpp diff --git a/include/vpkpp/vpkpp.h b/include/vpkpp/vpkpp.h index b9effb1db..31620225c 100644 --- a/include/vpkpp/vpkpp.h +++ b/include/vpkpp/vpkpp.h @@ -8,6 +8,7 @@ #include "format/FPX.h" #include "format/GCF.h" #include "format/GMA.h" +#include "format/HOG.h" #include "format/OO7.h" #include "format/OL.h" #include "format/ORE.h" diff --git a/src/vpkpp/_vpkpp.cmake b/src/vpkpp/_vpkpp.cmake index 23e3f034b..49c3a87fb 100644 --- a/src/vpkpp/_vpkpp.cmake +++ b/src/vpkpp/_vpkpp.cmake @@ -5,6 +5,7 @@ add_pretty_parser(vpkpp "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/FPX.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/GCF.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/GMA.h" + "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/HOG.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/OL.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/OO7.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/ORE.h" @@ -24,6 +25,7 @@ add_pretty_parser(vpkpp "${CMAKE_CURRENT_LIST_DIR}/format/FPX.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/GCF.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/GMA.cpp" + "${CMAKE_CURRENT_LIST_DIR}/format/HOG.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/OL.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/OO7.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/ORE.cpp" diff --git a/src/vpkpp/format/HOG.cpp b/src/vpkpp/format/HOG.cpp new file mode 100644 index 000000000..93875eeff --- /dev/null +++ b/src/vpkpp/format/HOG.cpp @@ -0,0 +1,80 @@ +#include + +#include + +#include + +using namespace sourcepp; +using namespace vpkpp; + +std::unique_ptr HOG::open(const std::string& path, const EntryCallback& callback) { + if (!std::filesystem::exists(path)) { + // File does not exist + return nullptr; + } + + auto* hog = new HOG{path}; + auto packFile = std::unique_ptr(hog); + + FileStream reader{hog->fullFilePath}; + reader.seek_in(0); + + // Verify signature + if (const auto signature = reader.read_string(3); signature != HOG_SIGNATURE) { + return nullptr; + } + + // Read file entries + while (true) { + // Create new entry + Entry entry = createNewEntry(); + + // Get file path + const auto entryPath = hog->cleanEntryPath(reader.read_string(13)); + + // Check if we're at EOF (must perform the check after reading beyond file bounds) + if (!reader) { + break; + } + + // Get file size and offset + entry.length = reader.read(); + entry.offset = reader.tell_in(); + + // Seek past file data + reader.seek_in_u(entry.offset + entry.length); + + // Put it in + hog->entries.emplace(entryPath, entry); + + if (callback) { + callback(entryPath, entry); + } + } + + return packFile; +} + +std::optional> HOG::readEntry(const std::string& path_) const { + auto path = this->cleanEntryPath(path_); + auto entry = this->findEntry(path); + if (!entry) { + return std::nullopt; + } + if (entry->unbaked) { + return readUnbakedEntry(*entry); + } + + // It's baked into the file on disk + FileStream stream{this->fullFilePath}; + if (!stream) { + return std::nullopt; + } + stream.seek_in_u(entry->offset); + return stream.read_bytes(entry->length); +} + +Attribute HOG::getSupportedEntryAttributes() const { + using enum Attribute; + return LENGTH; +} diff --git a/test/vpkpp.cpp b/test/vpkpp.cpp index 9bfa12a79..93149d247 100644 --- a/test/vpkpp.cpp +++ b/test/vpkpp.cpp @@ -5,6 +5,12 @@ using namespace sourcepp; using namespace vpkpp; +TEST(vpkpp, hog_read) { + const auto hog = PackFile::open(ASSET_ROOT "vpkpp/hog/chaos.hog"); + ASSERT_TRUE(hog); + EXPECT_EQ(hog->getEntryCount(), 5); +} + TEST(vpkpp, vpp_read) { const auto vpp = PackFile::open(ASSET_ROOT "vpkpp/vpp/v1.vpp"); ASSERT_TRUE(vpp); From 1a97dc713310ce86297a7d90cc638320bcb67a18 Mon Sep 17 00:00:00 2001 From: craftablescience Date: Sun, 23 Feb 2025 16:09:08 -0500 Subject: [PATCH 2/2] docs: add vpkpp hog support --- README.md | 13 ++++++++++--- docs/index.md | 12 +++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9dee67d19..b3c6fcd90 100644 --- a/README.md +++ b/README.md @@ -157,11 +157,11 @@ Several modern C++20 libraries for sanely parsing Valve formats, rolled into one - vpkpp + vpkpp 007 v1.1, v1.3 (007 - Nightfire) ✅ ❌ - C
C# + C
C# @@ -182,6 +182,12 @@ Several modern C++20 libraries for sanely parsing Valve formats, rolled into one ✅ + + HOG (Descent) + ✅ + ❌ + + OL (Worldcraft Object Library) ✅ @@ -226,7 +232,7 @@ Several modern C++20 libraries for sanely parsing Valve formats, rolled into one - VPP (Red Faction) + VPP v1-2 (Red Faction) ✅ ❌ @@ -373,6 +379,7 @@ found on PyPI in the [sourcepp](https://pypi.org/project/sourcepp) package. - `steampp` is based on the [SteamAppPathProvider](https://github.com/Trico-Everfire/SteamAppPathProvider) library by [@Trico Everfire](https://github.com/Trico-Everfire) and [Momentum Mod](https://momentum-mod.org) contributors. - `vpkpp`'s 007 parser is based on [reverse-engineering work](https://raw.githubusercontent.com/SmileyAG/dumpster/refs/heads/src_jb007nightfirepc_alurazoe/file_format_analysis.txt) by Alhexx. - `vpkpp`'s GCF parser was contributed by [@bt](https://github.com/caatge) and [@ymgve](https://github.com/ymgve). +- `vpkpp`'s HOG parser was contributed by [@erysdren](https://github.com/erysdren). - `vpkpp`'s OL parser is based on [reverse-engineering work](https://github.com/erysdren/scratch/blob/main/kaitai/worldcraft_ol.ksy) by [@erysdren](https://github.com/erysdren). - `vpkpp`'s ORE parser is based on [reverse-engineering work](https://github.com/erysdren/narbacular-drop-tools) by [@erysdren](https://github.com/erysdren). - `vpkpp`'s VPP parser was contributed by [@erysdren](https://github.com/erysdren). diff --git a/docs/index.md b/docs/index.md index dc8c90785..0ee0f1685 100644 --- a/docs/index.md +++ b/docs/index.md @@ -140,11 +140,11 @@ Several modern C++20 libraries for sanely parsing Valve formats, rolled into one ❌ - vpkpp + vpkpp 007 v1.1, v1.3 (007 - Nightfire) ✅ ❌ - C
C# + C
C# FPX v10 (Tactical Intervention) @@ -161,6 +161,11 @@ Several modern C++20 libraries for sanely parsing Valve formats, rolled into one ✅ ✅ + + HOG (Descent) + ✅ + ❌ + OL (Worldcraft Object Library) ✅ @@ -199,7 +204,7 @@ Several modern C++20 libraries for sanely parsing Valve formats, rolled into one ✅ - VPP (Red Faction) + VPP v1-2 (Red Faction) ✅ ❌ @@ -329,6 +334,7 @@ found on PyPI in the [sourcepp](https://pypi.org/project/sourcepp) package. - `steampp` is based on the [SteamAppPathProvider](https://github.com/Trico-Everfire/SteamAppPathProvider) library by [@Trico Everfire](https://github.com/Trico-Everfire) and [Momentum Mod](https://momentum-mod.org) contributors. - `vpkpp`'s 007 parser is based on [reverse-engineering work](https://raw.githubusercontent.com/SmileyAG/dumpster/refs/heads/src_jb007nightfirepc_alurazoe/file_format_analysis.txt) by Alhexx. - `vpkpp`'s GCF parser was contributed by [@bt](https://github.com/caatge) and [@ymgve](https://github.com/ymgve). +- `vpkpp`'s HOG parser was contributed by [@erysdren](https://github.com/erysdren). - `vpkpp`'s OL parser is based on [reverse-engineering work](https://github.com/erysdren/scratch/blob/main/kaitai/worldcraft_ol.ksy) by [@erysdren](https://github.com/erysdren). - `vpkpp`'s ORE parser is based on [reverse-engineering work](https://github.com/erysdren/narbacular-drop-tools) by [@erysdren](https://github.com/erysdren). - `vpkpp`'s VPP parser was contributed by [@erysdren](https://github.com/erysdren).