diff --git a/CMakeLists.txt b/CMakeLists.txt index 4bf1e10..df8c2fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,8 @@ # CMake requirement and general configuration -cmake_minimum_required(VERSION 3.12) -cmake_policy(VERSION 3.12) +cmake_minimum_required(VERSION 3.15) +cmake_policy(VERSION 3.15) set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}) if(DEFINED ENV{MS_COMPATIBLE}) set(CMAKE_GNUtoMS ON) # Enable compatibility level to exported libraries @@ -27,10 +27,10 @@ endif() set(RDI_LIB_NAME RDI) if(NOT MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++17 -static-libgcc -static-libstdc++") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Wall") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Wall /std:c++17") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Wall") endif() set(INSTALL_BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Installation directory for executables") @@ -43,13 +43,13 @@ option(RDI_STATIC "Build static lib" ON) option(BUILD_TOOLS "Build lib tools" ON) # Import needed packages and references their include path -find_package(RSPModel 2.2 REQUIRED) +find_package(RSPModel 2.3 REQUIRED) include_directories(${RSPModel_INCLUDE_DIR}) find_package(RSPTerrain 2.0 REQUIRED) include_directories(${RSPTerrain_INCLUDE_DIR}) find_package(RSPTexture 2.1 REQUIRED) include_directories(${RSPTexture_INCLUDE_DIR}) -find_package(Boost 1.80.0 EXACT REQUIRED) +find_package(Boost 1.81.0 REQUIRED) include_directories(${Boost_INCLUDE_DIR}) add_definitions(-DBOOST_ALL_NO_LIB) @@ -80,14 +80,22 @@ endif() # Declare the shared library instance if(RDI_SHARED) add_library(rdi-lib SHARED ${RDI_LIB_SOURCES}) - set_property(TARGET rdi-lib PROPERTY C_STANDARD 90) + set_property(TARGET rdi-lib PROPERTY C_STANDARD 17) set_property(TARGET rdi-lib PROPERTY CXX_STANDARD 17) + set_property(TARGET rdi-lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") set_target_properties(rdi-lib PROPERTIES VERSION 1.0.0) target_include_directories(rdi-lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) set_target_properties(rdi-lib PROPERTIES OUTPUT_NAME "${RDI_LIB_NAME}") + # As GNU-GCC have different runtime lib, we statically link them for this type of build + if (NOT MSVC) + target_link_libraries(rdi-lib PRIVATE + -static-libgcc + -static-libstdc++ + ) + endif() target_link_libraries(rdi-lib PRIVATE ${RSPModel_LIBRARIES} ${RSPTerrain_LIBRARIES} @@ -113,11 +121,20 @@ endif() # Declare the static library instance if(RDI_STATIC) add_library(rdi-libstatic STATIC ${RDI_LIB_SOURCES}) - set_property(TARGET rdi-libstatic PROPERTY C_STANDARD 90) + set_property(TARGET rdi-libstatic PROPERTY C_STANDARD 17) set_property(TARGET rdi-libstatic PROPERTY CXX_STANDARD 17) + set_property(TARGET rdi-libstatic PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") + set_target_properties(rdi-libstatic PROPERTIES VERSION 1.0.0) target_include_directories(rdi-libstatic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + # As GNU-GCC have different runtime lib, we statically link them for this type of build + if (NOT MSVC) + target_link_libraries(rdi-libstatic PRIVATE + -static-libgcc + -static-libstdc++ + ) + endif() target_link_libraries(rdi-libstatic PRIVATE ${RSPModel_LIBRARIES} ${RSPTerrain_LIBRARIES} @@ -158,17 +175,20 @@ if(BUILD_TOOLS) #add_executable(rdi-debug-tools ${RDI_TOOLS_SOURCES}) add_executable(rdi-debug-tools ./tools/RDIDebug.cpp) - set_property(TARGET rdi-debug-tools PROPERTY C_STANDARD 90) + set_property(TARGET rdi-debug-tools PROPERTY C_STANDARD 17) + set_property(TARGET rdi-debug-tools PROPERTY CXX_STANDARD 17) set_target_properties(rdi-debug-tools PROPERTIES OUTPUT_NAME "RDI-debug") list(APPEND RDI_TARGETS_LIST rdi-debug-tools) add_executable(erso-debug-tools ./tools/ErsoDebug.cpp) - set_property(TARGET erso-debug-tools PROPERTY C_STANDARD 90) + set_property(TARGET erso-debug-tools PROPERTY C_STANDARD 17) + set_property(TARGET erso-debug-tools PROPERTY CXX_STANDARD 17) set_target_properties(erso-debug-tools PROPERTIES OUTPUT_NAME "Erso-debug") list(APPEND RDI_TARGETS_LIST erso-debug-tools) add_executable(krennic-debug-tools ./tools/KrennicDebug.cpp) - set_property(TARGET krennic-debug-tools PROPERTY C_STANDARD 90) + set_property(TARGET krennic-debug-tools PROPERTY C_STANDARD 17) + set_property(TARGET krennic-debug-tools PROPERTY CXX_STANDARD 17) set_target_properties(krennic-debug-tools PROPERTIES OUTPUT_NAME "Krennic-debug") list(APPEND RDI_TARGETS_LIST krennic-debug-tools) @@ -180,6 +200,7 @@ if(BUILD_TOOLS) set_target_properties(krennic-debug-tools PROPERTIES IMPORT_PREFIX "lib") endif() + # Static libgcc and libstdc++ already linked in library if(RDI_SHARED) target_link_libraries(rdi-debug-tools PRIVATE rdi-lib ${Boost_LIBRARIES}) target_link_libraries(erso-debug-tools PRIVATE rdi-lib) diff --git a/conanfile.txt b/conanfile.txt index 1f3d10b..e12cdf7 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -1,8 +1,8 @@ [requires] -rspmodellib/2.2.0 +rspmodellib/2.3.0 rspterrainlib/2.0.4 rsptexturelib/2.1.0 -boost/1.80.0 +boost/1.81.0 [generators] cmake diff --git a/include/FileHandler/Generic.h b/include/FileHandler/Generic.h index bb1b1b9..59916bc 100644 --- a/include/FileHandler/Generic.h +++ b/include/FileHandler/Generic.h @@ -1,6 +1,6 @@ /** * @file GenericFile.h - * @date 23/09/2022 + * @date 01/02/2023 * @author JackCarterSmith * @copyright GPL-v3.0 * @brief Generic file object class. @@ -22,15 +22,38 @@ namespace DatFile { class GenericFile { public: - GenericFile( DatFile::DatFileEntryFile &hDat ); + // Default constructor + // Set the default class members and link against correct MEMFILE + GenericFile( DatFile::DatFileEntryFile* hDat ); virtual ~GenericFile(); + GenericFile(const GenericFile&) = default; + GenericFile& operator=(const GenericFile&) = default; + + GenericFile(GenericFile&&) = default; + GenericFile& operator=(GenericFile&&) = default; + + // Primary datas parsing/loading function + // This function must be called to load datas in memory and access to others function + void Load(); + bool isLoaded() const { return loaded; } + + // Free memory of object datas + // Should be called after using it + void Dispose(); + + std::string getFileName() const { return fileName; } + std::string getFullPath() const { return fullFilePath; } + std::string getFileExtension() const { return fileExtension; } MEMFILE get() { return pMemLoc; } protected: unsigned long size; - std::string fileName, fileExtension; + std::string fileName, fullFilePath, fileExtension; MEMFILE pMemLoc = nullptr; + +private: + bool loaded = false; }; } diff --git a/include/FileHandler/HMT.h b/include/FileHandler/HMT.h index 5c266a6..c0c41bc 100644 --- a/include/FileHandler/HMT.h +++ b/include/FileHandler/HMT.h @@ -17,7 +17,7 @@ namespace RDI { class HMT : public GenericFile { public: - HMT( DatFile::DatFileEntryFile &hDat ); + HMT( DatFile::DatFileEntryFile* hDat ); virtual ~HMT(); }; diff --git a/include/FileHandler/HOB.h b/include/FileHandler/HOB.h new file mode 100644 index 0000000..48f9ff2 --- /dev/null +++ b/include/FileHandler/HOB.h @@ -0,0 +1,120 @@ +/** + * @file HOB.h + * @date 31/01/2023 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief HOB file object class. + * + * Rogue data files use a specific (and size optimized) storage method of model. + * HOB handler parse these datas and store them in a more GPU-frienly BOOST-Lib + * type. + * + */ + +#include +#include +#include +#include +#include + + +#ifndef HOB_H_ +#define HOB_H_ + +#if defined(_MSC_VER) +# define RDI_ABI_EXPORT __declspec(dllexport) +# define RDI_ABI_IMPORT __declspec(dllimport) +#elif __GNUC__ >= 4 +# define RDI_ABI_EXPORT __attribute__ ((visibility("default"))) +# define RDI_ABI_IMPORT __attribute__ ((visibility("default"))) +#else +# define RDI_ABI_EXPORT +# define RDI_ABI_IMPORT +#endif + +#if defined(RDI_DLL) +# if defined(WIN32) +# if defined(RDI_DLLBUILD) +# define RDI_EXTERN RDI_ABI_EXPORT +# else +# define RDI_EXTERN RDI_ABI_IMPORT +# endif +# endif +#endif + +#ifndef RDI_EXTERN +# define RDI_EXTERN +#endif + +namespace RDI { + +class HOB final : public GenericFile { +public: + /* + struct Vertex { + Vertex() noexcept = default; + Vertex(float x, float y, float z) noexcept + : x(x),y(y),z(z) {} + Vertex(std::array vArray) noexcept + : x(vArray[0]),y(vArray[1]),z(vArray[2]) {} + + Vertex(const Vertex&) = default; + Vertex& operator=(const Vertex&) = default; + + Vertex(Vertex&&) = default; + Vertex& operator=(Vertex&&) = default; + + float x = 0.0f, y = 0.0f, z = 0.0f; + }; + */ + + struct Mesh { + // Empty vertices mesh should be considered as root/main mesh struct. + std::vector*> vertices; + std::vector indices; + + // Transform matrix not used for now, equal to identity matrix. + boost::qvm::mat transform; + //Material material; + + // Submesh are divided by material/texture + std::vector subMeshs; + }; + + /* -------------------------------------------------------------------------- */ + HOB( DatFile::DatFileEntryFile* hDat ); + ~HOB(); + + HOB(const HOB&) = default; + HOB& operator=(const HOB&) = default; + + HOB(HOB&&) = default; + HOB& operator=(HOB&&) = default; + + // Mandatory function from base class + RDI_EXTERN void Load(); + RDI_EXTERN void Dispose(); + + /* -------------------------------------------------------------------------- */ + RDI_EXTERN unsigned int getObjectCount() const; + RDI_EXTERN std::string* getObjectName( unsigned short index ) const; + + RDI_EXTERN HOB::Mesh* getMeshFromObject( unsigned short index ) const; + RDI_EXTERN HOB::Mesh* getMeshFromObject( std::string name ) const; + + //ObjectMaterials getMaterialFromObject( unsigned short index, unsigned short materialIndex ); + //ObjectMaterials getMaterialFromObject( std::string name, unsigned short materialIndex ); + //std::vector getMaterialsFromObject( unsigned short index ); + //std::vector getMaterialsFromObject( std::string name ); + + +private: + void* hobInstance = nullptr; + unsigned int objCnt = 0; + std::unordered_map objectMeshList; // Each object mesh ordered by index + +}; + +} + +#endif /* HOB_H_ */ diff --git a/include/RDI.hpp b/include/RDI.hpp index 28e02bd..a6a8368 100644 --- a/include/RDI.hpp +++ b/include/RDI.hpp @@ -1,6 +1,6 @@ /** * @file RDI.hpp - * @date 24/09/2022 + * @date 22/01/2023 * @author JackCarterSmith * @copyright GPL-v3.0 * @brief Rogue Data Interface library main entry file. @@ -10,6 +10,7 @@ #include #include #include "RDI_Datatypes.h" +#include "FileHandler/HOB.h" #ifndef RDI_HPP_ @@ -48,7 +49,7 @@ namespace RDI { * @brief Get the current library version. * @return String of the version. */ - RDI_EXTERN std::string RDI_getLibVersion( void ); + RDI_EXTERN const std::string RDI_getLibVersion( void ) noexcept; /** * @brief Allocate memory and create default structure to access datas. @@ -58,20 +59,26 @@ namespace RDI { */ RDI_EXTERN void RDI_Init( std::string roguePath ); + /* -------------------------------------------------------------------------- */ /* Erso specific methods */ - RDI_EXTERN unsigned char RDI_getSectionCount( void ); - RDI_EXTERN std::string RDI_getSectionName( unsigned char id ); - RDI_EXTERN unsigned int RDI_getSectionOffset( unsigned char id ); + RDI_EXTERN const unsigned char RDI_getSectionCount( void ); + RDI_EXTERN const std::string RDI_getSectionName( unsigned char id ); + RDI_EXTERN const unsigned int RDI_getSectionOffset( unsigned char id ); - RDI_EXTERN unsigned int RDI_getDirectoryElementCount( std::string path ); - RDI_EXTERN std::vector RDI_getDirectoryElements( std::string path ); - RDI_EXTERN bool RDI_isElementDirectory( std::string path ); + RDI_EXTERN const unsigned int RDI_getDirectoryElementCount( std::string path ); + RDI_EXTERN const std::vector RDI_getDirectoryElements( std::string path ); + RDI_EXTERN const bool RDI_isElementDirectory( std::string path ); + + /* -------------------------------------------------------------------------- */ /* Krennic specific methods */ - RDI_EXTERN std::vector RDI_getLevelsName( void ); - RDI_EXTERN std::vector RDI_getModelsName( void ); - RDI_EXTERN std::vector RDI_getTexturesName( void ); - RDI_EXTERN std::vector RDI_getMusicsName( void ); + RDI_EXTERN const std::vector RDI_getLevelsName( void ); + RDI_EXTERN const std::vector RDI_getModelsName( void ); + RDI_EXTERN const std::vector RDI_getTexturesName( void ); + RDI_EXTERN const std::vector RDI_getMusicsName( void ); + + RDI_EXTERN HOB* RDI_getModel( std::string modelName ); + /** * @brief Clean up global resources. diff --git a/src/Erso.hpp b/src/Erso.hpp index b49943c..fdb11e8 100644 --- a/src/Erso.hpp +++ b/src/Erso.hpp @@ -59,13 +59,14 @@ public: MEMFILE getRDat() const { return rDatPtr; } /* Workspace management */ - std::string getWorkingDirectory() { return workingDir; } + std::string getWorkingDirectory() const { return workingDir; } RDI_RESULT setWorkingDirectory( std::string newPath ); /* Generals informations getters */ unsigned char getDataSectionCount() const { return cDatSection; } std::string getDataSectionName( unsigned char id ) const; unsigned int getDataSectionOffset( unsigned char id ) const; + bool isUpdatedVersion() const { return (cDatSection > 2); } /* Tree manipulation/parse methods */ DatFile::DatFileEntry* getElement( boost::filesystem::path virtualDirPath ); diff --git a/src/Krennic.cpp b/src/Krennic.cpp index d5533ad..9704ba5 100644 --- a/src/Krennic.cpp +++ b/src/Krennic.cpp @@ -1,6 +1,6 @@ /** * @file Krennic.cpp - * @date 24/09/2022 + * @date 18/09/2023 * @author JackCarterSmith * @copyright GPL-v3.0 * @brief Main game assets parser and interface. @@ -10,15 +10,22 @@ * */ +#include +#include +#include #include #include #include #include +#include +#include +#include #include "Erso.hpp" #include "Krennic.hpp" using std::string; using std::vector; +using std::array; using namespace RDI::DatFile; @@ -40,32 +47,32 @@ struct PathDescriptor { }; Krennic::Krennic( Erso* pErso ) { - BuildLevelList(pErso); - BuildModelList(pErso); - BuildTextureList(pErso); - BuildMusicList(pErso); - BuildSampleList(pErso); + BuildLevelFileList(pErso); + BuildModelFileList(pErso); + BuildTextureFileList(pErso); + BuildMusicFileList(pErso); + BuildSampleFileList(pErso); } Krennic::~Krennic() {} -void Krennic::BuildLevelList( Erso* pErso ) { - const static vector legacyLvlPath = { +void Krennic::BuildLevelFileList( Erso* pErso ) { + const static array legacyLvlPath = { PathDescriptor("data/level", true) }; - listLevel.clear(); + listLevelsName.clear(); for (PathDescriptor legacyLvlSubpath : legacyLvlPath) { for (DatFileEntry* file : pErso->getElements(boost::filesystem::path(legacyLvlSubpath.path), "dat", legacyLvlSubpath.isRecursive)) { //TODO: level-class builder - listLevel.push_back(file->getPath()); + listLevelsName.push_back(file->getPath()); } } } -void Krennic::BuildModelList( Erso* pErso ) { - const static vector legacyModelPath = { +void Krennic::BuildModelFileList( Erso* pErso ) { + const static array legacyModelPath = { PathDescriptor("data", false), PathDescriptor("data/pl_crafts", false), PathDescriptor("data/reb_stuff", false), @@ -74,20 +81,36 @@ void Krennic::BuildModelList( Erso* pErso ) { PathDescriptor("data/level", true), PathDescriptor("data/frontend", true), PathDescriptor("data/dbg", false), + PathDescriptor("dbg_data", false) + }; + // Update version game legacy assets paths + const static array legacyModelPath2= { PathDescriptor("data2", false) }; - listModel.clear(); + listModels.clear(); + listModelsName.clear(); for (PathDescriptor legacyModelSubpath : legacyModelPath) { for (DatFileEntry* file : pErso->getElements(boost::filesystem::path(legacyModelSubpath.path), "_HOB", legacyModelSubpath.isRecursive)) { - //TODO: model-class builder - listModel.push_back(file->getPath()); + auto hobObj = new HOB(dynamic_cast(file)); + listModels.push_back(hobObj); + listModelsName.push_back(file->getPath()); + } + } + if (pErso->isUpdatedVersion()) { + for (PathDescriptor legacyModelSubpath2 : legacyModelPath2) { + for (DatFileEntry* file : pErso->getElements(boost::filesystem::path(legacyModelSubpath2.path), "_HOB", legacyModelSubpath2.isRecursive)) { + auto hobObj = new HOB(dynamic_cast(file)); + listModels.push_back(hobObj); + listModelsName.push_back(file->getPath()); + } } } } -void Krennic::BuildTextureList( Erso* pErso ) { - const static vector legacyTexturePath = { +void Krennic::BuildTextureFileList( Erso* pErso ) { + // Base game legacy assets paths + const static array legacyTexturePath = { PathDescriptor("data", false), PathDescriptor("data/pl_crafts", false), PathDescriptor("data/reb_stuff", false), @@ -96,34 +119,85 @@ void Krennic::BuildTextureList( Erso* pErso ) { PathDescriptor("data/level", true), PathDescriptor("data/frontend", true), PathDescriptor("data/dbg", false), + PathDescriptor("dbg_data", false) + }; + // Update version game legacy assets paths + const static array legacyTexturePath2= { PathDescriptor("data2", false) }; - listTexture.clear(); + listTexturesName.clear(); for (PathDescriptor legacyTextureSubpath : legacyTexturePath) { for (DatFileEntry* file : pErso->getElements(boost::filesystem::path(legacyTextureSubpath.path), "_HMT", legacyTextureSubpath.isRecursive)) { //TODO: texture-class builder - listTexture.push_back(file->getPath()); + listTexturesName.push_back(file->getPath()); + } + } + if (pErso->isUpdatedVersion()) { + for (PathDescriptor legacyTextureSubpath2 : legacyTexturePath2) { + for (DatFileEntry* file : pErso->getElements(boost::filesystem::path(legacyTextureSubpath2.path), "_HMT", legacyTextureSubpath2.isRecursive)) { + //TODO: texture-class builder + listTexturesName.push_back(file->getPath()); + } } } } -void Krennic::BuildMusicList( Erso* pErso ) { - const static vector legacyLvlPath = { +void Krennic::BuildMusicFileList( Erso* pErso ) { + const static array legacyLvlPath = { PathDescriptor("data/sound", false) }; - listMusic.clear(); + listMusicsName.clear(); for (PathDescriptor legacyMusicSubpath : legacyLvlPath) { for (DatFileEntry* file : pErso->getElements(boost::filesystem::path(legacyMusicSubpath.path), "_SNG", legacyMusicSubpath.isRecursive)) { //TODO: MusyX-Class builder - listMusic.push_back(file->getPath()); + listMusicsName.push_back(file->getPath()); } } } -void Krennic::BuildSampleList( Erso* pErso ) { +void Krennic::BuildSampleFileList( Erso* pErso ) { } +//void* Krennic::getLevel( std::string name ) {} + +HOB* Krennic::getModel( string name ) { + unsigned int i = 0; + + for (string fileName : listModelsName) { + if (fileName.find(name) != string::npos) { + return listModels.at(i); + } + i++; + } + + throw std::invalid_argument("Krennic can't found the requested model."); +} + +//HMT* Krennic::getTexture( std::string name ) {} + +//void* Krennic::getMusic( std::string name ) {} + +//void* Krennic::getSample( std::string name ) {} + + +void Krennic::DisposeLevels() {} + +void Krennic::DisposeModels() { + for (HOB* hobObj : listModels) { + if (hobObj->isLoaded()) + hobObj->Dispose(); + + delete hobObj; + } +} + +void Krennic::DisposeTextures() {} + +void Krennic::DisposeMusics() {} + +void Krennic::DisposeSamples() {} + } diff --git a/src/Krennic.hpp b/src/Krennic.hpp index 8b45d3d..20743a2 100644 --- a/src/Krennic.hpp +++ b/src/Krennic.hpp @@ -1,6 +1,6 @@ /** * @file Krennic.hpp - * @date 24/09/2022 + * @date 18/01/2023 * @author JackCarterSmith * @copyright GPL-v3.0 * @brief Main game assets parser and interface. @@ -15,6 +15,7 @@ #include #include #include +#include #include "Erso.hpp" @@ -33,11 +34,11 @@ public: * @return Array of filtered elements. */ ///@{ - std::vector getLevelsList() { return listLevel; } - std::vector getModelsList() { return listModel; } - std::vector getTexturesList() { return listTexture; } - std::vector getMusicsList() { return listMusic; } - std::vector getSamplesList() { return listSample; } + std::vector getLevelsList() { return listLevelsName; } + std::vector getModelsList() { return listModelsName; } + std::vector getTexturesList() { return listTexturesName; } + std::vector getMusicsList() { return listMusicsName; } + std::vector getSamplesList() { return listSamplesName; } ///@} /** @@ -47,7 +48,7 @@ public: */ ///@{ void* getLevel( std::string name ); - void* getModel( std::string name ); + HOB* getModel( std::string name ); HMT* getTexture( std::string name ); void* getMusic( std::string name ); void* getSample( std::string name ); @@ -64,17 +65,25 @@ public: GenericFile *getFile( boost::filesystem::path vPath ); private: - std::vector listLevel; - std::vector listModel; - std::vector listTexture; - std::vector listMusic; - std::vector listSample; + std::vector listModels; - void BuildLevelList( Erso* pErso ); - void BuildModelList( Erso* pErso ); - void BuildTextureList( Erso* pErso ); - void BuildMusicList( Erso* pErso ); - void BuildSampleList( Erso* pErso ); + std::vector listLevelsName; + std::vector listModelsName; + std::vector listTexturesName; + std::vector listMusicsName; + std::vector listSamplesName; + + void BuildLevelFileList( Erso* pErso ); + void BuildModelFileList( Erso* pErso ); + void BuildTextureFileList( Erso* pErso ); + void BuildMusicFileList( Erso* pErso ); + void BuildSampleFileList( Erso* pErso ); + + void DisposeLevels(); + void DisposeModels(); + void DisposeTextures(); + void DisposeMusics(); + void DisposeSamples(); }; } diff --git a/src/KrennicHandlerGeneric.cpp b/src/KrennicHandlerGeneric.cpp index 679bf3d..5b31aa2 100644 --- a/src/KrennicHandlerGeneric.cpp +++ b/src/KrennicHandlerGeneric.cpp @@ -1,6 +1,6 @@ /** * @file GenericFile.cpp - * @date 23/09/2022 + * @date 01/02/2023 * @author JackCarterSmith * @copyright GPL-v3.0 * @brief Generic file object class. @@ -16,13 +16,12 @@ using namespace RDI::DatFile; namespace RDI { -GenericFile::GenericFile( DatFile::DatFileEntryFile &hDat ) { - this->size = hDat.getSize(); - this->fileName = hDat.getName(); - this->fileExtension = ""; - this->pMemLoc = hDat.getDatas(); -} +GenericFile::GenericFile( DatFile::DatFileEntryFile* hDat ) + : size(hDat->getSize()), fileName(hDat->getName()), fullFilePath(hDat->getPath()), pMemLoc(hDat->getDatas()) {} GenericFile::~GenericFile() {} +void GenericFile::Load() { this->loaded = true; } +void GenericFile::Dispose() { this->loaded = false; } + } diff --git a/src/KrennicHandlerHMT.cpp b/src/KrennicHandlerHMT.cpp index 95c82d7..885e10f 100644 --- a/src/KrennicHandlerHMT.cpp +++ b/src/KrennicHandlerHMT.cpp @@ -15,7 +15,7 @@ using namespace RDI::DatFile; namespace RDI { -HMT::HMT( DatFile::DatFileEntryFile &hDat ): GenericFile( hDat ) { +HMT::HMT( DatFile::DatFileEntryFile* hDat ): GenericFile( hDat ) { this->fileExtension = "hmt"; } diff --git a/src/KrennicHandlerHOB.cpp b/src/KrennicHandlerHOB.cpp new file mode 100644 index 0000000..ba9f959 --- /dev/null +++ b/src/KrennicHandlerHOB.cpp @@ -0,0 +1,189 @@ +/** + * @file KrennicHandlerHOB.cpp + * @date 19/01/2023 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief HOB file object class. + * + */ + +#include +#include +#include +#include +#include +#include "DatFileEntry.hpp" +#include +#include + +using std::runtime_error; +using std::out_of_range; +using std::invalid_argument; +using std::string; +using namespace RDI::DatFile; +using namespace boost::qvm; + + +namespace RDI { + +HOB::Mesh* BuildObjectMesh( const T_RSPMODEL_OBJECT* hobPtr ); +HOB::Mesh* FillMesh( const T_RSPMODEL_OBJ_PARTS* objPtr ); + + +/* -------------------------------------------------------------------------- */ +HOB::HOB( DatFileEntryFile* hDat ) + : GenericFile(hDat) { + this->fileExtension = "hob"; +} + +HOB::~HOB() { + Dispose(); +} + + +/* -------------------------------------------------------------------------- */ +void HOB::Load() { + unsigned int i; + RSPMODEL_PARAMETERS rspParams; + unsigned short result = RSPLIB_SUCCESS; + + if (this->isLoaded()) return; + + this->hobInstance = new T_RSPMODEL_HOB{}; + auto hobInstancePtr = static_cast(this->hobInstance); + rspParams.raw = 0; + result = RSPModel_processHOBFileMemory(hobInstancePtr, this->pMemLoc, this->size, rspParams); + if (result != RSPLIB_SUCCESS) { + RSPModel_freeHOB(static_cast(this->hobInstance)); + delete static_cast(this->hobInstance); + + if (result == RSPLIB_ERROR_GENERIC) + throw out_of_range("HOB handler tried to process empty file."); + else + throw runtime_error("HOB handler processing failed with error: " + std::to_string(result)); + } + + this->objCnt = hobInstancePtr->obj_count; + this->objectMeshList.clear(); + for ( i = 0; i < this->objCnt; i++ ) + this->objectMeshList[string(hobInstancePtr->objects[i].name)] = BuildObjectMesh(hobInstancePtr->objects + i); + + RSPModel_freeHOB(static_cast(this->hobInstance)); + + // Must be called last to set loaded state + GenericFile::Load(); +} + +void HOB::Dispose() { + if (!this->isLoaded()) return; + + for ( const auto& [objName, objMesh] : this->objectMeshList ) { + for ( auto v : objMesh->vertices) + delete v; + + for ( auto sm : objMesh->subMeshs) + delete sm; + + delete objMesh; + } + + this->objectMeshList.clear(); + delete static_cast(this->hobInstance); + + // Must be called last to unset loaded state + GenericFile::Dispose(); +} + + +/* -------------------------------------------------------------------------- */ +unsigned int HOB::getObjectCount() const { + if (this->isLoaded()) + return this->objCnt; + + return RSPModel_getHOBFileMemObjCount(this->pMemLoc); +} + +std::string* HOB::getObjectName( unsigned short index ) const { + if (!this->isLoaded()) + throw runtime_error("HOB handler has not been loaded prior to function calling."); + + if (index >= this->objCnt) + throw out_of_range("HOB handler object index out of range!"); + + auto entry = objectMeshList.begin(); + std::advance(entry, index); + return new string(entry->first); +} + +HOB::Mesh* HOB::getMeshFromObject( unsigned short index ) const { + if (!this->isLoaded()) + throw runtime_error("HOB handler has not been loaded prior to function calling."); + + if (index >= this->objCnt) + throw out_of_range("HOB handler object index out of range!"); + + auto entry = objectMeshList.begin(); + std::advance(entry, index); + return entry->second; +} + +HOB::Mesh* HOB::getMeshFromObject( string name ) const { + if (!this->isLoaded()) + throw runtime_error("HOB handler has not been loaded prior to function calling."); + + for ( const auto& [objName, objMesh] : this->objectMeshList ) + if (objName == name) return objMesh; + + throw invalid_argument("HOB handler can't find requested object name."); +} + + +/* -------------------------------------------------------------------------- */ +HOB::Mesh* BuildObjectMesh( const T_RSPMODEL_OBJECT* hobPtr ) { + unsigned int i; + auto meshObj = new HOB::Mesh(); + + meshObj->indices.clear(); + meshObj->vertices.clear(); + meshObj->subMeshs.clear(); + + meshObj->transform = identity_mat(); + for (i = 0; i < hobPtr->object_part_count; i++) + meshObj->subMeshs.push_back(FillMesh(hobPtr->object_parts + i)); + + return meshObj; +} + +HOB::Mesh* FillMesh( const T_RSPMODEL_OBJ_PARTS* objPtr ) { + unsigned int i; + auto meshObj = new HOB::Mesh(); + + meshObj->indices.clear(); + meshObj->vertices.clear(); + meshObj->subMeshs.clear(); + + //meshObj->transform = mat(); + meshObj->transform = identity_mat(); + for (i = 0; i < objPtr->vertex_count; i++) { + auto v = new vec(); + v->a[0] = static_cast(-(objPtr->vertices[i].x)) / 1024.0; + v->a[1] = static_cast(-(objPtr->vertices[i].y)) / 1024.0; + v->a[2] = static_cast((objPtr->vertices[i].z)) / 1024.0; + meshObj->vertices.push_back(v); + } + for (i = 0; i < objPtr->face_count; i++) { + meshObj->indices.push_back(static_cast(objPtr->faces[i].indices[0])); + meshObj->indices.push_back(static_cast(objPtr->faces[i].indices[1])); + meshObj->indices.push_back(static_cast(objPtr->faces[i].indices[2])); + + if (objPtr->faces[i].flags_bits.fIsQuad) { + meshObj->indices.push_back(static_cast(objPtr->faces[i].indices[2])); + meshObj->indices.push_back(static_cast(objPtr->faces[i].indices[3])); + meshObj->indices.push_back(static_cast(objPtr->faces[i].indices[0])); + } + } + + return meshObj; +} + +} diff --git a/src/RDI.cpp b/src/RDI.cpp index 5940992..5f138e8 100644 --- a/src/RDI.cpp +++ b/src/RDI.cpp @@ -1,6 +1,6 @@ /** * @file RDI.cpp - * @date 24/09/2022 + * @date 22/01/2023 * @author JackCarterSmith * @copyright GPL-v3.0 * @brief Rogue Data Interface library main entry abstraction file. @@ -15,10 +15,13 @@ #include #include #include +#include +#include // Only for version getting, no processing. #include "config.h" #include "Erso.hpp" #include "DatFileEntry.hpp" #include "Krennic.hpp" +#include #include "RDI.hpp" using namespace RDI::DatFile; @@ -34,12 +37,12 @@ static RDI::Krennic *KrennicModule = nullptr; /* * Libs interface */ -std::string RDI::RDI_getLibVersion() { return PRG_VERSION; } +const std::string RDI::RDI_getLibVersion() noexcept { return PRG_VERSION; } void RDI::RDI_Init( std::string roguePath ) { -#ifdef DEBUG - std::cout << std::endl << "Running RDI v" << RDI_getLibVersion() << std::endl; - std::cout << "> RSPModelLib v" << "N/A" << std::endl; +#ifndef NDEBUG + std::cout << std::endl << "Running RDI v" << std::string(RDI::RDI_getLibVersion()) << std::endl; + std::cout << "> RSPModelLib v" << std::string(RSPModel_getVersion()) << std::endl; std::cout << "> RSPTerrainLib v" << "N/A" << std::endl; std::cout << "> RSPTextureLib v" << "N/A" << std::endl; #endif @@ -47,7 +50,7 @@ void RDI::RDI_Init( std::string roguePath ) { // Create new instance of Erso module // Load and extract datas files from DATA.DAT if (ErsoModule == nullptr) { -#ifdef DEBUG +#ifndef NDEBUG std::cout << "[RDI][DBG] Loading Erso module..." << std::endl; #endif ErsoModule = new RDI::Erso(roguePath); @@ -56,7 +59,7 @@ void RDI::RDI_Init( std::string roguePath ) { // Create new instance of Krennic module // Process datas from Erso and build new dataset to be used outside this library if (ErsoModule != nullptr && KrennicModule == nullptr) { -#ifdef DEBUG +#ifndef NDEBUG std::cout << "[RDI][DBG] Loading Krennic module..." << std::endl; #endif KrennicModule = new RDI::Krennic(ErsoModule); @@ -68,23 +71,23 @@ void RDI::RDI_Init( std::string roguePath ) { -unsigned char RDI::RDI_getSectionCount() { +const unsigned char RDI::RDI_getSectionCount() { if (ErsoModule == nullptr) return 0; else return ErsoModule->getDataSectionCount(); } -std::string RDI::RDI_getSectionName( unsigned char id ) { +const std::string RDI::RDI_getSectionName( unsigned char id ) { if (ErsoModule == nullptr) return ""; else return ErsoModule->getDataSectionName(id); } -unsigned int RDI::RDI_getSectionOffset( unsigned char id ) { +const unsigned int RDI::RDI_getSectionOffset( unsigned char id ) { if (ErsoModule == nullptr) return 0; else return ErsoModule->getDataSectionOffset(id); } -unsigned int RDI::RDI_getDirectoryElementCount( std::string path ) { +const unsigned int RDI::RDI_getDirectoryElementCount( std::string path ) { DatFileEntryDirectory* result = nullptr; if (path.empty()) return 0; @@ -95,7 +98,7 @@ unsigned int RDI::RDI_getDirectoryElementCount( std::string path ) { else return result->getSize(); } -std::vector RDI::RDI_getDirectoryElements( std::string path ) { +const std::vector RDI::RDI_getDirectoryElements( std::string path ) { DatFileEntryDirectory* de = nullptr; std::vector elementsNameArray; elementsNameArray.clear(); @@ -112,7 +115,7 @@ std::vector RDI::RDI_getDirectoryElements( std::string path ) { return elementsNameArray; } -bool RDI::RDI_isElementDirectory( std::string path ) { +const bool RDI::RDI_isElementDirectory( std::string path ) { DatFileEntry* result = nullptr; if (path.empty()) return false; @@ -124,22 +127,33 @@ bool RDI::RDI_isElementDirectory( std::string path ) { } -std::vector RDI::RDI_getLevelsName( void ) { +/* -------------------------------------------------------------------------- */ +const std::vector RDI::RDI_getLevelsName( void ) { return KrennicModule->getLevelsList(); } -std::vector RDI::RDI_getModelsName( void ) { +const std::vector RDI::RDI_getModelsName( void ) { return KrennicModule->getModelsList(); } -std::vector RDI::RDI_getTexturesName( void ) { +const std::vector RDI::RDI_getTexturesName( void ) { return KrennicModule->getTexturesList(); } -std::vector RDI::RDI_getMusicsName( void ) { +const std::vector RDI::RDI_getMusicsName( void ) { return KrennicModule->getMusicsList(); } +RDI::HOB* RDI::RDI_getModel( std::string modelName ) { + try { + return KrennicModule->getModel(modelName); + } + catch (std::invalid_argument const &ex) { // Can't find model, incorrect name? + throw std::invalid_argument(ex); + } +} + +/* -------------------------------------------------------------------------- */ void RDI::RDI_CleanUp(){ if (KrennicModule) delete KrennicModule; if (ErsoModule) delete ErsoModule; diff --git a/tools/RDIDebug.cpp b/tools/RDIDebug.cpp index 621c743..826417d 100644 --- a/tools/RDIDebug.cpp +++ b/tools/RDIDebug.cpp @@ -1,6 +1,6 @@ /** * @file RDIDebug.cpp - * @date 12/01/2023 + * @date 01/02/2023 * @author JackCarterSmith * @copyright GPL-v3.0 * @brief Debug app to test functions of RDI library. @@ -8,31 +8,249 @@ */ #include +#include #include +#include +#include +#include +#ifdef USE_MULTITHREADING +#include +#include +#endif #include #include #ifdef _WIN32 #include +#else +#include #endif using namespace boost::filesystem; using namespace std; -void PrintVirtualDirectoryContents( path, string, bool ); +static vector 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 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; - string prefix; bool use_unicode = true; #ifdef _WIN32 SetConsoleOutputCP(CP_UTF8); #endif - cout << "Using RDI lib v" << RDI::RDI_getLibVersion() << endl << endl; + 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 @@ -46,7 +264,8 @@ int main( int argc, char *argv[] ) { } // Print details about legacy DATA.DAT file - cout << "> Section found: " << RDI::RDI_getSectionCount() << endl; + //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; @@ -60,7 +279,7 @@ int main( int argc, char *argv[] ) { pathBuilder.clear(); pathBuilder.concat(RDI::RDI_getSectionName(i)); - PrintVirtualDirectoryContents(pathBuilder, prefix, use_unicode); + PrintVirtualDirectoryContents(pathBuilder, use_unicode); } // Print game elements Krennic module found @@ -70,9 +289,7 @@ int main( int argc, char *argv[] ) { cout << " " << lname << endl; } cout << endl << "> Models found: " << RDI::RDI_getModelsName().size() << endl; - for ( std::string mdname : RDI::RDI_getModelsName() ) { - cout << " " << mdname << endl; - } + ProcessModels(); cout << endl << "> Textures found: " << RDI::RDI_getTexturesName().size() << endl; for ( std::string txname : RDI::RDI_getTexturesName() ) { cout << " " << txname << endl; @@ -87,37 +304,3 @@ int main( int argc, char *argv[] ) { return 0; } - -void PrintVirtualDirectoryContents( path p, string outPrefix, bool unicode ) { - 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) { - cout << outPrefix << "\u251C " << eName << endl; - newOutPrefix.append("\u2502 "); - } else { - cout << outPrefix << "\\ " << eName << endl; - newOutPrefix.append("| "); - } - PrintVirtualDirectoryContents(newPath, newOutPrefix, unicode); - } else { - if (unicode) { - if (*--curDirElementsName.end() == eName) - cout << newOutPrefix << "\u2514 " << eName << endl; - else - cout << newOutPrefix << "\u251C " << eName << endl; - } else { - cout << newOutPrefix << "+ " << eName << endl; - } - } - } -}