aboutsummaryrefslogtreecommitdiff
path: root/libs/game_bethesda/src/games/oblivion
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/oblivion
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/oblivion')
-rw-r--r--libs/game_bethesda/src/games/oblivion/CMakeLists.txt21
-rw-r--r--libs/game_bethesda/src/games/oblivion/game_oblivion_en.ts17
-rw-r--r--libs/game_bethesda/src/games/oblivion/gameoblivion.cpp213
-rw-r--r--libs/game_bethesda/src/games/oblivion/gameoblivion.h51
-rw-r--r--libs/game_bethesda/src/games/oblivion/gameoblivion.json1
-rw-r--r--libs/game_bethesda/src/games/oblivion/oblivionbsainvalidation.cpp16
-rw-r--r--libs/game_bethesda/src/games/oblivion/oblivionbsainvalidation.h20
-rw-r--r--libs/game_bethesda/src/games/oblivion/obliviondataarchives.cpp32
-rw-r--r--libs/game_bethesda/src/games/oblivion/obliviondataarchives.h23
-rw-r--r--libs/game_bethesda/src/games/oblivion/oblivionmoddatachecker.cpp30
-rw-r--r--libs/game_bethesda/src/games/oblivion/oblivionmoddatachecker.h35
-rw-r--r--libs/game_bethesda/src/games/oblivion/oblivionmoddatacontent.h22
-rw-r--r--libs/game_bethesda/src/games/oblivion/oblivionsavegame.cpp74
-rw-r--r--libs/game_bethesda/src/games/oblivion/oblivionsavegame.h21
-rw-r--r--libs/game_bethesda/src/games/oblivion/oblivionscriptextender.cpp20
-rw-r--r--libs/game_bethesda/src/games/oblivion/oblivionscriptextender.h18
16 files changed, 614 insertions, 0 deletions
diff --git a/libs/game_bethesda/src/games/oblivion/CMakeLists.txt b/libs/game_bethesda/src/games/oblivion/CMakeLists.txt
new file mode 100644
index 0000000..df5ff8c
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.16)
+
+add_library(game_oblivion SHARED
+ gameoblivion.cpp
+ gameoblivion.h
+ oblivionbsainvalidation.cpp
+ oblivionbsainvalidation.h
+ obliviondataarchives.cpp
+ obliviondataarchives.h
+ oblivionmoddatachecker.cpp
+ oblivionmoddatachecker.h
+ oblivionmoddatacontent.h
+ oblivionsavegame.cpp
+ oblivionsavegame.h
+ oblivionscriptextender.cpp
+ oblivionscriptextender.h
+)
+mo2_configure_plugin(game_oblivion NO_SOURCES WARNINGS 4)
+mo2_default_source_group()
+target_link_libraries(game_oblivion PRIVATE game_gamebryo)
+mo2_install_plugin(game_oblivion)
diff --git a/libs/game_bethesda/src/games/oblivion/game_oblivion_en.ts b/libs/game_bethesda/src/games/oblivion/game_oblivion_en.ts
new file mode 100644
index 0000000..80b1dbb
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/game_oblivion_en.ts
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="en_US">
+<context>
+ <name>GameOblivion</name>
+ <message>
+ <location filename="gameoblivion.cpp" line="84"/>
+ <source>Oblivion Support Plugin</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="gameoblivion.cpp" line="94"/>
+ <source>Adds support for the game Oblivion</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/libs/game_bethesda/src/games/oblivion/gameoblivion.cpp b/libs/game_bethesda/src/games/oblivion/gameoblivion.cpp
new file mode 100644
index 0000000..9f7a174
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/gameoblivion.cpp
@@ -0,0 +1,213 @@
+#include "gameoblivion.h"
+
+#include "oblivionbsainvalidation.h"
+#include "obliviondataarchives.h"
+#include "oblivionmoddatachecker.h"
+#include "oblivionmoddatacontent.h"
+#include "oblivionsavegame.h"
+#include "oblivionscriptextender.h"
+
+#include "executableinfo.h"
+#include "pluginsetting.h"
+#include "steamutility.h"
+#include <gamebryogameplugins.h>
+#include <gamebryolocalsavegames.h>
+#include <gamebryosavegameinfo.h>
+#include <gamebryounmanagedmods.h>
+
+#include <QCoreApplication>
+#include <QDir>
+#include <QFileInfo>
+
+#include <memory>
+
+using namespace MOBase;
+
+GameOblivion::GameOblivion() {}
+
+bool GameOblivion::init(IOrganizer* moInfo)
+{
+ if (!GameGamebryo::init(moInfo)) {
+ return false;
+ }
+
+ auto dataArchives = std::make_shared<OblivionDataArchives>(this);
+ registerFeature(std::make_shared<OblivionScriptExtender>(this));
+ registerFeature(dataArchives);
+ registerFeature(std::make_shared<OblivionBSAInvalidation>(dataArchives.get(), this));
+ registerFeature(std::make_shared<GamebryoSaveGameInfo>(this));
+ registerFeature(std::make_shared<GamebryoLocalSavegames>(this, "oblivion.ini"));
+ registerFeature(std::make_shared<OblivionModDataChecker>(this));
+ registerFeature(
+ std::make_shared<OblivionModDataContent>(m_Organizer->gameFeatures()));
+ registerFeature(std::make_shared<GamebryoGamePlugins>(moInfo));
+ registerFeature(std::make_shared<GamebryoUnmangedMods>(this));
+ return true;
+}
+
+QString GameOblivion::gameName() const
+{
+ return "Oblivion";
+}
+
+QList<ExecutableInfo> GameOblivion::executables() const
+{
+ return QList<ExecutableInfo>()
+ << ExecutableInfo("Oblivion", findInGameFolder(binaryName()))
+ << ExecutableInfo("Oblivion Launcher", findInGameFolder(getLauncherName()))
+ << ExecutableInfo("Oblivion Mod Manager",
+ findInGameFolder("OblivionModManager.exe"))
+ << ExecutableInfo("BOSS", findInGameFolder("BOSS/BOSS.exe"))
+ << ExecutableInfo("LOOT", QFileInfo(getLootPath()))
+ .withArgument("--game=\"Oblivion\"")
+ << ExecutableInfo("Construction Set",
+ findInGameFolder("TESConstructionSet.exe"));
+}
+
+QList<ExecutableForcedLoadSetting> GameOblivion::executableForcedLoads() const
+{
+ return QList<ExecutableForcedLoadSetting>()
+ << ExecutableForcedLoadSetting("Oblivion.exe", "obse_1_2_416.dll")
+ .withForced()
+ .withEnabled()
+ << ExecutableForcedLoadSetting("TESConstructionSet.exe", "obse_editor_1_2.dll")
+ .withForced()
+ .withEnabled();
+}
+
+QString GameOblivion::name() const
+{
+ return "Oblivion Support Plugin";
+}
+
+QString GameOblivion::localizedName() const
+{
+ return tr("Oblivion Support Plugin");
+}
+
+QString GameOblivion::author() const
+{
+ return "Tannin & MO2 Team";
+}
+
+QString GameOblivion::description() const
+{
+ return tr("Adds support for the game Oblivion");
+}
+
+MOBase::VersionInfo GameOblivion::version() const
+{
+ return VersionInfo(1, 6, 1, VersionInfo::RELEASE_FINAL);
+}
+
+QList<PluginSetting> GameOblivion::settings() const
+{
+ return {PluginSetting("nehrim_downloads", "allow Nehrim downloads", QVariant(false))};
+}
+
+void GameOblivion::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 GameOblivion::savegameExtension() const
+{
+ return "ess";
+}
+
+QString GameOblivion::savegameSEExtension() const
+{
+ return "obse";
+}
+
+std::shared_ptr<const GamebryoSaveGame>
+GameOblivion::makeSaveGame(QString filePath) const
+{
+ return std::make_shared<const OblivionSaveGame>(filePath, this);
+}
+
+QString GameOblivion::steamAPPId() const
+{
+ return "22330";
+}
+
+QStringList GameOblivion::primaryPlugins() const
+{
+ return {"oblivion.esm", "update.esm"};
+}
+
+QString GameOblivion::gameShortName() const
+{
+ return "Oblivion";
+}
+
+QStringList GameOblivion::validShortNames() const
+{
+ QStringList shortNames;
+ if (m_Organizer->pluginSetting(name(), "nehrim_downloads").toBool()) {
+ shortNames.append("Nehrim");
+ }
+ return shortNames;
+}
+
+QString GameOblivion::gameNexusName() const
+{
+ return "Oblivion";
+}
+
+QStringList GameOblivion::iniFiles() const
+{
+ return {"oblivion.ini", "oblivionprefs.ini"};
+}
+
+QStringList GameOblivion::DLCPlugins() const
+{
+ return {"DLCBattlehornCastle.esp", "DLCShiveringIsles.esp", "Knights.esp",
+ "DLCFrostcrag.esp", "DLCSpellTomes.esp", "DLCMehrunesRazor.esp",
+ "DLCOrrery.esp", "DLCThievesDen.esp", "DLCVileLair.esp",
+ "DLCHorseArmor.esp"};
+}
+
+int GameOblivion::nexusModOrganizerID() const
+{
+ return 38277;
+}
+
+int GameOblivion::nexusGameID() const
+{
+ return 101;
+}
+
+QString GameOblivion::identifyGamePath() const
+{
+#ifdef _WIN32
+ return GameGamebryo::identifyGamePath();
+#else
+ // Prefer Steam app-id resolution to avoid matching Oblivion Remastered.
+ QString path = parseSteamLocation(steamAPPId(), "Oblivion");
+ if (!path.isEmpty() && looksValid(QDir(path))) {
+ return path;
+ }
+
+ path = MOBase::findSteamGame("Oblivion", "Oblivion.exe");
+ if (!path.isEmpty() && looksValid(QDir(path))) {
+ return path;
+ }
+
+ return GameGamebryo::identifyGamePath();
+#endif
+}
diff --git a/libs/game_bethesda/src/games/oblivion/gameoblivion.h b/libs/game_bethesda/src/games/oblivion/gameoblivion.h
new file mode 100644
index 0000000..124aa7f
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/gameoblivion.h
@@ -0,0 +1,51 @@
+#ifndef GAMEOBLIVION_H
+#define GAMEOBLIVION_H
+
+#include "gamegamebryo.h"
+
+#include <QObject>
+#include <QtGlobal>
+
+class GameOblivion : public GameGamebryo
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.tannin.GameOblivion" FILE "gameoblivion.json")
+
+public:
+ GameOblivion();
+
+ 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 QStringList validShortNames() 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;
+
+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:
+ QString identifyGamePath() const override;
+ std::shared_ptr<const GamebryoSaveGame> makeSaveGame(QString filePath) const override;
+ QString savegameExtension() const override;
+ QString savegameSEExtension() const override;
+};
+
+#endif // GAMEOBLIVION_H
diff --git a/libs/game_bethesda/src/games/oblivion/gameoblivion.json b/libs/game_bethesda/src/games/oblivion/gameoblivion.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/gameoblivion.json
@@ -0,0 +1 @@
+{}
diff --git a/libs/game_bethesda/src/games/oblivion/oblivionbsainvalidation.cpp b/libs/game_bethesda/src/games/oblivion/oblivionbsainvalidation.cpp
new file mode 100644
index 0000000..864d37c
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/oblivionbsainvalidation.cpp
@@ -0,0 +1,16 @@
+#include "oblivionbsainvalidation.h"
+
+OblivionBSAInvalidation::OblivionBSAInvalidation(MOBase::DataArchives* dataArchives,
+ MOBase::IPluginGame const* game)
+ : GamebryoBSAInvalidation(dataArchives, "oblivion.ini", game)
+{}
+
+QString OblivionBSAInvalidation::invalidationBSAName() const
+{
+ return "Oblivion - Invalidation.bsa";
+}
+
+unsigned long OblivionBSAInvalidation::bsaVersion() const
+{
+ return 0x67;
+}
diff --git a/libs/game_bethesda/src/games/oblivion/oblivionbsainvalidation.h b/libs/game_bethesda/src/games/oblivion/oblivionbsainvalidation.h
new file mode 100644
index 0000000..6804748
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/oblivionbsainvalidation.h
@@ -0,0 +1,20 @@
+#ifndef OBLIVIONBSAINVALIDATION_H
+#define OBLIVIONBSAINVALIDATION_H
+
+#include "gamebryobsainvalidation.h"
+#include "obliviondataarchives.h"
+
+#include <memory>
+
+class OblivionBSAInvalidation : public GamebryoBSAInvalidation
+{
+public:
+ OblivionBSAInvalidation(MOBase::DataArchives* dataArchives,
+ MOBase::IPluginGame const* game);
+
+private:
+ virtual QString invalidationBSAName() const override;
+ virtual unsigned long bsaVersion() const override;
+};
+
+#endif // OBLIVIONBSAINVALIDATION_H
diff --git a/libs/game_bethesda/src/games/oblivion/obliviondataarchives.cpp b/libs/game_bethesda/src/games/oblivion/obliviondataarchives.cpp
new file mode 100644
index 0000000..47c3dd4
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/obliviondataarchives.cpp
@@ -0,0 +1,32 @@
+#include "obliviondataarchives.h"
+#include <utility.h>
+
+QStringList OblivionDataArchives::vanillaArchives() const
+{
+ return {"Oblivion - Misc.bsa", "Oblivion - Textures - Compressed.bsa",
+ "Oblivion - Meshes.bsa", "Oblivion - Sounds.bsa",
+ "Oblivion - Voices1.bsa", "Oblivion - Voices2.bsa"};
+}
+
+QStringList OblivionDataArchives::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 OblivionDataArchives::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/oblivion/obliviondataarchives.h b/libs/game_bethesda/src/games/oblivion/obliviondataarchives.h
new file mode 100644
index 0000000..380f3ca
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/obliviondataarchives.h
@@ -0,0 +1,23 @@
+#ifndef OBLIVIONDATAARCHIVES_H
+#define OBLIVIONDATAARCHIVES_H
+
+#include <QDir>
+#include <QString>
+#include <QStringList>
+#include <gamebryodataarchives.h>
+#include <iprofile.h>
+
+class OblivionDataArchives : 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 // OBLIVIONDATAARCHIVES_H
diff --git a/libs/game_bethesda/src/games/oblivion/oblivionmoddatachecker.cpp b/libs/game_bethesda/src/games/oblivion/oblivionmoddatachecker.cpp
new file mode 100644
index 0000000..30a74ee
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/oblivionmoddatachecker.cpp
@@ -0,0 +1,30 @@
+#include "oblivionmoddatachecker.h"
+
+MOBase::ModDataChecker::CheckReturn OblivionModDataChecker::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>
+OblivionModDataChecker::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/oblivion/oblivionmoddatachecker.h b/libs/game_bethesda/src/games/oblivion/oblivionmoddatachecker.h
new file mode 100644
index 0000000..f62d015
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/oblivionmoddatachecker.h
@@ -0,0 +1,35 @@
+#ifndef OBLIVION_MODATACHECKER_H
+#define OBLIVION_MODATACHECKER_H
+
+#include <gamebryomoddatachecker.h>
+
+class OblivionModDataChecker : 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 // OBLIVION_MODATACHECKER_H
diff --git a/libs/game_bethesda/src/games/oblivion/oblivionmoddatacontent.h b/libs/game_bethesda/src/games/oblivion/oblivionmoddatacontent.h
new file mode 100644
index 0000000..4e412d0
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/oblivionmoddatacontent.h
@@ -0,0 +1,22 @@
+#ifndef OBLIVION_MODDATACONTENT_H
+#define OBLIVION_MODDATACONTENT_H
+
+#include <gamebryomoddatacontent.h>
+#include <ifiletree.h>
+
+class OblivionModDataContent : public GamebryoModDataContent
+{
+public:
+ /**
+ *
+ */
+ OblivionModDataContent(const MOBase::IGameFeatures* gameFeatures)
+ : GamebryoModDataContent(gameFeatures)
+ {
+ // Just need to disable some contents:
+ m_Enabled[CONTENT_MCM] = false;
+ m_Enabled[CONTENT_SKYPROC] = false;
+ }
+};
+
+#endif // OBLIVION_MODDATACONTENT_H
diff --git a/libs/game_bethesda/src/games/oblivion/oblivionsavegame.cpp b/libs/game_bethesda/src/games/oblivion/oblivionsavegame.cpp
new file mode 100644
index 0000000..2f26714
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/oblivionsavegame.cpp
@@ -0,0 +1,74 @@
+#include "oblivionsavegame.h"
+
+OblivionSaveGame::OblivionSaveGame(QString const& fileName, GameOblivion 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 OblivionSaveGame::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> OblivionSaveGame::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
+ uint32_t imageSize;
+ file.read(imageSize); // Screenshot size.
+ if (imageSize > 0) {
+ fields->Screenshot = file.readImage();
+ }
+
+ fields->Plugins = file.readPlugins();
+
+ return fields;
+}
diff --git a/libs/game_bethesda/src/games/oblivion/oblivionsavegame.h b/libs/game_bethesda/src/games/oblivion/oblivionsavegame.h
new file mode 100644
index 0000000..3961214
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/oblivionsavegame.h
@@ -0,0 +1,21 @@
+#ifndef OBLIVIONSAVEGAME_H
+#define OBLIVIONSAVEGAME_H
+
+#include "gamebryosavegame.h"
+#include "gameoblivion.h"
+
+class OblivionSaveGame : public GamebryoSaveGame
+{
+public:
+ OblivionSaveGame(QString const& fileName, GameOblivion 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 // OBLIVIONSAVEGAME_H
diff --git a/libs/game_bethesda/src/games/oblivion/oblivionscriptextender.cpp b/libs/game_bethesda/src/games/oblivion/oblivionscriptextender.cpp
new file mode 100644
index 0000000..f021a2e
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/oblivionscriptextender.cpp
@@ -0,0 +1,20 @@
+#include "oblivionscriptextender.h"
+
+#include <QString>
+#include <QStringList>
+
+OblivionScriptExtender::OblivionScriptExtender(GameGamebryo const* game)
+ : GamebryoScriptExtender(game)
+{}
+
+OblivionScriptExtender::~OblivionScriptExtender() {}
+
+QString OblivionScriptExtender::BinaryName() const
+{
+ return "obse_loader.exe";
+}
+
+QString OblivionScriptExtender::PluginPath() const
+{
+ return "obse/plugins";
+}
diff --git a/libs/game_bethesda/src/games/oblivion/oblivionscriptextender.h b/libs/game_bethesda/src/games/oblivion/oblivionscriptextender.h
new file mode 100644
index 0000000..1044bfa
--- /dev/null
+++ b/libs/game_bethesda/src/games/oblivion/oblivionscriptextender.h
@@ -0,0 +1,18 @@
+#ifndef OBLIVIONSCRIPTEXTENDER_H
+#define OBLIVIONSCRIPTEXTENDER_H
+
+#include "gamebryoscriptextender.h"
+
+class GameGamebryo;
+
+class OblivionScriptExtender : public GamebryoScriptExtender
+{
+public:
+ OblivionScriptExtender(const GameGamebryo* game);
+ ~OblivionScriptExtender();
+
+ virtual QString BinaryName() const override;
+ virtual QString PluginPath() const override;
+};
+
+#endif // OBLIVIONSCRIPTEXTENDER_H