diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 0b8aa8e275..012245937f 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -25,29 +25,89 @@ // Create local aliases for brevity namespace bpo = boost::program_options; -/// See if the file has the named extension -bool hasExtension(const std::filesystem::path& filename, std::string_view extensionToFind) +enum class FileType { - const auto extension = Files::pathToUnicodeString(filename.extension()); - return Misc::StringUtils::ciEqual(extension, extensionToFind); -} + BSA, + BA2, + BGEM, + BGSM, + NIF, + KF, + BTO, + BTR, + RDT, + PSA, + Unknown, +}; + +enum class FileClass +{ + Archive, + Material, + NIF, + Unknown, +}; -/// See if the file has the "nif" extension. -bool isNIF(const std::filesystem::path& filename) +std::pair classifyFile(const std::filesystem::path& filename) { - return hasExtension(filename, ".nif") || hasExtension(filename, ".kf"); + const std::string extension = Misc::StringUtils::lowerCase(Files::pathToUnicodeString(filename.extension())); + if (extension == ".bsa") + return { FileType::BSA, FileClass::Archive }; + if (extension == ".ba2") + return { FileType::BA2, FileClass::Archive }; + if (extension == ".bgem") + return { FileType::BGEM, FileClass::Material }; + if (extension == ".bgsm") + return { FileType::BGSM, FileClass::Material }; + if (extension == ".nif") + return { FileType::NIF, FileClass::NIF }; + if (extension == ".kf") + return { FileType::KF, FileClass::NIF }; + if (extension == ".bto") + return { FileType::BTO, FileClass::NIF }; + if (extension == ".btr") + return { FileType::BTR, FileClass::NIF }; + if (extension == ".rdt") + return { FileType::RDT, FileClass::NIF }; + if (extension == ".psa") + return { FileType::PSA, FileClass::NIF }; + + return { FileType::Unknown, FileClass::Unknown }; } -/// Check if the file is a material file. -bool isMaterial(const std::filesystem::path& filename) +std::string getFileTypeName(FileType fileType) { - return hasExtension(filename, ".bgem") || hasExtension(filename, ".bgsm"); + switch (fileType) + { + case FileType::BSA: + return "BSA"; + case FileType::BA2: + return "BA2"; + case FileType::BGEM: + return "BGEM"; + case FileType::BGSM: + return "BGSM"; + case FileType::NIF: + return "NIF"; + case FileType::KF: + return "KF"; + case FileType::BTO: + return "BTO"; + case FileType::BTR: + return "BTR"; + case FileType::RDT: + return "RDT"; + case FileType::PSA: + return "PSA"; + case FileType::Unknown: + default: + return {}; + } } -/// See if the file has the "bsa" extension. -bool isBSA(const std::filesystem::path& filename) +bool isBSA(const std::filesystem::path& path) { - return hasExtension(filename, ".bsa") || hasExtension(filename, ".ba2"); + return classifyFile(path).second == FileClass::Archive; } std::unique_ptr makeArchive(const std::filesystem::path& path) @@ -59,17 +119,17 @@ std::unique_ptr makeArchive(const std::filesystem::path& path) return nullptr; } -void readFile( +bool readFile( const std::filesystem::path& source, const std::filesystem::path& path, const VFS::Manager* vfs, bool quiet) { + const auto [fileType, fileClass] = classifyFile(path); + if (fileClass != FileClass::NIF && fileClass != FileClass::Material) + return false; + const std::string pathStr = Files::pathToUnicodeString(path); - const bool isNif = isNIF(path); if (!quiet) { - if (isNif) - std::cout << "Reading " << (hasExtension(path, ".nif") ? "NIF" : "KF") << " file '" << pathStr << "'"; - else - std::cout << "Reading " << (hasExtension(path, ".bgsm") ? "BGSM" : "BGEM") << " file '" << pathStr << "'"; + std::cout << "Reading " << getFileTypeName(fileType) << " file '" << pathStr << "'"; if (!source.empty()) std::cout << " from '" << Files::pathToUnicodeString(isBSA(source) ? source.filename() : source) << "'"; std::cout << std::endl; @@ -77,27 +137,35 @@ void readFile( const std::filesystem::path fullPath = !source.empty() ? source / path : path; try { - if (isNif) - { - Nif::NIFFile file(Files::pathToUnicodeString(fullPath)); - Nif::Reader reader(file, nullptr); - if (vfs != nullptr) - reader.parse(vfs->get(pathStr)); - else - reader.parse(Files::openConstrainedFileStream(fullPath)); - } - else + switch (fileClass) { - if (vfs != nullptr) - Bgsm::parse(vfs->get(pathStr)); - else - Bgsm::parse(Files::openConstrainedFileStream(fullPath)); + case FileClass::NIF: + { + Nif::NIFFile file(Files::pathToUnicodeString(fullPath)); + Nif::Reader reader(file, nullptr); + if (vfs != nullptr) + reader.parse(vfs->get(pathStr)); + else + reader.parse(Files::openConstrainedFileStream(fullPath)); + break; + } + case FileClass::Material: + { + if (vfs != nullptr) + Bgsm::parse(vfs->get(pathStr)); + else + Bgsm::parse(Files::openConstrainedFileStream(fullPath)); + break; + } + default: + break; } } catch (std::exception& e) { std::cerr << "Failed to read '" << pathStr << "':" << std::endl << e.what() << std::endl; } + return true; } /// Check all the nif files in a given VFS::Archive @@ -116,10 +184,7 @@ void readVFS(std::unique_ptr&& archive, const std::filesystem::pat for (const auto& name : vfs.getRecursiveDirectoryIterator()) { - if (isNIF(name.value()) || isMaterial(name.value())) - { - readFile(archivePath, name.value(), &vfs, quiet); - } + readFile(archivePath, name.value(), &vfs, quiet); } if (!archivePath.empty() && !isBSA(archivePath)) @@ -148,10 +213,11 @@ void readVFS(std::unique_ptr&& archive, const std::filesystem::pat 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, KF, BGEM/BGSM and BSA/BA2 files + bpo::options_description desc( + R"(Ensure that OpenMW can use the provided NIF, KF, BTO/BTR, RDT, PSA, BGEM/BGSM and BSA/BA2 files Usages: - niftest + niftest Scan the file or directories for NIF errors. Allowed options)"); @@ -240,18 +306,18 @@ int main(int argc, char** argv) const std::string pathStr = Files::pathToUnicodeString(path); try { - if (isNIF(path) || isMaterial(path)) - { - readFile({}, path, vfs.get(), quiet); - } - else if (auto archive = makeArchive(path)) - { - readVFS(std::move(archive), path, quiet); - } - else + const bool isFile = readFile({}, path, vfs.get(), quiet); + if (!isFile) { - std::cerr << "Error: '" << pathStr << "' is not a NIF/KF/BGEM/BGSM file, BSA/BA2 archive, or directory" - << std::endl; + if (auto archive = makeArchive(path)) + { + readVFS(std::move(archive), path, quiet); + } + else + { + std::cerr << "Error: '" << pathStr << "' is not a NIF file, material file, archive, or directory" + << std::endl; + } } } catch (std::exception& e)