/// Program to test .nif files both on the FileSystem and in BSA archives. #include #include #include #include #include #include #include #include #include #include #include // 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, const std::string& extensionToFind) { const auto extension = Files::pathToUnicodeString(filename.extension()); return Misc::StringUtils::ciEqual(extension, extensionToFind); } /// See if the file has the "nif" extension. bool isNIF(const std::filesystem::path& filename) { return hasExtension(filename, "nif"); } /// See if the file has the "bsa" extension. bool isBSA(const std::filesystem::path& filename) { return hasExtension(filename, "bsa"); } /// 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 = {}) { VFS::Manager myManager(true); myManager.addArchive(std::move(anArchive)); myManager.buildIndex(); for (const auto& name : myManager.getRecursiveDirectoryIterator("")) { try { if (isNIF(name)) { // std::cout << "Decoding: " << name << std::endl; Nif::NIFFile temp_nif(myManager.get(name), archivePath / name); } else if (isBSA(name)) { if (!archivePath.empty() && !isBSA(archivePath)) { // std::cout << "Reading BSA File: " << name << std::endl; readVFS(std::make_unique(archivePath / name), archivePath / name); // std::cout << "Done with BSA File: " << name << std::endl; } } } catch (std::exception& e) { std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl; } } } bool parseOptions(int argc, char** argv, std::vector& files) { 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. Allowed options)"); auto addOption = desc.add_options(); addOption("help,h", "print help message."); addOption("input-file", bpo::value(), "input file"); // Default option if none provided bpo::positional_options_description p; p.add("input-file", -1); bpo::variables_map variables; try { bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).options(desc).positional(p).run(); bpo::store(valid_opts, variables); bpo::notify(variables); if (variables.count("help")) { std::cout << desc << std::endl; return false; } if (variables.count("input-file")) { files = variables["input-file"].as(); return true; } } catch (std::exception& e) { std::cout << "ERROR parsing arguments: " << e.what() << "\n\n" << desc << std::endl; return false; } std::cout << "No input files or directories specified!" << std::endl; std::cout << desc << std::endl; return false; } int main(int argc, char** argv) { std::vector files; if (!parseOptions(argc, argv, files)) return 1; Nif::NIFFile::setLoadUnsupportedFiles(true); // std::cout << "Reading Files" << std::endl; for (const auto& path : files) { try { if (isNIF(path)) { // std::cout << "Decoding: " << name << std::endl; Nif::NIFFile temp_nif(Files::openConstrainedFileStream(path), path); } else if (isBSA(path)) { // std::cout << "Reading BSA File: " << name << std::endl; readVFS(std::make_unique(path)); } else if (std::filesystem::is_directory(path)) { // std::cout << "Reading All Files in: " << name << std::endl; readVFS(std::make_unique(path), path); } else { std::cerr << "ERROR: \"" << Files::pathToUnicodeString(path) << "\" is not a nif file, bsa file, or directory!" << std::endl; } } catch (std::exception& e) { std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl; } } return 0; }