/** * @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 #include #include #include #include #include #include #ifdef USE_MULTITHREADING #include #include #endif #include #include #include #ifdef _WIN32 #include #endif using namespace std; static vector levelCollection; static vector 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 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(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(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; }