commit 651890d422f56051730f3188fda11bbde66c1998 Author: Jeremy Rimpo Date: Thu Aug 1 21:08:42 2024 -0500 Initial migration from original FO4 plugin diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..6098e1f --- /dev/null +++ b/.clang-format @@ -0,0 +1,41 @@ +--- +# We'll use defaults from the LLVM style, but with 4 columns indentation. +BasedOnStyle: LLVM +IndentWidth: 2 +--- +Language: Cpp +DeriveLineEnding: false +UseCRLF: true +DerivePointerAlignment: false +PointerAlignment: Left +AlignConsecutiveAssignments: true +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Empty +AlwaysBreakTemplateDeclarations: Yes +AccessModifierOffset: -2 +AlignTrailingComments: true +SpacesBeforeTrailingComments: 2 +NamespaceIndentation: Inner +MaxEmptyLinesToKeep: 1 +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: false + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: true +ColumnLimit: 88 +ForEachMacros: ['Q_FOREACH', 'foreach'] diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f869712 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.cpp text eol=crlf +*.h text eol=crlf diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..f84cac7 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,16 @@ +name: Build Fallout 4 London Plugin + +on: + push: + branches: master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + runs-on: windows-2022 + steps: + - name: Build Fallout 4 London Plugin + uses: ModOrganizer2/build-with-mob-action@master + with: + mo2-dependencies: cmake_common uibase game_gamebryo diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 0000000..dce7309 --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,16 @@ +name: Lint Fallout 4 London Plugin + +on: + push: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Check format + uses: ModOrganizer2/check-formatting-action@master + with: + check-path: "." diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf71be7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +edit +CMakeLists.txt.user +/msbuild.log +/*std*.log +/*build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..31c1c8d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.16) + +if(DEFINED DEPENDENCIES_DIR) + include(${DEPENDENCIES_DIR}/modorganizer_super/cmake_common/mo2.cmake) +else() + include(${CMAKE_CURRENT_LIST_DIR}/cmake_common/mo2.cmake) +endif() + +project(game_fallout4london) +add_subdirectory(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..6730e1d --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.16) + +add_library(game_fallout4london SHARED) +mo2_configure_plugin(game_fallout4london + WARNINGS OFF + PRIVATE_DEPENDS creation) +mo2_install_target(game_fallout4london) diff --git a/src/SConscript b/src/SConscript new file mode 100644 index 0000000..696562d --- /dev/null +++ b/src/SConscript @@ -0,0 +1,14 @@ +Import('qt_env') + +env = qt_env.Clone() + +# Shouldn't this be GAMEFALLOUT3_LIBRARY +env.AppendUnique(CPPDEFINES = [ 'GAMEFALLOUT4_LIBRARY' ]) + +env.RequiresGamebryo() + +lib = env.SharedLibrary('gameFallout4London', env.Glob('*.cpp')) +env.InstallModule(lib) + +res = env['QT_USED_MODULES'] +Return('res') diff --git a/src/fo4london.qrc b/src/fo4london.qrc new file mode 100644 index 0000000..8f7bb2b --- /dev/null +++ b/src/fo4london.qrc @@ -0,0 +1,5 @@ + + + splash.png + + diff --git a/src/fo4londonbsainvalidation.cpp b/src/fo4londonbsainvalidation.cpp new file mode 100644 index 0000000..32e58d5 --- /dev/null +++ b/src/fo4londonbsainvalidation.cpp @@ -0,0 +1,68 @@ +#include "fallout4bsainvalidation.h" + +#include "dummybsa.h" +#include "iplugingame.h" +#include "iprofile.h" +#include "registry.h" +#include +#include + +Fallout4LondonBSAInvalidation::Fallout4LondonBSAInvalidation(MOBase::DataArchives* dataArchives, + MOBase::IPluginGame const* game) + : GamebryoBSAInvalidation(dataArchives, "Fallout4Custom.ini", game) +{ + m_IniFileName = "Fallout4Custom.ini"; + m_Game = game; +} + +bool Fallout4LondonBSAInvalidation::isInvalidationBSA(const QString& bsaName) +{ + return false; +} + +QString Fallout4LondonBSAInvalidation::invalidationBSAName() const +{ + return ""; +} + +unsigned long Fallout4LondonBSAInvalidation::bsaVersion() const +{ + return 0x68; +} + +bool Fallout4LondonBSAInvalidation::prepareProfile(MOBase::IProfile* profile) +{ + bool dirty = false; + QString basePath = profile->localSettingsEnabled() + ? profile->absolutePath() + : m_Game->documentsDirectory().absolutePath(); + QString iniFilePath = basePath + "/" + m_IniFileName; + WCHAR setting[MAX_PATH]; + + if (profile->invalidationActive(nullptr)) { + // write bInvalidateOlderFiles = 1, if needed + if (!::GetPrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"0", setting, + MAX_PATH, iniFilePath.toStdWString().c_str()) || + wcstol(setting, nullptr, 10) != 1) { + dirty = true; + if (!MOBase::WriteRegistryValue(L"Archive", L"bInvalidateOlderFiles", L"1", + iniFilePath.toStdWString().c_str())) { + qWarning("failed to override data directory in \"%s\"", + qUtf8Printable(m_IniFileName)); + } + } + if (!::GetPrivateProfileStringW(L"Archive", L"sResourceDataDirsFinal", L"STRINGS\\", + setting, MAX_PATH, + iniFilePath.toStdWString().c_str()) || + wcscmp(setting, L"") != 0) { + dirty = true; + if (!MOBase::WriteRegistryValue(L"Archive", L"sResourceDataDirsFinal", L"", + iniFilePath.toStdWString().c_str())) { + qWarning("failed to override data directory in \"%s\"", + qUtf8Printable(m_IniFileName)); + } + } + } + + return dirty; +} diff --git a/src/fo4londonbsainvalidation.h b/src/fo4londonbsainvalidation.h new file mode 100644 index 0000000..fc5d319 --- /dev/null +++ b/src/fo4londonbsainvalidation.h @@ -0,0 +1,33 @@ +#ifndef FALLOUT4BSAINVALIDATION_H +#define FALLOUT4BSAINVALIDATION_H + +#include "fallout4dataarchives.h" +#include "gamebryobsainvalidation.h" +#include +#include + +#include + +namespace MOBase +{ +class IPluginGame; +} + +class Fallout4LondonBSAInvalidation : public GamebryoBSAInvalidation +{ +public: + Fallout4LondonBSAInvalidation(MOBase::DataArchives* dataArchives, + MOBase::IPluginGame const* game); + virtual bool isInvalidationBSA(const QString& bsaName) override; + virtual bool prepareProfile(MOBase::IProfile* profile) override; + +private: + virtual QString invalidationBSAName() const override; + virtual unsigned long bsaVersion() const override; + +private: + QString m_IniFileName; + MOBase::IPluginGame const* m_Game; +}; + +#endif // FALLOUT4BSAINVALIDATION_H diff --git a/src/fo4londondataarchives.cpp b/src/fo4londondataarchives.cpp new file mode 100644 index 0000000..9320f26 --- /dev/null +++ b/src/fo4londondataarchives.cpp @@ -0,0 +1,48 @@ +#include "fallout4dataarchives.h" + +#include "iprofile.h" +#include + +QStringList Fallout4LondonDataArchives::vanillaArchives() const +{ + return {"Fallout4 - Textures1.ba2", "Fallout4 - Textures2.ba2", + "Fallout4 - Textures3.ba2", "Fallout4 - Textures4.ba2", + "Fallout4 - Textures5.ba2", "Fallout4 - Textures6.ba2", + "Fallout4 - Textures7.ba2", "Fallout4 - Textures8.ba2", + "Fallout4 - Textures9.ba2", "Fallout4 - Meshes.ba2", + "Fallout4 - MeshesExtra.ba2", "Fallout4 - Voices.ba2", + "Fallout4 - Sounds.ba2", "Fallout4 - Interface.ba2", + "Fallout4 - Animations.ba2", "Fallout4 - Materials.ba2", + "Fallout4 - Shaders.ba2", "Fallout4 - Startup.ba2", + "Fallout4 - Misc.ba2"}; +} + +QStringList Fallout4LondonDataArchives::archives(const MOBase::IProfile* profile) const +{ + QStringList result; + + QString iniFile = profile->localSettingsEnabled() + ? QDir(profile->absolutePath()).absoluteFilePath("fallout4.ini") + : localGameDirectory().absoluteFilePath("fallout4.ini"); + result.append(getArchivesFromKey(iniFile, "SResourceArchiveList")); + result.append(getArchivesFromKey(iniFile, "SResourceArchiveList2")); + + return result; +} + +void Fallout4LondonDataArchives::writeArchiveList(MOBase::IProfile* profile, + const QStringList& before) +{ + QString list = before.join(", "); + + QString iniFile = profile->localSettingsEnabled() + ? QDir(profile->absolutePath()).absoluteFilePath("fallout4.ini") + : localGameDirectory().absoluteFilePath("fallout4.ini"); + if (list.length() > 255) { + int splitIdx = list.lastIndexOf(",", 256); + setArchivesToKey(iniFile, "SResourceArchiveList", list.mid(0, splitIdx)); + setArchivesToKey(iniFile, "SResourceArchiveList2", list.mid(splitIdx + 2)); + } else { + setArchivesToKey(iniFile, "SResourceArchiveList", list); + } +} diff --git a/src/fo4londondataarchives.h b/src/fo4londondataarchives.h new file mode 100644 index 0000000..a9d0ef9 --- /dev/null +++ b/src/fo4londondataarchives.h @@ -0,0 +1,27 @@ +#ifndef FALLOUT4DATAARCHIVES_H +#define FALLOUT4DATAARCHIVES_H + +#include "gamebryodataarchives.h" + +namespace MOBase +{ +class IProfile; +} + +#include +#include + +class Fallout4LondonDataArchives : 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 // FALLOUT4DATAARCHIVES_H diff --git a/src/fo4londonmoddatachecker.h b/src/fo4londonmoddatachecker.h new file mode 100644 index 0000000..4a65789 --- /dev/null +++ b/src/fo4londonmoddatachecker.h @@ -0,0 +1,29 @@ +#ifndef FALLOUT4_MODATACHECKER_H +#define FALLOUT4_MODATACHECKER_H + +#include + +class Fallout4LondonModDataChecker : public GamebryoModDataChecker +{ +public: + using GamebryoModDataChecker::GamebryoModDataChecker; + +protected: + virtual const FileNameSet& possibleFolderNames() const override + { + static FileNameSet result{ + "interface", "meshes", "music", "scripts", "sound", "strings", + "textures", "trees", "video", "materials", "f4se", "distantlod", + "asi", "Tools", "MCM", "distantland", "mits", "dllplugins", + "CalienteTools", "shadersfx", "aaf"}; + return result; + } + virtual const FileNameSet& possibleFileExtensions() const override + { + static FileNameSet result{"esp", "esm", "esl", "ba2", + "modgroups", "ini", "csg", "cdx"}; + return result; + } +}; + +#endif // FALLOUT4_MODATACHECKER_H diff --git a/src/fo4londonmoddatacontent.h b/src/fo4londonmoddatacontent.h new file mode 100644 index 0000000..b1938ba --- /dev/null +++ b/src/fo4londonmoddatacontent.h @@ -0,0 +1,44 @@ +#ifndef FALLOUT4_MODDATACONTENT_H +#define FALLOUT4_MODDATACONTENT_H + +#include +#include + +class Fallout4LondonModDataContent : public GamebryoModDataContent +{ +protected: + enum Fallout4LondonContent + { + CONTENT_MATERIAL = CONTENT_NEXT_VALUE + }; + +public: + Fallout4LondonModDataContent(const MOBase::IGameFeatures* gameFeatures) + : GamebryoModDataContent(gameFeatures) + { + m_Enabled[CONTENT_SKYPROC] = false; + } + + std::vector getAllContents() const override + { + auto contents = GamebryoModDataContent::getAllContents(); + contents.push_back( + Content(CONTENT_MATERIAL, "Materials", ":/MO/gui/content/material")); + return contents; + } + + std::vector + getContentsFor(std::shared_ptr fileTree) const override + { + auto contents = GamebryoModDataContent::getContentsFor(fileTree); + for (auto e : *fileTree) { + if (e->compare("materials") == 0) { + contents.push_back(CONTENT_MATERIAL); + break; // Early break if you have nothing else to check. + } + } + return contents; + } +}; + +#endif // FALLOUT4_MODDATACONTENT_H diff --git a/src/fo4londonsavegame.cpp b/src/fo4londonsavegame.cpp new file mode 100644 index 0000000..bb8d833 --- /dev/null +++ b/src/fo4londonsavegame.cpp @@ -0,0 +1,79 @@ +#include "fallout4savegame.h" + +#include + +#include "gamefallout4.h" + +Fallout4LondonSaveGame::Fallout4LondonSaveGame(QString const& fileName, GameFallout4London const* game) + : GamebryoSaveGame(fileName, game, true) +{ + FileWrapper file(getFilepath(), "FO4_SAVEGAME"); + + FILETIME creationTime; + fetchInformationFields(file, m_SaveNumber, m_PCName, m_PCLevel, m_PCLocation, + creationTime); + + // A file time is a 64-bit value that represents the number of 100-nanosecond + // intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated Universal + // Time (UTC). So we need to convert that to something useful + SYSTEMTIME ctime; + ::FileTimeToSystemTime(&creationTime, &ctime); + + setCreationTime(ctime); +} + +void Fallout4LondonSaveGame::fetchInformationFields( + FileWrapper& file, unsigned long& saveNumber, QString& playerName, + unsigned short& playerLevel, QString& playerLocation, FILETIME& creationTime) const +{ + file.skip(); // header size + file.skip(); // header version + file.read(saveNumber); + + file.read(playerName); + + unsigned long temp; + file.read(temp); + playerLevel = static_cast(temp); + file.read(playerLocation); + + QString ignore; + file.read(ignore); // playtime as ascii hh.mm.ss + file.read(ignore); // race name (i.e. BretonRace) + + file.skip(); // Player gender (0 = male) + file.skip(2); // experience gathered, experience required + + file.read(creationTime); +} + +std::unique_ptr Fallout4LondonSaveGame::fetchDataFields() const +{ + FileWrapper file(getFilepath(), "FO4_SAVEGAME"); // 10bytes + + { + QString dummyName, dummyLocation; + unsigned short dummyLevel; + unsigned long dummySaveNumber; + FILETIME dummyTime; + + fetchInformationFields(file, dummySaveNumber, dummyName, dummyLevel, dummyLocation, + dummyTime); + } + + QString ignore; + std::unique_ptr fields = std::make_unique(); + + fields->Screenshot = file.readImage(384, true); + + uint8_t saveGameVersion = file.readChar(); + file.read(ignore); // game version + file.skip(); // plugin info size + + fields->Plugins = file.readPlugins(); + if (saveGameVersion >= 68) { + fields->LightPlugins = file.readLightPlugins(); + } + + return fields; +} diff --git a/src/fo4londonsavegame.h b/src/fo4londonsavegame.h new file mode 100644 index 0000000..8b00d33 --- /dev/null +++ b/src/fo4londonsavegame.h @@ -0,0 +1,24 @@ +#ifndef FALLOUT4SAVEGAME_H +#define FALLOUT4SAVEGAME_H + +#include "gamebryosavegame.h" + +#include + +class GameFallout4London; + +class Fallout4LondonSaveGame : public GamebryoSaveGame +{ +public: + Fallout4LondonSaveGame(QString const& fileName, GameFallout4London const* game); + +protected: + // Fetch easy-to-access information. + void fetchInformationFields(FileWrapper& file, unsigned long& saveNumber, + QString& playerName, unsigned short& playerLevel, + QString& playerLocation, FILETIME& creationTime) const; + + std::unique_ptr fetchDataFields() const override; +}; + +#endif // FALLOUT4SAVEGAME_H diff --git a/src/fo4londonscriptextender.cpp b/src/fo4londonscriptextender.cpp new file mode 100644 index 0000000..6658d4e --- /dev/null +++ b/src/fo4londonscriptextender.cpp @@ -0,0 +1,18 @@ +#include "fallout4scriptextender.h" + +#include +#include + +Fallout4LondonScriptExtender::Fallout4LondonScriptExtender(GameGamebryo const* game) + : GamebryoScriptExtender(game) +{} + +QString Fallout4LondonScriptExtender::BinaryName() const +{ + return "f4se_loader.exe"; +} + +QString Fallout4LondonScriptExtender::PluginPath() const +{ + return "f4se/plugins"; +} diff --git a/src/fo4londonscriptextender.h b/src/fo4londonscriptextender.h new file mode 100644 index 0000000..c6e462b --- /dev/null +++ b/src/fo4londonscriptextender.h @@ -0,0 +1,17 @@ +#ifndef FALLOUT4SCRIPTEXTENDER_H +#define FALLOUT4SCRIPTEXTENDER_H + +#include "gamebryoscriptextender.h" + +class GameGamebryo; + +class Fallout4LondonScriptExtender : public GamebryoScriptExtender +{ +public: + Fallout4LondonScriptExtender(GameGamebryo const* game); + + virtual QString BinaryName() const override; + virtual QString PluginPath() const override; +}; + +#endif // FALLOUT4SCRIPTEXTENDER_H diff --git a/src/fo4londonunmanagedmods.cpp b/src/fo4londonunmanagedmods.cpp new file mode 100644 index 0000000..42392ab --- /dev/null +++ b/src/fo4londonunmanagedmods.cpp @@ -0,0 +1,63 @@ +#include "fallout4unmanagedmods.h" + +Fallout4LondonUnmangedMods::Fallout4LondonUnmangedMods(const GameGamebryo* game) + : GamebryoUnmangedMods(game) +{} + +Fallout4LondonUnmangedMods::~Fallout4LondonUnmangedMods() {} + +QStringList Fallout4LondonUnmangedMods::mods(bool onlyOfficial) const +{ + QStringList result; + + QStringList pluginList = game()->primaryPlugins(); + QStringList otherPlugins = game()->DLCPlugins(); + otherPlugins.append(game()->CCPlugins()); + for (QString plugin : otherPlugins) { + pluginList.removeAll(plugin); + } + QDir dataDir(game()->dataDirectory()); + for (const QString& fileName : dataDir.entryList({"*.esp", "*.esl", "*.esm"})) { + if (!pluginList.contains(fileName, Qt::CaseInsensitive)) { + if (!onlyOfficial || pluginList.contains(fileName, Qt::CaseInsensitive)) { + result.append(fileName.chopped(4)); // trims the extension off + } + } + } + + return result; +} + +QStringList Fallout4LondonUnmangedMods::secondaryFiles(const QString& modName) const +{ + // file extension in FO4 is .ba2 instead of bsa + QStringList archives; + QDir dataDir = game()->dataDirectory(); + for (const QString& archiveName : dataDir.entryList({modName + "*.ba2"})) { + archives.append(dataDir.absoluteFilePath(archiveName)); + } + return archives; +} + +QString Fallout4LondonUnmangedMods::displayName(const QString& modName) const +{ + // unlike in earlier games, in fallout 4 the file name doesn't correspond to + // the public name + if (modName.compare("dlcrobot", Qt::CaseInsensitive) == 0) { + return "Automatron"; + } else if (modName.compare("dlcworkshop01", Qt::CaseInsensitive) == 0) { + return "Wasteland Workshop"; + } else if (modName.compare("dlccoast", Qt::CaseInsensitive) == 0) { + return "Far Harbor"; + } else if (modName.compare("dlcworkshop02", Qt::CaseInsensitive) == 0) { + return "Contraptions Workshop"; + } else if (modName.compare("dlcworkshop03", Qt::CaseInsensitive) == 0) { + return "Vault-Tec Workshop"; + } else if (modName.compare("dlcnukaworld", Qt::CaseInsensitive) == 0) { + return "Nuka-World"; + } else if (modName.compare("dlcultrahighresolution", Qt::CaseInsensitive) == 0) { + return "Ultra High Resolution Texture Pack"; + } else { + return modName; + } +} diff --git a/src/fo4londonunmanagedmods.h b/src/fo4londonunmanagedmods.h new file mode 100644 index 0000000..55568c9 --- /dev/null +++ b/src/fo4londonunmanagedmods.h @@ -0,0 +1,18 @@ +#ifndef FALLOUT4UNMANAGEDMODS_H +#define FALLOUT4UNMANAGEDMODS_H + +#include "gamebryounmanagedmods.h" +#include + +class Fallout4LondonUnmangedMods : public GamebryoUnmangedMods +{ +public: + Fallout4LondonUnmangedMods(const GameGamebryo* game); + ~Fallout4LondonUnmangedMods(); + + virtual QStringList mods(bool onlyOfficial) const override; + virtual QStringList secondaryFiles(const QString& modName) const override; + virtual QString displayName(const QString& modName) const override; +}; + +#endif // FALLOUT4UNMANAGEDMODS_H diff --git a/src/game_fo4london_en.ts b/src/game_fo4london_en.ts new file mode 100644 index 0000000..51ac151 --- /dev/null +++ b/src/game_fo4london_en.ts @@ -0,0 +1,28 @@ + + + + + GameFallout4London + + + Fallout 4 Support Plugin + + + + + Adds support for the game Fallout 4. +Splash by %1 + + + + + sTestFile entries are present + + + + + <p>You have sTestFile settings in your Fallout4Custom.ini. These must be removed or the game will not read the plugins.txt file. Management is disabled.</p> + + + + diff --git a/src/gamefo4london.cpp b/src/gamefo4london.cpp new file mode 100644 index 0000000..c50a40c --- /dev/null +++ b/src/gamefo4london.cpp @@ -0,0 +1,335 @@ +#include "gameFallout4London.h" + +#include "fallout4bsainvalidation.h" +#include "fallout4dataarchives.h" +#include "fallout4moddatachecker.h" +#include "fallout4moddatacontent.h" +#include "fallout4savegame.h" +#include "fallout4scriptextender.h" +#include "fallout4unmanagedmods.h" + +#include "versioninfo.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "scopeguard.h" + +using namespace MOBase; + +GameFallout4London::GameFallout4London() {} + +bool GameFallout4London::init(IOrganizer* moInfo) +{ + if (!GameGamebryo::init(moInfo)) { + return false; + } + + auto dataArchives = std::make_shared(this); + + registerFeature(std::make_shared(this)); + registerFeature(dataArchives); + registerFeature(std::make_shared(this, "fallout4custom.ini")); + registerFeature(std::make_shared(this)); + registerFeature( + std::make_shared(m_Organizer->gameFeatures())); + registerFeature(std::make_shared(this)); + registerFeature(std::make_shared(moInfo)); + registerFeature(std::make_shared(this)); + registerFeature(std::make_shared(dataArchives.get(), this)); + + return true; +} + +QString GameFallout4London::gameName() const +{ + return "Fallout 4"; +} + +void GameFallout4London::detectGame() +{ + m_GamePath = identifyGamePath(); + m_MyGamesPath = determineMyGamesPath("Fallout4London"); +} + +QList GameFallout4London::executables() const +{ + return QList() + << ExecutableInfo("F4SE", + findInGameFolder(m_Organizer->gameFeatures() + ->gameFeature() + ->loaderName())) + << ExecutableInfo("Fallout 4", findInGameFolder(binaryName())) + << ExecutableInfo("Fallout Launcher", findInGameFolder(getLauncherName())) + << ExecutableInfo("Creation Kit", findInGameFolder("CreationKit.exe")) + .withSteamAppId("1946160") + << ExecutableInfo("LOOT", QFileInfo(getLootPath())) + .withArgument("--game=\"Fallout4\""); +} + +QList GameFallout4London::executableForcedLoads() const +{ + return QList(); +} + +QString GameFallout4London::name() const +{ + return "Fallout 4 Support Plugin"; +} + +QString GameFallout4London::localizedName() const +{ + return tr("Fallout 4 Support Plugin"); +} + +QString GameFallout4London::author() const +{ + return "Tannin & MO2 Team"; +} + +QString GameFallout4London::description() const +{ + return tr("Adds support for the game Fallout 4.\n" + "Splash by %1") + .arg("nekoyoubi"); +} + +MOBase::VersionInfo GameFallout4London::version() const +{ + return VersionInfo(1, 8, 0, VersionInfo::RELEASE_FINAL); +} + +QList GameFallout4London::settings() const +{ + return QList(); +} + +MappingType GameFallout4London::mappings() const +{ + MappingType result; + if (testFilePlugins().isEmpty()) { + for (const QString& profileFile : {"plugins.txt", "loadorder.txt"}) { + result.push_back({m_Organizer->profilePath() + "/" + profileFile, + localAppFolder() + "/" + gameShortName() + "/" + profileFile, + false}); + } + } + return result; +} + +void GameFallout4London::initializeProfile(const QDir& path, ProfileSettings settings) const +{ + if (settings.testFlag(IPluginGame::MODS)) { + copyToProfile(localAppFolder() + "/Fallout4", path, "plugins.txt"); + } + + if (settings.testFlag(IPluginGame::CONFIGURATION)) { + if (settings.testFlag(IPluginGame::PREFER_DEFAULTS) || + !QFileInfo(myGamesPath() + "/fallout4.ini").exists()) { + copyToProfile(gameDirectory().absolutePath(), path, "fallout4_default.ini", + "fallout4.ini"); + } else { + copyToProfile(myGamesPath(), path, "fallout4.ini"); + } + + copyToProfile(myGamesPath(), path, "fallout4prefs.ini"); + copyToProfile(myGamesPath(), path, "fallout4custom.ini"); + } +} + +QString GameFallout4London::savegameExtension() const +{ + return "fos"; +} + +QString GameFallout4London::savegameSEExtension() const +{ + return "f4se"; +} + +std::shared_ptr +GameFallout4London::makeSaveGame(QString filePath) const +{ + return std::make_shared(filePath, this); +} + +QString GameFallout4London::steamAPPId() const +{ + return "377160"; +} + +QStringList GameFallout4::testFilePlugins() const +{ + QStringList plugins; + if (m_Organizer != nullptr && m_Organizer->profile() != nullptr) { + QString customIni( + m_Organizer->profile()->absoluteIniFilePath("Fallout4Custom.ini")); + if (QFile(customIni).exists()) { + for (int i = 1; i <= 10; ++i) { + QString setting("sTestFile"); + setting += std::to_string(i); + WCHAR value[MAX_PATH]; + DWORD length = ::GetPrivateProfileStringW( + L"General", setting.toStdWString().c_str(), L"", value, MAX_PATH, + customIni.toStdWString().c_str()); + if (length && wcscmp(value, L"") != 0) { + QString plugin = QString::fromWCharArray(value, length); + if (!plugin.isEmpty() && !plugins.contains(plugin)) + plugins.append(plugin); + } + } + } + } + return plugins; +} + +QStringList GameFallout4::primaryPlugins() const +{ + QStringList plugins = {"fallout4.esm", "dlcrobot.esm", + "dlcworkshop01.esm", "dlccoast.esm", + "dlcworkshop02.esm", "dlcworkshop03.esm", + "dlcnukaworld.esm", "dlcultrahighresolution.esm"}; + + auto testPlugins = testFilePlugins(); + if (loadOrderMechanism() == LoadOrderMechanism::None) { + plugins << testPlugins; + } else { + plugins << CCPlugins(); + } + + return plugins; +} + +QStringList GameFallout4::gameVariants() const +{ + return {"Regular"}; +} + +QString GameFallout4::gameShortName() const +{ + return "Fallout4"; +} + +QString GameFallout4::gameNexusName() const +{ + return "fallout4"; +} + +QStringList GameFallout4::iniFiles() const +{ + return {"fallout4.ini", "fallout4prefs.ini", "fallout4custom.ini"}; +} + +QStringList GameFallout4::DLCPlugins() const +{ + return {"dlcrobot.esm", + "dlcworkshop01.esm", + "dlccoast.esm", + "dlcworkshop02.esm", + "dlcworkshop03.esm", + "dlcnukaworld.esm", + "dlcultrahighresolution.esm"}; +} + +QStringList GameFallout4::CCPlugins() const +{ + QStringList plugins = {}; + QFile file(gameDirectory().absoluteFilePath("Fallout4.ccc")); + if (file.open(QIODevice::ReadOnly)) { + ON_BLOCK_EXIT([&file]() { + file.close(); + }); + + if (file.size() == 0) { + return plugins; + } + while (!file.atEnd()) { + QByteArray line = file.readLine().trimmed(); + QString modName; + if ((line.size() > 0) && (line.at(0) != '#')) { + modName = QString::fromUtf8(line.constData()).toLower(); + } + + if (modName.size() > 0) { + if (!plugins.contains(modName, Qt::CaseInsensitive)) { + plugins.append(modName); + } + } + } + } + return plugins; +} + +IPluginGame::SortMechanism GameFallout4::sortMechanism() const +{ + if (!testFilePresent()) + return IPluginGame::SortMechanism::LOOT; + return IPluginGame::SortMechanism::NONE; +} + +IPluginGame::LoadOrderMechanism GameFallout4::loadOrderMechanism() const +{ + if (!testFilePresent()) + return IPluginGame::LoadOrderMechanism::PluginsTxt; + return IPluginGame::LoadOrderMechanism::None; +} + +int GameFallout4::nexusModOrganizerID() const +{ + return 28715; +} + +int GameFallout4::nexusGameID() const +{ + return 1151; +} + +// Start Diagnose +std::vector GameFallout4::activeProblems() const +{ + std::vector result; + if (m_Organizer->managedGame() == this) { + if (testFilePresent()) + result.push_back(PROBLEM_TEST_FILE); + } + return result; +} + +bool GameFallout4::testFilePresent() const +{ + if (!testFilePlugins().isEmpty()) + return true; + return false; +} + +QString GameFallout4::shortDescription(unsigned int key) const +{ + switch (key) { + case PROBLEM_TEST_FILE: + return tr("sTestFile entries are present"); + } +} + +QString GameFallout4::fullDescription(unsigned int key) const +{ + switch (key) { + case PROBLEM_TEST_FILE: { + return tr("

