diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f61fb5..df8c2fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,26 +7,14 @@ # 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 endif() -# Import needed packages and references their include path -find_package(RSPModel 2.2 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) -include_directories(${Boost_INCLUDE_DIR}) -add_definitions(-DBOOST_ALL_NO_LIB) - - # Project definition if(DEFINED ENV{CI}) # Jenkins CI integration mode project(rdi VERSION $ENV{CI_VERSION}.$ENV{CI_BUILD_NUMBER} DESCRIPTION "Rogue Data Interface" LANGUAGES C CXX) @@ -37,8 +25,13 @@ else() # Standalone project mode, should not be used for release. endif() #set(RDI_LIB_NAME RDI${PROJECT_VERSION_MAJOR}${PROJECT_VERSION_MINOR}) set(RDI_LIB_NAME RDI) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") +if(NOT MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") + 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") +endif() set(INSTALL_BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Installation directory for executables") set(INSTALL_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Installation directory for libraries") @@ -49,6 +42,17 @@ option(RDI_SHARED "Build shared lib" ON) 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.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.81.0 REQUIRED) +include_directories(${Boost_INCLUDE_DIR}) +add_definitions(-DBOOST_ALL_NO_LIB) + # Push compile infos to source configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/src/config.h @ONLY) @@ -65,7 +69,7 @@ unset(RDI_TARGETS_LIST) # Define src/headers files file(GLOB_RECURSE RDI_LIB_SOURCES ./src/*.cpp ./src/*.c) source_group("Source Files" FILES ${RDI_LIB_SOURCES}) -file(GLOB RSP_LIB_PUBLIC_HRDS ./include/*.h) +file(GLOB_RECURSE RSP_LIB_PUBLIC_HRDS ./include/*.hpp ./include/*.h) # Building instructions for RSP-Texture library @@ -76,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} @@ -109,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} @@ -149,24 +170,45 @@ endif() if(BUILD_TOOLS) # Add lib debug tool to buildchain - file(GLOB_RECURSE RDI_TOOLS_SOURCES ./tools/*.cpp ./tools/*.c) - source_group("Tools src files" FILES ${RDI_TOOLS_SOURCES}) + #file(GLOB_RECURSE RDI_TOOLS_SOURCES ./tools/*.cpp ./tools/*.c) + #source_group("Tools src files" FILES ${RDI_TOOLS_SOURCES}) - add_executable(rdi-debug-tools ${RDI_TOOLS_SOURCES}) - set_property(TARGET rdi-debug-tools PROPERTY C_STANDARD 90) + #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 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 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 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) + if(MSVC) # msvc does not append 'lib' - do it here to have consistent name set_target_properties(rdi-debug-tools PROPERTIES IMPORT_PREFIX "lib") + set_target_properties(erso-debug-tools PROPERTIES IMPORT_PREFIX "lib") + 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) + target_link_libraries(rdi-debug-tools PRIVATE rdi-lib ${Boost_LIBRARIES}) + target_link_libraries(erso-debug-tools PRIVATE rdi-lib) + target_link_libraries(krennic-debug-tools PRIVATE rdi-lib) elseif(RDI_STATIC) - target_link_libraries(rdi-debug-tools PRIVATE rdi-libstatic) + target_link_libraries(rdi-debug-tools PRIVATE rdi-libstatic ${Boost_LIBRARIES}) + target_link_libraries(erso-debug-tools PRIVATE rdi-libstatic) + target_link_libraries(krennic-debug-tools PRIVATE rdi-libstatic) endif() endif() 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 new file mode 100644 index 0000000..59916bc --- /dev/null +++ b/include/FileHandler/Generic.h @@ -0,0 +1,61 @@ +/** + * @file GenericFile.h + * @date 01/02/2023 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief Generic file object class. + * + */ + +#include "../RDI_Datatypes.h" +#include + + +#ifndef GENERICFILE_H_ +#define GENERICFILE_H_ + +namespace RDI { + +namespace DatFile { + class DatFileEntryFile; +} + +class GenericFile { +public: + // 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, fullFilePath, fileExtension; + MEMFILE pMemLoc = nullptr; + +private: + bool loaded = false; +}; + +} + +#endif /* GENERICFILE_H_ */ diff --git a/src/datfiles/HMT.h b/include/FileHandler/HMT.h similarity index 53% rename from src/datfiles/HMT.h rename to include/FileHandler/HMT.h index c6c8cb7..c0c41bc 100644 --- a/src/datfiles/HMT.h +++ b/include/FileHandler/HMT.h @@ -7,17 +7,20 @@ * */ -#ifndef SRC_DATFILES_HMT_H_ -#define SRC_DATFILES_HMT_H_ +#include + + +#ifndef HMT_H_ +#define HMT_H_ namespace RDI { -class HMT { +class HMT : public GenericFile { public: - HMT(); + HMT( DatFile::DatFileEntryFile* hDat ); virtual ~HMT(); }; -} /* namespace RDI */ +} -#endif /* SRC_DATFILES_HMT_H_ */ +#endif /* HMT_H_ */ 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.h b/include/RDI.h deleted file mode 100644 index 4746737..0000000 --- a/include/RDI.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @file RDI.h - * @date 15/09/2022 - * @author JackCarterSmith - * @copyright GPL-v3.0 - * @brief Rogue Data Interface library main entry file. - * - */ - -#include "RDI_Datatypes.h" - - -#ifndef RDI_H_ -#define RDI_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 { - - /** - * @brief Get the current library version. - * @return String of the version. - */ - RDI_EXTERN std::string getLibVersion( void ); - - /** - * @brief Create a new Rogue Data file instance. - * @details Try to open DATA.DAT file at location specified by roguePath. - * If file can be opened, it's mapped in memory and process files list. - * - * @param[in] roguePath Path to DATA.DAT and DATA.HDR location. - * - * @return Handler of RogueData file, should be used with other function of this lib. - */ - RDI_EXTERN void CreateRDatHandler( std::string roguePath ); - - RDI_EXTERN unsigned char getSectionCount( void ); - RDI_EXTERN std::string getSectionName( unsigned char id ); - RDI_EXTERN unsigned int getSectionOffset( unsigned char id ); - - RDI_EXTERN unsigned int getDirectoryElementCount( std::string path ); - RDI_EXTERN std::vector getDirectoryElements( std::string path ); - RDI_EXTERN bool isElementDirectory( std::string path ); - - /** - * @brief Clean up global resources. - */ - RDI_EXTERN void DestroyRDatHandler( void ); - -} - -#endif /* RDI_H_ */ diff --git a/include/RDI.hpp b/include/RDI.hpp new file mode 100644 index 0000000..a6a8368 --- /dev/null +++ b/include/RDI.hpp @@ -0,0 +1,89 @@ +/** + * @file RDI.hpp + * @date 22/01/2023 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief Rogue Data Interface library main entry file. + * + */ + +#include +#include +#include "RDI_Datatypes.h" +#include "FileHandler/HOB.h" + + +#ifndef RDI_HPP_ +#define RDI_HPP_ + + +#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 { + + /** + * @brief Get the current library version. + * @return String of the version. + */ + RDI_EXTERN const std::string RDI_getLibVersion( void ) noexcept; + + /** + * @brief Allocate memory and create default structure to access datas. + * @details Try to open DATA.DAT file at location specified by roguePath. + * If file can be opened, it's mapped in memory and process files list. + * @param[in] roguePath Path to DATA.DAT and DATA.HDR location. + */ + RDI_EXTERN void RDI_Init( std::string roguePath ); + + /* -------------------------------------------------------------------------- */ + /* Erso specific methods */ + 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 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 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. + */ + RDI_EXTERN void RDI_CleanUp( void ); +} + +#endif /* RDI_HPP_ */ diff --git a/src/DatEntry.cpp b/src/DatEntry.cpp deleted file mode 100644 index 354c21a..0000000 --- a/src/DatEntry.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @file DatEntry.cpp - * @date 20/09/2022 - * @author JackCarterSmith - * @copyright GPL-v3.0 - * @brief Data file entry descriptor class. - * - */ - -#include "DatEntry.h" - - -namespace RDI { - -FileEntry::FileEntry( std::string name, DAT_FILE_FLAGS fFlags, unsigned int size, MEMFILE fPtr ) { - this->name = name; - this->size = size; - this->fFlags.raw = fFlags.raw; - this->fileMemPtr = fPtr; -} - -FileEntry::~FileEntry() {} - -/*std::string FileEntry::toString() { - -}*/ - - -DirectoryEntry::DirectoryEntry( std::string name ) { - this->name = name; - this->fFlags.raw = 0; - this->rootDir = true; - vSubFiles = new std::vector; -} - -DirectoryEntry::DirectoryEntry( std::string name, DAT_FILE_FLAGS fFlags ) { - this->name = name; - this->fFlags.raw = fFlags.raw; - this->rootDir = false; - vSubFiles = new std::vector; -} - -DirectoryEntry::DirectoryEntry( std::string name, DAT_FILE_FLAGS fFlags, DatEntry* hFileEntry ) { - this->name = name; - this->fFlags.raw = fFlags.raw; - vSubFiles = new std::vector; - AddEntry(hFileEntry); -} - -DirectoryEntry::~DirectoryEntry() { - for ( DatEntry* e : *vSubFiles ) - delete e; - delete vSubFiles; -} - -/*std::string DirectoryEntry::toString() { - -}*/ - -void DirectoryEntry::ClearLinkedFiles() { - vSubFiles->clear(); -} - -void DirectoryEntry::AddEntry( DatEntry* hFileEntry ) { - vSubFiles->push_back(hFileEntry); -} - -} diff --git a/src/DatEntry.h b/src/DatEntry.h deleted file mode 100644 index 119ed75..0000000 --- a/src/DatEntry.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * @file DatEntry.h - * @date 20/09/2022 - * @author JackCarterSmith - * @copyright GPL-v3.0 - * @brief Data file entry descriptor class. - * - */ - -#include "RDI_Datatypes.h" -#include -#include - - -#ifndef DATENTRY_H_ -#define DATENTRY_H_ - - -namespace RDI { - -typedef union u_file_flags { - struct { - // LSB - unsigned short unknown0:1; - unsigned short isFile:1; - unsigned short unknown1:5; - unsigned short isDirectory:1; - unsigned short unknown2:8; - // MSB - }; - unsigned short raw; -} DAT_FILE_FLAGS; - - - -class DatEntry { -public: - virtual ~DatEntry() = 0; - virtual bool isDirectory() = 0; - virtual bool isRootDirectory() = 0; - //virtual std::string toString() = 0; - virtual unsigned int getSize() = 0; - - std::string getName() { return name; }; - -protected: - std::string name; - DAT_FILE_FLAGS fFlags; -}; - -inline DatEntry::~DatEntry() {} - -class FileEntry : public DatEntry { -public: - FileEntry( std::string name, DAT_FILE_FLAGS fFlags, unsigned int size, MEMFILE fPtr ); - ~FileEntry(); - - MEMFILE getDatas() { return fileMemPtr; } - - //std::string toString(); - bool isDirectory() { return false; } - bool isRootDirectory() { return false; } - unsigned int getSize() { return size; } - -private: - MEMFILE fileMemPtr; - unsigned int size; -}; - - -class DirectoryEntry : public DatEntry { -public: - DirectoryEntry( std::string name ); - DirectoryEntry( std::string name, DAT_FILE_FLAGS fFlags ); - DirectoryEntry( std::string name, DAT_FILE_FLAGS fFlags, DatEntry* hFileEntry ); - ~DirectoryEntry(); - - void ClearLinkedFiles(); - void AddEntry( DatEntry* hFileEntry ); - std::vector getFiles() { return *vSubFiles; } - - //std::string toString(); - bool isDirectory() { return true; } - bool isRootDirectory() { return rootDir; } - unsigned int getSize() { return vSubFiles->size(); } - -private: - bool rootDir; - std::vector *vSubFiles; -}; - -} - -#endif /* DATENTRY_H_ */ diff --git a/src/DatFileEntry.cpp b/src/DatFileEntry.cpp new file mode 100644 index 0000000..e979532 --- /dev/null +++ b/src/DatFileEntry.cpp @@ -0,0 +1,59 @@ +/** + * @file DatFileEntry.cpp + * @date 12/01/2023 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief Data file entry descriptor objects helper class. + * + */ + +#include +#include +#include +#include "DatFileEntry.hpp" + +using std::string; +using std::vector; + + +namespace RDI { +namespace DatFile { + +DatFileEntryFile::DatFileEntryFile( string name, string path, DAT_FILE_FLAGS fFlags, unsigned int size, MEMFILE fPtr ) + : DatFileEntry::DatFileEntry(name, path, fFlags), size(size), fileMemPtr(fPtr) {} + +DatFileEntryFile::~DatFileEntryFile() {} + + +DatFileEntryDirectory::DatFileEntryDirectory( string name, string path ) + : DatFileEntry::DatFileEntry(name, path), rootDir(true) { + vSubFiles = new vector; +} + +DatFileEntryDirectory::DatFileEntryDirectory( string name, string path, DAT_FILE_FLAGS fFlags ) + : DatFileEntry::DatFileEntry(name, path, fFlags), rootDir(false) { + vSubFiles = new vector; +} + +DatFileEntryDirectory::DatFileEntryDirectory( string name, string path, DAT_FILE_FLAGS fFlags, DatFileEntry* hFileEntry ) + : DatFileEntry::DatFileEntry(name, path, fFlags), rootDir(false) { + vSubFiles = new vector; + AddEntry(hFileEntry); +} + +DatFileEntryDirectory::~DatFileEntryDirectory() { + for ( DatFileEntry* e : *vSubFiles ) + delete e; + delete vSubFiles; +} + +void DatFileEntryDirectory::ClearLinkedFiles() { + vSubFiles->clear(); +} + +void DatFileEntryDirectory::AddEntry( DatFileEntry* hFileEntry ) { + vSubFiles->push_back(hFileEntry); +} + +} +} diff --git a/src/DatFileEntry.hpp b/src/DatFileEntry.hpp new file mode 100644 index 0000000..f6f8447 --- /dev/null +++ b/src/DatFileEntry.hpp @@ -0,0 +1,106 @@ +/** + * @file DatFileEntry.hpp + * @date 12/01/2023 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief Data file entry descriptor objects helper class. + * + * DATA and BUNDLE file store in different location file's header and + * file's data. This helper class it's used to provide a more convenient + * access to the corresponding file inside memory (after Erso module's + * initialization). + * + */ + +#include +#include +#include + + +#ifndef ERSOENTRY_HPP_ +#define ERSOENTRY_HPP_ + + +namespace RDI { +namespace DatFile { + +typedef union u_file_flags { + struct { + // LSB + unsigned short unknown0:1; + unsigned short isFile:1; + unsigned short unknown1:5; + unsigned short isDirectory:1; + unsigned short unknown2:8; + // MSB + }; + unsigned short raw; +} DAT_FILE_FLAGS; + + + +class DatFileEntry { +public: + virtual ~DatFileEntry() = 0; + virtual bool isDirectory() = 0; + virtual bool isRootDirectory() = 0; + virtual unsigned int getSize() = 0; + + std::string getName() { return name; } + std::string getPath() { return virtualAbsPath; } + +protected: + DatFileEntry( std::string name, std::string path ) : name(name), virtualAbsPath(path) { + this->fFlags.raw = 0; + } + DatFileEntry( std::string name, std::string path, DAT_FILE_FLAGS fFlags ) + : name(name), virtualAbsPath(path), fFlags(fFlags) {} + + std::string name; + std::string virtualAbsPath; + DAT_FILE_FLAGS fFlags; +}; + +inline DatFileEntry::~DatFileEntry() {} + +class DatFileEntryFile : public DatFileEntry { +public: + DatFileEntryFile( std::string name, std::string path, DAT_FILE_FLAGS fFlags, unsigned int size, MEMFILE fPtr ); + ~DatFileEntryFile(); + + MEMFILE getDatas() { return fileMemPtr; } + + bool isDirectory() { return false; } + bool isRootDirectory() { return false; } + unsigned int getSize() { return size; } // In bytes + +private: + unsigned int size; + MEMFILE fileMemPtr; +}; + + +class DatFileEntryDirectory : public DatFileEntry { +public: + DatFileEntryDirectory( std::string name, std::string path ); + DatFileEntryDirectory( std::string name, std::string path, DAT_FILE_FLAGS fFlags ); + DatFileEntryDirectory( std::string name, std::string path, DAT_FILE_FLAGS fFlags, DatFileEntry* hFileEntry ); + ~DatFileEntryDirectory(); + + void ClearLinkedFiles(); + void AddEntry( DatFileEntry* hFileEntry ); + std::vector getFiles() { return *vSubFiles; } + + bool isDirectory() { return true; } + bool isRootDirectory() { return rootDir; } + unsigned int getSize() { return vSubFiles->size(); } // In number of elements + +private: + bool rootDir; + std::vector *vSubFiles; +}; + +} +} + +#endif /* ERSOENTRY_HPP_ */ diff --git a/src/Erso.cpp b/src/Erso.cpp new file mode 100644 index 0000000..9b59ea3 --- /dev/null +++ b/src/Erso.cpp @@ -0,0 +1,354 @@ +/** + * @file Erso.cpp + * @date 11/01/2023 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief Rogue DATA/BUNDLE file parser and interface. + * + */ + +#include +#include +#include +#include +#include +#include +#include "files_mms/data_struct.h" +#include "DatFileEntry.hpp" +#include "Erso.hpp" + +using std::string; +using std::ios; +using std::vector; +using boost::filesystem::path; +using boost::filesystem::file_size; + +using namespace RDI::DatFile; + + +namespace RDI { + +Erso::Erso( string fPath ) : workingDir(fPath) +{ + Process(); +} + +Erso::Erso( string fPath, RDI_RESULT* outResult ) : + workingDir(fPath) +{ + if (outResult) *outResult = Process(); + else Process(); +} + +Erso::~Erso() +{ + DisposeFile(); +} + + +string Erso::getDataSectionName( unsigned char id ) const { return pDatSection->at(id).name; } +unsigned int Erso::getDataSectionOffset( unsigned char id ) const { return pDatSection->at(id).offset; } + +/** + * Change the current working directory. + * This action will reconstruct files tree, all previous file entries became invalid. + * + * @param newPath New working directory path + * + * @return Error status. + */ +RDI_RESULT Erso::setWorkingDirectory( std::string newPath ) { + RDI_RESULT errCode = RDI_OK; + + DisposeFile(); + + this->workingDir = newPath; + Process(); + + return errCode; +} + + +/* ------------------------------------------------------------------------------------------------------------- */ +RDI_RESULT Erso::Process() { + RDI_RESULT errCode = RDI_OK; + + // Process header file and dump data file in memory. + errCode = DataFilesDumping(); + if (errCode == RDI_ERROR_FILESYSTEM) { + std::cout << "Data files (DATA.DAT/DATA.HDR) not found! Interrupt." << std::endl; + return errCode; + } else if (errCode == RDI_ERROR_MEMORY) { + std::cout << "Memory allocation or file access failed! Interrupt." << std::endl; + return errCode; + } + + // Process data file tree and filter file subtype into list. + CreateDataFilesTree(); + + return errCode; +} + +/** + * Helper function to search for legacy DATA.DAT/HDR and dump it in memory. + * + * @return Error status. + */ +RDI_RESULT Erso::DataFilesDumping() { + unsigned int i; + path fp(workingDir); + MEMFILE pTmpFile; + + // If input as a directory, assume it's contain DATA.DAT/HDR. + if (boost::filesystem::is_directory(fp)) fp.append("DATA.HDR"); + + // If file specified in input is the "datas" file, process the header (HDR) before. + if (fp.extension() == ".DAT") fp.replace_extension(".HDR"); + + // Open and parse data header file. + //if (boost::filesystem::exists(fp)) return RDI_ERROR_FILESYSTEM; + pTmpFile = CreateMemFile(fp); + if (!pTmpFile) return RDI_ERROR_MEMORY; + + cDatSection = file_size(fp) / sizeof(T_HDR_ENTRY); + pDatSection = new vector(cDatSection); + for ( i = 0; i < pDatSection->size(); i++ ) { + // Store header infos into structure to make easier access later. + pDatSection->at(i).name.append(((T_HDR_ENTRY*)(pTmpFile + i * sizeof(T_HDR_ENTRY)))->section_name); + pDatSection->at(i).offset = ((T_HDR_ENTRY*)(pTmpFile + i * sizeof(T_HDR_ENTRY)))->section_offset; + } + + free(pTmpFile); + + // Dump data file and store it's pointer into class member. + fp.replace_extension(".DAT"); + //if (boost::filesystem::exists(fp)) return RDI_ERROR_FILESYSTEM; + rDatPtr = CreateMemFile(fp); + if (!rDatPtr) return RDI_ERROR_MEMORY; + + return RDI_OK; +} + +/** + * Dump file into to newly allocated memory. + * @param filePath File path. + * + * @return Start memory pointer of the file. + */ +MEMFILE Erso::CreateMemFile( path filePath ) { + const unsigned int size = file_size(filePath); + std::fstream rdf; + MEMFILE fPtr = NULL; + + rdf.open(filePath.string(), ios::in | ios::binary); + if (rdf.is_open()) { + fPtr = (MEMFILE)malloc(size); + rdf.read(fPtr, size); + rdf.close(); + } + + return fPtr; +} + +/** + * Helper function to process files content for further use in this library. + * + * @return Error status. + */ +RDI_RESULT Erso::CreateDataFilesTree() { + unsigned int curEntriesCount = 0, i; + DatFileEntryDirectory* curDir = nullptr; + MEMFILE pFileDescriptors = nullptr; + + // Create new root files tree. One per data section. + pFTRoot = new std::vector; + pFTRoot->clear(); + for ( i = 0; i < cDatSection; i++ ) { + curDir = new DatFileEntryDirectory(pDatSection->at(i).name, pDatSection->at(i).name); + pFTRoot->push_back(curDir); + + // Recalculate files descriptor offset for current section. + pFileDescriptors = rDatPtr + pDatSection->at(i).offset + + ((T_DAT_SECTION*)(rDatPtr + pDatSection->at(i).offset))->file_headers_offset; + curEntriesCount = (((T_DAT_SECTION*)(rDatPtr + pDatSection->at(i).offset))->file_headers_size) / 32; + + // Process arborescente in recursive method. + ProcessTreeDirectoryContents(curDir, pFileDescriptors, curEntriesCount, rDatPtr + pDatSection->at(i).offset); + } + + return RDI_OK; +} + +/** + * Recursive function to process all sub-directory from a specific DatFileEntryDirectory root. + * + * @param pCurDir Root/current DatFileEntryDirectory from which the function begin to dig into + * @param pDesc MEMFILE pointer to the first entry of directory to be parsed + * @param count Number of elements in the pCurDir directory + * @param pSectionStart MEMFILE pointer to the beginning of parsed file + * + * @return Error status. + */ +RDI_RESULT Erso::ProcessTreeDirectoryContents( DatFileEntryDirectory* pCurDir, MEMFILE pDesc, const unsigned int count, MEMFILE pSectionStart ) { + unsigned int i, newDRECnt; + DAT_FILE_FLAGS curEntryFlags = {0}; + + DatFileEntryFile* newFile = nullptr; + DatFileEntryDirectory* newDir = nullptr; + std::string tmpStr; + + if ( count == 0 ) return RDI_ERROR_PROCESS; + + for ( i = 0; i < count; i++ ) { + curEntryFlags.raw = ((T_FILE_HEADER*)(pDesc + i * sizeof(T_FILE_HEADER)))->flags; + + tmpStr.clear(); + tmpStr.append(((T_FILE_HEADER*)(pDesc + i * sizeof(T_FILE_HEADER)))->name); + + // Test for file or directory + if (!curEntryFlags.isDirectory) { + newFile = new DatFileEntryFile(tmpStr, pCurDir->getPath() + "/" + tmpStr, curEntryFlags, + ((T_FILE_HEADER*)(pDesc + i * sizeof(T_FILE_HEADER)))->datas_size, + pSectionStart + ((T_FILE_HEADER*)(pDesc + i * sizeof(T_FILE_HEADER)))->datas_offset); + pCurDir->AddEntry(newFile); + } else { + newDir = new DatFileEntryDirectory(tmpStr, pCurDir->getPath() + "/" + tmpStr, curEntryFlags); + pCurDir->AddEntry(newDir); + + // Keep entries count in new directory to jump over after processing it. + newDRECnt = (((T_FILE_HEADER*)(pDesc + i * sizeof(T_FILE_HEADER)))->dir_entries_size - sizeof(T_FILE_HEADER)) / sizeof(T_FILE_HEADER); + ProcessTreeDirectoryContents(newDir, pDesc + (i + 1) * sizeof(T_FILE_HEADER), newDRECnt, pSectionStart); + if (newDRECnt <= count) i += newDRECnt; + } + } + + return RDI_OK; +} + +/** + * Free all allocated resources for files tree and MemFile + */ +void Erso::DisposeFile() { + for ( DatFileEntryDirectory* de : *pFTRoot ) + delete de; + delete pFTRoot; + free(rDatPtr); + delete pDatSection; +} + + +/* ------------------------------------------------------------------------------------------------------------- */ +/** + * Get DatFileEntry reference from virtual path + * + * @param virtualDirPath Path to the virtual resource + * + * @return Reference to the resource from specified input path + */ +DatFileEntry *Erso::getElement( path virtualDirPath ) { + bool skip = false; + path newPath; + + for ( DatFileEntry* de : *pFTRoot ) { + if (virtualDirPath.size() == 1 || virtualDirPath.begin() == --virtualDirPath.end()) { + if (de->getName() == virtualDirPath.filename().string()) + return de; + } else { + if (de->getName() == virtualDirPath.begin()->string()) { + for (auto& sp : virtualDirPath) { + if (skip) + newPath.append(sp); + skip = true; + } + return SearchEntryPath(dynamic_cast(de), newPath); + } + } + } + + return nullptr; +} + +/** + * Get a list of files corresponding to the specified pattern. + * + * @param virtualDirPath Origin of browsing + * @param pattern Search filename pattern + * @param recursive Do the search inside subfolders + * + * @return List of references to the files corresponding to pattern + */ +vector Erso::getElements( path virtualDirPath, string pattern, bool recursive ) { + vector result; + DatFileEntryDirectory* curDir = dynamic_cast(getElement(virtualDirPath)); + + if (recursive) { + auto recursFileList = SearchEntriesPattern(curDir, pattern); + result.insert(result.end(), recursFileList.begin(), recursFileList.end()); + } else { + for ( DatFileEntry* e : curDir->getFiles() ) { + if (e->getName().find(pattern) != string::npos) //TODO: Replace using RegEx search + result.push_back(e); + } + } + + return result; +} + +/** + * Browse inside virtual path to the final object from specific root DatFileEntryDirectory. + * + * @param curDir Origin of browsing + * @param virtualDirPath Requested file path + * + * @return Reference to the file resource + */ +DatFileEntry* Erso::SearchEntryPath( DatFileEntryDirectory* curDir, path virtualDirPath ) { + bool skip = false; + path newPath; + + for ( DatFileEntry* e : curDir->getFiles() ) { + if (virtualDirPath.size() == 1 || virtualDirPath.begin() == --virtualDirPath.end()) { + if (e->getName() == virtualDirPath.filename().string()) + return e; + } else { + if (e->isDirectory() && (e->getName() == virtualDirPath.begin()->string())) { + for (auto& sp : virtualDirPath) { + if (skip) + newPath.append(sp); + skip = true; + } + + return SearchEntryPath(dynamic_cast(e), newPath); + } + } + } + + return nullptr; +} + +/** + * Browse inside virtual path to find specific pattern and add it reference to the list in input. + * + * @param upDir Origin of browsing + * @param pattern Search filename pattern + * + * @return Vector list of DatFileEntry + */ +vector Erso::SearchEntriesPattern( DatFileEntryDirectory* upDir, string pattern ) { + vector subDatEntries; + + for ( DatFileEntry* e : upDir->getFiles() ) + if (e->getName().find(pattern) != string::npos) //TODO: Replace using RegEx search + subDatEntries.push_back(e); + + for ( DatFileEntry* e : upDir->getFiles() ) { + if (e->isDirectory()) { + auto recursFileList = SearchEntriesPattern(dynamic_cast(e), pattern); + subDatEntries.insert(subDatEntries.end(), recursFileList.begin(), recursFileList.end()); + } + } + + return subDatEntries; +} + +} diff --git a/src/Erso.hpp b/src/Erso.hpp new file mode 100644 index 0000000..fdb11e8 --- /dev/null +++ b/src/Erso.hpp @@ -0,0 +1,106 @@ +/** + * @file Erso.hpp + * @date 11/01/2023 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief Rogue DATA/BUNDLE file parser and interface. + * @warning This module should be initialized before every others modules call of RDI library. + * + * Erso is the first module of the RDI library, it's use research work of + * , dpethes and myself to parse legacy data files of Rogue + * game. + * Currently compatible legacy files are : + * - DATA.DAT/HDR: as primary elements package containing all levels, sound, + * textures, etc. + * - BUNDLE.000: as title menu resources like pictures, buttons and cursor + * textures, menu sounds, menu macro-scripting. + * - BUNDLE.001: not known for now. + * + * This module should be initialized before every others modules call of RDI + * library. + * + * Standard usage: + * - Create a single Erso object with the folder path of datas files as input + * argument. + * - All supported data files will be parsed and a virtual tree view will be + * created. + * - The virtual tree view can be browse using appropriate 'get' functions. + * + */ + +#include +#include +#include "RDI_Datatypes.h" + +#include "DatFileEntry.hpp" + + +#ifndef ERSO_HPP_ +#define ERSO_HPP_ + +namespace RDI { + +struct dataSection { + std::string name = ""; + unsigned int offset = 0; +}; + +/** + * Erso is class codename of the DATA/BUNDLE file parser of RDI library. + * + * @pre RDI should construct Erso class before doing anything on datas. + */ +class Erso final { +public: + Erso( std::string fPath ); + Erso( std::string fPath, RDI_RESULT* outResult ); + ~Erso(); + + MEMFILE getRDat() const { return rDatPtr; } + + /* Workspace management */ + 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 ); + std::vector getElements( boost::filesystem::path virtualDirPath, std::string pattern, bool recursive ); + +private: + std::string workingDir = "."; + + unsigned char cDatSection = 0; + std::vector *pDatSection = nullptr; + MEMFILE rDatPtr = nullptr; + + std::vector *pFTRoot = nullptr; + + + /* File processing methods */ + RDI_RESULT Process(); + RDI_RESULT DataFilesDumping(); + MEMFILE CreateMemFile( boost::filesystem::path filePath ); + RDI_RESULT CreateDataFilesTree(); + RDI_RESULT ProcessTreeDirectoryContents( + DatFile::DatFileEntryDirectory* pCurDir, + MEMFILE pDesc, + const unsigned int count, + MEMFILE pSectionStart + ); + void DisposeFile(); + + /* Tree manipulation/parse methods */ + DatFile::DatFileEntry* SearchEntryPath( DatFile::DatFileEntryDirectory *curDir, boost::filesystem::path virtualDirPath); + std::vector SearchEntriesPattern(DatFile::DatFileEntryDirectory *upDir, std::string pattern); + +}; + +} /* namespace RDI */ + +#endif /* ERSO_HPP_ */ diff --git a/src/Krennic.cpp b/src/Krennic.cpp new file mode 100644 index 0000000..9704ba5 --- /dev/null +++ b/src/Krennic.cpp @@ -0,0 +1,203 @@ +/** + * @file Krennic.cpp + * @date 18/09/2023 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief Main game assets parser and interface. + * + * Complete class would take Erso legacy files tree and extract levels, objects, + * textures, animations, etc. adapted datas for OpenGL/Direct3D application. + * + */ + +#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; + + +namespace RDI { + +struct PathDescriptor { + PathDescriptor(string p, bool r) noexcept + : path(p), isRecursive(r) {} + + PathDescriptor(const PathDescriptor&) = default; + PathDescriptor& operator=(const PathDescriptor&) = default; + + PathDescriptor(PathDescriptor&&) = default; + PathDescriptor& operator=(PathDescriptor&&) = default; + + string path; + bool isRecursive; +}; + +Krennic::Krennic( Erso* pErso ) { + BuildLevelFileList(pErso); + BuildModelFileList(pErso); + BuildTextureFileList(pErso); + BuildMusicFileList(pErso); + BuildSampleFileList(pErso); +} + +Krennic::~Krennic() {} + + +void Krennic::BuildLevelFileList( Erso* pErso ) { + const static array legacyLvlPath = { + PathDescriptor("data/level", true) + }; + + listLevelsName.clear(); + for (PathDescriptor legacyLvlSubpath : legacyLvlPath) { + for (DatFileEntry* file : pErso->getElements(boost::filesystem::path(legacyLvlSubpath.path), "dat", legacyLvlSubpath.isRecursive)) { + //TODO: level-class builder + listLevelsName.push_back(file->getPath()); + } + } +} + +void Krennic::BuildModelFileList( Erso* pErso ) { + const static array legacyModelPath = { + PathDescriptor("data", false), + PathDescriptor("data/pl_crafts", false), + PathDescriptor("data/reb_stuff", false), + PathDescriptor("data/imp_stuff", false), + PathDescriptor("data/gnrc_stuff", false), + 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) + }; + + listModels.clear(); + listModelsName.clear(); + for (PathDescriptor legacyModelSubpath : legacyModelPath) { + for (DatFileEntry* file : pErso->getElements(boost::filesystem::path(legacyModelSubpath.path), "_HOB", legacyModelSubpath.isRecursive)) { + 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::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), + PathDescriptor("data/imp_stuff", false), + PathDescriptor("data/gnrc_stuff", false), + 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) + }; + + listTexturesName.clear(); + for (PathDescriptor legacyTextureSubpath : legacyTexturePath) { + for (DatFileEntry* file : pErso->getElements(boost::filesystem::path(legacyTextureSubpath.path), "_HMT", legacyTextureSubpath.isRecursive)) { + //TODO: texture-class builder + 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::BuildMusicFileList( Erso* pErso ) { + const static array legacyLvlPath = { + PathDescriptor("data/sound", false) + }; + + listMusicsName.clear(); + for (PathDescriptor legacyMusicSubpath : legacyLvlPath) { + for (DatFileEntry* file : pErso->getElements(boost::filesystem::path(legacyMusicSubpath.path), "_SNG", legacyMusicSubpath.isRecursive)) { + //TODO: MusyX-Class builder + listMusicsName.push_back(file->getPath()); + } + } +} + +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 new file mode 100644 index 0000000..20743a2 --- /dev/null +++ b/src/Krennic.hpp @@ -0,0 +1,91 @@ +/** + * @file Krennic.hpp + * @date 18/01/2023 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief Main game assets parser and interface. + * + * Complete class would take Erso legacy files tree and extract levels, objects, + * textures, animations, etc. adapted datas for OpenGL/Direct3D application. + * + */ + +#include +#include +#include +#include +#include +#include +#include "Erso.hpp" + + +#ifndef KRENNIC_HPP_ +#define KRENNIC_HPP_ + +namespace RDI { + +class Krennic final { +public: + Krennic( Erso* pErso ); + ~Krennic(); + + /** + * Retrieve know list of legacy game files type. + * @return Array of filtered elements. + */ + ///@{ + 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; } + ///@} + + /** + * Obtain the class interface for datas in specified file type. + * @param name Name of the file, should be in list. + * @return File type class handler. + */ + ///@{ + void* getLevel( std::string name ); + HOB* getModel( std::string name ); + HMT* getTexture( std::string name ); + void* getMusic( std::string name ); + void* getSample( std::string name ); + ///@} + + /** + * @brief Retrieve default file instance. + * @details Unknown file type can be wrapped in dummy class to access raw + * content without parsing or other type of processing. + * + * @param[in] vPath Virtual path to the file. + * @return Generic file type class handler. + */ + GenericFile *getFile( boost::filesystem::path vPath ); + +private: + std::vector listModels; + + 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(); +}; + +} + +#endif /* KRENNIC_HPP_ */ diff --git a/src/KrennicHandlerGeneric.cpp b/src/KrennicHandlerGeneric.cpp new file mode 100644 index 0000000..5b31aa2 --- /dev/null +++ b/src/KrennicHandlerGeneric.cpp @@ -0,0 +1,27 @@ +/** + * @file GenericFile.cpp + * @date 01/02/2023 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief Generic file object class. + * + */ + +#include +#include "DatFileEntry.hpp" +#include + +using namespace RDI::DatFile; + + +namespace RDI { + +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 new file mode 100644 index 0000000..885e10f --- /dev/null +++ b/src/KrennicHandlerHMT.cpp @@ -0,0 +1,24 @@ +/** + * @file HMT.cpp + * @date 15/09/2022 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief HMT file object class. + * + */ + +#include +#include + +using namespace RDI::DatFile; + + +namespace RDI { + +HMT::HMT( DatFile::DatFileEntryFile* hDat ): GenericFile( hDat ) { + this->fileExtension = "hmt"; +} + +HMT::~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 93fe10a..5f138e8 100644 --- a/src/RDI.cpp +++ b/src/RDI.cpp @@ -1,9 +1,9 @@ /** * @file RDI.cpp - * @date 15/09/2022 + * @date 22/01/2023 * @author JackCarterSmith * @copyright GPL-v3.0 - * @brief Rogue Data Interface library main entry file. + * @brief Rogue Data Interface library main entry abstraction file. * */ @@ -15,67 +15,99 @@ #include #include #include +#include +#include // Only for version getting, no processing. #include "config.h" -#include "RDat.h" -#include "DatEntry.h" -#include "RDI.h" +#include "Erso.hpp" +#include "DatFileEntry.hpp" +#include "Krennic.hpp" +#include +#include "RDI.hpp" + +using namespace RDI::DatFile; /* - * Internal variable + * Internal modules instance */ -static RDI::RDat *hRDat = nullptr; +static RDI::Erso *ErsoModule = nullptr; +static RDI::Krennic *KrennicModule = nullptr; + /* * Libs interface */ +const std::string RDI::RDI_getLibVersion() noexcept { return PRG_VERSION; } -std::string RDI::getLibVersion() { return PRG_VERSION; } +void RDI::RDI_Init( std::string roguePath ) { +#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 -void RDI::CreateRDatHandler( std::string roguePath ){ - if (hRDat == nullptr) hRDat = new RDI::RDat(roguePath); + // Create new instance of Erso module + // Load and extract datas files from DATA.DAT + if (ErsoModule == nullptr) { +#ifndef NDEBUG + std::cout << "[RDI][DBG] Loading Erso module..." << std::endl; +#endif + ErsoModule = new RDI::Erso(roguePath); + } - //return hRDat; + // 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) { +#ifndef NDEBUG + std::cout << "[RDI][DBG] Loading Krennic module..." << std::endl; +#endif + KrennicModule = new RDI::Krennic(ErsoModule); + } } -unsigned char RDI::getSectionCount() { - if (hRDat == nullptr) return 0; - else return hRDat->getDataSectionCount(); + + + + +const unsigned char RDI::RDI_getSectionCount() { + if (ErsoModule == nullptr) return 0; + else return ErsoModule->getDataSectionCount(); } -std::string RDI::getSectionName( unsigned char id ) { - if (hRDat == nullptr) return ""; - else return hRDat->getDataSectionName(id); +const std::string RDI::RDI_getSectionName( unsigned char id ) { + if (ErsoModule == nullptr) return ""; + else return ErsoModule->getDataSectionName(id); } -unsigned int RDI::getSectionOffset( unsigned char id ) { - if (hRDat == nullptr) return 0; - else return hRDat->getDataSectionOffset(id); +const unsigned int RDI::RDI_getSectionOffset( unsigned char id ) { + if (ErsoModule == nullptr) return 0; + else return ErsoModule->getDataSectionOffset(id); } -unsigned int RDI::getDirectoryElementCount( std::string path ) { - DirectoryEntry* result = nullptr; +const unsigned int RDI::RDI_getDirectoryElementCount( std::string path ) { + DatFileEntryDirectory* result = nullptr; if (path.empty()) return 0; - result = dynamic_cast(hRDat->getElement(boost::filesystem::path(path))); + result = dynamic_cast(ErsoModule->getElement(boost::filesystem::path(path))); if (result == nullptr) return 0; else return result->getSize(); } -std::vector RDI::getDirectoryElements( std::string path ) { - DirectoryEntry* de = nullptr; +const std::vector RDI::RDI_getDirectoryElements( std::string path ) { + DatFileEntryDirectory* de = nullptr; std::vector elementsNameArray; elementsNameArray.clear(); if (!path.empty()) { - de = dynamic_cast(hRDat->getElement(boost::filesystem::path(path))); + de = dynamic_cast(ErsoModule->getElement(boost::filesystem::path(path))); if (de != nullptr && de->isDirectory()) { - for (DatEntry* de2 : de->getFiles()) + for (DatFileEntry* de2 : de->getFiles()) elementsNameArray.push_back(de2->getName()); } } @@ -83,18 +115,46 @@ std::vector RDI::getDirectoryElements( std::string path ) { return elementsNameArray; } -bool RDI::isElementDirectory( std::string path ) { - DatEntry* result = nullptr; +const bool RDI::RDI_isElementDirectory( std::string path ) { + DatFileEntry* result = nullptr; if (path.empty()) return false; - result = hRDat->getElement(boost::filesystem::path(path)); + result = ErsoModule->getElement(boost::filesystem::path(path)); if (result == nullptr) return false; else return (result->isDirectory() || result->isRootDirectory()); } -void RDI::DestroyRDatHandler(){ - if (hRDat) delete hRDat; +/* -------------------------------------------------------------------------- */ +const std::vector RDI::RDI_getLevelsName( void ) { + return KrennicModule->getLevelsList(); +} + +const std::vector RDI::RDI_getModelsName( void ) { + return KrennicModule->getModelsList(); +} + +const std::vector RDI::RDI_getTexturesName( void ) { + return KrennicModule->getTexturesList(); +} + +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/src/RDat.cpp b/src/RDat.cpp deleted file mode 100644 index 2ac2ab2..0000000 --- a/src/RDat.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/** - * @file RDat.cpp - * @date 20/09/2022 - * @author JackCarterSmith - * @copyright GPL-v3.0 - * @brief Rogue Dat file class interface. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "DatEntry.h" -#include "data_struct.h" -#include "RDat.h" - - -using std::string; -using std::ios; -using std::vector; -using boost::filesystem::path; -using boost::filesystem::file_size; - - -namespace RDI { - -RDat::RDat( string fPath ) { - RDI_RESULT errCode = RDI_OK; - - this->workingDir = fPath; - - // Process header file and dump data file in memory. - errCode = DumpLegacyFiles(); - if (errCode == RDI_ERROR_FILESYSTEM) { - std::cout << "Data files (DATA.DAT/DATA.HDR) not found! Interrupt." << std::endl; - return; - } else if (errCode == RDI_ERROR_MEMORY) { - std::cout << "Memory allocation or file access failed! Interrupt." << std::endl; - return; - } - - // Process data file tree and filter file subtype into list. - ProcessFilesTree(); -} - -RDat::~RDat() { - for ( DirectoryEntry* de : *pFTRoot ) - delete de; - delete pFTRoot; - free(rDatPtr); - delete pDatSection; -} - -string RDat::getDataSectionName( unsigned char id ) { return pDatSection->at(id).name; } -unsigned int RDat::getDataSectionOffset( unsigned char id ) { return pDatSection->at(id).offset; } - -/** - * Helper function to search for legacy DATA.DAT/HDR and dump it in memory. - * - * @return Error status. - */ -RDI_RESULT RDat::DumpLegacyFiles() { - unsigned int i; - path fp(workingDir); - MEMFILE pTmpFile; - - // If input as a directory, assume it's contain DATA.DAT/HDR. - if (boost::filesystem::is_directory(fp)) fp.append("DATA.HDR"); - - // If file specified in input is the "datas" file, process the header (HDR) before. - if (fp.extension() == ".DAT") fp.replace_extension(".HDR"); - - // Open and parse data header file. - //if (boost::filesystem::exists(fp)) return RDI_ERROR_FILESYSTEM; - pTmpFile = MallocFile(fp); - if (!pTmpFile) return RDI_ERROR_MEMORY; - - cDatSection = file_size(fp) / sizeof(T_HDR_ENTRY); - pDatSection = new vector(cDatSection); - for ( i = 0; i < pDatSection->size(); i++ ) { - // Store header infos into structure to make easier access later. - pDatSection->at(i).name.append(((T_HDR_ENTRY*)(pTmpFile + i * sizeof(T_HDR_ENTRY)))->section_name); - pDatSection->at(i).offset = ((T_HDR_ENTRY*)(pTmpFile + i * sizeof(T_HDR_ENTRY)))->section_offset; - } - - free(pTmpFile); - - // Dump data file and store it's pointer into class member. - fp.replace_extension(".DAT"); - //if (boost::filesystem::exists(fp)) return RDI_ERROR_FILESYSTEM; - rDatPtr = MallocFile(fp); - if (!rDatPtr) return RDI_ERROR_MEMORY; - - return RDI_OK; -} - -/** - * Dump file into to newly allocated memory. - * @param filePath File path. - * - * @return Start memory pointer of the file. - */ -MEMFILE RDat::MallocFile( path filePath ) { - const unsigned int size = file_size(filePath); - std::fstream rdf; - MEMFILE fPtr = NULL; - - rdf.open(filePath.string(), ios::in | ios::binary); - if (rdf.is_open()) { - fPtr = (MEMFILE)malloc(size); - rdf.read(fPtr, size); - rdf.close(); - } - - return fPtr; -} - -/** - * Helper function to process files content for further use in this library. - * - * @return Error status. - */ -RDI_RESULT RDat::ProcessFilesTree() { - unsigned int curEntriesCount = 0, i; - DirectoryEntry* curDir = nullptr; - MEMFILE pFileDescriptors = nullptr; - - // Create new root files tree. One per data section. - pFTRoot = new std::vector; - pFTRoot->clear(); - for ( i = 0; i < cDatSection; i++ ) { - curDir = new DirectoryEntry(pDatSection->at(i).name); - pFTRoot->push_back(curDir); - - // Recalculate files descriptor offset for current section. - pFileDescriptors = rDatPtr + pDatSection->at(i).offset + - ((T_DAT_SECTION*)(rDatPtr + pDatSection->at(i).offset))->file_headers_offset; - curEntriesCount = (((T_DAT_SECTION*)(rDatPtr + pDatSection->at(i).offset))->file_headers_size) / 32; - - // Process arborescente in recursive method. - ProcessDirectoryContents(curDir, pFileDescriptors, curEntriesCount, rDatPtr + pDatSection->at(i).offset); - } - - return RDI_OK; -} - -RDI_RESULT RDat::ProcessDirectoryContents( DirectoryEntry* pCurDir, MEMFILE pDesc, const unsigned int count, MEMFILE pSectionStart ) { - unsigned int i, newDRECnt; - DAT_FILE_FLAGS curEntryFlags = {0}; - - FileEntry* newFile = nullptr; - DirectoryEntry* newDir = nullptr; - std::string tmpStr; - - if ( count == 0) return RDI_ERROR_PROCESS; - - for ( i = 0; i < count; i++ ) { - curEntryFlags.raw = ((T_FILE_HEADER*)(pDesc + i * sizeof(T_FILE_HEADER)))->flags; - - tmpStr.clear(); - tmpStr.append(((T_FILE_HEADER*)(pDesc + i * sizeof(T_FILE_HEADER)))->name); - - // Test for file or directory - if (!curEntryFlags.isDirectory) { - newFile = new FileEntry(tmpStr, curEntryFlags, - ((T_FILE_HEADER*)(pDesc + i * sizeof(T_FILE_HEADER)))->datas_size, - pSectionStart + ((T_FILE_HEADER*)(pDesc + i * sizeof(T_FILE_HEADER)))->datas_offset); - pCurDir->AddEntry(newFile); - } else { - newDir = new DirectoryEntry(tmpStr, curEntryFlags); - pCurDir->AddEntry(newDir); - - // Keep entries count in new directory to jump over after processing it. - newDRECnt = (((T_FILE_HEADER*)(pDesc + i * sizeof(T_FILE_HEADER)))->dir_entries_size - sizeof(T_FILE_HEADER)) / sizeof(T_FILE_HEADER); - ProcessDirectoryContents(newDir, pDesc + (i + 1) * sizeof(T_FILE_HEADER), newDRECnt, pSectionStart); - if (newDRECnt <= count) i += newDRECnt; - } - } - - return RDI_OK; -} - -DatEntry *RDat::getElement( boost::filesystem::path virtualDirPath ) { - bool skip = false; - boost::filesystem::path newPath; - - for ( DatEntry* de : *pFTRoot ) { - if (virtualDirPath.size() == 1 || virtualDirPath.begin() == --virtualDirPath.end()) { - if (de->getName() == virtualDirPath.filename().string()) - return de; - } else { - if (de->getName() == virtualDirPath.begin()->string()) { - for (auto& sp : virtualDirPath) { - if (skip) - newPath.append(sp); - skip = true; - } - return SearchSectionForEntry(dynamic_cast(de), newPath); - } - } - } - - return nullptr; -} - -DatEntry *RDat::SearchSectionForEntry( DirectoryEntry *curDir, boost::filesystem::path virtualDirPath ) { - bool skip = false; - boost::filesystem::path newPath; - - for ( DatEntry* e : curDir->getFiles() ) { - if (virtualDirPath.size() == 1 || virtualDirPath.begin() == --virtualDirPath.end()) { - if (e->getName() == virtualDirPath.filename().string()) - return e; - } else { - if (e->isDirectory() && (e->getName() == virtualDirPath.begin()->string())) { - for (auto& sp : virtualDirPath) { - if (skip) - newPath.append(sp); - skip = true; - } - //TODO: add safety when trying to access inside a file - return SearchSectionForEntry(dynamic_cast(e), newPath); - } - } - } - - return nullptr; -} - -} diff --git a/src/RDat.h b/src/RDat.h deleted file mode 100644 index 1c1d476..0000000 --- a/src/RDat.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * @file RDat.h - * @date 15/09/2022 - * @author JackCarterSmith - * @copyright GPL-v3.0 - * @brief Rogue Dat file class interface. - * - */ - -#include -#include -#include "DatEntry.h" -#include "RDI_Datatypes.h" - - -#ifndef RDAT_H_ -#define RDAT_H_ - -namespace RDI { - -struct dataSection { - std::string name = ""; - unsigned int offset = 0; -}; - -class RDat final { -public: - RDat( std::string fPath ); - ~RDat(); - - MEMFILE getRDat() { - return rDatPtr; - } - - std::string getWorkingDirectory() { return workingDir; } - void setWorkingDirectory( std::string newPath ) { workingDir = newPath; } - - unsigned char getDataSectionCount() { return cDatSection; } - std::string getDataSectionName( unsigned char id ); - unsigned int getDataSectionOffset( unsigned char id ); - - DatEntry *getElement( boost::filesystem::path virtualDirPath ); - -private: - std::string workingDir = "."; - - unsigned char cDatSection = 0; - std::vector *pDatSection = nullptr; - MEMFILE rDatPtr = nullptr; - - std::vector *pFTRoot = nullptr; - - /* File processing methods */ - RDI_RESULT DumpLegacyFiles(); - MEMFILE MallocFile( boost::filesystem::path filePath ); - RDI_RESULT ProcessFilesTree(); - RDI_RESULT ProcessDirectoryContents( - DirectoryEntry* pCurDir, - MEMFILE pDesc, - const unsigned int count, - MEMFILE pSectionStart - ); - - /* Tree manipulation/parse methods */ - DatEntry *SearchSectionForEntry( DirectoryEntry *curDir, boost::filesystem::path virtualDirPath); - -}; - -} /* namespace RDI */ - -#endif /* RDAT_H_ */ diff --git a/src/datfiles/HMT.cpp b/src/datfiles/HMT.cpp deleted file mode 100644 index 77ed7eb..0000000 --- a/src/datfiles/HMT.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @file HMT.cpp - * @date 15/09/2022 - * @author JackCarterSmith - * @copyright GPL-v3.0 - * @brief HMT file object class. - * - */ - -#include "HMT.h" - -namespace RDI { - -HMT::HMT() { - // TODO Auto-generated constructor stub - -} - -HMT::~HMT() { - // TODO Auto-generated destructor stub -} - -} /* namespace RDI */ diff --git a/src/bundle_struct.h b/src/files_mms/bundle_struct.h similarity index 100% rename from src/bundle_struct.h rename to src/files_mms/bundle_struct.h diff --git a/src/data_struct.h b/src/files_mms/data_struct.h similarity index 100% rename from src/data_struct.h rename to src/files_mms/data_struct.h diff --git a/tools/ErsoDebug.cpp b/tools/ErsoDebug.cpp new file mode 100644 index 0000000..4cb30bd --- /dev/null +++ b/tools/ErsoDebug.cpp @@ -0,0 +1,16 @@ +/** + * @file ErsoDebug.cpp + * @date 24/09/2022 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief Debug app to test Erso module of RDI library. + * + */ + +#include + + +int main( int argc, char *argv[] ) { + + return 0; +} diff --git a/tools/KrennicDebug.cpp b/tools/KrennicDebug.cpp new file mode 100644 index 0000000..1e2812f --- /dev/null +++ b/tools/KrennicDebug.cpp @@ -0,0 +1,16 @@ +/** + * @file KrennicDebug.cpp + * @date 24/09/2022 + * @author JackCarterSmith + * @copyright GPL-v3.0 + * @brief Debug app to test Krennic module of RDI library. + * + */ + +#include + + +int main( int argc, char *argv[] ) { + + return 0; +} diff --git a/tools/RDIDebug.cpp b/tools/RDIDebug.cpp new file mode 100644 index 0000000..298a684 --- /dev/null +++ b/tools/RDIDebug.cpp @@ -0,0 +1,308 @@ +/** + * @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 +#include +#include +#include +#include +#include +#include +#ifdef USE_MULTITHREADING +#include +#include +#endif +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +using namespace boost::filesystem; +using namespace std; + + +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()); + + 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(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; +} diff --git a/tools/RDIdebug.cpp b/tools/RDIdebug.cpp deleted file mode 100644 index 61a7e30..0000000 --- a/tools/RDIdebug.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @file RDIdebug.cpp - * @date 17/09/2022 - * @author JackCarterSmith - * @copyright GPL-v3.0 - * @brief Debug app to test functions of RDI library. - * - */ - -#include -#include -#include -#include - - -void PrintVirtualDirectoryContents( boost::filesystem::path, std::string ); - -int main( int argc, char *argv[] ) { - unsigned int i; - boost::filesystem::path pathBuilder; - std::string prefix; - - printf("Using RDI lib v%s\n\n", RDI::getLibVersion().c_str()); - //cout << "Using RDI lib v" << RDI::getLibVersion() << endl << endl; - - if ( argc > 1 ) - RDI::CreateRDatHandler(argv[1]); - else - RDI::CreateRDatHandler("."); - - printf("> Section found: %d\n", RDI::getSectionCount()); - for ( i = 0; i < RDI::getSectionCount(); i++ ) { - printf(" -Section %d name: %s\n", i, RDI::getSectionName(i).c_str()); - printf(" -Section %d offset: 0x%X\n", i, RDI::getSectionOffset(i)); - } - - //prefix.append("\t"); - prefix.append(" "); - printf("\nSections files root:\n"); - for ( i = 0; i < RDI::getSectionCount(); i++ ) { - printf("%s:\n", RDI::getSectionName(i).c_str()); // Print root trees - - pathBuilder.clear(); - pathBuilder.concat(RDI::getSectionName(i)); - - PrintVirtualDirectoryContents(pathBuilder, prefix); - } - - RDI::DestroyRDatHandler(); - - return 0; -} - -void PrintVirtualDirectoryContents( boost::filesystem::path path, std::string outPrefix ) { - auto curDirElementsName = RDI::getDirectoryElements(path.string()); - auto newPath = boost::filesystem::path(path); - auto newOutPrefix = std::string(outPrefix); - - for ( std::string eName : curDirElementsName ) { - newPath.clear(); - newPath.concat(path); - newOutPrefix.clear(); - newOutPrefix.append(outPrefix); - - newPath.append(eName); - if (RDI::isElementDirectory(newPath.string())) { - printf("%s%s:\n", outPrefix.c_str(), eName.c_str()); - //newOutPrefix.append("\t"); - newOutPrefix.append(" "); - PrintVirtualDirectoryContents(newPath, newOutPrefix); - } else { - printf("%s%s\n", newOutPrefix.c_str(), eName.c_str()); - } - } -}