RDI/tools/RDIDebug.cpp
2023-02-03 18:12:04 +01:00

309 lines
8.3 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
#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 boost::filesystem;
using namespace std;
static vector<RDI::HOB*> modelCollection;
static void PrintVirtualDirectoryContents( path p, bool unicode, string outPrefix = "" ) {
auto curDirElementsName = RDI::RDI_getDirectoryElements(p.string());
auto newPath = 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 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;
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();
}
#ifdef USE_MULTITHREADING
static void GenerateModelOBJThreaded() {
unsigned int i, successCnt;
deque<thread*> process;
const unsigned int maxThreads = thread::hardware_concurrency();
if (modelCollection.empty())
return;
#ifdef _WIN32
CreateDirectory(".\\objOut", NULL);
#else
mkdir("./objOut");
#endif
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();
replace(parentPath.begin(), parentPath.end(), '/', '\\');
#ifdef _WIN32
CreateDirectory((".\\objOut\\" + parentPath).c_str(), NULL);
#else
mkdir(("./objOut/" + parentPath).c_str());
#endif
// 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 ProcessModels() {
unsigned int i,invalid = 0;
// Build models collection
modelCollection.clear();
for ( string mdname : RDI::RDI_getModelsName() ) {
auto filePath = 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()) {
cout << "Memory allocation failure!" << endl;
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 = path(hobFile->getFullPath()).string();
replace(parentPath.begin(), parentPath.end(), '/', '\\');
filesystem::create_directories(".\\objOut\\" + hobFile->getFullPath());
for ( i = 0; i < hobFile->getObjectCount(); i++ ) {
cout << hobFile->getFullPath() << "/" << *(hobFile->getObjectName(i)) << "...";
GenerateModelOBJ(hobFile->getMeshFromObject(i), parentPath, hobFile->getObjectName(i));
cout << " OK" << endl;
}
}
}
// Unload models
for ( RDI::HOB* hobFile : modelCollection ) {
hobFile->Dispose();
modelCollection.clear();
}
}
int main( int argc, char *argv[] ) {
unsigned int i;
path pathBuilder;
bool use_unicode = true;
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif
cout << "RDIDebug tool - using RDI lib v" << RDI::RDI_getLibVersion() << endl << 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
//cout << "> Section found: " << RDI::RDI_getSectionCount() << endl;
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;
for ( std::string lname : RDI::RDI_getLevelsName() ) {
cout << " " << lname << endl;
}
cout << endl << "> Models found: " << RDI::RDI_getModelsName().size() << endl;
auto t_start = chrono::high_resolution_clock::now();
ProcessModels();
auto t_end = chrono::high_resolution_clock::now();
cout << "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;
}