Update SdlPackFile I/O

SdlPackFile can now push and pop directories using host file I/O. This allows for reading from directories as though they were .pdbs (like the htdata2432 dir) while maintaining the pushed hierarchy. pdbs can still be used.
This commit is contained in:
Nathan Fulton 2017-05-01 21:58:24 -04:00
parent 2ef0ab5ae8
commit 5d7e0ddf3c
4 changed files with 315 additions and 11 deletions

View File

@ -1,11 +1,30 @@
#include "game/sdl/sdlpackfile.h"
#include "game/mempdbreader.h"
#include "game/sdl/hosthelpers.h"
#include "inc/rip.h"
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <iostream>
namespace wi {
bool FileIsOpen(File *pfil) {
if (pfil != NULL)
if (pfil->prnfo != NULL)
if (pfil->prnfo->cOpen > 0)
return true;
return false;
}
SdlPackFileReader::~SdlPackFileReader()
{
while (Pop())
;
}
PdbReader *SdlPackFileReader::OpenPdb(const char *pszDir,
const char *pszFn) {
// Load data from this directory
@ -15,8 +34,10 @@ PdbReader *SdlPackFileReader::OpenPdb(const char *pszDir,
strcpy(szT, pszDir);
strcat(szT, "/");
strcat(szT, pszFn);
} else {
} else if (pszFn != NULL) {
strcpy(szT, pszFn);
} else {
return false;
}
// PdbReader over memory
@ -49,4 +70,255 @@ bool SdlPackFileReader::DeletePdb(const char *pszDir, const char *pszFn) {
return true;
}
File *SdlPackFileReader::fopen(const char *pszFn, const char *pszMode) {
if (pszFn == NULL)
return NULL;
// Only support read binary mode
if (strcmp(pszMode, "rb") != 0)
return NULL;
// Go through the reader infos in reverse order
for (std::vector<SdlReaderInfo *>::reverse_iterator it = m_vrnfo.rbegin(); it != m_vrnfo.rend(); it++) {
// Is this reader a pdb?
if ((*it)->pszPdb != NULL) {
if (!strchr(pszFn, '/') && strlen(pszFn) <= kcbFilename) {
File *pfil = PackFileReader::fopen(pszFn, pszMode);
if (pfil != NULL)
return pfil;
}
// If couldn't load from pdb, continute to next reader
continue;
}
// Not pdb, load from dir
char pszFnFull[PATH_MAX];
sprintf(pszFnFull, "%s/%s", (*it)->pszDir, pszFn);
// Open with host I/O
File *pfil = new File();
pfil->pvData = HostOpenFile(pszFnFull, kfOfRead);
if (!pfil->pvData)
continue;
// Set prevalent info
HostSeekFile(pfil->pvData, 0, kfSeekEnd);
pfil->cbTotal = HostTellFile(pfil->pvData);
HostSeekFile(pfil->pvData, 0, kfSeekSet);
return pfil;
}
LOG() << "can't find file " << pszFn;
return NULL;
}
int SdlPackFileReader::fclose(File *pfil) {
if (!FileIsOpen(pfil))
HostCloseFile(pfil->pvData);
if (FileIsOpen(pfil))
PackFileReader::fclose(pfil);
return 0;
}
dword SdlPackFileReader::fread(void *pv, dword cb, int c, File *pfil) {
if (FileIsOpen(pfil))
return PackFileReader::fread(pv, cb, c, pfil);
dword cbT = cb * c;
dword cbR = c * HostReadFile(pv, c, cb, pfil->pvData);
cbT -= cbR;
return cbT == 0 ? c : cbR / cb;
}
int SdlPackFileReader::fseek(File *pfil, int off, int nOrigin) {
if (FileIsOpen(pfil))
return PackFileReader::fseek(pfil, off, nOrigin);
return HostSeekFile(pfil->pvData, off, nOrigin) != -1 ? 0 : -1;
}
dword SdlPackFileReader::ftell(File *pfil) {
if (FileIsOpen(pfil))
return PackFileReader::ftell(pfil);
return HostTellFile(pfil->pvData);
}
bool SdlPackFileReader::EnumFiles(Enum *penm, int key, char *pszFn, int cbFn) {
// PACKENUM_LAST will enum the top pdb
// PACKENUM_FIRST will endum the bottom dir
if (key == PACKENUM_LAST)
return PackFileReader::EnumFiles(penm, key, pszFn, cbFn);
if (key != PACKENUM_FIRST)
return false;
#if defined(__ANDROID__)
return HostHelpers::EnumFiles(penm, key, pszFn, cbFn);
#else
if (penm->m_pvNext == (void *)kEnmFirst) {
DIR *dir;
dir = opendir(BottomDir());
penm->m_pvNext = (void *)dir;
penm->m_dwUser = 0;
}
DIR *dir = (DIR *)penm->m_pvNext;
struct dirent *ent;
if ((ent = readdir(dir)) != NULL) {
strncpyz(pszFn, ent->d_name, cbFn);
penm->m_dwUser++;
return true;
}
closedir(dir);
#endif
return false;
}
void *SdlPackFileReader::MapFile(const char *pszFn, FileMap *pfmap, dword *pcb) {
if (!strchr(pszFn, '/') && strlen(pszFn) <= kcbFilename) {
void *pv = PackFileReader::MapFile(pszFn, pfmap, pcb);
if (pv != NULL)
return pv;
}
byte *pb = NULL;
File *pfil = this->fopen(pszFn, "rb");
if (pfil == NULL) {
return NULL;
}
this->fseek(pfil, 0, SEEK_END);
dword cb = ftell(pfil);
this->fseek(pfil, 0, SEEK_SET);
pb = new byte[cb];
if (pb == NULL) {
this->fclose(pfil);
return NULL;
}
if (this->fread(pb, cb, 1, pfil) != 1) {
this->fclose(pfil);
return NULL;
}
pfmap->pbAlloced = pb;
if (pcb != NULL) {
*pcb = cb;
}
this->fclose(pfil);
return pb;
}
void SdlPackFileReader::UnmapFile(FileMap *pfmap) {
if (pfmap != NULL)
if (pfmap->prnfo != NULL)
if (pfmap->prnfo->cOpen > 0)
PackFileReader::UnmapFile(pfmap);
if (pfmap->pbAlloced != NULL)
delete[] pfmap->pbAlloced;
}
bool SdlPackFileReader::Push(const char *pszDir, const char *pszFn) {
if (pszDir == NULL && pszFn == NULL)
return false;
SdlReaderInfo *prnfo = new SdlReaderInfo();
prnfo->pszDir = NULL;
if (pszDir != NULL)
prnfo->pszDir = AllocString(pszDir);
prnfo->pszPdb = NULL;
if (pszFn != NULL)
prnfo->pszPdb = AllocString(pszFn);
// If we have pszFn then we are pushing a pdb
if (pszFn != NULL) {
bool fSuccess = PackFileReader::Push(pszDir, pszFn);
if (fSuccess)
m_vrnfo.push_back(prnfo);
return fSuccess;
}
// Push the dir if it exists
#if defined(__ANDROID__)
wi::HostHelpers::DirExists(prnfo->pszDir);
#else
DIR *pdir;
if ((pdir = opendir(pszDir)) == NULL) {
return false;
} else {
closedir(pdir);
}
#endif
m_vrnfo.push_back(prnfo);
return true;
}
bool SdlPackFileReader::Pop() {
// Anything to pop?
if (m_vrnfo.empty())
return false;
// Pop an actual pdb?
bool fPdbRet = false;
bool fPdb = false;
if (m_vrnfo.back()->pszPdb != NULL) {
fPdb = true;
fPdbRet = PackFileReader::Pop();
}
// Pdb pop fail?
if (fPdb && !fPdbRet)
return false;
// Either a dir, or a successfully poped pdb, pop it off the vector
delete[] m_vrnfo.back()->pszDir;
delete[] m_vrnfo.back()->pszPdb;
m_vrnfo.pop_back();
return true;
}
char *SdlPackFileReader::BottomDir() {
for (std::vector<SdlReaderInfo *>::reverse_iterator it = m_vrnfo.rbegin();
it != m_vrnfo.rend(); it++) {
if ((*it)->pszDir != NULL && (*it)->pszPdb == NULL)
return (*it)->pszDir;
}
return NULL;
}
char *SdlPackFileReader::BottomPdb() {
for (std::vector<SdlReaderInfo *>::reverse_iterator it = m_vrnfo.rbegin();
it != m_vrnfo.rend(); it++) {
if ((*it)->pszPdb != NULL)
return (*it)->pszPdb;
}
return NULL;
}
} // namespace wi

View File

@ -3,14 +3,42 @@
#include "inc/basictypes.h"
#include "mpshared/packfile.h"
#include <stdio.h>
#include <vector>
namespace wi {
struct SdlReaderInfo {
char *pszDir;
char *pszPdb;
};
class SdlPackFileReader : public PackFileReader
{
public:
~SdlPackFileReader();
virtual File *fopen(const char *pszFn, const char *pszMode);
virtual int fclose(File *pfil);
virtual dword fread(void *pv, dword cb, int c, File *pfil);
virtual int fseek(File *pfil, int off, int nOrigin);
virtual dword ftell(File *pfil);
virtual bool EnumFiles(Enum *penm, int key, char *pszFn, int cbFn);
virtual void *MapFile(const char *pszFn, FileMap *pfmap, dword *pcb = NULL);
virtual void UnmapFile(FileMap *pfmap);
virtual bool Push(const char *pszDir, const char *pszFn);
virtual bool Pop();
char *BottomDir();
char *BottomPdb();
private:
virtual PdbReader *OpenPdb(const char *pszDir, const char *pszFn);
virtual bool DeletePdb(const char *pszDir, const char *pszFn);
std::vector<SdlReaderInfo *> m_vrnfo;
};
} // namespace wi

View File

@ -72,7 +72,9 @@ File *PackFileReader::fopen(const char *pszFn, const char *pszMode)
ReaderInfo *prnfo;
DirEntry dir;
if (!FindDirEntry(pszFn, &dir, &prnfo)) {
#if !defined(SDL)
LOG() << "can't find dir entry " << pszFn;
#endif
return NULL;
}
@ -362,6 +364,7 @@ void PackFileReader::UnmapFile(FileMap *pfmap)
pfmap->prnfo->cOpen--;
if (pfmap->pbAlloced != NULL) {
delete[] pfmap->pbAlloced;
pfmap->pbAlloced = NULL;
} else {
pfmap->prnfo->ppdbReader->UnmapRecord(pfmap->nRec, pfmap->pvCookie);
}

View File

@ -99,6 +99,7 @@ struct File
byte cRecs;
word nRecFirst;
word acb[1];
void *pvData;
};
// Used for MapFile / UnmapFile
@ -122,20 +123,20 @@ public:
PackFileReader();
~PackFileReader();
File *fopen(const char *pszFn, const char *pszMode);
int fclose(File *pfil);
dword fread(void *pv, dword cb, int c, File *pfil);
int fseek(File *pfil, int off, int nOrigin);
dword ftell(File *pfil);
bool EnumFiles(Enum *penm, int key, char *pszFn, int cbFn);
void *MapFile(const char *pszFn, FileMap *pfmap, dword *pcb = NULL);
void UnmapFile(FileMap *pfmap);
virtual File *fopen(const char *pszFn, const char *pszMode);
virtual int fclose(File *pfil);
virtual dword fread(void *pv, dword cb, int c, File *pfil);
virtual int fseek(File *pfil, int off, int nOrigin);
virtual dword ftell(File *pfil);
virtual bool EnumFiles(Enum *penm, int key, char *pszFn, int cbFn);
virtual void *MapFile(const char *pszFn, FileMap *pfmap, dword *pcb = NULL);
virtual void UnmapFile(FileMap *pfmap);
bool HashFile(const char *pszFn, byte *hash);
bool GetPdbName(const char *pszFn, char *pszPdb, int cbPdb,
char *pszDir = NULL, int cbDir = 0);
bool Push(const char *pszDir, const char *pszFn);
bool Pop();
virtual bool Push(const char *pszDir, const char *pszFn);
virtual bool Pop();
bool Delete(const char *pszDir, const char *pszFn);
private: