Merge game repositories into a single one.

This commit is contained in:
423 changed files with 19615 additions and 0 deletions
+41
View File
@@ -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']
+15
View File
@@ -0,0 +1,15 @@
484afb68695eadfa57f1cf9ffc9b6e166ee20174
59cc8639ca60de146e19de64f9dbf4380f52bcf3
dba50ca6cecfd73fc917bf19325947fdc06ab2c3
a070d542b2185c9568e309e540b8e61178b88ad4
99cc4566477a37dc07eeb6282ee5e4307a6d1914
2414eb383ad508992ab17d958f3959dc1a566748
7cce6c37f23c886f388e98828d496edd6c5d72f6
d8d13d2f4b54eaa4409ae97c168e0e4600c743c6
8cd4ed42e757921dcf18e6bf0a9d82fbc586a95a
2055815ed96457fc559b3ab2323403f3b20cbe00
168c64df2fb816d78e6cda9cec12ac4749ab4497
64d324bb78c54ed9d6cecb8e21aceb626ee8f36a
c4d7eaab7f15f8b1954cfeb56c3314f9b0e9c7ec
632ce9843dc9c263203ce4f376ebcfb1672cf1c3
891a7103e31bf928441ef8ae5a45cd1b21f1abb0
+7
View File
@@ -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
+17
View File
@@ -0,0 +1,17 @@
name: Build GameBryo Library
on:
push:
branches: master
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
runs-on: windows-2022
steps:
- name: Build GameBryo Library
uses: ModOrganizer2/build-with-mob-action@master
with:
mo2-third-parties: lz4 zlib
mo2-dependencies: cmake_common uibase
+16
View File
@@ -0,0 +1,16 @@
name: Lint GameBryo Library
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: "."
+5
View File
@@ -0,0 +1,5 @@
edit
CMakeLists.txt.user
/msbuild.log
/*std*.log
/*build
+12
View File
@@ -0,0 +1,12 @@
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_gamebryo)
add_subdirectory(src/gamebryo)
add_subdirectory(src/creation)
+10
View File
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.16)
add_library(game_creation STATIC)
mo2_configure_library(game_creation
WARNINGS OFF
TRANSLATIONS ON
PUBLIC_DEPENDS uibase
PRIVATE_DEPENDS lz4)
target_link_libraries(game_creation PUBLIC game_gamebryo)
mo2_install_target(game_creation)
+183
View File
@@ -0,0 +1,183 @@
#include "creationgameplugins.h"
#include <ipluginlist.h>
#include <report.h>
#include <safewritefile.h>
#include <scopeguard.h>
#include <QDir>
#include <QSet>
#include <QStringEncoder>
#include <QStringList>
using MOBase::IOrganizer;
using MOBase::IPluginGame;
using MOBase::IPluginList;
using MOBase::reportError;
using MOBase::SafeWriteFile;
CreationGamePlugins::CreationGamePlugins(IOrganizer* organizer)
: GamebryoGamePlugins(organizer)
{}
QStringList CreationGamePlugins::getLoadOrder()
{
QString loadOrderPath = organizer()->profile()->absolutePath() + "/loadorder.txt";
QString pluginsPath = organizer()->profile()->absolutePath() + "/plugins.txt";
bool loadOrderIsNew = !m_LastRead.isValid() || !QFileInfo(loadOrderPath).exists() ||
QFileInfo(loadOrderPath).lastModified() > m_LastRead;
bool pluginsIsNew =
!m_LastRead.isValid() || QFileInfo(pluginsPath).lastModified() > m_LastRead;
if (loadOrderIsNew || !pluginsIsNew) {
return readLoadOrderList(m_Organizer->pluginList(), loadOrderPath);
} else {
return readPluginList(m_Organizer->pluginList());
}
}
void CreationGamePlugins::writePluginList(const IPluginList* pluginList,
const QString& filePath)
{
SafeWriteFile file(filePath);
QStringEncoder encoder(QStringConverter::Encoding::System);
file->resize(0);
file->write(
encoder.encode("# This file was automatically generated by Mod Organizer.\r\n"));
bool invalidFileNames = false;
int writtenCount = 0;
QStringList plugins = pluginList->pluginNames();
std::sort(plugins.begin(), plugins.end(),
[pluginList](const QString& lhs, const QString& rhs) {
return pluginList->priority(lhs) < pluginList->priority(rhs);
});
QStringList PrimaryPlugins = organizer()->managedGame()->primaryPlugins();
QStringList DLCPlugins = organizer()->managedGame()->DLCPlugins();
QSet<QString> ManagedMods =
QSet<QString>(PrimaryPlugins.begin(), PrimaryPlugins.end());
QSet<QString> DLCSet = QSet<QString>(DLCPlugins.begin(), DLCPlugins.end());
ManagedMods.subtract(DLCSet);
PrimaryPlugins.append(QList<QString>(ManagedMods.begin(), ManagedMods.end()));
// TODO: do not write plugins in OFFICIAL_FILES container
for (const QString& pluginName : plugins) {
if (!PrimaryPlugins.contains(pluginName, Qt::CaseInsensitive)) {
if (pluginList->state(pluginName) == IPluginList::STATE_ACTIVE) {
auto result = encoder.encode(pluginName);
if (encoder.hasError()) {
invalidFileNames = true;
qCritical("invalid plugin name %s", qUtf8Printable(pluginName));
} else {
file->write("*");
file->write(result);
}
file->write("\r\n");
++writtenCount;
} else {
auto result = encoder.encode(pluginName);
if (encoder.hasError()) {
invalidFileNames = true;
qCritical("invalid plugin name %s", qUtf8Printable(pluginName));
} else {
file->write(result);
}
file->write("\r\n");
++writtenCount;
}
}
}
if (invalidFileNames) {
reportError(QObject::tr("Some of your plugins have invalid names! These "
"plugins can not be loaded by the game. Please see "
"mo_interface.log for a list of affected plugins "
"and rename them."));
}
file->commit();
}
QStringList CreationGamePlugins::readPluginList(MOBase::IPluginList* pluginList)
{
const auto plugins = pluginList->pluginNames();
const auto primaryPlugins = organizer()->managedGame()->primaryPlugins();
QStringList loadOrder(primaryPlugins);
for (const QString& pluginName : loadOrder) {
if (pluginList->state(pluginName) != IPluginList::STATE_MISSING) {
pluginList->setState(pluginName, IPluginList::STATE_ACTIVE);
}
}
QString filePath = organizer()->profile()->absolutePath() + "/plugins.txt";
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly)) {
qWarning("%s not found", qUtf8Printable(filePath));
return loadOrder;
}
ON_BLOCK_EXIT([&]() {
file.close();
});
if (file.size() == 0) {
// MO stores at least a header in the file. if it's completely empty the
// file is broken
qWarning("%s empty", qUtf8Printable(filePath));
return loadOrder;
}
QStringList pluginsFound;
while (!file.atEnd()) {
QByteArray line = file.readLine();
QString pluginName;
if ((line.size() > 0) && (line.at(0) != '#')) {
pluginName = QStringEncoder(QStringConverter::Encoding::System)
.encode(line.trimmed().constData());
}
if (!primaryPlugins.contains(pluginName, Qt::CaseInsensitive)) {
if (pluginName.startsWith('*')) {
pluginName.remove(0, 1);
if (pluginName.size() > 0) {
pluginList->setState(pluginName, IPluginList::STATE_ACTIVE);
pluginsFound.append(pluginName);
if (!loadOrder.contains(pluginName, Qt::CaseInsensitive)) {
loadOrder.append(pluginName);
}
}
} else {
if (pluginName.size() > 0) {
pluginList->setState(pluginName, IPluginList::STATE_INACTIVE);
pluginsFound.append(pluginName);
if (!loadOrder.contains(pluginName, Qt::CaseInsensitive)) {
loadOrder.append(pluginName);
}
}
}
} else {
pluginName.remove(0, 1);
pluginsFound.append(pluginName);
}
}
file.close();
// set all plugins not found inactive
for (const auto& pluginName : plugins) {
if (!pluginsFound.contains(pluginName, Qt::CaseInsensitive)) {
pluginList->setState(pluginName, IPluginList::STATE_INACTIVE);
}
}
return loadOrder;
}
bool CreationGamePlugins::lightPluginsAreSupported()
{
return true;
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef CREATIONGAMEPLUGINS_H
#define CREATIONGAMEPLUGINS_H
#include <gamebryogameplugins.h>
#include <imoinfo.h>
#include <iplugingame.h>
#include <map>
class CreationGamePlugins : public GamebryoGamePlugins
{
public:
CreationGamePlugins(MOBase::IOrganizer* organizer);
protected:
virtual void writePluginList(const MOBase::IPluginList* pluginList,
const QString& filePath) override;
virtual QStringList readPluginList(MOBase::IPluginList* pluginList) override;
virtual QStringList getLoadOrder() override;
virtual bool lightPluginsAreSupported() override;
};
#endif // CREATIONGAMEPLUGINS_H
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US">
<context>
<name>QObject</name>
<message>
<location filename="creationgameplugins.cpp" line="97"/>
<source>Some of your plugins have invalid names! These plugins can not be loaded by the game. Please see mo_interface.log for a list of affected plugins and rename them.</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
+10
View File
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.16)
add_library(game_gamebryo STATIC)
mo2_configure_library(game_gamebryo
WARNINGS OFF
TRANSLATIONS ON
AUTOMOC ON
PUBLIC_DEPENDS uibase
PRIVATE_DEPENDS zlib lz4)
mo2_install_target(game_gamebryo)
+192
View File
@@ -0,0 +1,192 @@
/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dummybsa.h"
#include <QFile>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
static void writeUlong(unsigned char* buffer, int offset, unsigned long value)
{
union
{
unsigned long ulValue;
unsigned char cValue[4];
};
ulValue = value;
memcpy(buffer + offset, cValue, 4);
}
static void writeUlonglong(unsigned char* buffer, int offset, unsigned long long value)
{
union
{
unsigned long long ullValue;
unsigned char cValue[8];
};
ullValue = value;
memcpy(buffer + offset, cValue, 8);
}
static unsigned long genHashInt(const unsigned char* pos, const unsigned char* end)
{
unsigned long hash = 0;
for (; pos < end; ++pos) {
hash *= 0x1003f;
hash += *pos;
}
return hash;
}
static unsigned long long genHash(const char* fileName)
{
char fileNameLower[MAX_PATH + 1];
int i = 0;
for (; i < MAX_PATH && fileName[i] != '\0'; ++i) {
fileNameLower[i] = static_cast<char>(tolower(fileName[i]));
if (fileNameLower[i] == '\\') {
fileNameLower[i] = '/';
}
}
fileNameLower[i] = '\0';
unsigned char* fileNameLowerU = reinterpret_cast<unsigned char*>(fileNameLower);
char* ext = strrchr(fileNameLower, '.');
if (ext == nullptr) {
ext = fileNameLower + strlen(fileNameLower);
}
unsigned char* extU = reinterpret_cast<unsigned char*>(ext);
int length = ext - fileNameLower;
unsigned long long hash = 0ULL;
if (length > 0) {
hash = *(extU - 1) | ((length > 2 ? *(ext - 2) : 0) << 8) | (length << 16) |
(fileNameLowerU[0] << 24);
}
if (strlen(ext) > 0) {
if (strcmp(ext + 1, "kf") == 0) {
hash |= 0x80;
} else if (strcmp(ext + 1, "nif") == 0) {
hash |= 0x8000;
} else if (strcmp(ext + 1, "dds") == 0) {
hash |= 0x8080;
} else if (strcmp(ext + 1, "wav") == 0) {
hash |= 0x80000000;
}
unsigned long long temp =
static_cast<unsigned long long>(genHashInt(fileNameLowerU + 1, extU - 2));
temp += static_cast<unsigned long long>(genHashInt(extU, extU + strlen(ext)));
hash |= (temp & 0xFFFFFFFF) << 32;
}
return hash;
}
DummyBSA::DummyBSA(unsigned long bsaVersion)
: m_Version(bsaVersion), m_FolderName(""), m_FileName("dummy.dds"),
m_TotalFileNameLength(0)
{}
void DummyBSA::writeHeader(QFile& file)
{
unsigned char header[] = {
'B', 'S', 'A', '\0', // magic string
0xDE, 0xAD, 0xBE, 0xEF, // version - insert later
0x24, 0x00, 0x00, 0x00, // offset to folder recors. header size is static
0xDE, 0xAD, 0xBE, 0xEF, // archive flags - insert later
0x01, 0x00, 0x00, 0x00, // folder count
0x01, 0x00, 0x00, 0x00, // file count
0xDE, 0xAD, 0xBE, 0xEF, // total folder names length - insert later
0xDE, 0xAD, 0xBE, 0xEF, // total file names length - insert later
0xDE, 0xAD, 0xBE, 0xEF // file flags - insert later
};
writeUlong(header, 4, m_Version);
writeUlong(header, 12, 0x01 | 0x02); // has directories and has files.
writeUlong(header, 24,
static_cast<unsigned long>(m_FolderName.length()) +
1); // empty folder name
writeUlong(header, 28, m_TotalFileNameLength); // single character file name
writeUlong(header, 32, 2); // has dds
file.write(reinterpret_cast<char*>(header), sizeof(header));
}
void DummyBSA::writeFolderRecord(QFile& file, const std::string& folderName)
{
unsigned char folderRecord[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, // folder hash
0x01, 0x00, 0x00, 0x00, // file count
0xDE, 0xAD, 0xBE, 0xEF, // offset to folder name
};
// we'd usually have to sort folders be the hash value generated here
writeUlonglong(folderRecord, 0, genHash(folderName.c_str()));
writeUlong(folderRecord, 12,
0x34 + m_TotalFileNameLength); // TODO: this should be calculated properly
file.write(reinterpret_cast<char*>(folderRecord), sizeof(folderRecord));
}
void DummyBSA::writeFileRecord(QFile& file, const std::string& fileName)
{
unsigned char fileRecord[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, // file name hash
0xDE, 0xAD, 0xBE, 0xEF, // size
0xDE, 0xAD, 0xBE, 0xEF, // offset to file data
};
// we'd usually have to sort files by the value generated here
writeUlonglong(fileRecord, 0, genHash(fileName.c_str()));
writeUlong(fileRecord, 8, 0);
writeUlong(
fileRecord, 12,
0x44 + static_cast<unsigned long>(fileName.length() + 1) +
4); // after this record we expect the filename and 4 bytes of file size
file.write(reinterpret_cast<char*>(fileRecord), sizeof(fileRecord));
}
void DummyBSA::writeFileRecordBlocks(QFile& file, const std::string& folderName)
{
file.write(folderName.c_str(), folderName.length() + 1);
writeFileRecord(file, m_FileName);
}
void DummyBSA::write(const QString& fileName)
{
QFile file(fileName);
file.open(QIODevice::WriteOnly);
m_TotalFileNameLength = static_cast<unsigned long>(m_FileName.length() + 1);
writeHeader(file);
writeFolderRecord(file, m_FolderName);
writeFileRecordBlocks(file, m_FolderName);
file.write(m_FileName.c_str(), m_FileName.length() + 1);
char fileSize[] = {0x00, 0x00, 0x00, 0x00};
file.write(fileSize, sizeof(fileSize));
file.close();
}
+59
View File
@@ -0,0 +1,59 @@
/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DUMMYBSA_H
#define DUMMYBSA_H
#include <QFile>
#include <QString>
/**
* @brief Class for creating a dummy bsa used for archive invalidation
**/
class DummyBSA
{
public:
/**
* @brief constructor
*
**/
DummyBSA(unsigned long bsaVersion);
/**
* @brief write to the specified file
*
* @param fileName name of the file to write to
**/
void write(const QString& fileName);
private:
void writeHeader(QFile& file);
void writeFolderRecord(QFile& file, const std::string& folderName);
void writeFileRecord(QFile& file, const std::string& fileName);
void writeFileRecordBlocks(QFile& file, const std::string& folderName);
private:
unsigned long m_Version;
std::string m_FolderName;
std::string m_FileName;
unsigned long m_TotalFileNameLength;
};
#endif // DUMMYBSA_H
+170
View File
@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US">
<context>
<name>GamebryoModDataContent</name>
<message>
<location filename="gamebryomoddatacontent.cpp" line="15"/>
<source>Plugins (ESP/ESM/ESL)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="16"/>
<source>Optional Plugins</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="17"/>
<source>Interface</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="18"/>
<source>Meshes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="19"/>
<source>Bethesda Archive</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="20"/>
<source>Scripts (Papyrus)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="21"/>
<source>Script Extender Plugin</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="22"/>
<source>Script Extender Files</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="23"/>
<source>SkyProc Patcher</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="24"/>
<source>Sound or Music</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="25"/>
<source>Textures</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="26"/>
<source>MCM Configuration</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="27"/>
<source>INI Files</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="28"/>
<source>FaceGen Data</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryomoddatacontent.cpp" line="29"/>
<source>ModGroup Files</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>GamebryoSaveGameInfoWidget</name>
<message>
<location filename="gamebryosavegameinfowidget.ui" line="39"/>
<source>Save #</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryosavegameinfowidget.ui" line="51"/>
<source>Character</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryosavegameinfowidget.ui" line="63"/>
<source>Level</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryosavegameinfowidget.ui" line="75"/>
<source>Location</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryosavegameinfowidget.ui" line="87"/>
<source>Date</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryosavegameinfowidget.cpp" line="78"/>
<source>Has Script Extender Data</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryosavegameinfowidget.cpp" line="83"/>
<source>Missing ESPs</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryosavegameinfowidget.cpp" line="116"/>
<location filename="gamebryosavegameinfowidget.cpp" line="154"/>
<location filename="gamebryosavegameinfowidget.cpp" line="193"/>
<source>None</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryosavegameinfowidget.cpp" line="122"/>
<source>Missing ESHs</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryosavegameinfowidget.cpp" line="161"/>
<source>Missing ESLs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="gamebryogameplugins.cpp" line="130"/>
<source>Some of your plugins have invalid names! These plugins can not be loaded by the game. Please see mo_interface.log for a list of affected plugins and rename them.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryosavegame.cpp" line="48"/>
<source>%1, #%2, Level %3, %4</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryosavegame.cpp" line="102"/>
<source>failed to open %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamebryosavegame.cpp" line="112"/>
<source>wrong file format - expected %1 got &apos;%2&apos; for %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamegamebryo.cpp" line="318"/>
<source>failed to query registry path (preflight): %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="gamegamebryo.cpp" line="326"/>
<source>failed to query registry path (read): %1</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
+133
View File
@@ -0,0 +1,133 @@
#include "gamebryobsainvalidation.h"
#include "dummybsa.h"
#include "iplugingame.h"
#include "iprofile.h"
#include "registry.h"
#include <imoinfo.h>
#include <utility.h>
#include <QDir>
#include <QStringList>
#include <Windows.h>
GamebryoBSAInvalidation::GamebryoBSAInvalidation(MOBase::DataArchives* dataArchives,
const QString& iniFilename,
MOBase::IPluginGame const* game)
: m_DataArchives(dataArchives), m_IniFileName(iniFilename), m_Game(game)
{}
bool GamebryoBSAInvalidation::isInvalidationBSA(const QString& bsaName)
{
static QStringList invalidation{invalidationBSAName()};
for (const QString& file : invalidation) {
if (file.compare(bsaName, Qt::CaseInsensitive) == 0) {
return true;
}
}
return false;
}
void GamebryoBSAInvalidation::deactivate(MOBase::IProfile* profile)
{
prepareProfile(profile);
}
void GamebryoBSAInvalidation::activate(MOBase::IProfile* profile)
{
prepareProfile(profile);
}
bool GamebryoBSAInvalidation::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];
// 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 activate BSA invalidation in \"%s\"",
qUtf8Printable(m_IniFileName));
}
}
if (profile->invalidationActive(nullptr)) {
// add the dummy bsa to the archive string, if needed
QStringList archives = m_DataArchives->archives(profile);
bool bsaInstalled = false;
for (const QString& archive : archives) {
if (isInvalidationBSA(archive)) {
bsaInstalled = true;
break;
}
}
if (!bsaInstalled) {
m_DataArchives->addArchive(profile, 0, invalidationBSAName());
dirty = true;
}
// create the dummy bsa if necessary
QString bsaFile = m_Game->dataDirectory().absoluteFilePath(invalidationBSAName());
if (!QFile::exists(bsaFile)) {
DummyBSA bsa(bsaVersion());
bsa.write(bsaFile);
dirty = true;
}
// write SInvalidationFile = "", if needed
if (::GetPrivateProfileStringW(L"Archive", L"SInvalidationFile",
L"ArchiveInvalidation.txt", setting, MAX_PATH,
iniFilePath.toStdWString().c_str()) ||
wcscmp(setting, L"") != 0) {
dirty = true;
if (!MOBase::WriteRegistryValue(L"Archive", L"SInvalidationFile", L"",
iniFilePath.toStdWString().c_str())) {
qWarning("failed to activate BSA invalidation in \"%s\"",
qUtf8Printable(m_IniFileName));
}
}
} else {
// remove the dummy bsa from the archive string, if needed
QStringList archivesBefore = m_DataArchives->archives(profile);
for (const QString& archive : archivesBefore) {
if (isInvalidationBSA(archive)) {
m_DataArchives->removeArchive(profile, archive);
dirty = true;
}
}
// delete the dummy bsa, if needed
QString bsaFile = m_Game->dataDirectory().absoluteFilePath(invalidationBSAName());
if (QFile::exists(bsaFile)) {
MOBase::shellDeleteQuiet(bsaFile);
dirty = true;
}
// write SInvalidationFile = "ArchiveInvalidation.txt", if needed
if (!::GetPrivateProfileStringW(L"Archive", L"SInvalidationFile", L"", setting,
MAX_PATH, iniFilePath.toStdWString().c_str()) ||
wcscmp(setting, L"ArchiveInvalidation.txt") != 0) {
dirty = true;
if (!MOBase::WriteRegistryValue(L"Archive", L"SInvalidationFile",
L"ArchiveInvalidation.txt",
iniFilePath.toStdWString().c_str())) {
qWarning("failed to activate BSA invalidation in \"%s\"",
qUtf8Printable(m_IniFileName));
}
}
}
return dirty;
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef GAMEBRYOBSAINVALIDATION_H
#define GAMEBRYOBSAINVALIDATION_H
#include <QString>
#include <bsainvalidation.h>
#include <dataarchives.h>
#include <memory>
namespace MOBase
{
class IPluginGame;
}
class GamebryoBSAInvalidation : public MOBase::BSAInvalidation
{
public:
GamebryoBSAInvalidation(MOBase::DataArchives* dataArchives,
const QString& iniFilename, MOBase::IPluginGame const* game);
virtual bool isInvalidationBSA(const QString& bsaName) override;
virtual void deactivate(MOBase::IProfile* profile) override;
virtual void activate(MOBase::IProfile* profile) override;
virtual bool prepareProfile(MOBase::IProfile* profile) override;
private:
virtual QString invalidationBSAName() const = 0;
virtual unsigned long
bsaVersion() const = 0; // 0x67 for oblivion, 0x68 for everything else
private:
MOBase::DataArchives* m_DataArchives;
QString m_IniFileName;
MOBase::IPluginGame const* m_Game;
};
#endif // GAMEBRYOBSAINVALIDATION_H
+80
View File
@@ -0,0 +1,80 @@
#include "gamebryodataarchives.h"
#include <Windows.h>
#include <registry.h>
#include <utility.h>
#include "gamegamebryo.h"
GamebryoDataArchives::GamebryoDataArchives(const GameGamebryo* game) : m_Game{game} {}
QDir GamebryoDataArchives::gameDirectory() const
{
return QDir(m_Game->gameDirectory()).absolutePath();
}
QDir GamebryoDataArchives::localGameDirectory() const
{
return QDir(m_Game->myGamesPath()).absolutePath();
}
QStringList GamebryoDataArchives::getArchivesFromKey(const QString& iniFile,
const QString& key,
const int size) const
{
wchar_t* buffer = new wchar_t[size];
QStringList result;
std::wstring iniFileW = QDir::toNativeSeparators(iniFile).toStdWString();
// epic ms fail: GetPrivateProfileString uses errno (for whatever reason) to signal a
// fail since the return value has a different meaning (number of bytes copied).
// HOWEVER, it will not set errno to 0 if NO error occured
errno = 0;
if (::GetPrivateProfileStringW(L"Archive", key.toStdWString().c_str(), L"", buffer,
size, iniFileW.c_str()) != 0) {
result.append(QString::fromStdWString(buffer).split(','));
}
for (int i = 0; i < result.count(); ++i) {
result[i] = result[i].trimmed();
}
delete[] buffer;
return result;
}
void GamebryoDataArchives::setArchivesToKey(const QString& iniFile, const QString& key,
const QString& value)
{
if (!MOBase::WriteRegistryValue(L"Archive", key.toStdWString().c_str(),
value.toStdWString().c_str(),
iniFile.toStdWString().c_str())) {
qWarning("failed to set archives in \"%s\"", qUtf8Printable(iniFile));
}
}
void GamebryoDataArchives::addArchive(MOBase::IProfile* profile, int index,
const QString& archiveName)
{
QStringList current = archives(profile);
if (current.contains(archiveName, Qt::CaseInsensitive)) {
return;
}
current.insert(index != INT_MAX ? index : current.size(), archiveName);
writeArchiveList(profile, current);
}
void GamebryoDataArchives::removeArchive(MOBase::IProfile* profile,
const QString& archiveName)
{
QStringList current = archives(profile);
if (!current.contains(archiveName, Qt::CaseInsensitive)) {
return;
}
current.removeAll(archiveName);
writeArchiveList(profile, current);
}
+37
View File
@@ -0,0 +1,37 @@
#ifndef GAMEBRYODATAARCHIVES_H
#define GAMEBRYODATAARCHIVES_H
#include <QDir>
#include "dataarchives.h"
class GameGamebryo;
class GamebryoDataArchives : public MOBase::DataArchives
{
public:
GamebryoDataArchives(const GameGamebryo* game);
virtual void addArchive(MOBase::IProfile* profile, int index,
const QString& archiveName) override;
virtual void removeArchive(MOBase::IProfile* profile,
const QString& archiveName) override;
protected:
QDir gameDirectory() const;
QDir localGameDirectory() const;
QStringList getArchivesFromKey(const QString& iniFile, const QString& key,
int size = 256) const;
void setArchivesToKey(const QString& iniFile, const QString& key,
const QString& value);
private:
const GameGamebryo* m_Game;
virtual void writeArchiveList(MOBase::IProfile* profile,
const QStringList& before) = 0;
};
#endif // GAMEBRYODATAARCHIVES_H
+248
View File
@@ -0,0 +1,248 @@
#include "gamebryogameplugins.h"
#include <imodinterface.h>
#include <iplugingame.h>
#include <ipluginlist.h>
#include <report.h>
#include <safewritefile.h>
#include <scopeguard.h>
#include <utility.h>
#include <QDateTime>
#include <QDir>
#include <QString>
#include <QStringEncoder>
#include <QStringList>
using MOBase::IOrganizer;
using MOBase::IPluginList;
using MOBase::reportError;
using MOBase::SafeWriteFile;
GamebryoGamePlugins::GamebryoGamePlugins(IOrganizer* organizer) : m_Organizer(organizer)
{}
void GamebryoGamePlugins::writePluginLists(const IPluginList* pluginList)
{
if (!m_LastRead.isValid()) {
// attempt to write uninitialized plugin lists
return;
}
writePluginList(pluginList, m_Organizer->profile()->absolutePath() + "/plugins.txt");
writeLoadOrderList(pluginList,
m_Organizer->profile()->absolutePath() + "/loadorder.txt");
m_LastRead = QDateTime::currentDateTime();
}
void GamebryoGamePlugins::readPluginLists(MOBase::IPluginList* pluginList)
{
QString loadOrderPath = organizer()->profile()->absolutePath() + "/loadorder.txt";
QString pluginsPath = organizer()->profile()->absolutePath() + "/plugins.txt";
bool loadOrderIsNew = !m_LastRead.isValid() || !QFileInfo(loadOrderPath).exists() ||
QFileInfo(loadOrderPath).lastModified() > m_LastRead;
bool pluginsIsNew =
!m_LastRead.isValid() || QFileInfo(pluginsPath).lastModified() > m_LastRead;
if (loadOrderIsNew || !pluginsIsNew) {
// read both files if they are both new or both older than the last read
QStringList loadOrder = readLoadOrderList(pluginList, loadOrderPath);
pluginList->setLoadOrder(loadOrder);
readPluginList(pluginList);
} else {
// If the plugins is new but not loadorder, we must reparse the load order from the
// plugin files
QStringList loadOrder = readPluginList(pluginList);
pluginList->setLoadOrder(loadOrder);
}
m_LastRead = QDateTime::currentDateTime();
}
QStringList GamebryoGamePlugins::getLoadOrder()
{
QString loadOrderPath = organizer()->profile()->absolutePath() + "/loadorder.txt";
QString pluginsPath = organizer()->profile()->absolutePath() + "/plugins.txt";
bool loadOrderIsNew = !m_LastRead.isValid() || !QFileInfo(loadOrderPath).exists() ||
QFileInfo(loadOrderPath).lastModified() > m_LastRead;
bool pluginsIsNew =
!m_LastRead.isValid() || QFileInfo(pluginsPath).lastModified() > m_LastRead;
if (loadOrderIsNew || !pluginsIsNew) {
return readLoadOrderList(m_Organizer->pluginList(), loadOrderPath);
} else {
return readPluginList(m_Organizer->pluginList());
}
}
void GamebryoGamePlugins::writePluginList(const MOBase::IPluginList* pluginList,
const QString& filePath)
{
return writeList(pluginList, filePath, false);
}
void GamebryoGamePlugins::writeLoadOrderList(const MOBase::IPluginList* pluginList,
const QString& filePath)
{
return writeList(pluginList, filePath, true);
}
void GamebryoGamePlugins::writeList(const IPluginList* pluginList,
const QString& filePath, bool loadOrder)
{
SafeWriteFile file(filePath);
QStringEncoder encoder = loadOrder
? QStringEncoder(QStringConverter::Encoding::Utf8)
: QStringEncoder(QStringConverter::Encoding::System);
file->resize(0);
file->write(
encoder.encode("# This file was automatically generated by Mod Organizer.\r\n"));
bool invalidFileNames = false;
int writtenCount = 0;
QStringList plugins = pluginList->pluginNames();
std::sort(plugins.begin(), plugins.end(),
[pluginList](const QString& lhs, const QString& rhs) {
return pluginList->priority(lhs) < pluginList->priority(rhs);
});
for (const QString& pluginName : plugins) {
if (loadOrder || (pluginList->state(pluginName) == IPluginList::STATE_ACTIVE)) {
auto result = encoder.encode(pluginName);
if (encoder.hasError()) {
invalidFileNames = true;
qCritical("invalid plugin name %s", qUtf8Printable(pluginName));
} else {
file->write(result);
}
file->write("\r\n");
++writtenCount;
}
}
if (invalidFileNames) {
reportError(QObject::tr("Some of your plugins have invalid names! These "
"plugins can not be loaded by the game. Please see "
"mo_interface.log for a list of affected plugins "
"and rename them."));
}
if (writtenCount == 0) {
qWarning("plugin list would be empty, this is almost certainly wrong. Not "
"saving.");
} else {
file->commit();
}
}
QStringList GamebryoGamePlugins::readLoadOrderList(MOBase::IPluginList* pluginList,
const QString& filePath)
{
QStringList pluginNames = organizer()->managedGame()->primaryPlugins();
std::set<QString> pluginLookup;
for (auto&& name : pluginNames) {
pluginLookup.insert(name.toLower());
}
const auto b = MOBase::forEachLineInFile(filePath, [&](QString s) {
if (!pluginLookup.contains(s.toLower())) {
pluginLookup.insert(s);
pluginNames.push_back(std::move(s));
}
});
if (!b) {
return readPluginList(pluginList);
}
return pluginNames;
}
QStringList GamebryoGamePlugins::readPluginList(MOBase::IPluginList* pluginList)
{
QStringList primary = organizer()->managedGame()->primaryPlugins();
for (const QString& pluginName : primary) {
if (pluginList->state(pluginName) != IPluginList::STATE_MISSING) {
pluginList->setState(pluginName, IPluginList::STATE_ACTIVE);
}
}
QStringList plugins = pluginList->pluginNames();
QStringList pluginsClone(plugins);
// Do not sort the primary plugins. Their load order should be locked as defined in
// "primaryPlugins".
for (const auto& plugin : pluginsClone) {
if (primary.contains(plugin, Qt::CaseInsensitive))
plugins.removeAll(plugin);
}
// Always use filetime loadorder to get the actual load order
std::sort(plugins.begin(), plugins.end(),
[&](const QString& lhs, const QString& rhs) {
MOBase::IModInterface* lhm =
organizer()->modList()->getMod(pluginList->origin(lhs));
MOBase::IModInterface* rhm =
organizer()->modList()->getMod(pluginList->origin(rhs));
QDir lhd = organizer()->managedGame()->dataDirectory();
QDir rhd = organizer()->managedGame()->dataDirectory();
if (lhm != nullptr)
lhd = lhm->absolutePath();
if (rhm != nullptr)
rhd = rhm->absolutePath();
QString lhp = lhd.absoluteFilePath(lhs);
QString rhp = rhd.absoluteFilePath(rhs);
return QFileInfo(lhp).lastModified() < QFileInfo(rhp).lastModified();
});
// Determine plugin active state by the plugins.txt file.
bool pluginsTxtExists = true;
QString filePath = organizer()->profile()->absolutePath() + "/plugins.txt";
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly)) {
pluginsTxtExists = false;
}
ON_BLOCK_EXIT([&]() {
file.close();
});
if (file.size() == 0) {
// MO stores at least a header in the file. if it's completely empty the
// file is broken
pluginsTxtExists = false;
}
QStringList activePlugins;
QStringList inactivePlugins;
if (pluginsTxtExists) {
while (!file.atEnd()) {
QByteArray line = file.readLine();
QString pluginName;
if ((line.size() > 0) && (line.at(0) != '#')) {
QStringEncoder encoder(QStringConverter::Encoding::System);
pluginName = encoder.encode(line.trimmed().constData());
}
if (pluginName.size() > 0) {
pluginList->setState(pluginName, IPluginList::STATE_ACTIVE);
activePlugins.push_back(pluginName);
}
}
for (const auto& pluginName : plugins) {
if (!activePlugins.contains(pluginName, Qt::CaseInsensitive)) {
pluginList->setState(pluginName, IPluginList::STATE_INACTIVE);
}
}
} else {
for (const QString& pluginName : plugins) {
pluginList->setState(pluginName, IPluginList::STATE_INACTIVE);
}
}
return primary + plugins;
}

Some files were not shown because too many files have changed in this diff Show More