/** * @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 "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; } }