niftest: parse BTO, BTR, RDT and PSA files

pull/3236/head
Alexei Kotov 4 months ago
parent b9cb028809
commit 70187bb5ac

@ -25,29 +25,89 @@
// Create local aliases for brevity // Create local aliases for brevity
namespace bpo = boost::program_options; namespace bpo = boost::program_options;
/// See if the file has the named extension enum class FileType
bool hasExtension(const std::filesystem::path& filename, std::string_view extensionToFind)
{ {
const auto extension = Files::pathToUnicodeString(filename.extension()); BSA,
return Misc::StringUtils::ciEqual(extension, extensionToFind); 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. std::pair<FileType, FileClass> classifyFile(const std::filesystem::path& filename)
bool isNIF(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. std::string getFileTypeName(FileType fileType)
bool isMaterial(const std::filesystem::path& filename)
{ {
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& path)
bool isBSA(const std::filesystem::path& filename)
{ {
return hasExtension(filename, ".bsa") || hasExtension(filename, ".ba2"); return classifyFile(path).second == FileClass::Archive;
} }
std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path) std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
@ -59,17 +119,17 @@ std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
return nullptr; return nullptr;
} }
void readFile( bool readFile(
const std::filesystem::path& source, const std::filesystem::path& path, const VFS::Manager* vfs, bool quiet) 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 std::string pathStr = Files::pathToUnicodeString(path);
const bool isNif = isNIF(path);
if (!quiet) if (!quiet)
{ {
if (isNif) std::cout << "Reading " << getFileTypeName(fileType) << " file '" << pathStr << "'";
std::cout << "Reading " << (hasExtension(path, ".nif") ? "NIF" : "KF") << " file '" << pathStr << "'";
else
std::cout << "Reading " << (hasExtension(path, ".bgsm") ? "BGSM" : "BGEM") << " file '" << pathStr << "'";
if (!source.empty()) if (!source.empty())
std::cout << " from '" << Files::pathToUnicodeString(isBSA(source) ? source.filename() : source) << "'"; std::cout << " from '" << Files::pathToUnicodeString(isBSA(source) ? source.filename() : source) << "'";
std::cout << std::endl; std::cout << std::endl;
@ -77,7 +137,9 @@ void readFile(
const std::filesystem::path fullPath = !source.empty() ? source / path : path; const std::filesystem::path fullPath = !source.empty() ? source / path : path;
try try
{ {
if (isNif) switch (fileClass)
{
case FileClass::NIF:
{ {
Nif::NIFFile file(Files::pathToUnicodeString(fullPath)); Nif::NIFFile file(Files::pathToUnicodeString(fullPath));
Nif::Reader reader(file, nullptr); Nif::Reader reader(file, nullptr);
@ -85,19 +147,25 @@ void readFile(
reader.parse(vfs->get(pathStr)); reader.parse(vfs->get(pathStr));
else else
reader.parse(Files::openConstrainedFileStream(fullPath)); reader.parse(Files::openConstrainedFileStream(fullPath));
break;
} }
else case FileClass::Material:
{ {
if (vfs != nullptr) if (vfs != nullptr)
Bgsm::parse(vfs->get(pathStr)); Bgsm::parse(vfs->get(pathStr));
else else
Bgsm::parse(Files::openConstrainedFileStream(fullPath)); Bgsm::parse(Files::openConstrainedFileStream(fullPath));
break;
}
default:
break;
} }
} }
catch (std::exception& e) catch (std::exception& e)
{ {
std::cerr << "Failed to read '" << pathStr << "':" << std::endl << e.what() << std::endl; std::cerr << "Failed to read '" << pathStr << "':" << std::endl << e.what() << std::endl;
} }
return true;
} }
/// Check all the nif files in a given VFS::Archive /// Check all the nif files in a given VFS::Archive
@ -115,12 +183,9 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
vfs.buildIndex(); vfs.buildIndex();
for (const auto& name : vfs.getRecursiveDirectoryIterator()) 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)) if (!archivePath.empty() && !isBSA(archivePath))
{ {
@ -148,10 +213,11 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
bool parseOptions(int argc, char** argv, Files::PathContainer& files, Files::PathContainer& archives, bool parseOptions(int argc, char** argv, Files::PathContainer& files, Files::PathContainer& archives,
bool& writeDebugLog, bool& quiet) 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: Usages:
niftest <nif files, kf files, bgem/bgsm files, BSA/BA2 files, or directories> niftest <nif files, kf files, bto/btr files, rdt files, psa files, bgem/bgsm files, BSA/BA2 files, or directories>
Scan the file or directories for NIF errors. Scan the file or directories for NIF errors.
Allowed options)"); Allowed options)");
@ -240,20 +306,20 @@ int main(int argc, char** argv)
const std::string pathStr = Files::pathToUnicodeString(path); const std::string pathStr = Files::pathToUnicodeString(path);
try try
{ {
if (isNIF(path) || isMaterial(path)) const bool isFile = readFile({}, path, vfs.get(), quiet);
if (!isFile)
{ {
readFile({}, path, vfs.get(), quiet); if (auto archive = makeArchive(path))
}
else if (auto archive = makeArchive(path))
{ {
readVFS(std::move(archive), path, quiet); readVFS(std::move(archive), path, quiet);
} }
else else
{ {
std::cerr << "Error: '" << pathStr << "' is not a NIF/KF/BGEM/BGSM file, BSA/BA2 archive, or directory" std::cerr << "Error: '" << pathStr << "' is not a NIF file, material file, archive, or directory"
<< std::endl; << std::endl;
} }
} }
}
catch (std::exception& e) catch (std::exception& e)
{ {
std::cerr << "Failed to read '" << pathStr << "': " << e.what() << std::endl; std::cerr << "Failed to read '" << pathStr << "': " << e.what() << std::endl;

Loading…
Cancel
Save