From 1a2c56fefb85651fda26c3cb23842cb98eb3c51a Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 8 Feb 2023 21:25:01 +0100 Subject: [PATCH 1/4] Fix checking for file extension in niftest --- apps/niftest/niftest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 6ea00651ca..af165ce817 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -33,12 +33,12 @@ bool hasExtension(const std::filesystem::path& filename, const std::string& exte /// See if the file has the "nif" extension. bool isNIF(const std::filesystem::path& filename) { - return hasExtension(filename, "nif"); + return hasExtension(filename, ".nif"); } /// See if the file has the "bsa" extension. bool isBSA(const std::filesystem::path& filename) { - return hasExtension(filename, "bsa"); + return hasExtension(filename, ".bsa"); } /// Check all the nif files in a given VFS::Archive From ac5f22445e078c8f302e8604ce279b5ecb76e1d1 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 8 Feb 2023 21:25:26 +0100 Subject: [PATCH 2/4] Support compressed BSA archives by niftest --- apps/niftest/niftest.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index af165ce817..75cf5772d9 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -41,10 +41,31 @@ bool isBSA(const std::filesystem::path& filename) return hasExtension(filename, ".bsa"); } +std::unique_ptr makeBsaArchive(const std::filesystem::path& path) +{ + switch (Bsa::CompressedBSAFile::detectVersion(path)) + { + case Bsa::BSAVER_UNKNOWN: + std::cerr << '"' << path << "\" is unknown BSA archive" << std::endl; + return nullptr; + case Bsa::BSAVER_UNCOMPRESSED: + return std::make_unique(path); + case Bsa::BSAVER_COMPRESSED: + return std::make_unique(path); + } + + std::cerr << '"' << path << "\" is unsupported BSA archive" << std::endl; + + return nullptr; +} + /// Check all the nif files in a given VFS::Archive /// \note Can not read a bsa file inside of a bsa file. void readVFS(std::unique_ptr&& anArchive, const std::filesystem::path& archivePath = {}) { + if (anArchive == nullptr) + return; + VFS::Manager myManager(true); myManager.addArchive(std::move(anArchive)); myManager.buildIndex(); @@ -65,7 +86,7 @@ void readVFS(std::unique_ptr&& anArchive, const std::filesystem::p if (!archivePath.empty() && !isBSA(archivePath)) { // std::cout << "Reading BSA File: " << name << std::endl; - readVFS(std::make_unique(archivePath / name), archivePath / name); + readVFS(makeBsaArchive(archivePath / name), archivePath / name); // std::cout << "Done with BSA File: " << name << std::endl; } } @@ -144,7 +165,7 @@ int main(int argc, char** argv) else if (isBSA(path)) { // std::cout << "Reading BSA File: " << name << std::endl; - readVFS(std::make_unique(path)); + readVFS(makeBsaArchive(path)); } else if (std::filesystem::is_directory(path)) { From 2c43a8558afe2ffc3b5b4b7479c6e77d36f1cc0d Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 8 Feb 2023 21:36:05 +0100 Subject: [PATCH 3/4] Add option to disable debug log for unsupported nif files It becomes quite extensive and not very useful when loading oblivion content files. --- apps/niftest/niftest.cpp | 9 +++++++-- apps/openmw/mwrender/renderingmanager.cpp | 1 + components/nif/niffile.cpp | 15 +++++++++++---- components/nif/niffile.hpp | 3 +++ docs/source/reference/modding/settings/models.rst | 10 ++++++++++ files/settings-default.cfg | 3 +++ 6 files changed, 35 insertions(+), 6 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 75cf5772d9..9f3f8066c6 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -98,7 +98,7 @@ void readVFS(std::unique_ptr&& anArchive, const std::filesystem::p } } -bool parseOptions(int argc, char** argv, std::vector& files) +bool parseOptions(int argc, char** argv, std::vector& files, bool& writeDebugLog) { bpo::options_description desc(R"(Ensure that OpenMW can use the provided NIF and BSA files @@ -109,6 +109,7 @@ Usages: Allowed options)"); auto addOption = desc.add_options(); addOption("help,h", "print help message."); + addOption("write-debug-log,v", "write debug log for unsupported nif files"); addOption("input-file", bpo::value(), "input file"); // Default option if none provided @@ -126,6 +127,7 @@ Allowed options)"); std::cout << desc << std::endl; return false; } + writeDebugLog = variables.count("write-debug-log") > 0; if (variables.count("input-file")) { files = variables["input-file"].as(); @@ -146,10 +148,13 @@ Allowed options)"); int main(int argc, char** argv) { std::vector files; - if (!parseOptions(argc, argv, files)) + bool writeDebugLog = false; + if (!parseOptions(argc, argv, files, writeDebugLog)) return 1; Nif::Reader::setLoadUnsupportedFiles(true); + Nif::Reader::setWriteNifDebugLog(writeDebugLog); + // std::cout << "Reading Files" << std::endl; for (const auto& path : files) { diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 130ad577ce..f226b089f4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -600,6 +600,7 @@ namespace MWRender NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor); NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect); Nif::Reader::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models")); + Nif::Reader::setWriteNifDebugLog(Settings::Manager::getBool("write nif debug log", "Models")); mStateUpdater->setFogEnd(mViewDistance); diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index fb3d92ff4f..2da28bb5d9 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -220,13 +220,14 @@ namespace Nif NIFFile::VER_MW, }; const bool supportedVersion = std::find(supportedVers.begin(), supportedVers.end(), ver) != supportedVers.end(); + const bool writeDebugLog = sWriteNifDebugLog; if (!supportedVersion) { - if (sLoadUnsupportedFiles) + if (!sLoadUnsupportedFiles) + throw Nif::Exception("Unsupported NIF version: " + printVersion(ver), filename); + if (writeDebugLog) Log(Debug::Warning) << " NIFFile Warning: Unsupported NIF version: " << printVersion(ver) << ". Proceed with caution! File: " << filename; - else - throw Nif::Exception("Unsupported NIF version: " + printVersion(ver), filename); } // NIF data endianness @@ -322,7 +323,7 @@ namespace Nif r = entry->second(); - if (!supportedVersion) + if (!supportedVersion && writeDebugLog) Log(Debug::Verbose) << "NIF Debug: Reading record of type " << rec << ", index " << i << " (" << filename << ")"; @@ -364,12 +365,18 @@ namespace Nif } std::atomic_bool Reader::sLoadUnsupportedFiles = false; + std::atomic_bool Reader::sWriteNifDebugLog = false; void Reader::setLoadUnsupportedFiles(bool load) { sLoadUnsupportedFiles = load; } + void Reader::setWriteNifDebugLog(bool value) + { + sWriteNifDebugLog = value; + } + std::string Reader::getString(std::uint32_t index) const { if (index == std::numeric_limits::max()) diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 5f5f112eaf..f2adb698d0 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -110,6 +110,7 @@ namespace Nif bool& mUseSkinning; static std::atomic_bool sLoadUnsupportedFiles; + static std::atomic_bool sWriteNifDebugLog; /// Get the file's version in a human readable form ///\returns A string containing a human readable NIF version number @@ -145,6 +146,8 @@ namespace Nif unsigned int getBethVersion() const { return bethVer; } static void setLoadUnsupportedFiles(bool load); + + static void setWriteNifDebugLog(bool load); }; using NIFFilePtr = std::shared_ptr; diff --git a/docs/source/reference/modding/settings/models.rst b/docs/source/reference/modding/settings/models.rst index b870268567..998be9e6ea 100644 --- a/docs/source/reference/modding/settings/models.rst +++ b/docs/source/reference/modding/settings/models.rst @@ -263,3 +263,13 @@ weatherblizzard Path to the file used for the blizzard clouds weather effect in Morrowind. OpenMW doesn't use this file, instead it renders a similar looking particle effect. Changing this won't have any effect. + +write nif debug log +------------------- + +:Type: boolean +:Range: True/False +:Default: True + +If enabled, log the loading process of unsupported NIF files. +:ref:`load unsupported nif files` setting must be enabled for this setting to have any effect. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index e052aefcf2..fe93e019bf 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -1116,6 +1116,9 @@ weathersnow = meshes/snow.nif # Blizzard weather effect weatherblizzard = meshes/blizzard.nif +# Enable to write logs when loading unsupported nif file +write nif debug log = true + [Groundcover] # enable separate groundcover handling From d20b05c7fbf1b198b43b89bd6844db43230ff538 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 8 Feb 2023 21:53:44 +0100 Subject: [PATCH 4/4] Support reading specific nif files from archive by niftest --- apps/niftest/niftest.cpp | 45 ++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 9f3f8066c6..21f40de86f 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -59,6 +59,15 @@ std::unique_ptr makeBsaArchive(const std::filesystem::path& path) return nullptr; } +std::unique_ptr makeArchive(const std::filesystem::path& path) +{ + if (isBSA(path)) + return makeBsaArchive(path); + if (std::filesystem::is_directory(path)) + return std::make_unique(path); + return nullptr; +} + /// Check all the nif files in a given VFS::Archive /// \note Can not read a bsa file inside of a bsa file. void readVFS(std::unique_ptr&& anArchive, const std::filesystem::path& archivePath = {}) @@ -98,7 +107,8 @@ void readVFS(std::unique_ptr&& anArchive, const std::filesystem::p } } -bool parseOptions(int argc, char** argv, std::vector& files, bool& writeDebugLog) +bool parseOptions(int argc, char** argv, std::vector& files, bool& writeDebugLog, + std::vector& archives) { bpo::options_description desc(R"(Ensure that OpenMW can use the provided NIF and BSA files @@ -110,6 +120,7 @@ Allowed options)"); auto addOption = desc.add_options(); addOption("help,h", "print help message."); addOption("write-debug-log,v", "write debug log for unsupported nif files"); + addOption("archives", bpo::value(), "path to archive files to provide files"); addOption("input-file", bpo::value(), "input file"); // Default option if none provided @@ -131,6 +142,8 @@ Allowed options)"); if (variables.count("input-file")) { files = variables["input-file"].as(); + if (const auto it = variables.find("archives"); it != variables.end()) + archives = it->second.as(); return true; } } @@ -149,12 +162,25 @@ int main(int argc, char** argv) { std::vector files; bool writeDebugLog = false; - if (!parseOptions(argc, argv, files, writeDebugLog)) + std::vector archives; + if (!parseOptions(argc, argv, files, writeDebugLog, archives)) return 1; Nif::Reader::setLoadUnsupportedFiles(true); Nif::Reader::setWriteNifDebugLog(writeDebugLog); + std::unique_ptr vfs; + if (!archives.empty()) + { + vfs = std::make_unique(true); + for (const std::filesystem::path& path : archives) + if (auto archive = makeArchive(path)) + vfs->addArchive(std::move(archive)); + else + std::cerr << '"' << path << "\" is unsupported archive" << std::endl; + vfs->buildIndex(); + } + // std::cout << "Reading Files" << std::endl; for (const auto& path : files) { @@ -165,17 +191,14 @@ int main(int argc, char** argv) // std::cout << "Decoding: " << name << std::endl; Nif::NIFFile file(path); Nif::Reader reader(file); - reader.parse(Files::openConstrainedFileStream(path)); + if (vfs != nullptr) + reader.parse(vfs->get(Files::pathToUnicodeString(path))); + else + reader.parse(Files::openConstrainedFileStream(path)); } - else if (isBSA(path)) + else if (auto archive = makeArchive(path)) { - // std::cout << "Reading BSA File: " << name << std::endl; - readVFS(makeBsaArchive(path)); - } - else if (std::filesystem::is_directory(path)) - { - // std::cout << "Reading All Files in: " << name << std::endl; - readVFS(std::make_unique(path), path); + readVFS(std::move(archive), path); } else {