aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSulfurNitride <lukew19@proton.me>2026-06-23 16:10:53 -0500
committerSulfurNitride <lukew19@proton.me>2026-06-23 16:10:53 -0500
commit08939441e50e52c685c531a16704fb8f6e1138bd (patch)
tree962932046d46d4fd33953c073e3eb95aeb768096
parentf415cfae26153b0853466316c0d8d1738e029976 (diff)
Fix FOMOD casing and VFS test coverage
-rw-r--r--CMakeLists.txt3
-rw-r--r--libs/installer_fomod/src/fomodinstallerdialog.cpp21
-rw-r--r--libs/installer_fomod/src/installerfomod.cpp15
-rw-r--r--src/src/prefixsetuprunner.cpp20
-rw-r--r--src/tests/test_vfs_file_ops.cpp26
5 files changed, 54 insertions, 31 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7ec1a74..ebb415d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -64,6 +64,7 @@ if(HAS_NO_DIRECT_EXTERN_ACCESS)
endif()
option(BUILD_TESTING "Build tests" OFF)
+option(BUILD_FLUORINE_TESTING "Build Fluorine standalone tests" OFF)
option(BUILD_BSA_FFI "Build Rust BSA/BA2 FFI library" ON)
option(BUILD_DOTNET_PLUGINS "Build C++/CLI .NET plugins (Windows+MSVC only)" OFF)
option(BUILD_PLUGIN_PYTHON "Build plugin_python bridge on Linux" ON)
@@ -285,7 +286,7 @@ endif()
# Main organizer executable
add_subdirectory(src/src)
-if (BUILD_TESTING)
+if (BUILD_FLUORINE_TESTING)
enable_testing()
add_subdirectory(src/tests)
endif()
diff --git a/libs/installer_fomod/src/fomodinstallerdialog.cpp b/libs/installer_fomod/src/fomodinstallerdialog.cpp
index 7c27726..9e3eaaa 100644
--- a/libs/installer_fomod/src/fomodinstallerdialog.cpp
+++ b/libs/installer_fomod/src/fomodinstallerdialog.cpp
@@ -166,6 +166,21 @@ static QString tempArchivePath(const QString& archivePath)
return QDir(QDir::tempPath()).filePath(normalizeArchivePath(archivePath));
}
+static QString fomodFilePath(const QString& fomodPath, const QString& fomodDirName,
+ const QString& fileName)
+{
+ const QString directory = tempArchivePath(joinArchivePath(fomodPath, fomodDirName));
+ const QDir dir(directory);
+
+ for (const QString& candidate : dir.entryList(QDir::Files | QDir::NoDotAndDotDot)) {
+ if (candidate.compare(fileName, Qt::CaseInsensitive) == 0) {
+ return dir.filePath(candidate);
+ }
+ }
+
+ return dir.filePath(fileName);
+}
+
QByteArray skipXmlHeader(QIODevice& file)
{
static const unsigned char UTF16LE_BOM[] = {0xFF, 0xFE};
@@ -265,8 +280,7 @@ void FomodInstallerDialog::readXml(QFile& file,
void FomodInstallerDialog::readInfoXml()
{
- QFile file(tempArchivePath(joinArchivePath(
- joinArchivePath(m_FomodPath, m_FomodDirName), "info.xml")));
+ QFile file(fomodFilePath(m_FomodPath, m_FomodDirName, "info.xml"));
// We don't need a info.xml file, so we just return if we cannot open it:
if (!file.open(QIODevice::ReadOnly)) {
@@ -277,8 +291,7 @@ void FomodInstallerDialog::readInfoXml()
void FomodInstallerDialog::readModuleConfigXml()
{
- QFile file(tempArchivePath(joinArchivePath(
- joinArchivePath(m_FomodPath, m_FomodDirName), "ModuleConfig.xml")));
+ QFile file(fomodFilePath(m_FomodPath, m_FomodDirName, "ModuleConfig.xml"));
if (!file.open(QIODevice::ReadOnly)) {
throw Exception(tr("%1 missing.").arg(file.fileName()));
}
diff --git a/libs/installer_fomod/src/installerfomod.cpp b/libs/installer_fomod/src/installerfomod.cpp
index e526610..9d9690a 100644
--- a/libs/installer_fomod/src/installerfomod.cpp
+++ b/libs/installer_fomod/src/installerfomod.cpp
@@ -1,5 +1,7 @@
#include "installerfomod.h"
+#include <algorithm>
+
#include <QImageReader>
#include <QStringList>
#include <QtPlugin>
@@ -115,10 +117,14 @@ InstallerFomod::findFomodDirectory(std::shared_ptr<const IFileTree> tree) const
bool InstallerFomod::isArchiveSupported(std::shared_ptr<const IFileTree> tree) const
{
tree = findFomodDirectory(tree);
- if (tree != nullptr) {
- return tree->exists("ModuleConfig.xml", FileTreeEntry::FILE);
+ if (tree == nullptr) {
+ return false;
}
- return false;
+
+ return std::any_of(tree->begin(), tree->end(), [](const auto& entry) {
+ return entry->isFile() &&
+ entry->name().compare("ModuleConfig.xml", Qt::CaseInsensitive) == 0;
+ });
}
void InstallerFomod::appendImageFiles(
@@ -145,7 +151,8 @@ InstallerFomod::buildFomodTree(std::shared_ptr<const IFileTree> tree) const
for (auto entry : *fomodTree) {
if (entry->isFile() &&
- (entry->compare("info.xml") == 0 || entry->compare("ModuleConfig.xml") == 0)) {
+ (entry->name().compare("info.xml", Qt::CaseInsensitive) == 0 ||
+ entry->name().compare("ModuleConfig.xml", Qt::CaseInsensitive) == 0)) {
entries.push_back(entry);
}
}
diff --git a/src/src/prefixsetuprunner.cpp b/src/src/prefixsetuprunner.cpp
index f599a30..d6ce33e 100644
--- a/src/src/prefixsetuprunner.cpp
+++ b/src/src/prefixsetuprunner.cpp
@@ -220,19 +220,6 @@ static const char* DOTNET8_X64_URL =
"https://download.visualstudio.microsoft.com/download/pr/136f4593-e3cd-4d52-bc25-579cdf46e80c/"
"8b98c1347293b48c56c3a68d72f586a1/dotnet-runtime-8.0.12-win-x64.exe";
-static const char* DOTNET_DESKTOP6_X86_URL =
- "https://download.visualstudio.microsoft.com/download/pr/cdc314df-4a4c-4709-868d-b974f336f77f/"
- "acd5ab7637e456c8a3aa667661324f6d/windowsdesktop-runtime-6.0.36-win-x86.exe";
-static const char* DOTNET_DESKTOP6_X64_URL =
- "https://download.visualstudio.microsoft.com/download/pr/f6b6c5dc-e02d-4738-9559-296e938dabcb/"
- "b66d365729359df8e8ea131197715076/windowsdesktop-runtime-6.0.36-win-x64.exe";
-
-static const QStringList DOTNET_DESKTOP6_X86_SHA256 = {
- QStringLiteral("4e77bd970df0a06528ee88d33e4a8c9fb85beedbdd7219b017083acf0c3aa39e"),
-};
-static const QStringList DOTNET_DESKTOP6_X64_SHA256 = {
- QStringLiteral("0d20debb26fc8b2bc84f25fbd9d4596a6364af8517ebf012e8b871127b798941"),
-};
static const QStringList DOTNET6_X86_SHA256 = {
QStringLiteral("3b3cb4636251a582158f4b6b340f20b3861e6793eb9a3e64bda29cbf32da3604"),
};
@@ -682,13 +669,6 @@ void PrefixSetupRunner::buildStepList()
// Runtime installers (run via Wine).
addStep("vcrun2022", "Visual C++ 2022",
[this] { return stepVcrun2022(); });
- addStep("dotnetdesktop6", ".NET Desktop Runtime 6",
- [this] {
- return stepDotNetInstallPair(
- DOTNET_DESKTOP6_X86_URL, DOTNET_DESKTOP6_X64_URL,
- ".NET Desktop 6", DOTNET_DESKTOP6_X86_SHA256,
- DOTNET_DESKTOP6_X64_SHA256);
- });
addStep("dotnet_runtimes", ".NET Runtimes (6-9)",
[this] { return stepDotNetRuntimes(); });
addStep("dotnet10_sdk", ".NET 10 SDK",
diff --git a/src/tests/test_vfs_file_ops.cpp b/src/tests/test_vfs_file_ops.cpp
index 9a37f52..56f516a 100644
--- a/src/tests/test_vfs_file_ops.cpp
+++ b/src/tests/test_vfs_file_ops.cpp
@@ -92,7 +92,7 @@ TEST(VfsFileOps, CreateFileReturnsWritableStagingHandle)
OverwriteManager overwriteManager(staging.string(), overwrite.string());
std::string realPath;
- const int fd = overwriteManager.createFile("ShaderCache/Lighting/test.pso",
+ const int fd = overwriteManager.createFile("Data/ShaderCache/Lighting/test.pso",
0600, &realPath);
ASSERT_GE(fd, 0);
@@ -100,10 +100,32 @@ TEST(VfsFileOps, CreateFileReturnsWritableStagingHandle)
ASSERT_EQ(write(fd, payload, sizeof(payload) - 1), ssize_t(sizeof(payload) - 1));
close(fd);
- EXPECT_EQ(fs::path(realPath), staging / "ShaderCache/Lighting/test.pso");
+ EXPECT_EQ(fs::path(realPath), staging / "Data/ShaderCache/Lighting/test.pso");
EXPECT_EQ(readText(realPath), "shader");
}
+TEST(VfsFileOps, CreateShaderCacheFileReusesExistingCaseVariant)
+{
+ TempRoot tmp;
+ ASSERT_FALSE(tmp.path().empty());
+
+ const fs::path staging = tmp.path() / "staging";
+ const fs::path overwrite = tmp.path() / "overwrite";
+ fs::create_directories(staging / "Data" / "ShaderCache" / "Lighting");
+
+ OverwriteManager overwriteManager(staging.string(), overwrite.string());
+ std::error_code ec;
+ std::string realPath;
+ const int fd = overwriteManager.createFile("data/shadercache/lighting/test.pso",
+ 0600, &realPath, &ec);
+ ASSERT_GE(fd, 0);
+ ASSERT_FALSE(ec);
+ close(fd);
+
+ EXPECT_EQ(fs::path(realPath),
+ staging / "Data" / "ShaderCache" / "Lighting" / "test.pso");
+}
+
TEST(VfsFileOps, CreateFileReusesExistingCaseVariant)
{
TempRoot tmp;