mirror of
https://github.com/ZDoom/Raze.git
synced 2025-05-30 08:51:08 +00:00
- added the main FileSystem class.
This commit is contained in:
parent
82c844e405
commit
cfd9edbe71
12 changed files with 1090 additions and 1562 deletions
|
@ -74,8 +74,11 @@ bool FLumpFile::Open(bool quiet)
|
|||
Lumps[0].LumpSize = (int)Reader.GetLength();
|
||||
Lumps[0].Flags = 0;
|
||||
Lumps[0].FullName = FileName;
|
||||
Lumps[0].BaseName = ExtractFileBase(FileName);
|
||||
auto p = FileName.LastIndexOf('/');
|
||||
Lumps[0].PathLen = FileName.LastIndexOf('/') + 1;
|
||||
Lumps[0].ExtStart = FileName.LastIndexOf('.');
|
||||
NumLumps = 1;
|
||||
if (Lumps[0].ExtStart < Lumps[0].PathLen) Lumps[0].ExtStart = -1;
|
||||
/*
|
||||
if (!quiet)
|
||||
{
|
||||
|
|
|
@ -104,11 +104,10 @@ void BloodCrypt (void *data, int key, int len)
|
|||
|
||||
class FRFFFile : public FResourceFile
|
||||
{
|
||||
FRFFLump *Lumps;
|
||||
TArray<FRFFLump> Lumps;
|
||||
|
||||
public:
|
||||
FRFFFile(const char * filename, FileReader &file);
|
||||
virtual ~FRFFFile();
|
||||
virtual bool Open(bool quiet);
|
||||
virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; }
|
||||
};
|
||||
|
@ -123,7 +122,6 @@ public:
|
|||
FRFFFile::FRFFFile(const char *filename, FileReader &file)
|
||||
: FResourceFile(filename, file)
|
||||
{
|
||||
Lumps = NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -146,19 +144,22 @@ bool FRFFFile::Open(bool quiet)
|
|||
Reader.Read (lumps, header.NumLumps * sizeof(RFFLump));
|
||||
BloodCrypt (lumps, header.DirOfs, header.NumLumps * sizeof(RFFLump));
|
||||
|
||||
Lumps = new FRFFLump[NumLumps];
|
||||
Lumps.Grow(NumLumps);
|
||||
|
||||
//if (!quiet) Printf(", %d lumps\n", NumLumps);
|
||||
for (uint32_t i = 0; i < NumLumps; ++i)
|
||||
{
|
||||
Lumps[i].Position = LittleLong(lumps[i].FilePos);
|
||||
Lumps[i].LumpSize = LittleLong(lumps[i].Size);
|
||||
Lumps[i].Owner = this;
|
||||
Lumps.Reserve(1);
|
||||
auto& Lump = Lumps.Last();
|
||||
|
||||
Lump.Position = LittleLong(lumps[i].FilePos);
|
||||
Lump.LumpSize = LittleLong(lumps[i].Size);
|
||||
Lump.Owner = this;
|
||||
if (lumps[i].Flags & 0x10)
|
||||
{
|
||||
Lumps[i].Flags |= LUMPF_BLOODCRYPT;
|
||||
Lump.Flags |= LUMPF_BLOODCRYPT;
|
||||
}
|
||||
Lumps[i].IndexNum = LittleLong(lumps[i].IndexNum);
|
||||
Lump.IndexNum = LittleLong(lumps[i].IndexNum);
|
||||
// Rearrange the name and extension to construct the fullname.
|
||||
char name[13];
|
||||
strncpy(name, lumps[i].Name, 8);
|
||||
|
@ -170,20 +171,20 @@ bool FRFFFile::Open(bool quiet)
|
|||
name[len+2] = lumps[i].Extension[1];
|
||||
name[len+3] = lumps[i].Extension[2];
|
||||
name[len+4] = 0;
|
||||
Lumps[i].LumpNameSetup(name);
|
||||
Lump.LumpNameSetup(name);
|
||||
if (Lump.IndexNum > 0)
|
||||
{
|
||||
// Create a second entry for looking up by index.
|
||||
Lumps.Reserve(1);
|
||||
auto& l = Lumps.Last();
|
||||
l = Lumps[Lumps.Size() - 2];
|
||||
snprintf(name, 13, "{%d}.%c%c%c", l.IndexNum, lumps[i].Extension[0], lumps[i].Extension[1], lumps[i].Extension[2]);
|
||||
}
|
||||
}
|
||||
delete[] lumps;
|
||||
return true;
|
||||
}
|
||||
|
||||
FRFFFile::~FRFFFile()
|
||||
{
|
||||
if (Lumps != NULL)
|
||||
{
|
||||
delete[] Lumps;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
919
source/common/filesystem/filesystem.cpp
Normal file
919
source/common/filesystem/filesystem.cpp
Normal file
|
@ -0,0 +1,919 @@
|
|||
/*
|
||||
** filesystem.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2009 Randy Heit
|
||||
** Copyright 2005-2019 Christoph Oelckers
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "m_argv.h"
|
||||
#include "cmdlib.h"
|
||||
#include "printf.h"
|
||||
//#include "c_dispatch.h"
|
||||
#include "filesystem.h"
|
||||
#include "superfasthash.h"
|
||||
#include "resourcefile.h"
|
||||
//#include "md5.h"
|
||||
//#include "doomstat.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
#define NULL_INDEX (0xffffffff)
|
||||
|
||||
struct FileSystem::FileRecord
|
||||
{
|
||||
int rfnum;
|
||||
FResourceLump *lump;
|
||||
};
|
||||
|
||||
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||||
|
||||
static void PrintLastError ();
|
||||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
FileSystem Files;
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
|
||||
FileSystem::~FileSystem ()
|
||||
{
|
||||
DeleteAll();
|
||||
}
|
||||
|
||||
void FileSystem::DeleteAll ()
|
||||
{
|
||||
FileInfo.Clear();
|
||||
NumEntries = 0;
|
||||
|
||||
for (int i = Files.Size() - 1; i >= 0; --i)
|
||||
{
|
||||
delete Files[i];
|
||||
}
|
||||
Files.Clear();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// InitMultipleFiles
|
||||
//
|
||||
// Pass a null terminated list of files to use. All files are optional,
|
||||
// but at least one file must be found. Lump names can appear multiple
|
||||
// times. The name searcher looks backwards, so a later file can
|
||||
// override an earlier one.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::InitMultipleFiles (TArray<FString> &filenames, const TArray<FString> &deletelumps)
|
||||
{
|
||||
int numfiles;
|
||||
|
||||
// open all the files, load headers, and count lumps
|
||||
DeleteAll();
|
||||
numfiles = 0;
|
||||
|
||||
for(unsigned i=0;i<filenames.Size(); i++)
|
||||
{
|
||||
int baselump = NumEntries;
|
||||
AddFile (filenames[i]);
|
||||
}
|
||||
|
||||
NumEntries = FileInfo.Size();
|
||||
if (NumEntries == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// [RH] Set up hash table
|
||||
Hashes.Resize(8 * NumEntries);
|
||||
FirstFileIndex_BaseName = &Hashes[0];
|
||||
NextFileIndex_BaseName = &Hashes[NumEntries];
|
||||
FirstFileIndex_FullName = &Hashes[NumEntries*2];
|
||||
NextFileIndex_FullName = &Hashes[NumEntries*3];
|
||||
FirstFileIndex_NoExt = &Hashes[NumEntries*4];
|
||||
NextFileIndex_NoExt = &Hashes[NumEntries*5];
|
||||
FirstFileIndex_BaseExt = &Hashes[NumEntries*6];
|
||||
NextFileIndex_BaseExt = &Hashes[NumEntries*7];
|
||||
InitHashChains ();
|
||||
FileInfo.ShrinkToFit();
|
||||
Files.ShrinkToFit();
|
||||
return NumEntries;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// AddFile
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FileSystem::AddFile (const char *filename, FileReader *filer)
|
||||
{
|
||||
int startlump;
|
||||
bool isdir = false;
|
||||
FileReader fr;
|
||||
|
||||
if (filer == nullptr)
|
||||
{
|
||||
// Does this exist? If so, is it a directory?
|
||||
if (!DirEntryExists(filename, &isdir))
|
||||
{
|
||||
Printf("%s: File or Directory not found\n", filename);
|
||||
PrintLastError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isdir)
|
||||
{
|
||||
if (!fr.OpenFile(filename))
|
||||
{ // Didn't find file
|
||||
Printf ("%s: File not found\n", filename);
|
||||
PrintLastError ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else fr = std::move(*filer);
|
||||
|
||||
Printf (" adding %s", filename);
|
||||
startlump = NumEntries;
|
||||
|
||||
FResourceFile *resfile;
|
||||
|
||||
if (!isdir)
|
||||
resfile = FResourceFile::OpenResourceFile(filename, fr);
|
||||
else
|
||||
resfile = FResourceFile::OpenDirectory(filename);
|
||||
|
||||
if (resfile != NULL)
|
||||
{
|
||||
uint32_t lumpstart = FileInfo.Size();
|
||||
|
||||
resfile->SetFirstLump(lumpstart);
|
||||
for (uint32_t i=0; i < resfile->LumpCount(); i++)
|
||||
{
|
||||
FResourceLump *lump = resfile->GetLump(i);
|
||||
FileSystem::FileRecord *lump_p = &FileInfo[FileInfo.Reserve(1)];
|
||||
|
||||
lump_p->lump = lump;
|
||||
lump_p->rfnum = Files.Size();
|
||||
}
|
||||
|
||||
Files.Push(resfile);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetFileRecordCheckIfWadLoaded
|
||||
//
|
||||
// Returns true if the specified wad is loaded, false otherwise.
|
||||
// If a fully-qualified path is specified, then the wad must match exactly.
|
||||
// Otherwise, any wad with that name will work, whatever its path.
|
||||
// Returns the wads index if found, or -1 if not.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::CheckIfResourceFileLoaded (const char *name) noexcept
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (strrchr (name, '/') != NULL)
|
||||
{
|
||||
for (i = 0; i < Files.Size(); ++i)
|
||||
{
|
||||
if (stricmp (GetResourceFileFullName (i), name) == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < Files.Size(); ++i)
|
||||
{
|
||||
if (stricmp (GetResourceFileName (i), name) == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetFileRecordFindFile
|
||||
//
|
||||
// Same as above but looks for a fully qualified name from a .zip
|
||||
// These don't care about namespaces though because those are part
|
||||
// of the path.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::FindFile (const char *name, ELookupMode lookupmode, int filenum) const noexcept
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (name == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
uint32_t* fli;
|
||||
uint32_t* nli;
|
||||
|
||||
switch (lookupmode)
|
||||
{
|
||||
case ELookupMode::FullName:
|
||||
fli = FirstFileIndex_FullName;
|
||||
nli = NextFileIndex_FullName;
|
||||
break;
|
||||
|
||||
case ELookupMode::NoExtension:
|
||||
fli = FirstFileIndex_NoExt;
|
||||
nli = NextFileIndex_NoExt;
|
||||
break;
|
||||
|
||||
case ELookupMode::BaseName:
|
||||
fli = FirstFileIndex_BaseName;
|
||||
nli = NextFileIndex_BaseName;
|
||||
break;
|
||||
|
||||
case ELookupMode::BaseWithExtension:
|
||||
fli = FirstFileIndex_BaseExt;
|
||||
nli = NextFileIndex_BaseExt;
|
||||
break;
|
||||
|
||||
}
|
||||
auto len = strlen(name);
|
||||
|
||||
for (i = fli[MakeKey(name) % NumEntries]; i != NULL_INDEX; i = nli[i])
|
||||
{
|
||||
auto lump = FileInfo[i].lump;
|
||||
if (FileInfo[i].rfnum != filenum) continue;
|
||||
const char* fn = lump->FullName.GetChars();
|
||||
const char* fnstart, * fnend;
|
||||
if (lookupmode == ELookupMode::BaseName || lookupmode == ELookupMode::BaseWithExtension) fnstart = fn + lump->PathLen;
|
||||
else fnstart = fn;
|
||||
|
||||
if ((lookupmode == ELookupMode::NoExtension || lookupmode == ELookupMode::BaseName) && lump->ExtStart >= 0) fnend = fn + lump->ExtStart;
|
||||
else fnend = fn + lump->FullName.Len();
|
||||
|
||||
if ((fnend - fnstart) == (ptrdiff_t)len)
|
||||
{
|
||||
if (!strnicmp(name, fnstart, len))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetFileRecordGetFile
|
||||
//
|
||||
// Calls GetFileRecordFindFile, but bombs out if not found.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::GetFile (const char *name, ELookupMode lookupmode, int filenum) const
|
||||
{
|
||||
int i;
|
||||
|
||||
i = FindFile (name, lookupmode, filenum);
|
||||
|
||||
if (i == -1)
|
||||
{
|
||||
FStringf error("GetFile: %s not found!", name);
|
||||
throw FileSystemError(error.GetChars());
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetFileRecordLumpLength
|
||||
//
|
||||
// Returns the buffer size needed to load the given lump.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::FileLength (int lump) const
|
||||
{
|
||||
if ((size_t)lump >= NumEntries)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return FileInfo[lump].lump->LumpSize;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetLumpOffset
|
||||
//
|
||||
// Returns the offset from the beginning of the file to the lump.
|
||||
// Returns -1 if the lump is compressed or can't be read directly
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::GetFileOffset (int lump)
|
||||
{
|
||||
if ((size_t)lump >= NumEntries)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return FileInfo[lump].lump->GetFileOffset();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetLumpOffset
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::GetFileFlags (int lump)
|
||||
{
|
||||
if ((size_t)lump >= NumEntries)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return FileInfo[lump].lump->Flags;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetFileRecordInitHashChains
|
||||
//
|
||||
// Prepares the lumpinfos for hashing.
|
||||
// (Hey! This looks suspiciously like something from Boom! :-)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FileSystem::InitHashChains (void)
|
||||
{
|
||||
char name[8];
|
||||
unsigned int i, j;
|
||||
|
||||
// Mark all buckets as empty
|
||||
memset(FirstFileIndex_BaseExt, 255, NumEntries * sizeof(FirstFileIndex_BaseExt[0]));
|
||||
memset(NextFileIndex_BaseExt, 255, NumEntries * sizeof(NextFileIndex_BaseExt[0]));
|
||||
memset (FirstFileIndex_BaseName, 255, NumEntries*sizeof(FirstFileIndex_BaseName[0]));
|
||||
memset (NextFileIndex_BaseName, 255, NumEntries*sizeof(NextFileIndex_BaseName[0]));
|
||||
memset (FirstFileIndex_FullName, 255, NumEntries*sizeof(FirstFileIndex_FullName[0]));
|
||||
memset (NextFileIndex_FullName, 255, NumEntries*sizeof(NextFileIndex_FullName[0]));
|
||||
memset(FirstFileIndex_NoExt, 255, NumEntries * sizeof(FirstFileIndex_NoExt[0]));
|
||||
memset(NextFileIndex_NoExt, 255, NumEntries * sizeof(NextFileIndex_NoExt[0]));
|
||||
|
||||
// Now set up the chains
|
||||
for (i = 0; i < (unsigned)NumEntries; i++)
|
||||
{
|
||||
|
||||
// Do the same for the full paths
|
||||
auto lump = FileInfo[i].lump;
|
||||
auto& Name = lump->FullName;
|
||||
if (Name.IsNotEmpty())
|
||||
{
|
||||
j = MakeKey(Name) % NumEntries;
|
||||
NextFileIndex_FullName[i] = FirstFileIndex_FullName[j];
|
||||
FirstFileIndex_FullName[j] = i;
|
||||
|
||||
j = MakeKey(Name + lump->PathLen) % NumEntries;
|
||||
NextFileIndex_BaseExt[i] = FirstFileIndex_BaseExt[j];
|
||||
FirstFileIndex_BaseExt[j] = i;
|
||||
|
||||
j = MakeKey(Name, lump->ExtStart) % NumEntries;
|
||||
NextFileIndex_NoExt[i] = FirstFileIndex_NoExt[j];
|
||||
FirstFileIndex_NoExt[j] = i;
|
||||
|
||||
if (lump->ExtStart > lump->PathLen)
|
||||
{
|
||||
j = MakeKey(Name, lump->ExtStart) % NumEntries;
|
||||
NextFileIndex_NoExt[i] = FirstFileIndex_NoExt[j];
|
||||
FirstFileIndex_NoExt[j] = i;
|
||||
|
||||
j = MakeKey(Name + lump->PathLen, lump->ExtStart - lump->PathLen) % NumEntries;
|
||||
NextFileIndex_BaseName[i] = FirstFileIndex_BaseName[j];
|
||||
FirstFileIndex_BaseName[j] = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
NextFileIndex_NoExt[i] = NextFileIndex_FullName[i];
|
||||
FirstFileIndex_NoExt[i] = FirstFileIndex_FullName[i];
|
||||
|
||||
NextFileIndex_BaseName[i] = NextFileIndex_BaseExt[i];
|
||||
FirstFileIndex_BaseName[j] = FirstFileIndex_BaseExt[i];
|
||||
}
|
||||
|
||||
FString nameNoExt = Name;
|
||||
auto dot = nameNoExt.LastIndexOf('.');
|
||||
auto slash = nameNoExt.LastIndexOf('/');
|
||||
if (dot > slash) nameNoExt.Truncate(dot);
|
||||
|
||||
j = MakeKey(nameNoExt) % NumEntries;
|
||||
NextFileIndex_NoExt[i] = FirstFileIndex_NoExt[j];
|
||||
FirstFileIndex_NoExt[j] = i;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Iterate
|
||||
//
|
||||
// Find a named lump. Specifically allows duplicates for merging of e.g.
|
||||
// SNDINFO lumps.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::Iterate (const char *name, int *lastlump, ELookupMode lookupmode)
|
||||
{
|
||||
union
|
||||
{
|
||||
char name8[8];
|
||||
uint64_t qname;
|
||||
};
|
||||
FileRecord *lump_p;
|
||||
|
||||
|
||||
assert(lastlump != NULL && *lastlump >= 0);
|
||||
lump_p = &FileInfo[*lastlump];
|
||||
auto len = strlen(name);
|
||||
while (lump_p < &FileInfo[NumEntries])
|
||||
{
|
||||
auto lump = lump_p->lump;
|
||||
const char* fn = lump->FullName.GetChars();
|
||||
const char* fnstart, * fnend;
|
||||
if (lookupmode == ELookupMode::BaseName || lookupmode == ELookupMode::BaseWithExtension) fnstart = fn + lump->PathLen;
|
||||
else fnstart = fn;
|
||||
|
||||
if ((lookupmode == ELookupMode::NoExtension || lookupmode == ELookupMode::BaseName) && lump->ExtStart >= 0) fnend = fn + lump->ExtStart;
|
||||
else fnend = fn + lump->FullName.Len();
|
||||
|
||||
if ((fnend - fnstart) == (ptrdiff_t)len)
|
||||
{
|
||||
if (!strnicmp(name, fnstart, len))
|
||||
{
|
||||
int lump = int(lump_p - &FileInfo[0]);
|
||||
*lastlump = lump + 1;
|
||||
return lump;
|
||||
}
|
||||
}
|
||||
lump_p++;
|
||||
}
|
||||
*lastlump = NumEntries;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetFileRecordGetLumpName
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
const char *FileSystem::GetFileName (int lump) const
|
||||
{
|
||||
if ((size_t)lump >= NumEntries)
|
||||
return nullptr;
|
||||
else
|
||||
return FileInfo[lump].lump->FullName;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FileSystem :: GetLumpFullPath
|
||||
//
|
||||
// Returns the name of the lump's wad prefixed to the lump's full name.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FString FileSystem::GetFileFullPath(int lump) const
|
||||
{
|
||||
FString foo;
|
||||
|
||||
if ((size_t) lump < NumEntries)
|
||||
{
|
||||
foo << GetResourceFileName(FileInfo[lump].rfnum) << ':' << GetFileName(lump);
|
||||
}
|
||||
return foo;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FileSystem :: GetFileIndexNum
|
||||
//
|
||||
// Returns the index number for this lump. This is *not* the lump's position
|
||||
// in the lump directory, but rather a special value that RFF can associate
|
||||
// with files. Other archive types will return 0, since they don't have it.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::GetRFFIndexNum(int lump) const
|
||||
{
|
||||
if ((size_t)lump >= NumEntries)
|
||||
return 0;
|
||||
else
|
||||
return FileInfo[lump].lump->GetIndexNum();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetFileRecordGetLumpFile
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::GetFileContainer (int lump) const
|
||||
{
|
||||
if ((size_t)lump >= FileInfo.Size())
|
||||
return -1;
|
||||
return FileInfo[lump].rfnum;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetLumpsInFolder
|
||||
//
|
||||
// Gets all lumps within a single folder in the hierarchy.
|
||||
// If 'atomic' is set, it treats folders as atomic, i.e. only the
|
||||
// content of the last found resource file having the given folder name gets used.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static int folderentrycmp(const void *a, const void *b)
|
||||
{
|
||||
auto A = (FolderEntry*)a;
|
||||
auto B = (FolderEntry*)b;
|
||||
return strcmp(A->name, B->name);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
unsigned FileSystem::GetFilesInFolder(const char *inpath, TArray<FolderEntry> &result, bool atomic) const
|
||||
{
|
||||
FString path = inpath;
|
||||
path.Substitute("\\", "/");
|
||||
path.ToLower();
|
||||
if (path[path.Len() - 1] != '/') path += '/';
|
||||
result.Clear();
|
||||
for (unsigned i = 0; i < FileInfo.Size(); i++)
|
||||
{
|
||||
if (FileInfo[i].lump->FullName.IndexOf(path) == 0)
|
||||
{
|
||||
// Only if it hasn't been replaced.
|
||||
if ((unsigned)FindFile(FileInfo[i].lump->FullName) == i)
|
||||
{
|
||||
result.Push({ FileInfo[i].lump->FullName.GetChars(), i });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result.Size())
|
||||
{
|
||||
int maxfile = -1;
|
||||
if (atomic)
|
||||
{
|
||||
// Find the highest resource file having content in the given folder.
|
||||
for (auto & entry : result)
|
||||
{
|
||||
int thisfile = FileInfo[entry.lumpnum].rfnum;
|
||||
if (thisfile > maxfile) maxfile = thisfile;
|
||||
}
|
||||
// Delete everything from older files.
|
||||
for (int i = result.Size() - 1; i >= 0; i--)
|
||||
{
|
||||
if (FileInfo[result[i].lumpnum].rfnum != maxfile) result.Delete(i);
|
||||
}
|
||||
}
|
||||
qsort(result.Data(), result.Size(), sizeof(FolderEntry), folderentrycmp);
|
||||
}
|
||||
return result.Size();
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetFileRecordReadFile
|
||||
//
|
||||
// Loads the lump into a TArray and returns it.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
TArray<uint8_t> FileSystem::GetFileData(int lump, int pad)
|
||||
{
|
||||
auto lumpr = OpenFileReader(lump);
|
||||
auto size = lumpr.GetLength();
|
||||
TArray<uint8_t> data(size + pad, true);
|
||||
auto numread = lumpr.Read(data.Data(), size);
|
||||
|
||||
if (numread != size)
|
||||
{
|
||||
FStringf err("GetFileRecordReadFile: only read %ld of %ld on lump %i\n", numread, size, lump);
|
||||
throw FileSystemError(err);
|
||||
}
|
||||
if (pad > 0) memset(&data[size], 0, pad);
|
||||
return data;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ReadFile - variant 2
|
||||
//
|
||||
// Loads the lump into a newly created buffer and returns it.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FileData FileSystem::ReadFile (int lump)
|
||||
{
|
||||
return FileData(FString(ELumpNum(lump)));
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OpenLumpReader
|
||||
//
|
||||
// uses a more abstract interface to allow for easier low level optimization later
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
||||
FileReader FileSystem::OpenFileReader(int lump)
|
||||
{
|
||||
if ((unsigned)lump >= (unsigned)FileInfo.Size())
|
||||
{
|
||||
FStringf err("OpenFileReader: %u >= NumEntries", lump);
|
||||
throw FileSystemError(err);
|
||||
}
|
||||
|
||||
auto rl = FileInfo[lump].lump;
|
||||
auto rd = rl->GetReader();
|
||||
|
||||
if (rl->RefCount == 0 && rd != nullptr && !rd->GetBuffer() && !(rl->Flags & (LUMPF_BLOODCRYPT | LUMPF_COMPRESSED)))
|
||||
{
|
||||
FileReader rdr;
|
||||
rdr.OpenFilePart(*rd, rl->GetFileOffset(), rl->LumpSize);
|
||||
return rdr;
|
||||
}
|
||||
return rl->NewReader(); // This always gets a reader to the cache
|
||||
}
|
||||
|
||||
FileReader FileSystem::ReopenFileReader(int lump, bool alwayscache)
|
||||
{
|
||||
if ((unsigned)lump >= (unsigned)FileInfo.Size())
|
||||
{
|
||||
FStringf err("ReopenFileReader: %u >= NumEntries", lump);
|
||||
throw FileSystemError(err);
|
||||
}
|
||||
|
||||
auto rl = FileInfo[lump].lump;
|
||||
auto rd = rl->GetReader();
|
||||
|
||||
if (rl->RefCount == 0 && rd != nullptr && !rd->GetBuffer() && !alwayscache && !(rl->Flags & (LUMPF_BLOODCRYPT|LUMPF_COMPRESSED)))
|
||||
{
|
||||
int fileno = FileInfo[lump].rfnum;
|
||||
const char *filename = GetResourceFileFullName(fileno);
|
||||
FileReader fr;
|
||||
if (fr.OpenFile(filename, rl->GetFileOffset(), rl->LumpSize))
|
||||
{
|
||||
return fr;
|
||||
}
|
||||
}
|
||||
return rl->NewReader(); // This always gets a reader to the cache
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetFileRecordGetResourceFileName
|
||||
//
|
||||
// Returns the name of the given wad.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
const char *FileSystem::GetResourceFileName (int rfnum) const noexcept
|
||||
{
|
||||
const char *name, *slash;
|
||||
|
||||
if ((uint32_t)rfnum >= Files.Size())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
name = Files[rfnum]->FileName;
|
||||
slash = strrchr (name, '/');
|
||||
return slash != NULL ? slash+1 : name;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::GetFirstEntry (int rfnum) const noexcept
|
||||
{
|
||||
if ((uint32_t)rfnum >= Files.Size())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Files[rfnum]->GetFirstLump();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::GetLastEntry (int rfnum) const noexcept
|
||||
{
|
||||
if ((uint32_t)rfnum >= Files.Size())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Files[rfnum]->GetFirstLump() + Files[rfnum]->LumpCount() - 1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FileSystem::GetEntryCount (int rfnum) const noexcept
|
||||
{
|
||||
if ((uint32_t)rfnum >= Files.Size())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Files[rfnum]->LumpCount();
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetFileRecordGetResourceFileFullName
|
||||
//
|
||||
// Returns the name of the given wad, including any path
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
const char *FileSystem::GetResourceFileFullName (int rfnum) const noexcept
|
||||
{
|
||||
if ((unsigned int)rfnum >= Files.Size())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Files[rfnum]->FileName;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// IsEncryptedFile
|
||||
//
|
||||
// Returns true if the first 256 bytes of the lump are encrypted for Blood.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FileSystem::IsEncryptedFile(int lump) const noexcept
|
||||
{
|
||||
if ((unsigned)lump >= (unsigned)NumEntries)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return !!(FileInfo[lump].lump->Flags & LUMPF_BLOODCRYPT);
|
||||
}
|
||||
|
||||
|
||||
// FileData -----------------------------------------------------------------
|
||||
|
||||
FileData::FileData ()
|
||||
{
|
||||
}
|
||||
|
||||
FileData::FileData (const FileData ©)
|
||||
{
|
||||
Block = copy.Block;
|
||||
}
|
||||
|
||||
FileData &FileData::operator = (const FileData ©)
|
||||
{
|
||||
Block = copy.Block;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FileData::FileData (const FString &source)
|
||||
: Block (source)
|
||||
{
|
||||
}
|
||||
|
||||
FileData::~FileData ()
|
||||
{
|
||||
}
|
||||
|
||||
FString::FString (ELumpNum lumpnum)
|
||||
{
|
||||
auto lumpr = Files.OpenFileReader ((int)lumpnum);
|
||||
auto size = lumpr.GetLength ();
|
||||
AllocBuffer (1 + size);
|
||||
auto numread = lumpr.Read (&Chars[0], size);
|
||||
Chars[size] = '\0';
|
||||
|
||||
if (numread != size)
|
||||
{
|
||||
FStringf err("ConstructStringFromLump: Only read %ld of %ld bytes on lump %i (%s)\n",
|
||||
numread, size, lumpnum, Files.GetFileName((int)lumpnum));
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PrintLastError
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
#ifdef _WIN32
|
||||
//#define WIN32_LEAN_AND_MEAN
|
||||
//#include <windows.h>
|
||||
|
||||
#if 0
|
||||
extern "C" {
|
||||
__declspec(dllimport) unsigned long __stdcall FormatMessageA(
|
||||
unsigned long dwFlags,
|
||||
const void *lpSource,
|
||||
unsigned long dwMessageId,
|
||||
unsigned long dwLanguageId,
|
||||
char **lpBuffer,
|
||||
unsigned long nSize,
|
||||
va_list *Arguments
|
||||
);
|
||||
__declspec(dllimport) void * __stdcall LocalFree (void *);
|
||||
__declspec(dllimport) unsigned long __stdcall GetLastError ();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void PrintLastError ()
|
||||
{
|
||||
char *lpMsgBuf;
|
||||
FormatMessageA(0x1300 /*FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS*/,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
1 << 10 /*MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)*/, // Default language
|
||||
(LPSTR)&lpMsgBuf,
|
||||
0,
|
||||
NULL
|
||||
);
|
||||
Printf (TEXTCOLOR_RED " %s\n", lpMsgBuf);
|
||||
// Free the buffer.
|
||||
LocalFree( lpMsgBuf );
|
||||
}
|
||||
#else
|
||||
static void PrintLastError ()
|
||||
{
|
||||
Printf (TEXTCOLOR_RED " %s\n", strerror(errno));
|
||||
}
|
||||
#endif
|
||||
|
142
source/common/filesystem/filesystem.h
Normal file
142
source/common/filesystem/filesystem.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
#pragma once
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// File system I/O functions.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdexcept>
|
||||
#include "files.h"
|
||||
#include "tarray.h"
|
||||
#include "zstring.h"
|
||||
|
||||
// We do not want to expose the resource file interface here.
|
||||
class FResourceFile;
|
||||
struct FResourceLump;
|
||||
|
||||
class FileSystemError : std::runtime_error
|
||||
{
|
||||
public:
|
||||
FileSystemError(const char* err) : std::runtime_error(err) {}
|
||||
};
|
||||
|
||||
// A file in memory.
|
||||
class FileData
|
||||
{
|
||||
public:
|
||||
FileData ();
|
||||
|
||||
FileData (const FileData ©);
|
||||
FileData &operator= (const FileData ©);
|
||||
~FileData ();
|
||||
void *GetMem () { return Block.Len() == 0 ? NULL : (void *)Block.GetChars(); }
|
||||
size_t GetSize () { return Block.Len(); }
|
||||
FString GetString () { return Block; }
|
||||
|
||||
private:
|
||||
FileData (const FString &source);
|
||||
|
||||
FString Block;
|
||||
|
||||
friend class FileSystem;
|
||||
};
|
||||
|
||||
struct FolderEntry
|
||||
{
|
||||
const char *name;
|
||||
unsigned lumpnum;
|
||||
};
|
||||
|
||||
enum class ELookupMode
|
||||
{
|
||||
FullName = 0,
|
||||
NoExtension = 1,
|
||||
BaseName = 2,
|
||||
BaseWithExtension = 3
|
||||
};
|
||||
|
||||
class FileSystem
|
||||
{
|
||||
public:
|
||||
FileSystem () = default;
|
||||
~FileSystem ();
|
||||
|
||||
int InitMultipleFiles (TArray<FString> &filenames, const TArray<FString> &todelete);
|
||||
void AddFile (const char *filename, FileReader *wadinfo = NULL);
|
||||
int CheckIfResourceFileLoaded (const char *name) noexcept;
|
||||
|
||||
const char *GetResourceFileName (int filenum) const noexcept;
|
||||
const char *GetResourceFileFullName (int filenum) const noexcept;
|
||||
|
||||
int GetFirstEntry(int filenum) const noexcept;
|
||||
int GetLastEntry(int filenum) const noexcept;
|
||||
int GetEntryCount(int filenum) const noexcept;
|
||||
|
||||
int FindFile (const char *name, ELookupMode lookupmode = ELookupMode::FullName, int filenum = -1) const noexcept;
|
||||
int GetFile (const char *name, ELookupMode lookupmode = ELookupMode::FullName, int filenum = -1) const; // Like FindFile, but throws an exception when it cannot find what it looks for.
|
||||
|
||||
int FindFile (const FString &name, ELookupMode lookupmode = ELookupMode::FullName, int filenum = -1) const noexcept { return FindFile(name.GetChars(), lookupmode, filenum); }
|
||||
int GetFile (const FString &name, ELookupMode lookupmode = ELookupMode::FullName, int filenum = -1) const { return GetFile(name.GetChars(), lookupmode, filenum); }
|
||||
|
||||
int FindFile (const std::string &name, ELookupMode lookupmode = ELookupMode::FullName, int filenum = -1) const noexcept { return FindFile(name.c_str(), lookupmode, filenum); }
|
||||
int GetFile (const std::string &name, ELookupMode lookupmode = ELookupMode::FullName, int filenum = -1) const { return GetFile(name.c_str(), lookupmode, filenum); }
|
||||
|
||||
TArray<uint8_t> GetFileData(int file, int pad = 0); // reads file into a writable buffer and optionally adds some padding at the end. (FileData isn't writable!)
|
||||
FileData ReadFile (int file);
|
||||
FileData ReadFile (const char *name) { return ReadFile (GetFile (name)); }
|
||||
|
||||
FileReader OpenFileReader(int file); // opens a reader that redirects to the containing file's one.
|
||||
FileReader ReopenFileReader(int file, bool alwayscache = false); // opens an independent reader.
|
||||
|
||||
int Iterate (const char *name, int *lastfile, ELookupMode lookupmode = ELookupMode::FullName); // [RH] Find files with duplication
|
||||
|
||||
static uint32_t FileNameHash (const char *name); // [RH] Create hash key from a given name
|
||||
|
||||
int FileLength (int file) const;
|
||||
int GetFileOffset (int file); // [RH] Returns offset of file in the wadfile
|
||||
int GetFileFlags (int file); // Return the flags for this file
|
||||
void GetFileName (char *to, int file) const; // [RH] Copies the file name to to using uppercopy
|
||||
const char *GetFileName (int file) const;
|
||||
FString GetFileFullPath (int file) const; // [RH] Returns wad's name + file's full name
|
||||
int GetFileContainer (int file) const; // [RH] Returns filenum for a specified file
|
||||
int GetRFFIndexNum (int file) const; // Returns the RFF index number for this file
|
||||
unsigned GetFilesInFolder(const char *path, TArray<FolderEntry> &result, bool atomic) const;
|
||||
|
||||
bool IsEncryptedFile(int file) const noexcept;
|
||||
|
||||
int GetNumResourceFiles() const { return NumFiles; }
|
||||
int GetNumEntries () const { return NumEntries; }
|
||||
|
||||
protected:
|
||||
|
||||
struct FileRecord;
|
||||
|
||||
TArray<FResourceFile *> Files;
|
||||
TArray<FileRecord> FileInfo;
|
||||
|
||||
TArray<uint32_t> Hashes; // one allocation for all hash lists.
|
||||
uint32_t *FirstFileIndex_BaseName; // Hash information for the base name (no path and no extension)
|
||||
uint32_t *NextFileIndex_BaseName;
|
||||
|
||||
uint32_t* FirstFileIndex_BaseExt; // Hash information for the base name (no path and no extension)
|
||||
uint32_t* NextFileIndex_BaseExt;
|
||||
|
||||
uint32_t *FirstFileIndex_FullName; // The same information for fully qualified paths
|
||||
uint32_t *NextFileIndex_FullName;
|
||||
|
||||
uint32_t *FirstFileIndex_NoExt; // The same information for fully qualified paths but without the extension
|
||||
uint32_t *NextFileIndex_NoExt;
|
||||
|
||||
uint32_t NumFiles = 0; // Not necessarily the same as FileInfo.Size()
|
||||
uint32_t NumEntries;
|
||||
|
||||
void InitHashChains (); // [RH] Set up the lumpinfo hashing
|
||||
|
||||
private:
|
||||
void DeleteAll();
|
||||
};
|
||||
|
||||
extern FileSystem Files;
|
||||
|
|
@ -84,11 +84,9 @@ FResourceLump::~FResourceLump()
|
|||
|
||||
void FResourceLump::LumpNameSetup(FString iname)
|
||||
{
|
||||
long slash = iname.LastIndexOf('/');
|
||||
FString base = (slash >= 0) ? iname.Mid(slash + 1) : iname;
|
||||
auto dot = base.LastIndexOf('.');
|
||||
if (dot >= 0) base.Truncate(dot);
|
||||
BaseName = base;
|
||||
PathLen = iname.LastIndexOf('/') + 1;
|
||||
ExtStart = iname.LastIndexOf('.');
|
||||
if (ExtStart <= PathLen) ExtStart = -1;
|
||||
FullName = iname;
|
||||
}
|
||||
|
||||
|
@ -373,7 +371,6 @@ void FResourceFile::JunkLeftoverFilters(void *lumps, size_t lumpsize, uint32_t m
|
|||
{
|
||||
FResourceLump *lump = (FResourceLump *)p;
|
||||
lump->FullName = "";
|
||||
lump->BaseName = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,8 +49,9 @@ struct FResourceLump
|
|||
int LumpSize = 0;
|
||||
int RefCount = 0;
|
||||
int Flags = 0;
|
||||
int PathLen = 0;
|
||||
int ExtStart = -1;
|
||||
FString FullName; // Name with extension and path
|
||||
FString BaseName; // Name without extension and path
|
||||
FResourceFile * Owner = nullptr;
|
||||
TArray<uint8_t> Cache;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue