From 7ee008e150bc5bcf76082d726f719ee0fdfda982 Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Wed, 11 Feb 2026 02:37:39 -0600 Subject: Fluorine Manager: full Linux port of Mod Organizer 2 Complete native Linux port with FUSE-based virtual filesystem, Proton/umu-run integration, and Flatpak packaging. Key features: - FUSE VFS replacing Windows USVFS (in-process + standalone helper for Flatpak) - Proton/GE-Proton/umu-run launcher with env var forwarding - Flatpak support (sandbox-aware VFS, NXM handler, umu-run) - Wine prefix management UI - Case-insensitive path resolution for Linux filesystems - QSettings-safe INI handling (avoids Bethesda INI corruption) - Portable instance support with auto-generated launcher scripts Co-Authored-By: Claude Opus 4.6 --- libs/esptk/include/esptk/espexceptions.h | 23 ++++++++++ libs/esptk/include/esptk/espfile.h | 72 ++++++++++++++++++++++++++++++++ libs/esptk/include/esptk/esptypes.h | 20 +++++++++ libs/esptk/include/esptk/record.h | 60 ++++++++++++++++++++++++++ libs/esptk/include/esptk/subrecord.h | 44 +++++++++++++++++++ libs/esptk/include/esptk/tes3record.h | 32 ++++++++++++++ libs/esptk/include/esptk/tes3subrecord.h | 42 +++++++++++++++++++ 7 files changed, 293 insertions(+) create mode 100644 libs/esptk/include/esptk/espexceptions.h create mode 100644 libs/esptk/include/esptk/espfile.h create mode 100644 libs/esptk/include/esptk/esptypes.h create mode 100644 libs/esptk/include/esptk/record.h create mode 100644 libs/esptk/include/esptk/subrecord.h create mode 100644 libs/esptk/include/esptk/tes3record.h create mode 100644 libs/esptk/include/esptk/tes3subrecord.h (limited to 'libs/esptk/include') diff --git a/libs/esptk/include/esptk/espexceptions.h b/libs/esptk/include/esptk/espexceptions.h new file mode 100644 index 0000000..9830ed6 --- /dev/null +++ b/libs/esptk/include/esptk/espexceptions.h @@ -0,0 +1,23 @@ +#ifndef ESPEXCEPTIONS_H +#define ESPEXCEPTIONS_H + +#include + +namespace ESP +{ + +class InvalidRecordException : public std::runtime_error +{ +public: + InvalidRecordException(const std::string& message) : std::runtime_error(message) {} +}; + +class InvalidFileException : public std::runtime_error +{ +public: + InvalidFileException(const std::string& message) : std::runtime_error(message) {} +}; + +} // namespace ESP + +#endif // ESPEXCEPTIONS_H diff --git a/libs/esptk/include/esptk/espfile.h b/libs/esptk/include/esptk/espfile.h new file mode 100644 index 0000000..04be76e --- /dev/null +++ b/libs/esptk/include/esptk/espfile.h @@ -0,0 +1,72 @@ +#ifndef ESPFILE_H +#define ESPFILE_H + +#include "record.h" +#include "tes3record.h" +#include +#include +#include + +namespace ESP +{ + +class SubRecord; + +class File +{ +public: + File(const std::string& fileName); + File(const std::wstring& fileName); + + Record readRecord(); + + bool isMaster() const; + bool isLight(bool overlaySupport = false) const; + bool isMedium() const; + bool isOverlay() const; + bool isBlueprint() const; + bool isDummy() const; + uint16_t formVersion() const { return m_MainRecord.formVersion(); }; + float headerVersion() const { return m_Header.version; } + std::string author() const { return m_Author; } + std::string description() const { return m_Description; } + std::set masters() const { return m_Masters; } + +private: + void init(); + + void onHEDR(const SubRecord& rec); + void onMAST(const SubRecord& rec); + void onCNAM(const SubRecord& rec); + void onSNAM(const SubRecord& rec); + +private: + std::ifstream m_File; + + struct + { + float version; + int32_t numRecords; + uint32_t nextObjectId; + } m_Header; + + struct + { + float version; + uint32_t unknown; + char author[32]; + char description[256]; + uint32_t numRecords; + } m_TES3Header; + + Record m_MainRecord; + + std::string m_Author; + std::string m_Description; + + std::set m_Masters; +}; + +} // namespace ESP + +#endif // ESPFILE_H diff --git a/libs/esptk/include/esptk/esptypes.h b/libs/esptk/include/esptk/esptypes.h new file mode 100644 index 0000000..135dde1 --- /dev/null +++ b/libs/esptk/include/esptk/esptypes.h @@ -0,0 +1,20 @@ +#ifndef ESPTYPES_H +#define ESPTYPES_H + +#include +#include + +template +static T readType(std::istream& stream) +{ + union + { + char buffer[sizeof(T)]; + T value; + }; + memset(buffer, 0x42, sizeof(T)); + stream.read(buffer, sizeof(T)); + return value; +} + +#endif // ESPTYPES_H diff --git a/libs/esptk/include/esptk/record.h b/libs/esptk/include/esptk/record.h new file mode 100644 index 0000000..3a2c996 --- /dev/null +++ b/libs/esptk/include/esptk/record.h @@ -0,0 +1,60 @@ +#ifndef RECORD_H +#define RECORD_H + +#include +#include +#include + +namespace ESP +{ + +/** + * @brief record storage class without record-specific information + */ +class Record +{ +public: + enum EFlag + { + FLAG_MASTER = 0x00000001, + FLAG_LIGHT_ALTERNATE = 0x00000100, // SF light flag (FE/FF memory space) + FLAG_LIGHT = 0x00000200, // SSE & FO4 light flag (FE/FF memory space) + FLAG_OVERLAY = 0x00000200, // SF overlay flag (does not claim new memory space, + // overrules light flag) + FLAG_MEDIUM = 0x00000400, // SF Creation update, indicates medium plugin type (FD + // memory space) + FLAG_BLUEPRINT = + 0x0000800, // SF blueprint flag (force loads after all other plugins) + FLAG_COMPRESSED = 0x00040000 + }; + +public: + Record(); + + bool readFrom(std::istream& stream); + + bool flagSet(EFlag flag) const; + + uint16_t formVersion() const { return m_FormVersion; } + + const std::vector& data() const { return m_Data; } + +private: + struct Header + { + char type[4]; + uint32_t dataSize; + uint32_t flags; + uint32_t id; + uint32_t revision; + } m_Header; + + uint16_t m_FormVersion; + std::vector m_Data; + + bool m_OblivionStyle; +}; + +} // namespace ESP + +#endif // RECORD_H diff --git a/libs/esptk/include/esptk/subrecord.h b/libs/esptk/include/esptk/subrecord.h new file mode 100644 index 0000000..f9eb36c --- /dev/null +++ b/libs/esptk/include/esptk/subrecord.h @@ -0,0 +1,44 @@ +#ifndef SUBRECORD_H +#define SUBRECORD_H + +#include +#include +#include + +namespace ESP +{ + +/** + * @brief sub-record storage class without record-specific information + */ +class SubRecord +{ +public: + enum EType + { + TYPE_UNKNOWN, + TYPE_HEDR, + TYPE_CNAM, + TYPE_SNAM, + TYPE_MAST, + TYPE_ONAM + }; + + static const int NUM_TYPES = TYPE_ONAM; + +public: + SubRecord(); + + bool readFrom(std::istream& stream, uint32_t sizeOverride = 0UL); + + EType type() const { return m_Type; } + const std::vector& data() const { return m_Data; } + +private: + EType m_Type; + std::vector m_Data; +}; + +} // namespace ESP + +#endif // SUBRECORD_H diff --git a/libs/esptk/include/esptk/tes3record.h b/libs/esptk/include/esptk/tes3record.h new file mode 100644 index 0000000..19fa040 --- /dev/null +++ b/libs/esptk/include/esptk/tes3record.h @@ -0,0 +1,32 @@ +#ifndef TES3_RECORD_H +#define TES3_RECORD_H + +#include +#include +#include + +namespace ESP +{ + +/** + * @brief record storage class without record-specific information + */ +class TES3Record +{ +public: + TES3Record(); + + bool readFrom(std::istream& stream); + +private: + struct Header + { + char type[4]; + float version; + long unknown; + } m_Header; +}; + +} // namespace ESP + +#endif // TES3_RECORD_H diff --git a/libs/esptk/include/esptk/tes3subrecord.h b/libs/esptk/include/esptk/tes3subrecord.h new file mode 100644 index 0000000..966afcb --- /dev/null +++ b/libs/esptk/include/esptk/tes3subrecord.h @@ -0,0 +1,42 @@ +#ifndef TES3_SUBRECORD_H +#define TES3_SUBRECORD_H + +#include +#include +#include + +namespace ESP +{ + +/** + * @brief sub-record storage class without record-specific information + */ +class TES3SubRecord +{ +public: + enum EType + { + TYPE_UNKNOWN, + TYPE_HEDR, + TYPE_MAST, + TYPE_DATA + }; + + static const int NUM_TYPES = TYPE_DATA; + +public: + TES3SubRecord(); + + bool readFrom(std::istream& stream, uint32_t sizeOverride = 0UL); + + EType type() const { return m_Type; } + const std::vector& data() const { return m_Data; } + +private: + EType m_Type; + std::vector m_Data; +}; + +} // namespace ESP + +#endif // TES3_SUBRECORD_H -- cgit v1.3.1