234 lines
6.7 KiB
C++
234 lines
6.7 KiB
C++
/**
|
|
* @file RDat.cpp
|
|
* @date 20/09/2022
|
|
* @author JackCarterSmith
|
|
* @copyright GPL-v3.0
|
|
* @brief Rogue Dat file class interface.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <vector>
|
|
#include <boost/filesystem.hpp>
|
|
#include <RDI_Datatypes.h>
|
|
#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<struct dataSection>(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<DirectoryEntry*>;
|
|
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<DirectoryEntry *>(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<DirectoryEntry *>(e), newPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
}
|