diff --git a/include/RDI_Datatypes.h b/include/RDI_Datatypes.h index 7c040a6..4797195 100644 --- a/include/RDI_Datatypes.h +++ b/include/RDI_Datatypes.h @@ -12,6 +12,8 @@ namespace RDI { +typedef char* MEMFILE; + typedef enum e_rdi_result { RDI_OK, diff --git a/src/DatEntry.cpp b/src/DatEntry.cpp new file mode 100644 index 0000000..dbed5da --- /dev/null +++ b/src/DatEntry.cpp @@ -0,0 +1,63 @@ +/** + * @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->fFlags.sectionRootDir = 1; + vSubFiles = new std::vector; +} + +DirectoryEntry::DirectoryEntry( std::string name, DAT_FILE_FLAGS fFlags ) { + this->name = name; + this->fFlags.raw = fFlags.raw; + 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() {} + +/*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 new file mode 100644 index 0000000..61fb1c2 --- /dev/null +++ b/src/DatEntry.h @@ -0,0 +1,85 @@ +/** + * @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 { + unsigned short unknown0:1; + unsigned short isFile:1; + unsigned short unknown1:5; + unsigned short isDirectory:1; + unsigned short unknown2:7; + unsigned short sectionRootDir:1; + }; + unsigned short raw; +} DAT_FILE_FLAGS; + + + +class DatEntry { +public: + virtual ~DatEntry() = 0; + virtual bool isDirectory() = 0; + //virtual std::string toString() = 0; + virtual unsigned int getSize() = 0; + +protected: + std::string name; + DAT_FILE_FLAGS fFlags; +}; + + +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; } + 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::string toString(); + bool isDirectory() { return true; } + unsigned int getSize() { return vSubFiles->size(); } + +private: + std::vector *vSubFiles; +}; + +} + +#endif /* DATENTRY_H_ */ diff --git a/src/RDat.cpp b/src/RDat.cpp index a695c3c..c231aef 100644 --- a/src/RDat.cpp +++ b/src/RDat.cpp @@ -1,6 +1,6 @@ /** * @file RDat.cpp - * @date 15/09/2022 + * @date 20/09/2022 * @author JackCarterSmith * @copyright GPL-v3.0 * @brief Rogue Dat file class interface. @@ -16,6 +16,7 @@ #include #include #include +#include "DatEntry.h" #include "data_struct.h" #include "RDat.h" @@ -29,105 +30,154 @@ using boost::filesystem::file_size; namespace RDI { - RDat::RDat( string fPath ) { - RDI_RESULT errCode = RDI_OK; +RDat::RDat( string fPath ) { + RDI_RESULT errCode = RDI_OK; - this->workingDir = fPath; + 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 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() { + 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(cDatSection); + 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; + 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)->flags; + + tmpStr.clear(); + tmpStr.append(((T_FILE_HEADER*)pDesc)->name); + + // Test for file or directory + if (!curEntryFlags.isDirectory) { + newFile = new FileEntry(tmpStr, curEntryFlags, ((T_FILE_HEADER*)pDesc)->datas_size, pSectionStart + ((T_FILE_HEADER*)pDesc)->datas_offset); + pCurDir->AddEntry(newFile); + } else { + newDir = new DirectoryEntry(tmpStr, curEntryFlags); + pCurDir->AddEntry(newDir); + + ProcessDirectoryContents(newDir, + pDesc + i * sizeof(T_FILE_HEADER), + (((T_FILE_HEADER*)pCurDir)->dir_entries_size - sizeof(T_FILE_HEADER)) / sizeof(T_FILE_HEADER), + pSectionStart); } - - // Process data file tree and filter file subtype into list. - ProcessFilesTree(); } - RDat::~RDat() { - free(rDatPtr); - delete pDatSection; - } + return RDI_OK; +} - 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() { - - - return RDI_OK; - } - - - -} /* namespace RDI */ +} diff --git a/src/RDat.h b/src/RDat.h index 6975de6..f15f894 100644 --- a/src/RDat.h +++ b/src/RDat.h @@ -9,6 +9,7 @@ #include #include +#include "DatEntry.h" #include "RDI_Datatypes.h" @@ -17,42 +18,48 @@ namespace RDI { - typedef char* MEMFILE; +struct dataSection { + std::string name = ""; + unsigned int offset = 0; +}; - struct dataSection { - std::string name = ""; - unsigned int offset = 0; - }; +class RDat final { +public: + RDat( std::string fPath ); + ~RDat(); - class RDat { - public: - RDat( std::string fPath ); - ~RDat(); + MEMFILE getRDat() { + return rDatPtr; + } - MEMFILE getRDat() { - return rDatPtr; - } + std::string getWorkingDirectory() { return workingDir; } + void setWorkingDirectory( std::string newPath ) { workingDir = newPath; } - 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 ); - unsigned char getDataSectionCount() { return cDatSection; } - std::string getDataSectionName( unsigned char id ); - unsigned int getDataSectionOffset( unsigned char id ); +private: + std::string workingDir = "."; - private: - std::string workingDir = "."; + unsigned char cDatSection = 0; + std::vector *pDatSection = nullptr; + MEMFILE rDatPtr = nullptr; - 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(); + /* 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 + ); - }; +}; } /* namespace RDI */ diff --git a/src/data_struct.h b/src/data_struct.h index e979278..7e9fbfc 100644 --- a/src/data_struct.h +++ b/src/data_struct.h @@ -32,9 +32,12 @@ namespace RDI { /* * DATA.DAT content type + * +---------------+-------------------------------------------+ + * | T_DAT_SECTION | T_FILE_HEADER (file_headers_size x bytes) | + * +---------------+-------------------------------------------+ */ typedef struct PACK file_header { - unsigned int datas_offset; + unsigned int datas_offset; // From the start of section unsigned int datas_size; // If file is directory, equal to sum of it's file. unsigned int reserved0; // 0xFFFFFFFF unsigned short flags; @@ -43,7 +46,7 @@ namespace RDI { } T_FILE_HEADER; typedef struct PACK dat_section { - unsigned int file_headers_offset; + unsigned int file_headers_offset; // From the start of section unsigned int file_headers_size; unsigned char files_datas; // Should be used as start pointer for memcpy. } T_DAT_SECTION;