You have sTestFile settings in your " + "Fallout4Custom.ini. These must be removed or " + "the game will not read the plugins.txt file. " + "Management is disabled.

"); + } + } +} diff --git a/src/gamefo4london.h b/src/gamefo4london.h new file mode 100644 index 0000000..1c0856b --- /dev/null +++ b/src/gamefo4london.h @@ -0,0 +1,73 @@ +#ifndef GAMEFALLOUT4_H +#define GAMEFALLOUT4_H + +#include "gamegamebryo.h" +#include "iplugindiagnose.h" + +#include +#include + +class GameFallout4London : public GameGamebryo, public MOBase::IPluginDiagnose +{ + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame MOBase::IPluginDiagnose) + Q_PLUGIN_METADATA(IID "org.tannin.GameFallout4London" FILE "gamefallout4.json") + +public: + GameFallout4London(); + + virtual bool init(MOBase::IOrganizer* moInfo) override; + +public: + QStringList testFilePlugins() const; + +public: // IPluginGame interface + virtual QString gameName() const override; + virtual void detectGame() override; + virtual QList executables() const override; + virtual QList + executableForcedLoads() const override; + virtual void initializeProfile(const QDir& path, + ProfileSettings settings) const override; + virtual QString steamAPPId() const override; + virtual QStringList primaryPlugins() const override; + virtual QStringList gameVariants() const override; + virtual QString gameShortName() const override; + virtual QString gameNexusName() const override; + virtual QStringList iniFiles() const override; + virtual QStringList DLCPlugins() const override; + virtual QStringList CCPlugins() const override; + virtual SortMechanism sortMechanism() const override; + virtual LoadOrderMechanism loadOrderMechanism() const override; + virtual int nexusModOrganizerID() const override; + virtual int nexusGameID() const override; + +public: // IPluginDiagnose interface + virtual std::vector activeProblems() const override; + virtual QString shortDescription(unsigned int key) const override; + virtual QString fullDescription(unsigned int key) const override; + virtual bool hasGuidedFix(unsigned int key) const override { return false; } + virtual void startGuidedFix(unsigned int key) 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 settings() const override; + virtual MappingType mappings() const override; + +protected: + std::shared_ptr makeSaveGame(QString filePath) const override; + QString savegameExtension() const override; + QString savegameSEExtension() const override; + +private: + bool testFilePresent() const; + +private: + static const unsigned int PROBLEM_TEST_FILE = 1; +}; + +#endif // GAMEFallout4London_H diff --git a/src/gamefo4london.json b/src/gamefo4london.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/gamefo4london.json @@ -0,0 +1 @@ +{} diff --git a/src/gamefo4london.pro b/src/gamefo4london.pro new file mode 100644 index 0000000..f78bc45 --- /dev/null +++ b/src/gamefo4london.pro @@ -0,0 +1,50 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2014-11-15T15:36:33 +# +#------------------------------------------------- + + +TARGET = gameFallout3 +TEMPLATE = lib + +CONFIG += plugins +CONFIG += dll + +DEFINES += GAMEFALLOUT4_LIBRARY + +SOURCES += gamefallout4.cpp \ + fallout4bsainvalidation.cpp \ + fallout4scriptextender.cpp \ + fallout4dataarchives.cpp \ + fallout4savegame.cpp \ + fallout4savegameinfo.cpp + +HEADERS += gamefallout4.h \ + fallout4bsainvalidation.h \ + fallout4scriptextender.h \ + fallout4dataarchives.h \ + fallout4savegame.h \ + fallout4savegameinfo.h + +CONFIG(debug, debug|release) { + LIBS += -L"$${OUT_PWD}/../gameGamebryo/debug" + PRE_TARGETDEPS += \ + $$OUT_PWD/../gameGamebryo/debug/gameGamebryo.lib +} else { + LIBS += -L"$${OUT_PWD}/../gameGamebryo/release" + PRE_TARGETDEPS += \ + $$OUT_PWD/../gameGamebryo/release/gameGamebryo.lib +} + +include(../plugin_template.pri) + +INCLUDEPATH += "$${BOOSTPATH}" "$${PWD}/../gamefeatures" "$${PWD}/../gamegamebryo" + +LIBS += -ladvapi32 -lole32 -lgameGamebryo + +OTHER_FILES += \ + gamefallout4.json\ + SConscript \ + CMakeLists.txt + diff --git a/src/splash.png b/src/splash.png new file mode 100644 index 0000000..d612e7b Binary files /dev/null and b/src/splash.png differ