RDI/tools/RDIDebug.cpp

375 lines
10 KiB
C++

/**
* @file RDIDebug.cpp
* @date 01/02/2023
* @author JackCarterSmith
* @copyright GPL-v3.0
* @brief Debug app to test functions of RDI library.
*
*/
#if !(__cplusplus < 201703L) && !(_MSVC_LANG < 201703L)
#error "This code run on C++17 or upper."
#endif
#include <iostream>
#include <fstream>
#include <chrono>
#include <string>
#include <algorithm>
#include <stdexcept>
#include <vector>
#ifdef USE_MULTITHREADING
#include <deque>
#include <thread>
#endif
#include <filesystem>
#include <boost/filesystem.hpp>
#include <RDI.hpp>
#ifdef _WIN32
#include <windows.h>
#endif
using namespace std;
static vector<RDI::LVL*> levelCollection;
static vector<RDI::HOB*> modelCollection;
static void PrintVirtualDirectoryContents( boost::filesystem::path p, bool unicode, string outPrefix = "" ) {
auto curDirElementsName = RDI::RDI_getDirectoryElements(p.string());
auto newPath = boost::filesystem::path(p);
auto newOutPrefix = string(outPrefix);
for ( string eName : curDirElementsName ) {
newPath.clear();
newPath.concat(p);
newOutPrefix.clear();
newOutPrefix.append(outPrefix);
newPath.append(eName);
if (RDI::RDI_isElementDirectory(newPath.string())) {
if (unicode) {
if (*--curDirElementsName.end() == eName) {
cout << outPrefix << "\u2514 " << eName << endl;
newOutPrefix.append(" ");
} else {
cout << outPrefix << "\u251C " << eName << endl;
newOutPrefix.append("\u2502 ");
}
} else {
if (*--curDirElementsName.end() == eName) {
cout << outPrefix << "\\ " << eName << endl;
newOutPrefix.append(" ");
} else {
cout << outPrefix << "\\ " << eName << endl;
newOutPrefix.append("| ");
}
}
PrintVirtualDirectoryContents(newPath, unicode, newOutPrefix);
} else {
if (unicode) {
if (*--curDirElementsName.end() == eName)
cout << newOutPrefix << "\u2514 " << eName << endl;
else
cout << newOutPrefix << "\u251C " << eName << endl;
} else {
cout << newOutPrefix << "+ " << eName << endl;
}
}
}
}
static void PrintModel( const RDI::HOB::Mesh* mesh, string prefix = "" ) {
if (mesh == nullptr)
return;
string newPrefix = string(prefix);
for ( auto submesh : mesh->subMeshs ) {
for ( auto v : submesh->vertices )
cout << "[ " << v->a[0] << "; " << v->a[1] << "; " << v->a[2] << " ]" << endl;
newPrefix.append(" ");
PrintModel(submesh, newPrefix);
}
}
static void GenerateLevelOBJ( const RDI::LVL::Mesh* mesh, const string outFolder ) {
if (mesh == nullptr || outFolder.empty())
return;
unsigned long i;
try {
ofstream objFile("objOut/" + outFolder + "/mapmesh.obj");
for ( auto v : mesh->vertices ) {
objFile << "v " << v->a[0] << " " << v->a[1] << " " << v->a[2] << endl;
}
objFile << endl;
for ( i = 0; i < mesh->indices.size(); i += 3 ) {
objFile << "f " << mesh->indices[i]+1;
objFile << " " << mesh->indices[i+1]+1;
objFile << " " << mesh->indices[i+2]+1 << endl;
}
objFile << endl;
objFile.close();
} catch (exception const& ex) {
throw exception(ex);
}
}
static void GenerateModelOBJ( const RDI::HOB::Mesh* mesh, const string outFolder, const string* name ) {
if (mesh == nullptr || name == nullptr || outFolder.empty())
return;
unsigned long i, i_offset = 0;
try {
ofstream objFile("objOut/" + outFolder + "/" + (*name) + ".obj");
for ( auto submesh : mesh->subMeshs ) {
for ( auto v : submesh->vertices ) {
objFile << "v " << v->a[0] << " " << v->a[1] << " " << v->a[2] << endl;
}
objFile << endl;
for ( i = 0; i < submesh->indices.size(); i += 3 ) {
objFile << "f " << submesh->indices[i]+i_offset+1;
objFile << " " << submesh->indices[i+1]+i_offset+1;
objFile << " " << submesh->indices[i+2]+i_offset+1 << endl;
}
objFile << endl;
i_offset += submesh->vertices.size();
}
objFile.close();
} catch (exception const& ex) {
throw exception(ex);
}
}
#ifdef USE_MULTITHREADING
static void GenerateModelOBJThreaded() {
unsigned int i, successCnt;
deque<thread*> process;
const unsigned int maxThreads = thread::hardware_concurrency();
if (modelCollection.empty())
return;
for ( RDI::HOB* hobFile : modelCollection ) {
successCnt = 0;
cout << " Processing " << hobFile->getFullPath() << "..." << endl;
if (hobFile->isLoaded() && (hobFile->getObjectCount() > 0)) {
string parentPath = path(hobFile->getFullPath()).parent_path().filename().string();
filesystem::create_directories("./objOut/" + parentPath);
// Create initial threads for files processing
for ( i = 0; i < maxThreads; i++ ) {
if (i >= hobFile->getObjectCount())
break;
process.push_back(new thread(
GenerateModelOBJ,
hobFile->getMeshFromObject(i),
parentPath,
hobFile->getObjectName(i)
));
}
// Loop until all threads finished and no more file should be processed
while (true) {
process.front()->join();
//delete process.front();
process.pop_front();
++successCnt;
if (successCnt >= hobFile->getObjectCount())
break;
process.push_back(new thread(
GenerateModelOBJ,
hobFile->getMeshFromObject(successCnt),
parentPath,
hobFile->getObjectName(successCnt)
));
}
}
}
}
#endif
static void ProcessLevels() {
unsigned int invalid = 0;
// Build levels collection
levelCollection.clear();
for ( string lvlname : RDI::RDI_getLevelsName() ) {
auto filePath = boost::filesystem::path(lvlname);
levelCollection.push_back(RDI::RDI_getLevel(filePath.string()));
}
// Try to load level using low-level library
for ( RDI::LVL* lvlFile : levelCollection ) {
try {
lvlFile->Load();
if (lvlFile->getLevelMesh()->vertices.empty())
invalid++;
} catch (exception const &ex) {
cout << "Failed loading level file!" << endl;
}
}
cout << "Number of invalid memory allocation: " << invalid << endl;
// Launch level map OBJ file building
for ( RDI::LVL* lvlFile : levelCollection ) {
if (lvlFile->isLoaded()) {
filesystem::create_directories("./objOut/" + lvlFile->getFullPath());
cout << " " << lvlFile->getFullPath() << "...";
try {
GenerateLevelOBJ(lvlFile->getLevelMesh(), lvlFile->getFullPath());
cout << " OK" << endl;
} catch (exception const& ex) {
cout << " FAIL" << endl;
}
}
}
// Unload models
for ( RDI::LVL* lvlFile : levelCollection ) {
lvlFile->Dispose();
levelCollection.clear();
}
}
static void ProcessModels() {
unsigned int i,invalid = 0;
// Build models collection
modelCollection.clear();
for ( string mdname : RDI::RDI_getModelsName() ) {
auto filePath = boost::filesystem::path(mdname);
modelCollection.push_back(RDI::RDI_getModel(filePath.string()));
}
// Try to load models using low-level library
for ( RDI::HOB* hobFile : modelCollection ) {
try {
hobFile->Load();
for ( i = 0; i < hobFile->getObjectCount(); i++ ) {
if (hobFile->getMeshFromObject(i)->subMeshs.empty())
invalid++;
}
} catch (out_of_range const &ex) {
//cout << "Empty object" << endl;
}
}
cout << "Number of invalid memory allocation: " << invalid << endl;
// Launch threaded OBJ file building
//GenerateModelOBJThreaded();
//PrintModel(meshCollection.back());
for ( RDI::HOB* hobFile : modelCollection ) {
if (hobFile->isLoaded() && hobFile->getObjectCount()) {
string parentPath = boost::filesystem::path(hobFile->getFullPath()).string();
filesystem::create_directories("./objOut/" + hobFile->getFullPath());
for ( i = 0; i < hobFile->getObjectCount(); i++ ) {
cout << " " << hobFile->getFullPath() << "/" << *(hobFile->getObjectName(i)) << "...";
try {
GenerateModelOBJ(hobFile->getMeshFromObject(i), parentPath, hobFile->getObjectName(i));
cout << " OK" << endl;
} catch (exception const& ex) {
cout << " FAIL" << endl;
}
}
}
}
// Unload models
for ( RDI::HOB* hobFile : modelCollection ) {
hobFile->Dispose();
modelCollection.clear();
}
}
int main( int argc, char *argv[] ) {
unsigned int i;
boost::filesystem::path pathBuilder;
bool use_unicode = true;
chrono::_V2::system_clock::time_point t_start, t_end;
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif
cout << "RDIDebug tool started..." << endl;
// Initialize RDI (Erso and Krennic module) with folder provided in program
// argument, or the current directory by default
if ( argc > 2 ) {
use_unicode = atoi(argv[2]);
RDI::RDI_Init(argv[1]);
} else if (argc > 1) {
RDI::RDI_Init(argv[1]);
} else {
RDI::RDI_Init(".");
}
// Print details about legacy DATA.DAT file
printf("> Section found: %d\n", RDI::RDI_getSectionCount());
for ( i = 0; i < RDI::RDI_getSectionCount(); i++ ) {
cout << " -Section " << i << " name: " << RDI::RDI_getSectionName(i) << endl;
cout << " -Section " << i << " offset: 0x" << hex << uppercase << RDI::RDI_getSectionOffset(i) << dec << endl;
}
// Print DATA.DAT tree view with unicode "style"
cout << endl << "DATA.DAT files tree view:" << endl;
for ( i = 0; i < RDI::RDI_getSectionCount(); i++ ) {
cout << RDI::RDI_getSectionName(i) << ":" << endl;
pathBuilder.clear();
pathBuilder.concat(RDI::RDI_getSectionName(i));
PrintVirtualDirectoryContents(pathBuilder, use_unicode);
}
// Print game elements Krennic module found
cout << endl << "Legacy files processing result:";
cout << endl << "> Levels found: " << RDI::RDI_getLevelsName().size() << endl;
t_start = chrono::high_resolution_clock::now();
ProcessLevels();
t_end = chrono::high_resolution_clock::now();
cout << endl << "Levels processing in " << chrono::duration_cast<chrono::seconds>(t_end - t_start).count() << "s" << endl;
cout << endl << "> Models found: " << RDI::RDI_getModelsName().size() << endl;
t_start = chrono::high_resolution_clock::now();
ProcessModels();
t_end = chrono::high_resolution_clock::now();
cout << endl << "Models processing in " << chrono::duration_cast<chrono::seconds>(t_end - t_start).count() << "s" << endl;
cout << endl << "> Textures found: " << RDI::RDI_getTexturesName().size() << endl;
for ( std::string txname : RDI::RDI_getTexturesName() ) {
cout << " " << txname << endl;
}
cout << endl << "> Musics found: " << RDI::RDI_getMusicsName().size() << endl;
for ( std::string mname : RDI::RDI_getMusicsName() ) {
cout << " " << mname << endl;
}
// Release RDI library
RDI::RDI_CleanUp();
return 0;
}