diff --git a/apps/bsatool/bsatool.cpp b/apps/bsatool/bsatool.cpp index de755e7d1d..28711df929 100644 --- a/apps/bsatool/bsatool.cpp +++ b/apps/bsatool/bsatool.cpp @@ -194,7 +194,8 @@ int extract(std::unique_ptr& bsa, Arguments& info) // Get a stream for the file to extract for (auto it = bsa->getList().rbegin(); it != bsa->getList().rend(); ++it) { - if (Misc::StringUtils::ciEqual(Misc::StringUtils::stringToU8String(it->name()), archivePath)) + auto streamPath = Misc::StringUtils::stringToU8String(it->name()); + if (Misc::StringUtils::ciEqual(streamPath, archivePath) || Misc::StringUtils::ciEqual(streamPath, extractPath)) { stream = bsa->getFile(&*it); break; diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 004e45765c..29488fb677 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -45,9 +45,6 @@ std::unique_ptr makeBsaArchive(const std::filesystem::path& path) { switch (Bsa::BSAFile::detectVersion(path)) { - case Bsa::BSAVER_UNKNOWN: - std::cerr << '"' << path << "\" is unknown BSA archive" << std::endl; - return nullptr; case Bsa::BSAVER_COMPRESSED: return std::make_unique::type>(path); case Bsa::BSAVER_BA2_GNRL: @@ -56,11 +53,11 @@ std::unique_ptr makeBsaArchive(const std::filesystem::path& path) return std::make_unique::type>(path); case Bsa::BSAVER_UNCOMPRESSED: return std::make_unique::type>(path); + case Bsa::BSAVER_UNKNOWN: + default: + std::cerr << "'" << Files::pathToUnicodeString(path) << "' is not a recognized BSA archive" << std::endl; + return nullptr; } - - std::cerr << '"' << path << "\" is unsupported BSA archive" << std::endl; - - return nullptr; } std::unique_ptr makeArchive(const std::filesystem::path& path) @@ -72,58 +69,86 @@ std::unique_ptr makeArchive(const std::filesystem::path& path) return nullptr; } +void readNIF( + const std::filesystem::path& source, const std::filesystem::path& path, const VFS::Manager* vfs, bool quiet) +{ + const std::string pathStr = Files::pathToUnicodeString(path); + if (!quiet) + { + std::cout << "Reading NIF file '" << pathStr << "'"; + if (!source.empty()) + std::cout << " from '" << Files::pathToUnicodeString(isBSA(source) ? source.filename() : source) << "'"; + std::cout << std::endl; + } + std::filesystem::path fullPath = !source.empty() ? source / path : path; + try + { + Nif::NIFFile file(fullPath); + Nif::Reader reader(file); + if (vfs != nullptr) + reader.parse(vfs->get(pathStr)); + else + reader.parse(Files::openConstrainedFileStream(fullPath)); + } + catch (std::exception& e) + { + std::cerr << "Failed to read '" << pathStr << "':" << std::endl << e.what() << std::endl; + } +} + /// 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 = {}) +void readVFS(std::unique_ptr&& archive, const std::filesystem::path& archivePath, bool quiet) { - if (anArchive == nullptr) + if (archive == nullptr) return; - VFS::Manager myManager; - myManager.addArchive(std::move(anArchive)); - myManager.buildIndex(); + if (!quiet) + std::cout << "Reading data source '" << Files::pathToUnicodeString(archivePath) << "'" << std::endl; + + VFS::Manager vfs; + vfs.addArchive(std::move(archive)); + vfs.buildIndex(); - for (const auto& name : myManager.getRecursiveDirectoryIterator("")) + for (const auto& name : vfs.getRecursiveDirectoryIterator("")) { - try + if (isNIF(name)) { - if (isNIF(name)) - { - // std::cout << "Decoding: " << name << std::endl; - Nif::NIFFile file(archivePath / name); - Nif::Reader reader(file); - reader.parse(myManager.get(name)); - } - else if (isBSA(name)) - { - if (!archivePath.empty() && !isBSA(archivePath)) - { - // std::cout << "Reading BSA File: " << name << std::endl; - readVFS(makeBsaArchive(archivePath / name), archivePath / name); - // std::cout << "Done with BSA File: " << name << std::endl; - } - } + readNIF(archivePath, name, &vfs, quiet); } - catch (std::exception& e) + } + + if (!archivePath.empty() && !isBSA(archivePath)) + { + Files::PathContainer dataDirs = { archivePath }; + const Files::Collections fileCollections = Files::Collections(dataDirs); + const Files::MultiDirCollection& bsaCol = fileCollections.getCollection(".bsa"); + const Files::MultiDirCollection& ba2Col = fileCollections.getCollection(".ba2"); + for (auto& file : bsaCol) + { + readVFS(makeBsaArchive(file.second), file.second, quiet); + } + for (auto& file : ba2Col) { - std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl; + readVFS(makeBsaArchive(file.second), file.second, quiet); } } } -bool parseOptions(int argc, char** argv, std::vector& files, bool& writeDebugLog, - std::vector& archives) +bool parseOptions(int argc, char** argv, Files::PathContainer& files, Files::PathContainer& archives, + bool& writeDebugLog, bool& quiet) { bpo::options_description desc(R"(Ensure that OpenMW can use the provided NIF and BSA files Usages: - niftool - Scan the file or directories for nif errors. + niftest + Scan the file or directories for NIF errors. 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("quiet,q", "do not log read archives/files"); addOption("archives", bpo::value(), "path to archive files to provide files"); addOption("input-file", bpo::value(), "input file"); @@ -143,17 +168,18 @@ Allowed options)"); return false; } writeDebugLog = variables.count("write-debug-log") > 0; + quiet = variables.count("quiet") > 0; if (variables.count("input-file")) { - files = variables["input-file"].as(); + files = asPathContainer(variables["input-file"].as()); if (const auto it = variables.find("archives"); it != variables.end()) - archives = it->second.as(); + archives = asPathContainer(it->second.as()); return true; } } catch (std::exception& e) { - std::cout << "ERROR parsing arguments: " << e.what() << "\n\n" << desc << std::endl; + std::cout << "Error parsing arguments: " << e.what() << "\n\n" << desc << std::endl; return false; } @@ -164,64 +190,62 @@ Allowed options)"); int main(int argc, char** argv) { - std::vector files; + Files::PathContainer files, sources; bool writeDebugLog = false; - std::vector archives; - if (!parseOptions(argc, argv, files, writeDebugLog, archives)) + bool quiet = false; + if (!parseOptions(argc, argv, files, sources, writeDebugLog, quiet)) return 1; Nif::Reader::setLoadUnsupportedFiles(true); Nif::Reader::setWriteNifDebugLog(writeDebugLog); std::unique_ptr vfs; - if (!archives.empty()) + if (!sources.empty()) { vfs = std::make_unique(); - for (const std::filesystem::path& path : archives) + for (const std::filesystem::path& path : sources) { + const std::string pathStr = Files::pathToUnicodeString(path); + if (!quiet) + std::cout << "Adding data source '" << pathStr << "'" << std::endl; + try { if (auto archive = makeArchive(path)) vfs->addArchive(std::move(archive)); else - std::cerr << '"' << path << "\" is unsupported archive" << std::endl; - vfs->buildIndex(); + std::cerr << "Error: '" << pathStr << "' is not an archive or directory" << std::endl; } catch (std::exception& e) { - std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl; + std::cerr << "Failed to add data source '" << pathStr << "': " << e.what() << std::endl; } } + + vfs->buildIndex(); } - // std::cout << "Reading Files" << std::endl; for (const auto& path : files) { + const std::string pathStr = Files::pathToUnicodeString(path); try { if (isNIF(path)) { - // std::cout << "Decoding: " << name << std::endl; - Nif::NIFFile file(path); - Nif::Reader reader(file); - if (vfs != nullptr) - reader.parse(vfs->get(Files::pathToUnicodeString(path))); - else - reader.parse(Files::openConstrainedFileStream(path)); + readNIF({}, path, vfs.get(), quiet); } else if (auto archive = makeArchive(path)) { - readVFS(std::move(archive), path); + readVFS(std::move(archive), path, quiet); } else { - std::cerr << "ERROR: \"" << Files::pathToUnicodeString(path) - << "\" is not a nif file, bsa/ba2 file, or directory!" << std::endl; + std::cerr << "Error: '" << pathStr << "' is not a NIF file, BSA/BA2 archive, or directory" << std::endl; } } catch (std::exception& e) { - std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl; + std::cerr << "Failed to read '" << pathStr << "': " << e.what() << std::endl; } } return 0; diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 37e40938d3..d6d063a254 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -512,6 +512,10 @@ namespace Nif void Reader::parse(Files::IStreamPtr&& stream) { + const bool writeDebug = sWriteNifDebugLog; + if (writeDebug) + Log(Debug::Verbose) << "NIF Debug: Reading file: '" << mFilename << "'"; + const std::array fileHash = Files::getHash(mFilename, *stream); mHash.append(reinterpret_cast(fileHash.data()), fileHash.size() * sizeof(std::uint64_t)); @@ -538,15 +542,9 @@ namespace Nif }; const bool supportedVersion = std::find(supportedVers.begin(), supportedVers.end(), mVersion) != supportedVers.end(); - const bool writeDebugLog = sWriteNifDebugLog; - if (!supportedVersion) - { - if (!sLoadUnsupportedFiles) - throw Nif::Exception("Unsupported NIF version: " + versionToString(mVersion), mFilename); - if (writeDebugLog) - Log(Debug::Warning) << " NIFFile Warning: Unsupported NIF version: " << versionToString(mVersion) - << ". Proceed with caution! File: " << mFilename; - } + + if (!supportedVersion && !sLoadUnsupportedFiles) + throw Nif::Exception("Unsupported NIF version: " + versionToString(mVersion), mFilename); const bool hasEndianness = mVersion >= NIFStream::generateVersion(20, 0, 0, 4); const bool hasUserVersion = mVersion >= NIFStream::generateVersion(10, 0, 1, 8); @@ -603,6 +601,17 @@ namespace Nif } } + if (writeDebug) + { + std::stringstream versionInfo; + versionInfo << "NIF Debug: Version: " << versionToString(mVersion); + if (mUserVersion) + versionInfo << "\nUser version: " << mUserVersion; + if (mBethVersion) + versionInfo << "\nBSStream version: " << mBethVersion; + Log(Debug::Verbose) << versionInfo.str(); + } + if (hasRecTypeListings) { // TODO: 20.3.1.2 uses DJB hashes instead of strings @@ -658,9 +667,8 @@ namespace Nif r = entry->second(); - if (!supportedVersion && writeDebugLog) - Log(Debug::Verbose) << "NIF Debug: Reading record of type " << rec << ", index " << i << " (" - << mFilename << ")"; + if (writeDebug) + Log(Debug::Verbose) << "NIF Debug: Reading record of type " << rec << ", index " << i; assert(r != nullptr); assert(r->recType != RC_MISSING); diff --git a/docs/source/reference/modding/settings/models.rst b/docs/source/reference/modding/settings/models.rst index 998be9e6ea..4cf8d4c0d6 100644 --- a/docs/source/reference/modding/settings/models.rst +++ b/docs/source/reference/modding/settings/models.rst @@ -269,7 +269,6 @@ write nif debug log :Type: boolean :Range: True/False -:Default: True +:Default: False -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. +If enabled, log the loading process of NIF files. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index da1c97519a..4a90a46cc5 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -1136,8 +1136,8 @@ 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 +# Enable to write logs when loading NIF files +write nif debug log = false [Groundcover]