aboutsummaryrefslogtreecommitdiff
path: root/libs/game_bethesda/src/games/nehrim
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_bethesda/src/games/nehrim
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_bethesda/src/games/nehrim')
-rw-r--r--libs/game_bethesda/src/games/nehrim/CMakeLists.txt21
-rw-r--r--libs/game_bethesda/src/games/nehrim/game_nehrim_en.ts17
-rw-r--r--libs/game_bethesda/src/games/nehrim/gamenehrim.cpp205
-rw-r--r--libs/game_bethesda/src/games/nehrim/gamenehrim.h56
-rw-r--r--libs/game_bethesda/src/games/nehrim/gamenehrim.json1
-rw-r--r--libs/game_bethesda/src/games/nehrim/nehrimbsainvalidation.cpp16
-rw-r--r--libs/game_bethesda/src/games/nehrim/nehrimbsainvalidation.h20
-rw-r--r--libs/game_bethesda/src/games/nehrim/nehrimdataarchives.cpp31
-rw-r--r--libs/game_bethesda/src/games/nehrim/nehrimdataarchives.h23
-rw-r--r--libs/game_bethesda/src/games/nehrim/nehrimmoddatachecker.cpp30
-rw-r--r--libs/game_bethesda/src/games/nehrim/nehrimmoddatachecker.h35
-rw-r--r--libs/game_bethesda/src/games/nehrim/nehrimmoddatacontent.h22
-rw-r--r--libs/game_bethesda/src/games/nehrim/nehrimsavegame.cpp72
-rw-r--r--libs/game_bethesda/src/games/nehrim/nehrimsavegame.h21
-rw-r--r--libs/game_bethesda/src/games/nehrim/nehrimscriptextender.cpp20
-rw-r--r--libs/game_bethesda/src/games/nehrim/nehrimscriptextender.h18
16 files changed, 608 insertions, 0 deletions
diff --git a/libs/game_bethesda/src/games/nehrim/CMakeLists.txt b/libs/game_bethesda/src/games/nehrim/CMakeLists.txt
new file mode 100644
index 0000000..a1c1c1f
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.16)
+
+add_library(game_nehrim SHARED
+ gamenehrim.cpp
+ gamenehrim.h
+ nehrimbsainvalidation.cpp
+ nehrimbsainvalidation.h
+ nehrimdataarchives.cpp
+ nehrimdataarchives.h
+ nehrimmoddatachecker.cpp
+ nehrimmoddatachecker.h
+ nehrimmoddatacontent.h
+ nehrimsavegame.cpp
+ nehrimsavegame.h
+ nehrimscriptextender.cpp
+ nehrimscriptextender.h
+)
+mo2_configure_plugin(game_nehrim NO_SOURCES WARNINGS 4)
+mo2_default_source_group()
+target_link_libraries(game_nehrim PRIVATE game_gamebryo)
+mo2_install_plugin(game_nehrim)
diff --git a/libs/game_bethesda/src/games/nehrim/game_nehrim_en.ts b/libs/game_bethesda/src/games/nehrim/game_nehrim_en.ts
new file mode 100644
index 0000000..b5c79e4
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/game_nehrim_en.ts
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="en_US">
+<context>
+ <name>GameNehrim</name>
+ <message>
+ <location filename="gamenehrim.cpp" line="84"/>
+ <source>Nehrim Support Plugin</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="gamenehrim.cpp" line="94"/>
+ <source>Adds support for the game Nehrim</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/libs/game_bethesda/src/games/nehrim/gamenehrim.cpp b/libs/game_bethesda/src/games/nehrim/gamenehrim.cpp
new file mode 100644
index 0000000..2e3c8c5
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/gamenehrim.cpp
@@ -0,0 +1,205 @@
+#include "gamenehrim.h"
+
+#include "nehrimbsainvalidation.h"
+#include "nehrimdataarchives.h"
+#include "nehrimmoddatachecker.h"
+#include "nehrimmoddatacontent.h"
+#include "nehrimsavegame.h"
+#include "nehrimscriptextender.h"
+
+#include "executableinfo.h"
+#include "pluginsetting.h"
+#include <gamebryogameplugins.h>
+#include <gamebryolocalsavegames.h>
+#include <gamebryosavegameinfo.h>
+#include <gamebryounmanagedmods.h>
+
+#include <QCoreApplication>
+#include <QDir>
+#include <QFileInfo>
+
+#include <memory>
+
+using namespace MOBase;
+
+GameNehrim::GameNehrim() {}
+
+bool GameNehrim::init(IOrganizer* moInfo)
+{
+ if (!GameGamebryo::init(moInfo)) {
+ return false;
+ }
+
+ auto dataArchives = std::make_shared<NehrimDataArchives>(this);
+ registerFeature(std::make_shared<NehrimScriptExtender>(this));
+ registerFeature(dataArchives);
+ registerFeature(std::make_shared<NehrimBSAInvalidation>(dataArchives.get(), this));
+ registerFeature(std::make_shared<GamebryoSaveGameInfo>(this));
+ registerFeature(std::make_shared<GamebryoLocalSavegames>(this, "oblivion.ini"));
+ registerFeature(std::make_shared<NehrimModDataChecker>(this));
+ registerFeature(std::make_shared<NehrimModDataContent>(m_Organizer->gameFeatures()));
+ registerFeature(std::make_shared<GamebryoGamePlugins>(moInfo));
+ registerFeature(std::make_shared<GamebryoUnmangedMods>(this));
+ return true;
+}
+
+QString GameNehrim::gameName() const
+{
+ return "Nehrim";
+}
+
+QList<ExecutableInfo> GameNehrim::executables() const
+{
+ return QList<ExecutableInfo>()
+ << ExecutableInfo("Nehrim", findInGameFolder("Oblivion.exe"))
+ << ExecutableInfo("Nehrim Launcher", findInGameFolder("NehrimLauncher.exe"))
+ << ExecutableInfo("Oblivion Mod Manager",
+ findInGameFolder("OblivionModManager.exe"))
+ << ExecutableInfo("BOSS", findInGameFolder("BOSS/BOSS.exe"))
+ << ExecutableInfo("LOOT", QFileInfo(getLootPath()))
+ .withArgument("--game=\"Nehrim\"")
+ << ExecutableInfo("Construction Set",
+ findInGameFolder("TESConstructionSet.exe"));
+}
+
+QList<ExecutableForcedLoadSetting> GameNehrim::executableForcedLoads() const
+{
+ // TODO Search game directory for OBSE DLLs
+ return QList<ExecutableForcedLoadSetting>()
+ << ExecutableForcedLoadSetting("Oblivion.exe", "obse_1_2_416.dll")
+ .withForced()
+ .withEnabled()
+ << ExecutableForcedLoadSetting("TESConstructionSet.exe", "obse_editor_1_2.dll")
+ .withForced()
+ .withEnabled();
+}
+
+QString GameNehrim::name() const
+{
+ return "Nehrim Support Plugin";
+}
+
+QString GameNehrim::localizedName() const
+{
+ return tr("Nehrim Support Plugin");
+}
+
+QString GameNehrim::author() const
+{
+ return "Tannin & MO2 Team";
+}
+
+QString GameNehrim::description() const
+{
+ return tr("Adds support for the game Nehrim");
+}
+
+MOBase::VersionInfo GameNehrim::version() const
+{
+ return VersionInfo(1, 1, 1, VersionInfo::RELEASE_FINAL);
+}
+
+QList<PluginSetting> GameNehrim::settings() const
+{
+ return QList<PluginSetting>();
+}
+
+void GameNehrim::initializeProfile(const QDir& path, ProfileSettings settings) const
+{
+ if (settings.testFlag(IPluginGame::MODS)) {
+ copyToProfile(localAppFolder() + "/Oblivion", path, "plugins.txt");
+ }
+
+ if (settings.testFlag(IPluginGame::CONFIGURATION)) {
+ if (settings.testFlag(IPluginGame::PREFER_DEFAULTS) ||
+ !QFileInfo(myGamesPath() + "/oblivion.ini").exists()) {
+ copyToProfile(gameDirectory().absolutePath(), path, "oblivion_default.ini",
+ "oblivion.ini");
+ } else {
+ copyToProfile(myGamesPath(), path, "oblivion.ini");
+ }
+
+ copyToProfile(myGamesPath(), path, "oblivionprefs.ini");
+ }
+}
+
+QString GameNehrim::savegameExtension() const
+{
+ return "ess";
+}
+
+QString GameNehrim::savegameSEExtension() const
+{
+ return "obse";
+}
+
+std::shared_ptr<const GamebryoSaveGame> GameNehrim::makeSaveGame(QString filePath) const
+{
+ return std::make_shared<const NehrimSaveGame>(filePath, this);
+}
+
+QString GameNehrim::steamAPPId() const
+{
+ return "22330";
+}
+
+QStringList GameNehrim::primaryPlugins() const
+{
+ return {"Nehrim.esm", "Translation.esp"};
+}
+
+QString GameNehrim::gameShortName() const
+{
+ return "Nehrim";
+}
+
+QString GameNehrim::gameNexusName() const
+{
+ return "Nehrim";
+}
+
+QStringList GameNehrim::iniFiles() const
+{
+ return {"oblivion.ini", "oblivionprefs.ini"};
+}
+
+QStringList GameNehrim::DLCPlugins() const
+{
+ return {};
+}
+
+int GameNehrim::nexusModOrganizerID() const
+{
+ return -1;
+}
+
+int GameNehrim::nexusGameID() const
+{
+ return 3312;
+}
+
+QStringList GameNehrim::primarySources() const
+{
+ return {"Oblivion"};
+}
+
+QStringList GameNehrim::validShortNames() const
+{
+ return {"Oblivion"};
+}
+
+QString GameNehrim::identifyGamePath() const
+{
+#ifdef _WIN32
+ QString path = "Software\\Bethesda Softworks\\Oblivion";
+ return findInRegistry(HKEY_LOCAL_MACHINE, path.toStdWString().c_str(),
+ L"Installed Path");
+#else
+ return GameGamebryo::identifyGamePath();
+#endif
+}
+
+QString GameNehrim::binaryName() const
+{
+ return "NehrimLauncher.exe";
+}
diff --git a/libs/game_bethesda/src/games/nehrim/gamenehrim.h b/libs/game_bethesda/src/games/nehrim/gamenehrim.h
new file mode 100644
index 0000000..a8af24c
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/gamenehrim.h
@@ -0,0 +1,56 @@
+#ifndef GAMENEHRIM_H
+#define GAMENEHRIM_H
+
+#include "gamegamebryo.h"
+
+#include <QObject>
+#include <QtGlobal>
+
+class GameNehrim : public GameGamebryo
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.tannin.GameNehrim" FILE "gamenehrim.json")
+
+public:
+ GameNehrim();
+
+ virtual bool init(MOBase::IOrganizer* moInfo) override;
+
+public: // IPluginGame interface
+ virtual QString gameName() const override;
+ virtual QList<MOBase::ExecutableInfo> executables() const override;
+ virtual QList<MOBase::ExecutableForcedLoadSetting>
+ executableForcedLoads() const override;
+ virtual void initializeProfile(const QDir& path,
+ ProfileSettings settings) const override;
+ virtual QString steamAPPId() const override;
+ virtual QStringList primaryPlugins() const override;
+ virtual QString gameShortName() const override;
+ virtual QString gameNexusName() const override;
+ virtual QStringList iniFiles() const override;
+ virtual QStringList DLCPlugins() const override;
+ virtual int nexusModOrganizerID() const override;
+ virtual int nexusGameID() const override;
+ virtual QStringList primarySources() const override;
+ virtual QStringList validShortNames() const override;
+
+ // Weird stuff happens in these functions due to Nehrim
+ // technically being in the Oblivion folder
+ virtual QString identifyGamePath() const override;
+ virtual QString binaryName() const override;
+
+public: // IPlugin interface
+ virtual QString name() const override;
+ virtual QString localizedName() const override;
+ virtual QString author() const override;
+ virtual QString description() const override;
+ virtual MOBase::VersionInfo version() const override;
+ virtual QList<MOBase::PluginSetting> settings() const override;
+
+protected:
+ std::shared_ptr<const GamebryoSaveGame> makeSaveGame(QString filePath) const override;
+ QString savegameExtension() const override;
+ QString savegameSEExtension() const override;
+};
+
+#endif // GAMENEHRIM_H
diff --git a/libs/game_bethesda/src/games/nehrim/gamenehrim.json b/libs/game_bethesda/src/games/nehrim/gamenehrim.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/gamenehrim.json
@@ -0,0 +1 @@
+{}
diff --git a/libs/game_bethesda/src/games/nehrim/nehrimbsainvalidation.cpp b/libs/game_bethesda/src/games/nehrim/nehrimbsainvalidation.cpp
new file mode 100644
index 0000000..ac89d54
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/nehrimbsainvalidation.cpp
@@ -0,0 +1,16 @@
+#include "nehrimbsainvalidation.h"
+
+NehrimBSAInvalidation::NehrimBSAInvalidation(MOBase::DataArchives* dataArchives,
+ MOBase::IPluginGame const* game)
+ : GamebryoBSAInvalidation(dataArchives, "oblivion.ini", game)
+{}
+
+QString NehrimBSAInvalidation::invalidationBSAName() const
+{
+ return "Nehrim - Invalidation.bsa";
+}
+
+unsigned long NehrimBSAInvalidation::bsaVersion() const
+{
+ return 0x67;
+}
diff --git a/libs/game_bethesda/src/games/nehrim/nehrimbsainvalidation.h b/libs/game_bethesda/src/games/nehrim/nehrimbsainvalidation.h
new file mode 100644
index 0000000..3a2e2b3
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/nehrimbsainvalidation.h
@@ -0,0 +1,20 @@
+#ifndef NEHRIMBSAINVALIDATION_H
+#define NEHRIMBSAINVALIDATION_H
+
+#include "gamebryobsainvalidation.h"
+#include "nehrimdataarchives.h"
+
+#include <memory>
+
+class NehrimBSAInvalidation : public GamebryoBSAInvalidation
+{
+public:
+ NehrimBSAInvalidation(MOBase::DataArchives* dataArchives,
+ MOBase::IPluginGame const* game);
+
+private:
+ virtual QString invalidationBSAName() const override;
+ virtual unsigned long bsaVersion() const override;
+};
+
+#endif // NEHRIMBSAINVALIDATION_H
diff --git a/libs/game_bethesda/src/games/nehrim/nehrimdataarchives.cpp b/libs/game_bethesda/src/games/nehrim/nehrimdataarchives.cpp
new file mode 100644
index 0000000..8fe5b06
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/nehrimdataarchives.cpp
@@ -0,0 +1,31 @@
+#include "nehrimdataarchives.h"
+#include <utility.h>
+
+QStringList NehrimDataArchives::vanillaArchives() const
+{
+ return {"N - Meshes.bsa", "N - Textures1.bsa", "N - Textures2.bsa", "N - Misc.bsa",
+ "N - Sounds.bsa", "L - Voices.bsa", "L - Misc.bsa"};
+}
+
+QStringList NehrimDataArchives::archives(const MOBase::IProfile* profile) const
+{
+ QStringList result;
+
+ QString iniFile = profile->localSettingsEnabled()
+ ? QDir(profile->absolutePath()).absoluteFilePath("oblivion.ini")
+ : localGameDirectory().absoluteFilePath("oblivion.ini");
+ result.append(getArchivesFromKey(iniFile, "SArchiveList"));
+
+ return result;
+}
+
+void NehrimDataArchives::writeArchiveList(MOBase::IProfile* profile,
+ const QStringList& before)
+{
+ QString list = before.join(", ");
+
+ QString iniFile = profile->localSettingsEnabled()
+ ? QDir(profile->absolutePath()).absoluteFilePath("oblivion.ini")
+ : localGameDirectory().absoluteFilePath("oblivion.ini");
+ setArchivesToKey(iniFile, "SArchiveList", list);
+}
diff --git a/libs/game_bethesda/src/games/nehrim/nehrimdataarchives.h b/libs/game_bethesda/src/games/nehrim/nehrimdataarchives.h
new file mode 100644
index 0000000..2b6425c
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/nehrimdataarchives.h
@@ -0,0 +1,23 @@
+#ifndef NEHRIMDATAARCHIVES_H
+#define NEHRIMDATAARCHIVES_H
+
+#include <QDir>
+#include <QString>
+#include <QStringList>
+#include <gamebryodataarchives.h>
+#include <iprofile.h>
+
+class NehrimDataArchives : public GamebryoDataArchives
+{
+public:
+ using GamebryoDataArchives::GamebryoDataArchives;
+
+ virtual QStringList vanillaArchives() const override;
+ virtual QStringList archives(const MOBase::IProfile* profile) const override;
+
+private:
+ virtual void writeArchiveList(MOBase::IProfile* profile,
+ const QStringList& before) override;
+};
+
+#endif // NEHRIMDATAARCHIVES_H
diff --git a/libs/game_bethesda/src/games/nehrim/nehrimmoddatachecker.cpp b/libs/game_bethesda/src/games/nehrim/nehrimmoddatachecker.cpp
new file mode 100644
index 0000000..3ec4eb0
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/nehrimmoddatachecker.cpp
@@ -0,0 +1,30 @@
+#include "nehrimmoddatachecker.h"
+
+MOBase::ModDataChecker::CheckReturn NehrimModDataChecker::dataLooksValid(
+ std::shared_ptr<const MOBase::IFileTree> fileTree) const
+{
+ // Check with Gamebryo stuff:
+ auto check = GamebryoModDataChecker::dataLooksValid(fileTree);
+ if (check == CheckReturn::VALID) {
+ return check;
+ }
+
+ // Check for OBSE_ files:
+ for (auto const& entry : *fileTree) {
+ if (entry->isDir() || !entry->name().startsWith("OBSE", Qt::CaseInsensitive)) {
+ return CheckReturn::INVALID;
+ }
+ }
+
+ return CheckReturn::FIXABLE;
+}
+
+std::shared_ptr<MOBase::IFileTree>
+NehrimModDataChecker::fix(std::shared_ptr<MOBase::IFileTree> fileTree) const
+{
+ // If we arrive here, it means all files starts with OBSE.
+ auto data = fileTree->createOrphanTree();
+ auto obse = data->addDirectory("OBSE/Plugins");
+ obse->merge(fileTree);
+ return data;
+}
diff --git a/libs/game_bethesda/src/games/nehrim/nehrimmoddatachecker.h b/libs/game_bethesda/src/games/nehrim/nehrimmoddatachecker.h
new file mode 100644
index 0000000..c8717dd
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/nehrimmoddatachecker.h
@@ -0,0 +1,35 @@
+#ifndef NEHRIM_MODATACHECKER_H
+#define NEHRIM_MODATACHECKER_H
+
+#include <gamebryomoddatachecker.h>
+
+class NehrimModDataChecker : public GamebryoModDataChecker
+{
+public:
+ using GamebryoModDataChecker::GamebryoModDataChecker;
+
+ CheckReturn
+ dataLooksValid(std::shared_ptr<const MOBase::IFileTree> fileTree) const override;
+ std::shared_ptr<MOBase::IFileTree>
+ fix(std::shared_ptr<MOBase::IFileTree> fileTree) const override;
+
+protected:
+ virtual const FileNameSet& possibleFolderNames() const override
+ {
+ static FileNameSet result{"fonts", "interface", "menus",
+ "meshes", "music", "scripts",
+ "shaders", "sound", "strings",
+ "textures", "trees", "video",
+ "facegen", "obse", "distantlod",
+ "asi", "distantland", "mits",
+ "dllplugins", "CalienteTools", "NetScriptFramework"};
+ return result;
+ }
+ virtual const FileNameSet& possibleFileExtensions() const override
+ {
+ static FileNameSet result{"esp", "esm", "bsa", "modgroups", "ini"};
+ return result;
+ }
+};
+
+#endif // NEHRIM_MODATACHECKER_H
diff --git a/libs/game_bethesda/src/games/nehrim/nehrimmoddatacontent.h b/libs/game_bethesda/src/games/nehrim/nehrimmoddatacontent.h
new file mode 100644
index 0000000..c640377
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/nehrimmoddatacontent.h
@@ -0,0 +1,22 @@
+#ifndef NEHRIM_MODDATACONTENT_H
+#define NEHRIM_MODDATACONTENT_H
+
+#include <gamebryomoddatacontent.h>
+#include <ifiletree.h>
+
+class NehrimModDataContent : public GamebryoModDataContent
+{
+public:
+ /**
+ *
+ */
+ NehrimModDataContent(const MOBase::IGameFeatures* gameFeatures)
+ : GamebryoModDataContent(gameFeatures)
+ {
+ // Just need to disable some contents:
+ m_Enabled[CONTENT_MCM] = false;
+ m_Enabled[CONTENT_SKYPROC] = false;
+ }
+};
+
+#endif // NEHRIM_MODDATACONTENT_H
diff --git a/libs/game_bethesda/src/games/nehrim/nehrimsavegame.cpp b/libs/game_bethesda/src/games/nehrim/nehrimsavegame.cpp
new file mode 100644
index 0000000..4a6f912
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/nehrimsavegame.cpp
@@ -0,0 +1,72 @@
+#include "nehrimsavegame.h"
+
+NehrimSaveGame::NehrimSaveGame(QString const& fileName, GameNehrim const* game)
+ : GamebryoSaveGame(fileName, game)
+{
+ FileWrapper file(getFilepath(), "TES4SAVEGAME");
+ file.setPluginString(GamebryoSaveGame::StringType::TYPE_BSTRING);
+
+ _SYSTEMTIME creationTime;
+ fetchInformationFields(file, m_SaveNumber, m_PCName, m_PCLevel, m_PCLocation,
+ creationTime);
+ setCreationTime(creationTime);
+}
+
+void NehrimSaveGame::fetchInformationFields(FileWrapper& file,
+ uint32_t& saveNumber,
+ QString& playerName,
+ unsigned short& playerLevel,
+ QString& playerLocation,
+ _SYSTEMTIME& creationTime) const
+{
+ file.skip<unsigned char>(); // Major version
+ file.skip<unsigned char>(); // Minor version
+
+ file.skip<_SYSTEMTIME>(); // exe last modified (!)
+
+ file.skip<uint32_t>(); // Header version
+ file.skip<uint32_t>(); // Header size
+
+ file.read(saveNumber);
+
+ file.read(playerName);
+ file.read(playerLevel);
+ file.read(playerLocation);
+
+ file.skip<float>(); // game days
+ file.skip<uint32_t>(); // game ticks
+
+ // there is a save time stored here. So use it rather than the file time, which
+ // could have been copied.
+ // Note: This says it uses getlocaltime api to obtain it which is u/s - if so
+ // we should ignore this.
+ file.read(creationTime);
+}
+
+std::unique_ptr<GamebryoSaveGame::DataFields> NehrimSaveGame::fetchDataFields() const
+{
+ FileWrapper file(getFilepath(), "TES4SAVEGAME");
+ file.setPluginString(GamebryoSaveGame::StringType::TYPE_BSTRING);
+
+ std::unique_ptr<DataFields> fields = std::make_unique<DataFields>();
+
+ {
+ QString dummyName, dummyLocation;
+ unsigned short dummyLevel;
+ uint32_t dummySaveNumber;
+ _SYSTEMTIME dummyTime;
+
+ fetchInformationFields(file, dummySaveNumber, dummyName, dummyLevel, dummyLocation,
+ dummyTime);
+ }
+
+ // Note that screenshot size, width, height and data are apparently the same
+ // structure
+ file.skip<uint32_t>(); // Screenshot size.
+
+ fields->Screenshot = file.readImage();
+
+ fields->Plugins = file.readPlugins();
+
+ return fields;
+}
diff --git a/libs/game_bethesda/src/games/nehrim/nehrimsavegame.h b/libs/game_bethesda/src/games/nehrim/nehrimsavegame.h
new file mode 100644
index 0000000..cdbab22
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/nehrimsavegame.h
@@ -0,0 +1,21 @@
+#ifndef NEHRIMSAVEGAME_H
+#define NEHRIMSAVEGAME_H
+
+#include "gamebryosavegame.h"
+#include "gamenehrim.h"
+
+class NehrimSaveGame : public GamebryoSaveGame
+{
+public:
+ NehrimSaveGame(QString const& fileName, GameNehrim const* game);
+
+protected:
+ // Fetch easy-to-access information.
+ void fetchInformationFields(FileWrapper& wrapper, uint32_t& saveNumber,
+ QString& playerName, unsigned short& playerLevel,
+ QString& playerLocation, _SYSTEMTIME& creationTime) const;
+
+ std::unique_ptr<DataFields> fetchDataFields() const override;
+};
+
+#endif // NEHRIMSAVEGAME_H
diff --git a/libs/game_bethesda/src/games/nehrim/nehrimscriptextender.cpp b/libs/game_bethesda/src/games/nehrim/nehrimscriptextender.cpp
new file mode 100644
index 0000000..c50c59e
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/nehrimscriptextender.cpp
@@ -0,0 +1,20 @@
+#include "nehrimscriptextender.h"
+
+#include <QString>
+#include <QStringList>
+
+NehrimScriptExtender::NehrimScriptExtender(GameGamebryo const* game)
+ : GamebryoScriptExtender(game)
+{}
+
+NehrimScriptExtender::~NehrimScriptExtender() {}
+
+QString NehrimScriptExtender::BinaryName() const
+{
+ return "obse_loader.exe";
+}
+
+QString NehrimScriptExtender::PluginPath() const
+{
+ return "obse/plugins";
+}
diff --git a/libs/game_bethesda/src/games/nehrim/nehrimscriptextender.h b/libs/game_bethesda/src/games/nehrim/nehrimscriptextender.h
new file mode 100644
index 0000000..3bdac2a
--- /dev/null
+++ b/libs/game_bethesda/src/games/nehrim/nehrimscriptextender.h
@@ -0,0 +1,18 @@
+#ifndef NEHRIMSCRIPTEXTENDER_H
+#define NEHRIMSCRIPTEXTENDER_H
+
+#include "gamebryoscriptextender.h"
+
+class GameGamebryo;
+
+class NehrimScriptExtender : public GamebryoScriptExtender
+{
+public:
+ NehrimScriptExtender(const GameGamebryo* game);
+ ~NehrimScriptExtender();
+
+ virtual QString BinaryName() const override;
+ virtual QString PluginPath() const override;
+};
+
+#endif // NEHRIMSCRIPTEXTENDER_H