HOB file handler prototype Mesh building should be moved to RSPModel lib Tree rendering unicode chars fix Updated version detection HOB file handler Debug HOB handler Pointer probably deallocated, need to check it Fix HOB loader and disposer Using HOB instance instead of static methods Review DLL support for MSVC build Vertices values calculation fix Added export obj method to debug tool Missed levels name separation Cleaned model processing Multithreading branch reported to later
307 lines
8.1 KiB
C++
307 lines
8.1 KiB
C++
/**
|
|
* @file RDIDebug.cpp
|
|
* @date 01/02/2023
|
|
* @author JackCarterSmith
|
|
* @copyright GPL-v3.0
|
|
* @brief Debug app to test functions of RDI library.
|
|
*
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
#ifdef USE_MULTITHREADING
|
|
#include <deque>
|
|
#include <thread>
|
|
#endif
|
|
#include <boost/filesystem.hpp>
|
|
#include <RDI.hpp>
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <sys/stat.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());
|
|
#ifdef _WIN32
|
|
CreateDirectory(".\\objOut", NULL);
|
|
#else
|
|
mkdir("./objOut");
|
|
#endif
|
|
|
|
for ( RDI::HOB* hobFile : modelCollection ) {
|
|
if (hobFile->getObjectCount()) {
|
|
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
|
|
|
|
for ( i = 0; i < hobFile->getObjectCount(); i++ ) {
|
|
cout << " Processing " << hobFile->getFullPath() << "..." << endl;
|
|
GenerateModelOBJ(hobFile->getMeshFromObject(i), parentPath, hobFile->getObjectName(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
ProcessModels();
|
|
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;
|
|
}
|