aboutsummaryrefslogtreecommitdiff
path: root/libs/game_gamebryo/src/gamebryo/gamebryosavegame.h
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
commit7ee008e150bc5bcf76082d726f719ee0fdfda982 (patch)
tree27fb39be241fdb5ac2734c574de678977d1856d0 /libs/game_gamebryo/src/gamebryo/gamebryosavegame.h
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 <noreply@anthropic.com>
Diffstat (limited to 'libs/game_gamebryo/src/gamebryo/gamebryosavegame.h')
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryosavegame.h240
1 files changed, 240 insertions, 0 deletions
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryosavegame.h b/libs/game_gamebryo/src/gamebryo/gamebryosavegame.h
new file mode 100644
index 0000000..44d0584
--- /dev/null
+++ b/libs/game_gamebryo/src/gamebryo/gamebryosavegame.h
@@ -0,0 +1,240 @@
+#ifndef GAMEBRYOSAVEGAME_H
+#define GAMEBRYOSAVEGAME_H
+
+#include "isavegame.h"
+#include "memoizedlock.h"
+
+#include <QDateTime>
+#include <QFile>
+#include <QImage>
+#include <QString>
+#include <QStringList>
+
+#include <stddef.h>
+#include <stdexcept>
+
+struct _SYSTEMTIME;
+
+namespace MOBase
+{
+class IPluginGame;
+} // namespace MOBase
+
+class GameGamebryo;
+
+class GamebryoSaveGame : public MOBase::ISaveGame
+{
+public:
+ GamebryoSaveGame(QString const& file, GameGamebryo const* game,
+ bool const lightEnabled = false, bool const mediumEnabled = false);
+
+ virtual ~GamebryoSaveGame();
+
+public: // ISaveGame interface
+ virtual QString getFilepath() const override;
+ virtual QDateTime getCreationTime() const override;
+ virtual QString getName() const override;
+ virtual QString getSaveGroupIdentifier() const override;
+ virtual QStringList allFiles() const override;
+
+public:
+ bool hasScriptExtenderFile() const;
+
+ // Simple getters
+ virtual QString getPCName() const { return m_PCName; }
+ virtual unsigned short getPCLevel() const { return m_PCLevel; }
+ virtual QString getPCLocation() const { return m_PCLocation; }
+ virtual unsigned long getSaveNumber() const { return m_SaveNumber; }
+
+ QStringList const& getPlugins() const { return m_DataFields.value()->Plugins; }
+ QStringList const& getMediumPlugins() const
+ {
+ return m_DataFields.value()->MediumPlugins;
+ }
+ QStringList const& getLightPlugins() const
+ {
+ return m_DataFields.value()->LightPlugins;
+ }
+ QImage const& getScreenshot() const { return m_DataFields.value()->Screenshot; }
+
+ bool isMediumEnabled() const { return m_MediumEnabled; }
+
+ bool isLightEnabled() const { return m_LightEnabled; }
+
+ enum class StringType
+ {
+ TYPE_BZSTRING,
+ TYPE_BSTRING,
+ TYPE_WSTRING
+ };
+
+ enum class StringFormat
+ {
+ UTF8,
+ LOCAL8BIT
+ };
+
+protected:
+ friend class FileWrapper;
+
+ class FileWrapper
+ {
+ public:
+ /**
+ * @brief Construct the save file information.
+ *
+ * @param filepath The path to the save file.
+ * @params expected Expecte bytes at start of file.
+ *
+ **/
+ FileWrapper(QString const& filepath, QString const& expected);
+
+ /** Set this for save games that have a marker at the end of each
+ * field. Specifically fallout
+ **/
+ void setHasFieldMarkers(bool);
+
+ /** Set bz string mode (1 byte length, null terminated)
+ **/
+ void setPluginString(StringType);
+
+ /** Set string format (utf-8, windows local 8 bit strings)
+ **/
+ void setPluginStringFormat(StringFormat);
+
+ template <typename T>
+ void skip(int count = 1)
+ {
+ if (!m_File.seek(m_File.pos() + count * sizeof(T))) {
+ throw std::runtime_error("unexpected end of file");
+ }
+ }
+
+ template <typename T>
+ void read(T& value)
+ {
+ int read = m_File.read(reinterpret_cast<char*>(&value), sizeof(T));
+ if (read != sizeof(T)) {
+ throw std::runtime_error("unexpected end of file");
+ }
+ if (m_HasFieldMarkers) {
+ skip<char>();
+ }
+ }
+
+ template <>
+ void read<QString>(QString& value);
+
+ void seek(unsigned long pos)
+ {
+ if (!m_File.seek(pos)) {
+ throw std::runtime_error("unexpected end of file");
+ }
+ }
+
+ void read(void* buff, std::size_t length);
+
+ /* Reads RGB image from save
+ * Assumes picture dimentions come immediately before the save
+ */
+ QImage readImage(int scale = 0, bool alpha = false);
+
+ /* Reads RGB image from save */
+ QImage readImage(unsigned long width, unsigned long height, int scale = 0,
+ bool alpha = false);
+
+ /* Sets the compression type. */
+ void setCompressionType(uint16_t type);
+
+ /* uncompress the begining of the compressed block */
+ bool openCompressedData(int bytesToIgnore = 0);
+
+ /* read the next compressed block */
+ bool readNextChunk();
+
+ /* frees the uncompressed block */
+ void closeCompressedData();
+
+ /* Read the save game version in the compressed block */
+ uint8_t readChar(int bytesToIgnore = 0);
+
+ uint16_t readShort(int bytesToIgnore = 0);
+
+ uint32_t readInt(int bytesToIgnore = 0);
+
+ uint64_t readLong(int bytesToIgnore = 0);
+
+ float_t readFloat(int bytesToIgnore = 0);
+
+ /* Read the plugin list */
+ QStringList readPlugins(int bytesToIgnore = 0, int extraData = 0,
+ const QStringList& corePlugins = {});
+
+ /* Read the light plugin list */
+ QStringList readLightPlugins(int bytesToIgnore = 0, int extraData = 0,
+ const QStringList& corePlugins = {});
+
+ /* Read the medium plugin list */
+ QStringList readMediumPlugins(int bytesToIgnore = 0, int extraData = 0,
+ const QStringList& corePlugins = {});
+
+ void close();
+
+ private:
+ QFile m_File;
+ uint64_t m_NextChunk;
+ uint64_t m_UncompressedSize;
+ bool m_HasFieldMarkers;
+ StringType m_PluginString;
+ StringFormat m_PluginStringFormat;
+ QDataStream* m_Data;
+ uint16_t m_CompressionType = 0;
+
+ private:
+ template <typename T>
+ void readQDataStream(QDataStream& data, T& value);
+
+ void readQDataStream(QDataStream& data, void* buff, std::size_t length);
+
+ void skipQDataStream(QDataStream& data, std::size_t length);
+
+ QStringList readPluginData(uint32_t count, int extraData,
+ const QStringList corePlugins);
+ };
+
+ void setCreationTime(_SYSTEMTIME const& time);
+
+ GameGamebryo const* m_Game;
+ bool m_MediumEnabled;
+ bool m_LightEnabled;
+
+ QString m_FileName;
+ QString m_PCName;
+ unsigned short m_PCLevel;
+ QString m_PCLocation;
+ unsigned long m_SaveNumber;
+ QDateTime m_CreationTime;
+
+ // Those three fields are usually much slower to fetch than
+ // the other, so we do not fetch them if not needed.
+ //
+ // This is virtual so child class can add fields if those are
+ // hard to access.
+ struct DataFields
+ {
+ QStringList Plugins;
+ QStringList LightPlugins;
+ QStringList MediumPlugins;
+ QImage Screenshot;
+
+ // We need this constructor.
+ DataFields() {}
+ virtual ~DataFields() {}
+ };
+ MOBase::MemoizedLocked<std::unique_ptr<DataFields>> m_DataFields;
+
+ // Fetch the field.
+ virtual std::unique_ptr<DataFields> fetchDataFields() const = 0;
+};
+
+#endif // GAMEBRYOSAVEGAME_H