From 3be0ee824afd300ebddbe1496e459b542a2de824 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sun, 26 Nov 2023 21:57:41 +0300 Subject: [PATCH] niftest updates Properly read archives within the supplied data directories Don't print quote marks redundantly Reduce code duplication Improve logging --- apps/niftest/niftest.cpp | 141 +++++++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 59 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 004e45765c..77752d25bd 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,85 @@ 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) +{ + if (!quiet) + { + std::cout << "Reading NIF file '" << Files::pathToUnicodeString(path) << "'"; + 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(Files::pathToUnicodeString(path))); + else + reader.parse(Files::openConstrainedFileStream(fullPath)); + } + catch (std::exception& e) + { + std::cerr << "Error, an exception has occurred: " << 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; - for (const auto& name : myManager.getRecursiveDirectoryIterator("")) + VFS::Manager vfs; + vfs.addArchive(std::move(archive)); + vfs.buildIndex(); + + 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) { - std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl; + readVFS(makeBsaArchive(file.second), file.second, quiet); + } + for (auto& file : ba2Col) + { + 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 +167,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 +189,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) { + if (!quiet) + std::cout << "Adding data source '" << Files::pathToUnicodeString(path) << "'" << 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: '" << Files::pathToUnicodeString(path) << "' 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 << "Error, an exception has occurred: " << e.what() << std::endl; } } + + vfs->buildIndex(); } - // std::cout << "Reading Files" << std::endl; for (const auto& path : files) { 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: '" << Files::pathToUnicodeString(path) + << "' 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 << "Error, an exception has occurred: " << e.what() << std::endl; } } return 0;