mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-11-10 14:52:01 +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
|
@ -819,6 +819,7 @@ set (PCH_SOURCES
|
||||||
common/utility/memarena.cpp
|
common/utility/memarena.cpp
|
||||||
common/utility/sc_man.cpp
|
common/utility/sc_man.cpp
|
||||||
|
|
||||||
|
common/filesystem/filesystem.cpp
|
||||||
common/filesystem/ancientzip.cpp
|
common/filesystem/ancientzip.cpp
|
||||||
common/filesystem/file_zip.cpp
|
common/filesystem/file_zip.cpp
|
||||||
common/filesystem/file_7z.cpp
|
common/filesystem/file_7z.cpp
|
||||||
|
|
|
@ -74,8 +74,11 @@ bool FLumpFile::Open(bool quiet)
|
||||||
Lumps[0].LumpSize = (int)Reader.GetLength();
|
Lumps[0].LumpSize = (int)Reader.GetLength();
|
||||||
Lumps[0].Flags = 0;
|
Lumps[0].Flags = 0;
|
||||||
Lumps[0].FullName = FileName;
|
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;
|
NumLumps = 1;
|
||||||
|
if (Lumps[0].ExtStart < Lumps[0].PathLen) Lumps[0].ExtStart = -1;
|
||||||
/*
|
/*
|
||||||
if (!quiet)
|
if (!quiet)
|
||||||
{
|
{
|
||||||
|
|
|
@ -104,11 +104,10 @@ void BloodCrypt (void *data, int key, int len)
|
||||||
|
|
||||||
class FRFFFile : public FResourceFile
|
class FRFFFile : public FResourceFile
|
||||||
{
|
{
|
||||||
FRFFLump *Lumps;
|
TArray<FRFFLump> Lumps;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FRFFFile(const char * filename, FileReader &file);
|
FRFFFile(const char * filename, FileReader &file);
|
||||||
virtual ~FRFFFile();
|
|
||||||
virtual bool Open(bool quiet);
|
virtual bool Open(bool quiet);
|
||||||
virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; }
|
virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; }
|
||||||
};
|
};
|
||||||
|
@ -123,7 +122,6 @@ public:
|
||||||
FRFFFile::FRFFFile(const char *filename, FileReader &file)
|
FRFFFile::FRFFFile(const char *filename, FileReader &file)
|
||||||
: FResourceFile(filename, file)
|
: FResourceFile(filename, file)
|
||||||
{
|
{
|
||||||
Lumps = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -146,19 +144,22 @@ bool FRFFFile::Open(bool quiet)
|
||||||
Reader.Read (lumps, header.NumLumps * sizeof(RFFLump));
|
Reader.Read (lumps, header.NumLumps * sizeof(RFFLump));
|
||||||
BloodCrypt (lumps, header.DirOfs, 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);
|
//if (!quiet) Printf(", %d lumps\n", NumLumps);
|
||||||
for (uint32_t i = 0; i < NumLumps; ++i)
|
for (uint32_t i = 0; i < NumLumps; ++i)
|
||||||
{
|
{
|
||||||
Lumps[i].Position = LittleLong(lumps[i].FilePos);
|
Lumps.Reserve(1);
|
||||||
Lumps[i].LumpSize = LittleLong(lumps[i].Size);
|
auto& Lump = Lumps.Last();
|
||||||
Lumps[i].Owner = this;
|
|
||||||
|
Lump.Position = LittleLong(lumps[i].FilePos);
|
||||||
|
Lump.LumpSize = LittleLong(lumps[i].Size);
|
||||||
|
Lump.Owner = this;
|
||||||
if (lumps[i].Flags & 0x10)
|
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.
|
// Rearrange the name and extension to construct the fullname.
|
||||||
char name[13];
|
char name[13];
|
||||||
strncpy(name, lumps[i].Name, 8);
|
strncpy(name, lumps[i].Name, 8);
|
||||||
|
@ -170,20 +171,20 @@ bool FRFFFile::Open(bool quiet)
|
||||||
name[len+2] = lumps[i].Extension[1];
|
name[len+2] = lumps[i].Extension[1];
|
||||||
name[len+3] = lumps[i].Extension[2];
|
name[len+3] = lumps[i].Extension[2];
|
||||||
name[len+4] = 0;
|
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;
|
delete[] lumps;
|
||||||
return true;
|
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)
|
void FResourceLump::LumpNameSetup(FString iname)
|
||||||
{
|
{
|
||||||
long slash = iname.LastIndexOf('/');
|
PathLen = iname.LastIndexOf('/') + 1;
|
||||||
FString base = (slash >= 0) ? iname.Mid(slash + 1) : iname;
|
ExtStart = iname.LastIndexOf('.');
|
||||||
auto dot = base.LastIndexOf('.');
|
if (ExtStart <= PathLen) ExtStart = -1;
|
||||||
if (dot >= 0) base.Truncate(dot);
|
|
||||||
BaseName = base;
|
|
||||||
FullName = iname;
|
FullName = iname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,7 +371,6 @@ void FResourceFile::JunkLeftoverFilters(void *lumps, size_t lumpsize, uint32_t m
|
||||||
{
|
{
|
||||||
FResourceLump *lump = (FResourceLump *)p;
|
FResourceLump *lump = (FResourceLump *)p;
|
||||||
lump->FullName = "";
|
lump->FullName = "";
|
||||||
lump->BaseName = "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,8 +49,9 @@ struct FResourceLump
|
||||||
int LumpSize = 0;
|
int LumpSize = 0;
|
||||||
int RefCount = 0;
|
int RefCount = 0;
|
||||||
int Flags = 0;
|
int Flags = 0;
|
||||||
|
int PathLen = 0;
|
||||||
|
int ExtStart = -1;
|
||||||
FString FullName; // Name with extension and path
|
FString FullName; // Name with extension and path
|
||||||
FString BaseName; // Name without extension and path
|
|
||||||
FResourceFile * Owner = nullptr;
|
FResourceFile * Owner = nullptr;
|
||||||
TArray<uint8_t> Cache;
|
TArray<uint8_t> Cache;
|
||||||
|
|
||||||
|
|
|
@ -1,653 +0,0 @@
|
||||||
/*
|
|
||||||
** file_zip.cpp
|
|
||||||
**
|
|
||||||
**---------------------------------------------------------------------------
|
|
||||||
** Copyright 1998-2009 Randy Heit
|
|
||||||
** Copyright 2005-2009 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.
|
|
||||||
**---------------------------------------------------------------------------
|
|
||||||
**
|
|
||||||
**
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include "file_zip.h"
|
|
||||||
#include "cmdlib.h"
|
|
||||||
#include "basics.h"
|
|
||||||
#include "w_zip.h"
|
|
||||||
|
|
||||||
#include "ancientzip.h"
|
|
||||||
|
|
||||||
#define BUFREADCOMMENT (0x400)
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Decompression subroutine
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
static bool UncompressZipLump(char *Cache, FileReader &Reader, int Method, int LumpSize, int CompressedSize, int GPFlags)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
switch (Method)
|
|
||||||
{
|
|
||||||
case METHOD_STORED:
|
|
||||||
{
|
|
||||||
Reader.Read(Cache, LumpSize);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case METHOD_DEFLATE:
|
|
||||||
case METHOD_BZIP2:
|
|
||||||
case METHOD_LZMA:
|
|
||||||
{
|
|
||||||
FileReader frz;
|
|
||||||
if (frz.OpenDecompressor(Reader, LumpSize, Method, false, [](const char* err) { throw std::runtime_error(err); }))
|
|
||||||
{
|
|
||||||
frz.Read(Cache, LumpSize);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fixme: These should also use a stream
|
|
||||||
case METHOD_IMPLODE:
|
|
||||||
{
|
|
||||||
FZipExploder exploder;
|
|
||||||
exploder.Explode((unsigned char *)Cache, LumpSize, Reader, CompressedSize, GPFlags);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case METHOD_SHRINK:
|
|
||||||
{
|
|
||||||
ShrinkLoop((unsigned char *)Cache, LumpSize, Reader, CompressedSize);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::runtime_error &err)
|
|
||||||
{
|
|
||||||
Printf("%s\n", err.what());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FCompressedBuffer::Decompress(char *destbuffer)
|
|
||||||
{
|
|
||||||
FileReader mr;
|
|
||||||
mr.OpenMemory(mBuffer, mCompressedSize);
|
|
||||||
return UncompressZipLump(destbuffer, mr, mMethod, mSize, mCompressedSize, mZipFlags);
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Finds the central directory end record in the end of the file.
|
|
||||||
// Taken from Quake3 source but the file in question is not GPL'ed. ;)
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
|
|
||||||
static uint32_t Zip_FindCentralDir(FileReader &fin)
|
|
||||||
{
|
|
||||||
unsigned char buf[BUFREADCOMMENT + 4];
|
|
||||||
uint32_t FileSize;
|
|
||||||
uint32_t uBackRead;
|
|
||||||
uint32_t uMaxBack; // maximum size of global comment
|
|
||||||
uint32_t uPosFound=0;
|
|
||||||
|
|
||||||
FileSize = (uint32_t)fin.GetLength();
|
|
||||||
uMaxBack = std::min<uint32_t>(0xffff, FileSize);
|
|
||||||
|
|
||||||
uBackRead = 4;
|
|
||||||
while (uBackRead < uMaxBack)
|
|
||||||
{
|
|
||||||
uint32_t uReadSize, uReadPos;
|
|
||||||
int i;
|
|
||||||
if (uBackRead + BUFREADCOMMENT > uMaxBack)
|
|
||||||
uBackRead = uMaxBack;
|
|
||||||
else
|
|
||||||
uBackRead += BUFREADCOMMENT;
|
|
||||||
uReadPos = FileSize - uBackRead;
|
|
||||||
|
|
||||||
uReadSize = std::min<uint32_t>((BUFREADCOMMENT + 4), (FileSize - uReadPos));
|
|
||||||
|
|
||||||
if (fin.Seek(uReadPos, FileReader::SeekSet) != 0) break;
|
|
||||||
|
|
||||||
if (fin.Read(buf, (int32_t)uReadSize) != (int32_t)uReadSize) break;
|
|
||||||
|
|
||||||
for (i = (int)uReadSize - 3; (i--) > 0;)
|
|
||||||
{
|
|
||||||
if (buf[i] == 'P' && buf[i+1] == 'K' && buf[i+2] == 5 && buf[i+3] == 6)
|
|
||||||
{
|
|
||||||
uPosFound = uReadPos + i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uPosFound != 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return uPosFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Zip file
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FZipFile::FZipFile(const char * filename, FileReader &file)
|
|
||||||
: FResourceFile(filename, file)
|
|
||||||
{
|
|
||||||
Lumps = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FZipFile::Open(bool quiet)
|
|
||||||
{
|
|
||||||
uint32_t centraldir = Zip_FindCentralDir(Reader);
|
|
||||||
FZipEndOfCentralDirectory info;
|
|
||||||
int skipped = 0;
|
|
||||||
|
|
||||||
Lumps = NULL;
|
|
||||||
|
|
||||||
if (centraldir == 0)
|
|
||||||
{
|
|
||||||
if (!quiet) Printf(TEXTCOLOR_RED "\n%s: ZIP file corrupt!\n", FileName.GetChars());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the central directory info.
|
|
||||||
Reader.Seek(centraldir, FileReader::SeekSet);
|
|
||||||
Reader.Read(&info, sizeof(FZipEndOfCentralDirectory));
|
|
||||||
|
|
||||||
// No multi-disk zips!
|
|
||||||
if (info.NumEntries != info.NumEntriesOnAllDisks ||
|
|
||||||
info.FirstDisk != 0 || info.DiskNumber != 0)
|
|
||||||
{
|
|
||||||
if (!quiet) Printf(TEXTCOLOR_RED "\n%s: Multipart Zip files are not supported.\n", FileName.GetChars());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
NumLumps = LittleShort(info.NumEntries);
|
|
||||||
Lumps = new FZipLump[NumLumps];
|
|
||||||
|
|
||||||
// Load the entire central directory. Too bad that this contains variable length entries...
|
|
||||||
int dirsize = LittleLong(info.DirectorySize);
|
|
||||||
void *directory = malloc(dirsize);
|
|
||||||
Reader.Seek(LittleLong(info.DirectoryOffset), FileReader::SeekSet);
|
|
||||||
Reader.Read(directory, dirsize);
|
|
||||||
|
|
||||||
char *dirptr = (char*)directory;
|
|
||||||
FZipLump *lump_p = Lumps;
|
|
||||||
|
|
||||||
FString name0;
|
|
||||||
bool foundspeciallump = false;
|
|
||||||
|
|
||||||
// Check if all files have the same prefix so that this can be stripped out.
|
|
||||||
// This will only be done if there is either a MAPINFO, ZMAPINFO or GAMEINFO lump in the subdirectory, denoting a ZDoom mod.
|
|
||||||
if (NumLumps > 1) for (uint32_t i = 0; i < NumLumps; i++)
|
|
||||||
{
|
|
||||||
FZipCentralDirectoryInfo *zip_fh = (FZipCentralDirectoryInfo *)dirptr;
|
|
||||||
|
|
||||||
int len = LittleShort(zip_fh->NameLength);
|
|
||||||
FString name(dirptr + sizeof(FZipCentralDirectoryInfo), len);
|
|
||||||
|
|
||||||
dirptr += sizeof(FZipCentralDirectoryInfo) +
|
|
||||||
LittleShort(zip_fh->NameLength) +
|
|
||||||
LittleShort(zip_fh->ExtraLength) +
|
|
||||||
LittleShort(zip_fh->CommentLength);
|
|
||||||
|
|
||||||
if (dirptr > ((char*)directory) + dirsize) // This directory entry goes beyond the end of the file.
|
|
||||||
{
|
|
||||||
free(directory);
|
|
||||||
if (!quiet) Printf(TEXTCOLOR_RED "\n%s: Central directory corrupted.", FileName.GetChars());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
name.ToLower();
|
|
||||||
if (i == 0)
|
|
||||||
{
|
|
||||||
// check for special names, if one of these gets found this must be treated as a normal zip.
|
|
||||||
bool isspecial = !name.Compare("flats/") ||
|
|
||||||
name.IndexOf("/") < 0 ||
|
|
||||||
!name.Compare("textures/") ||
|
|
||||||
!name.Compare("hires/") ||
|
|
||||||
!name.Compare("sprites/") ||
|
|
||||||
!name.Compare("voxels/") ||
|
|
||||||
!name.Compare("colormaps/") ||
|
|
||||||
!name.Compare("acs/") ||
|
|
||||||
!name.Compare("maps/") ||
|
|
||||||
!name.Compare("voices/") ||
|
|
||||||
!name.Compare("patches/") ||
|
|
||||||
!name.Compare("graphics/") ||
|
|
||||||
!name.Compare("sounds/") ||
|
|
||||||
!name.Compare("music/");
|
|
||||||
if (isspecial) break;
|
|
||||||
name0 = name;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (name.IndexOf(name0) != 0)
|
|
||||||
{
|
|
||||||
name0 = "";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (!foundspeciallump)
|
|
||||||
{
|
|
||||||
// at least one of the more common definition lumps must be present.
|
|
||||||
if (name.IndexOf(name0 + "mapinfo") == 0) foundspeciallump = true;
|
|
||||||
else if (name.IndexOf(name0 + "zmapinfo") == 0) foundspeciallump = true;
|
|
||||||
else if (name.IndexOf(name0 + "gameinfo") == 0) foundspeciallump = true;
|
|
||||||
else if (name.IndexOf(name0 + "sndinfo") == 0) foundspeciallump = true;
|
|
||||||
else if (name.IndexOf(name0 + "sbarinfo") == 0) foundspeciallump = true;
|
|
||||||
else if (name.IndexOf(name0 + "menudef") == 0) foundspeciallump = true;
|
|
||||||
else if (name.IndexOf(name0 + "gldefs") == 0) foundspeciallump = true;
|
|
||||||
else if (name.IndexOf(name0 + "animdefs") == 0) foundspeciallump = true;
|
|
||||||
else if (name.IndexOf(name0 + "decorate.") == 0) foundspeciallump = true; // DECORATE is a common subdirectory name, so the check needs to be a bit different.
|
|
||||||
else if (name.Compare(name0 + "decorate") == 0) foundspeciallump = true;
|
|
||||||
else if (name.IndexOf(name0 + "zscript.") == 0) foundspeciallump = true; // same here.
|
|
||||||
else if (name.Compare(name0 + "zscript") == 0) foundspeciallump = true;
|
|
||||||
else if (name.Compare(name0 + "maps/") == 0) foundspeciallump = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If it ran through the list without finding anything it should not attempt any path remapping.
|
|
||||||
if (!foundspeciallump) name0 = "";
|
|
||||||
|
|
||||||
dirptr = (char*)directory;
|
|
||||||
lump_p = Lumps;
|
|
||||||
for (uint32_t i = 0; i < NumLumps; i++)
|
|
||||||
{
|
|
||||||
FZipCentralDirectoryInfo *zip_fh = (FZipCentralDirectoryInfo *)dirptr;
|
|
||||||
|
|
||||||
int len = LittleShort(zip_fh->NameLength);
|
|
||||||
FString name(dirptr + sizeof(FZipCentralDirectoryInfo), len);
|
|
||||||
if (name0.IsNotEmpty()) name = name.Mid(name0.Len());
|
|
||||||
dirptr += sizeof(FZipCentralDirectoryInfo) +
|
|
||||||
LittleShort(zip_fh->NameLength) +
|
|
||||||
LittleShort(zip_fh->ExtraLength) +
|
|
||||||
LittleShort(zip_fh->CommentLength);
|
|
||||||
|
|
||||||
if (dirptr > ((char*)directory) + dirsize) // This directory entry goes beyond the end of the file.
|
|
||||||
{
|
|
||||||
free(directory);
|
|
||||||
if (!quiet) Printf(TEXTCOLOR_RED "\n%s: Central directory corrupted.", FileName.GetChars());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip Directories
|
|
||||||
if (name.IsEmpty() || (name.Back() == '/' && LittleLong(zip_fh->UncompressedSize) == 0))
|
|
||||||
{
|
|
||||||
skipped++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore unknown compression formats
|
|
||||||
zip_fh->Method = LittleShort(zip_fh->Method);
|
|
||||||
if (zip_fh->Method != METHOD_STORED &&
|
|
||||||
zip_fh->Method != METHOD_DEFLATE &&
|
|
||||||
zip_fh->Method != METHOD_LZMA &&
|
|
||||||
zip_fh->Method != METHOD_BZIP2 &&
|
|
||||||
zip_fh->Method != METHOD_IMPLODE &&
|
|
||||||
zip_fh->Method != METHOD_SHRINK)
|
|
||||||
{
|
|
||||||
if (!quiet) Printf(TEXTCOLOR_YELLOW "\n%s: '%s' uses an unsupported compression algorithm (#%d).\n", FileName.GetChars(), name.GetChars(), zip_fh->Method);
|
|
||||||
skipped++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Also ignore encrypted entries
|
|
||||||
zip_fh->Flags = LittleShort(zip_fh->Flags);
|
|
||||||
if (zip_fh->Flags & ZF_ENCRYPTED)
|
|
||||||
{
|
|
||||||
if (!quiet) Printf(TEXTCOLOR_YELLOW "\n%s: '%s' is encrypted. Encryption is not supported.\n", FileName.GetChars(), name.GetChars());
|
|
||||||
skipped++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
FixPathSeperator(name);
|
|
||||||
name.ToLower();
|
|
||||||
|
|
||||||
lump_p->LumpNameSetup(name);
|
|
||||||
lump_p->LumpSize = LittleLong(zip_fh->UncompressedSize);
|
|
||||||
lump_p->Owner = this;
|
|
||||||
// The start of the Reader will be determined the first time it is accessed.
|
|
||||||
lump_p->Flags = LUMPF_ZIPFILE | LUMPFZIP_NEEDFILESTART;
|
|
||||||
lump_p->Method = uint8_t(zip_fh->Method);
|
|
||||||
if (lump_p->Method != METHOD_STORED) lump_p->Flags |= LUMPF_COMPRESSED;
|
|
||||||
lump_p->GPFlags = zip_fh->Flags;
|
|
||||||
lump_p->CRC32 = zip_fh->CRC32;
|
|
||||||
lump_p->CompressedSize = LittleLong(zip_fh->CompressedSize);
|
|
||||||
lump_p->Position = LittleLong(zip_fh->LocalHeaderOffset);
|
|
||||||
|
|
||||||
lump_p++;
|
|
||||||
}
|
|
||||||
// Resize the lump record array to its actual size
|
|
||||||
NumLumps -= skipped;
|
|
||||||
free(directory);
|
|
||||||
|
|
||||||
if (!quiet) Printf(TEXTCOLOR_NORMAL ", %d lumps\n", NumLumps);
|
|
||||||
|
|
||||||
PostProcessArchive(&Lumps[0], sizeof(FZipLump));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Zip file
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FZipFile::~FZipFile()
|
|
||||||
{
|
|
||||||
if (Lumps != NULL) delete [] Lumps;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FCompressedBuffer FZipLump::GetRawData()
|
|
||||||
{
|
|
||||||
FCompressedBuffer cbuf = { (unsigned)LumpSize, (unsigned)CompressedSize, Method, GPFlags, CRC32, new char[CompressedSize] };
|
|
||||||
if (Flags & LUMPFZIP_NEEDFILESTART) SetLumpAddress();
|
|
||||||
Owner->Reader.Seek(Position, FileReader::SeekSet);
|
|
||||||
Owner->Reader.Read(cbuf.mBuffer, CompressedSize);
|
|
||||||
return cbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// SetLumpAddress
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void FZipLump::SetLumpAddress()
|
|
||||||
{
|
|
||||||
// This file is inside a zip and has not been opened before.
|
|
||||||
// Position points to the start of the local file header, which we must
|
|
||||||
// read and skip so that we can get to the actual file data.
|
|
||||||
FZipLocalFileHeader localHeader;
|
|
||||||
int skiplen;
|
|
||||||
|
|
||||||
Owner->Reader.Seek(Position, FileReader::SeekSet);
|
|
||||||
Owner->Reader.Read(&localHeader, sizeof(localHeader));
|
|
||||||
skiplen = LittleShort(localHeader.NameLength) + LittleShort(localHeader.ExtraLength);
|
|
||||||
Position += sizeof(localHeader) + skiplen;
|
|
||||||
Flags &= ~LUMPFZIP_NEEDFILESTART;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Get reader (only returns non-NULL if not encrypted)
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FileReader *FZipLump::GetReader()
|
|
||||||
{
|
|
||||||
// Don't return the reader if this lump is encrypted
|
|
||||||
// In that case always force caching of the lump
|
|
||||||
if (Method == METHOD_STORED)
|
|
||||||
{
|
|
||||||
if (Flags & LUMPFZIP_NEEDFILESTART) SetLumpAddress();
|
|
||||||
Owner->Reader.Seek(Position, FileReader::SeekSet);
|
|
||||||
return &Owner->Reader;
|
|
||||||
}
|
|
||||||
else return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Fills the lump cache and performs decompression
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int FZipLump::FillCache()
|
|
||||||
{
|
|
||||||
if (Flags & LUMPFZIP_NEEDFILESTART) SetLumpAddress();
|
|
||||||
|
|
||||||
Owner->Reader.Seek(Position, FileReader::SeekSet);
|
|
||||||
Cache.Resize(LumpSize);
|
|
||||||
UncompressZipLump((char*)Cache.Data(), Owner->Reader, Method, LumpSize, CompressedSize, GPFlags);
|
|
||||||
RefCount = 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int FZipLump::GetFileOffset()
|
|
||||||
{
|
|
||||||
if (Method != METHOD_STORED) return -1;
|
|
||||||
if (Flags & LUMPFZIP_NEEDFILESTART) SetLumpAddress();
|
|
||||||
return Position;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// File open
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FResourceFile *CheckZip(const char *filename, FileReader &file, bool quiet)
|
|
||||||
{
|
|
||||||
char head[4];
|
|
||||||
|
|
||||||
if (file.GetLength() >= (long)sizeof(FZipLocalFileHeader))
|
|
||||||
{
|
|
||||||
file.Seek(0, FileReader::SeekSet);
|
|
||||||
file.Read(&head, 4);
|
|
||||||
file.Seek(0, FileReader::SeekSet);
|
|
||||||
if (!memcmp(head, "PK\x3\x4", 4))
|
|
||||||
{
|
|
||||||
FResourceFile *rf = new FZipFile(filename, file);
|
|
||||||
if (rf->Open(quiet)) return rf;
|
|
||||||
|
|
||||||
file = std::move(rf->Reader); // to avoid destruction of reader
|
|
||||||
delete rf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// time_to_dos
|
|
||||||
//
|
|
||||||
// Converts time from struct tm to the DOS format used by zip files.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
static std::pair<uint16_t, uint16_t> time_to_dos(struct tm *time)
|
|
||||||
{
|
|
||||||
std::pair<uint16_t, uint16_t> val;
|
|
||||||
if (time == NULL || time->tm_year < 80)
|
|
||||||
{
|
|
||||||
val.first = val.second = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
val.first = (time->tm_year - 80) * 512 + (time->tm_mon + 1) * 32 + time->tm_mday;
|
|
||||||
val.second= time->tm_hour * 2048 + time->tm_min * 32 + time->tm_sec / 2;
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// append_to_zip
|
|
||||||
//
|
|
||||||
// Write a given file to the zipFile.
|
|
||||||
//
|
|
||||||
// zipfile: zip object to be written to
|
|
||||||
//
|
|
||||||
// returns: position = success, -1 = error
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int AppendToZip(FileWriter *zip_file, const char *filename, FCompressedBuffer &content, std::pair<uint16_t, uint16_t> &dostime)
|
|
||||||
{
|
|
||||||
FZipLocalFileHeader local;
|
|
||||||
int position;
|
|
||||||
|
|
||||||
local.Magic = ZIP_LOCALFILE;
|
|
||||||
local.VersionToExtract[0] = 20;
|
|
||||||
local.VersionToExtract[1] = 0;
|
|
||||||
local.Flags = content.mMethod == METHOD_DEFLATE ? LittleShort((uint16_t)2) : LittleShort((uint16_t)content.mZipFlags);
|
|
||||||
local.Method = LittleShort((uint16_t)content.mMethod);
|
|
||||||
local.ModDate = LittleShort(dostime.first);
|
|
||||||
local.ModTime = LittleShort(dostime.second);
|
|
||||||
local.CRC32 = content.mCRC32;
|
|
||||||
local.UncompressedSize = LittleLong(content.mSize);
|
|
||||||
local.CompressedSize = LittleLong(content.mCompressedSize);
|
|
||||||
local.NameLength = LittleShort((unsigned short)strlen(filename));
|
|
||||||
local.ExtraLength = 0;
|
|
||||||
|
|
||||||
// Fill in local directory header.
|
|
||||||
|
|
||||||
position = (int)zip_file->Tell();
|
|
||||||
|
|
||||||
// Write out the header, file name, and file data.
|
|
||||||
if (zip_file->Write(&local, sizeof(local)) != sizeof(local) ||
|
|
||||||
zip_file->Write(filename, strlen(filename)) != strlen(filename) ||
|
|
||||||
zip_file->Write(content.mBuffer, content.mCompressedSize) != content.mCompressedSize)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// write_central_dir
|
|
||||||
//
|
|
||||||
// Writes the central directory entry for a file.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int AppendCentralDirectory(FileWriter *zip_file, const char *filename, FCompressedBuffer &content, std::pair<uint16_t, uint16_t> &dostime, int position)
|
|
||||||
{
|
|
||||||
FZipCentralDirectoryInfo dir;
|
|
||||||
|
|
||||||
dir.Magic = ZIP_CENTRALFILE;
|
|
||||||
dir.VersionMadeBy[0] = 20;
|
|
||||||
dir.VersionMadeBy[1] = 0;
|
|
||||||
dir.VersionToExtract[0] = 20;
|
|
||||||
dir.VersionToExtract[1] = 0;
|
|
||||||
dir.Flags = content.mMethod == METHOD_DEFLATE ? LittleShort((uint16_t)2) : LittleShort((uint16_t)content.mZipFlags);
|
|
||||||
dir.Method = LittleShort((uint16_t)content.mMethod);
|
|
||||||
dir.ModTime = LittleShort(dostime.first);
|
|
||||||
dir.ModDate = LittleShort(dostime.second);
|
|
||||||
dir.CRC32 = content.mCRC32;
|
|
||||||
dir.CompressedSize = LittleLong(content.mCompressedSize);
|
|
||||||
dir.UncompressedSize = LittleLong(content.mSize);
|
|
||||||
dir.NameLength = LittleShort((unsigned short)strlen(filename));
|
|
||||||
dir.ExtraLength = 0;
|
|
||||||
dir.CommentLength = 0;
|
|
||||||
dir.StartingDiskNumber = 0;
|
|
||||||
dir.InternalAttributes = 0;
|
|
||||||
dir.ExternalAttributes = 0;
|
|
||||||
dir.LocalHeaderOffset = LittleLong(position);
|
|
||||||
|
|
||||||
if (zip_file->Write(&dir, sizeof(dir)) != sizeof(dir) ||
|
|
||||||
zip_file->Write(filename, strlen(filename)) != strlen(filename))
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WriteZip(const char *filename, TArray<FString> &filenames, TArray<FCompressedBuffer> &content)
|
|
||||||
{
|
|
||||||
// try to determine local time
|
|
||||||
struct tm *ltime;
|
|
||||||
time_t ttime;
|
|
||||||
ttime = time(nullptr);
|
|
||||||
ltime = localtime(&ttime);
|
|
||||||
auto dostime = time_to_dos(ltime);
|
|
||||||
|
|
||||||
TArray<int> positions;
|
|
||||||
|
|
||||||
if (filenames.Size() != content.Size()) return false;
|
|
||||||
|
|
||||||
auto f = FileWriter::Open(filename);
|
|
||||||
if (f != nullptr)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < filenames.Size(); i++)
|
|
||||||
{
|
|
||||||
int pos = AppendToZip(f, filenames[i], content[i], dostime);
|
|
||||||
if (pos == -1)
|
|
||||||
{
|
|
||||||
delete f;
|
|
||||||
remove(filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
positions.Push(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dirofs = (int)f->Tell();
|
|
||||||
for (unsigned i = 0; i < filenames.Size(); i++)
|
|
||||||
{
|
|
||||||
if (AppendCentralDirectory(f, filenames[i], content[i], dostime, positions[i]) < 0)
|
|
||||||
{
|
|
||||||
delete f;
|
|
||||||
remove(filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the directory terminator.
|
|
||||||
FZipEndOfCentralDirectory dirend;
|
|
||||||
dirend.Magic = ZIP_ENDOFDIR;
|
|
||||||
dirend.DiskNumber = 0;
|
|
||||||
dirend.FirstDisk = 0;
|
|
||||||
dirend.NumEntriesOnAllDisks = dirend.NumEntries = LittleShort((uint16_t)filenames.Size());
|
|
||||||
dirend.DirectoryOffset = LittleLong(dirofs);
|
|
||||||
dirend.DirectorySize = LittleLong((uint32_t)(f->Tell() - dirofs));
|
|
||||||
dirend.ZipCommentLength = 0;
|
|
||||||
if (f->Write(&dirend, sizeof(dirend)) != sizeof(dirend))
|
|
||||||
{
|
|
||||||
delete f;
|
|
||||||
remove(filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
delete f;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
#ifndef __FILE_ZIP_H
|
|
||||||
#define __FILE_ZIP_H
|
|
||||||
|
|
||||||
#include "resourcefile.h"
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
LUMPFZIP_NEEDFILESTART = 128
|
|
||||||
};
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Zip Lump
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
struct FZipLump : public FResourceLump
|
|
||||||
{
|
|
||||||
uint16_t GPFlags;
|
|
||||||
uint8_t Method;
|
|
||||||
int CompressedSize;
|
|
||||||
int Position;
|
|
||||||
unsigned CRC32;
|
|
||||||
|
|
||||||
virtual FileReader *GetReader();
|
|
||||||
virtual int FillCache();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void SetLumpAddress();
|
|
||||||
virtual int GetFileOffset();
|
|
||||||
FCompressedBuffer GetRawData();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Zip file
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
class FZipFile : public FResourceFile
|
|
||||||
{
|
|
||||||
FZipLump *Lumps;
|
|
||||||
|
|
||||||
public:
|
|
||||||
FZipFile(const char * filename, FileReader &file);
|
|
||||||
virtual ~FZipFile();
|
|
||||||
bool Open(bool quiet);
|
|
||||||
virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,602 +0,0 @@
|
||||||
/*
|
|
||||||
** resourcefile.cpp
|
|
||||||
**
|
|
||||||
** Base classes for resource file management
|
|
||||||
**
|
|
||||||
**---------------------------------------------------------------------------
|
|
||||||
** Copyright 2009 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.
|
|
||||||
**---------------------------------------------------------------------------
|
|
||||||
**
|
|
||||||
**
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <zlib.h>
|
|
||||||
#include "resourcefile.h"
|
|
||||||
#include "cmdlib.h"
|
|
||||||
#include "basics.h"
|
|
||||||
#include "gametype.h"
|
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// File reader that reads from a lump's cache
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
class FLumpReader : public MemoryReader
|
|
||||||
{
|
|
||||||
FResourceLump *source;
|
|
||||||
|
|
||||||
public:
|
|
||||||
FLumpReader(FResourceLump *src)
|
|
||||||
: MemoryReader(NULL, src->LumpSize), source(src)
|
|
||||||
{
|
|
||||||
src->CacheLump();
|
|
||||||
bufptr = (const char*)src->Cache.Data();
|
|
||||||
}
|
|
||||||
|
|
||||||
~FLumpReader()
|
|
||||||
{
|
|
||||||
source->ReleaseCache();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Base class for resource lumps
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FResourceLump::~FResourceLump()
|
|
||||||
{
|
|
||||||
Owner = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Sets up the lump name information for anything not coming from a WAD file.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
FullName = iname;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// this is just for completeness. For non-Zips only an uncompressed lump can
|
|
||||||
// be returned.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FCompressedBuffer FResourceLump::GetRawData()
|
|
||||||
{
|
|
||||||
FCompressedBuffer cbuf = { (unsigned)LumpSize, (unsigned)LumpSize, METHOD_STORED, 0, 0, new char[LumpSize] };
|
|
||||||
memcpy(cbuf.mBuffer, CacheLump(), LumpSize);
|
|
||||||
cbuf.mCRC32 = crc32(0, (uint8_t*)cbuf.mBuffer, LumpSize);
|
|
||||||
ReleaseCache();
|
|
||||||
return cbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Returns the owner's FileReader if it can be used to access this lump
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FileReader *FResourceLump::GetReader()
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Returns a file reader to the lump's cache
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FileReader FResourceLump::NewReader()
|
|
||||||
{
|
|
||||||
return FileReader(new FLumpReader(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Caches a lump's content and increases the reference counter
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void *FResourceLump::CacheLump()
|
|
||||||
{
|
|
||||||
if (Cache.Size())
|
|
||||||
{
|
|
||||||
if (RefCount > 0) RefCount++;
|
|
||||||
}
|
|
||||||
else if (LumpSize > 0)
|
|
||||||
{
|
|
||||||
FillCache();
|
|
||||||
}
|
|
||||||
return Cache.Data();
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Decrements reference counter and frees lump if counter reaches 0
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int FResourceLump::ReleaseCache()
|
|
||||||
{
|
|
||||||
if (LumpSize > 0 && RefCount > 0)
|
|
||||||
{
|
|
||||||
if (--RefCount == 0)
|
|
||||||
{
|
|
||||||
Cache.Reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return RefCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Opens a resource file
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
typedef FResourceFile * (*CheckFunc)(const char *filename, FileReader &file, bool quiet);
|
|
||||||
|
|
||||||
FResourceFile *CheckWad(const char *filename, FileReader &file, bool quiet);
|
|
||||||
FResourceFile *CheckGRP(const char *filename, FileReader &file, bool quiet);
|
|
||||||
FResourceFile *CheckRFF(const char *filename, FileReader &file, bool quiet);
|
|
||||||
FResourceFile *CheckPak(const char *filename, FileReader &file, bool quiet);
|
|
||||||
FResourceFile *CheckZip(const char *filename, FileReader &file, bool quiet);
|
|
||||||
FResourceFile *Check7Z(const char *filename, FileReader &file, bool quiet);
|
|
||||||
FResourceFile *CheckLump(const char *filename,FileReader &file, bool quiet);
|
|
||||||
FResourceFile *CheckDir(const char *filename, bool quiet);
|
|
||||||
|
|
||||||
static CheckFunc funcs[] = { CheckGRP, CheckRFF, CheckZip, Check7Z, CheckPak, CheckLump };
|
|
||||||
|
|
||||||
FResourceFile *FResourceFile::DoOpenResourceFile(const char *filename, FileReader &file, bool quiet, bool containeronly)
|
|
||||||
{
|
|
||||||
for(size_t i = 0; i < countof(funcs) - containeronly; i++)
|
|
||||||
{
|
|
||||||
FResourceFile *resfile = funcs[i](filename, file, quiet);
|
|
||||||
if (resfile != NULL) return resfile;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
FResourceFile *FResourceFile::OpenResourceFile(const char *filename, FileReader &file, bool quiet, bool containeronly)
|
|
||||||
{
|
|
||||||
return DoOpenResourceFile(filename, file, quiet, containeronly);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FResourceFile *FResourceFile::OpenResourceFile(const char *filename, bool quiet, bool containeronly)
|
|
||||||
{
|
|
||||||
FileReader file;
|
|
||||||
if (!file.OpenFile(filename)) return nullptr;
|
|
||||||
return DoOpenResourceFile(filename, file, quiet, containeronly);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
FResourceFile *FResourceFile::OpenResourceFileFromLump(int lumpnum, bool quiet, bool containeronly)
|
|
||||||
{
|
|
||||||
FileReader file = Wads.ReopenLumpReader(lumpnum);
|
|
||||||
return DoOpenResourceFile("internal", file, quiet, containeronly);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
FResourceFile *FResourceFile::OpenDirectory(const char *filename, bool quiet)
|
|
||||||
{
|
|
||||||
return CheckDir(filename, quiet);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Resource file base class
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FResourceFile::FResourceFile(const char *filename)
|
|
||||||
: FileName(filename)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FResourceFile::FResourceFile(const char *filename, FileReader &r)
|
|
||||||
: FResourceFile(filename)
|
|
||||||
{
|
|
||||||
Reader = std::move(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
FResourceFile::~FResourceFile()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int lumpcmp(const void * a, const void * b)
|
|
||||||
{
|
|
||||||
FResourceLump * rec1 = (FResourceLump *)a;
|
|
||||||
FResourceLump * rec2 = (FResourceLump *)b;
|
|
||||||
|
|
||||||
return rec1->FullName.CompareNoCase(rec2->FullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FResourceFile :: PostProcessArchive
|
|
||||||
//
|
|
||||||
// Sorts files by name.
|
|
||||||
// For files named "filter/<game>/*": Using the same filter rules as config
|
|
||||||
// autoloading, move them to the end and rename them without the "filter/"
|
|
||||||
// prefix. Filtered files that don't match are deleted.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void FResourceFile::PostProcessArchive(void *lumps, size_t lumpsize)
|
|
||||||
{
|
|
||||||
// Entries in archives are sorted alphabetically
|
|
||||||
qsort(lumps, NumLumps, lumpsize, lumpcmp);
|
|
||||||
|
|
||||||
|
|
||||||
// Filter out lumps using the same names as the Autoload.* sections
|
|
||||||
// in the ini file use. We reduce the maximum lump concidered after
|
|
||||||
// each one so that we don't risk refiltering already filtered lumps.
|
|
||||||
uint32_t max = NumLumps;
|
|
||||||
max -= FilterLumpsByGameType(gametype, lumps, lumpsize, max);
|
|
||||||
|
|
||||||
long len;
|
|
||||||
int lastpos = -1;
|
|
||||||
FString file;
|
|
||||||
|
|
||||||
while ((len = LumpFilterIWAD.IndexOf('.', lastpos+1)) > 0)
|
|
||||||
{
|
|
||||||
max -= FilterLumps(LumpFilterIWAD.Left(len), lumps, lumpsize, max);
|
|
||||||
lastpos = len;
|
|
||||||
}
|
|
||||||
JunkLeftoverFilters(lumps, lumpsize, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FResourceFile :: FilterLumps
|
|
||||||
//
|
|
||||||
// Finds any lumps between [0,<max>) that match the pattern
|
|
||||||
// "filter/<filtername>/*" and moves them to the end of the lump list.
|
|
||||||
// Returns the number of lumps moved.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int FResourceFile::FilterLumps(FString filtername, void *lumps, size_t lumpsize, uint32_t max)
|
|
||||||
{
|
|
||||||
FString filter;
|
|
||||||
uint32_t start, end;
|
|
||||||
|
|
||||||
if (filtername.IsEmpty())
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
filter << "filter/" << filtername << '/';
|
|
||||||
|
|
||||||
bool found = FindPrefixRange(filter, lumps, lumpsize, max, start, end);
|
|
||||||
|
|
||||||
// Workaround for old Doom filter names.
|
|
||||||
if (!found && filtername.IndexOf("doom.id.doom") == 0)
|
|
||||||
{
|
|
||||||
filter.Substitute("doom.id.doom", "doom.doom");
|
|
||||||
found = FindPrefixRange(filter, lumps, lumpsize, max, start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found)
|
|
||||||
{
|
|
||||||
void *from = (uint8_t *)lumps + start * lumpsize;
|
|
||||||
|
|
||||||
// Remove filter prefix from every name
|
|
||||||
void *lump_p = from;
|
|
||||||
for (uint32_t i = start; i < end; ++i, lump_p = (uint8_t *)lump_p + lumpsize)
|
|
||||||
{
|
|
||||||
FResourceLump *lump = (FResourceLump *)lump_p;
|
|
||||||
assert(lump->FullName.CompareNoCase(filter, (int)filter.Len()) == 0);
|
|
||||||
lump->LumpNameSetup(lump->FullName.Mid(filter.Len()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move filtered lumps to the end of the lump list.
|
|
||||||
size_t count = (end - start) * lumpsize;
|
|
||||||
void *to = (uint8_t *)lumps + NumLumps * lumpsize - count;
|
|
||||||
assert (to >= from);
|
|
||||||
|
|
||||||
if (from != to)
|
|
||||||
{
|
|
||||||
// Copy filtered lumps to a temporary buffer.
|
|
||||||
uint8_t *filteredlumps = new uint8_t[count];
|
|
||||||
memcpy(filteredlumps, from, count);
|
|
||||||
|
|
||||||
// Shift lumps left to make room for the filtered ones at the end.
|
|
||||||
memmove(from, (uint8_t *)from + count, (NumLumps - end) * lumpsize);
|
|
||||||
|
|
||||||
// Copy temporary buffer to newly freed space.
|
|
||||||
memcpy(to, filteredlumps, count);
|
|
||||||
|
|
||||||
delete[] filteredlumps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return end - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FResourceFile :: FilterLumpsByGameType
|
|
||||||
//
|
|
||||||
// Matches any lumps that match "filter/game-<gametype>/*". Includes
|
|
||||||
// inclusive gametypes like Raven.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int FResourceFile::FilterLumpsByGameType(int type, void *lumps, size_t lumpsize, uint32_t max)
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
for (int i = GAME_MAX; i >= 0; i++)
|
|
||||||
{
|
|
||||||
if ((type & i) && GameFilters[i])
|
|
||||||
{
|
|
||||||
count += FilterLumps(GameFilters[i], lumps, lumpsize, max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FResourceFile :: JunkLeftoverFilters
|
|
||||||
//
|
|
||||||
// Deletes any lumps beginning with "filter/" that were not matched.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void FResourceFile::JunkLeftoverFilters(void *lumps, size_t lumpsize, uint32_t max)
|
|
||||||
{
|
|
||||||
uint32_t start, end;
|
|
||||||
if (FindPrefixRange("filter/", lumps, lumpsize, max, start, end))
|
|
||||||
{
|
|
||||||
// Since the resource lumps may contain non-POD data besides the
|
|
||||||
// full name, we "delete" them by erasing their names so they
|
|
||||||
// can't be found.
|
|
||||||
void *stop = (uint8_t *)lumps + end * lumpsize;
|
|
||||||
for (void *p = (uint8_t *)lumps + start * lumpsize; p < stop; p = (uint8_t *)p + lumpsize)
|
|
||||||
{
|
|
||||||
FResourceLump *lump = (FResourceLump *)p;
|
|
||||||
lump->FullName = "";
|
|
||||||
lump->BaseName = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FResourceFile :: FindPrefixRange
|
|
||||||
//
|
|
||||||
// Finds a range of lumps that start with the prefix string. <start> is left
|
|
||||||
// indicating the first matching one. <end> is left at one plus the last
|
|
||||||
// matching one.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
bool FResourceFile::FindPrefixRange(FString filter, void *lumps, size_t lumpsize, uint32_t maxlump, uint32_t &start, uint32_t &end)
|
|
||||||
{
|
|
||||||
uint32_t min, max, mid, inside;
|
|
||||||
FResourceLump *lump;
|
|
||||||
int cmp;
|
|
||||||
|
|
||||||
end = start = 0;
|
|
||||||
|
|
||||||
// Pretend that our range starts at 1 instead of 0 so that we can avoid
|
|
||||||
// unsigned overflow if the range starts at the first lump.
|
|
||||||
lumps = (uint8_t *)lumps - lumpsize;
|
|
||||||
|
|
||||||
// Binary search to find any match at all.
|
|
||||||
min = 1, max = maxlump;
|
|
||||||
while (min <= max)
|
|
||||||
{
|
|
||||||
mid = min + (max - min) / 2;
|
|
||||||
lump = (FResourceLump *)((uint8_t *)lumps + mid * lumpsize);
|
|
||||||
cmp = lump->FullName.CompareNoCase(filter, (int)filter.Len());
|
|
||||||
if (cmp == 0)
|
|
||||||
break;
|
|
||||||
else if (cmp < 0)
|
|
||||||
min = mid + 1;
|
|
||||||
else
|
|
||||||
max = mid - 1;
|
|
||||||
}
|
|
||||||
if (max < min)
|
|
||||||
{ // matched nothing
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Binary search to find first match.
|
|
||||||
inside = mid;
|
|
||||||
min = 1, max = mid;
|
|
||||||
while (min <= max)
|
|
||||||
{
|
|
||||||
mid = min + (max - min) / 2;
|
|
||||||
lump = (FResourceLump *)((uint8_t *)lumps + mid * lumpsize);
|
|
||||||
cmp = lump->FullName.CompareNoCase(filter, (int)filter.Len());
|
|
||||||
// Go left on matches and right on misses.
|
|
||||||
if (cmp == 0)
|
|
||||||
max = mid - 1;
|
|
||||||
else
|
|
||||||
min = mid + 1;
|
|
||||||
}
|
|
||||||
start = mid + (cmp != 0) - 1;
|
|
||||||
|
|
||||||
// Binary search to find last match.
|
|
||||||
min = inside, max = maxlump;
|
|
||||||
while (min <= max)
|
|
||||||
{
|
|
||||||
mid = min + (max - min) / 2;
|
|
||||||
lump = (FResourceLump *)((uint8_t *)lumps + mid * lumpsize);
|
|
||||||
cmp = lump->FullName.CompareNoCase(filter, (int)filter.Len());
|
|
||||||
// Go right on matches and left on misses.
|
|
||||||
if (cmp == 0)
|
|
||||||
min = mid + 1;
|
|
||||||
else
|
|
||||||
max = mid - 1;
|
|
||||||
}
|
|
||||||
end = mid - (cmp != 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Needs to be virtual in the base class. Implemented only for WADs
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void FResourceFile::FindStrifeTeaserVoices ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Finds a lump by a given name. Used for savegames
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FResourceLump *FResourceFile::FindLump(const char *name)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < NumLumps; i++)
|
|
||||||
{
|
|
||||||
FResourceLump *lump = GetLump(i);
|
|
||||||
if (!stricmp(name, lump->FullName))
|
|
||||||
{
|
|
||||||
return lump;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Caches a lump's content and increases the reference counter
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FileReader *FUncompressedLump::GetReader()
|
|
||||||
{
|
|
||||||
Owner->Reader.Seek(Position, FileReader::SeekSet);
|
|
||||||
return &Owner->Reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Caches a lump's content and increases the reference counter
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int FUncompressedLump::FillCache()
|
|
||||||
{
|
|
||||||
Owner->Reader.Seek(Position, FileReader::SeekSet);
|
|
||||||
Cache.Resize(LumpSize);
|
|
||||||
Owner->Reader.Read(Cache.Data(), LumpSize);
|
|
||||||
RefCount = 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Base class for uncompressed resource files
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FUncompressedFile::FUncompressedFile(const char *filename)
|
|
||||||
: FResourceFile(filename)
|
|
||||||
{}
|
|
||||||
|
|
||||||
FUncompressedFile::FUncompressedFile(const char *filename, FileReader &r)
|
|
||||||
: FResourceFile(filename, r)
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// external lump
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FExternalLump::FExternalLump(const char *_filename, int filesize)
|
|
||||||
: Filename(_filename)
|
|
||||||
{
|
|
||||||
if (filesize == -1)
|
|
||||||
{
|
|
||||||
FileReader f;
|
|
||||||
|
|
||||||
if (f.OpenFile(_filename))
|
|
||||||
{
|
|
||||||
LumpSize = (int)f.GetLength();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LumpSize = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LumpSize = filesize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Caches a lump's content and increases the reference counter
|
|
||||||
// For external lumps this reopens the file each time it is accessed
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int FExternalLump::FillCache()
|
|
||||||
{
|
|
||||||
Cache.Resize(LumpSize);
|
|
||||||
FileReader f;
|
|
||||||
|
|
||||||
if (f.OpenFile(Filename))
|
|
||||||
{
|
|
||||||
f.Read(Cache.Data(), LumpSize);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memset(Cache.Data(), 0, LumpSize);
|
|
||||||
}
|
|
||||||
RefCount = 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
|
@ -1,148 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
#ifndef __RESFILE_H
|
|
||||||
#define __RESFILE_H
|
|
||||||
|
|
||||||
#include "zstring.h"
|
|
||||||
#include "files.h"
|
|
||||||
#include "printf.h"
|
|
||||||
|
|
||||||
enum ELumpFlags
|
|
||||||
{
|
|
||||||
LUMPF_ZIPFILE=1, // contains a full path
|
|
||||||
LUMPF_BLOODCRYPT = 2, // encrypted
|
|
||||||
LUMPF_COMPRESSED = 4, // compressed
|
|
||||||
LUMPF_SEQUENTIAL = 8, // compressed but a sequential reader can be retrieved.
|
|
||||||
};
|
|
||||||
|
|
||||||
class FResourceFile;
|
|
||||||
|
|
||||||
// This holds a compresed Zip entry with all needed info to decompress it.
|
|
||||||
struct FCompressedBuffer
|
|
||||||
{
|
|
||||||
unsigned mSize;
|
|
||||||
unsigned mCompressedSize;
|
|
||||||
int mMethod;
|
|
||||||
int mZipFlags;
|
|
||||||
unsigned mCRC32;
|
|
||||||
char *mBuffer;
|
|
||||||
|
|
||||||
bool Decompress(char *destbuffer);
|
|
||||||
void Clean()
|
|
||||||
{
|
|
||||||
mSize = mCompressedSize = 0;
|
|
||||||
if (mBuffer != nullptr)
|
|
||||||
{
|
|
||||||
delete[] mBuffer;
|
|
||||||
mBuffer = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FResourceLump
|
|
||||||
{
|
|
||||||
friend class FResourceFile;
|
|
||||||
|
|
||||||
int LumpSize = 0;
|
|
||||||
int RefCount = 0;
|
|
||||||
int Flags = 0;
|
|
||||||
FString FullName; // Name with extension and path
|
|
||||||
FString BaseName; // Name without extension and path
|
|
||||||
FResourceFile * Owner = nullptr;
|
|
||||||
TArray<uint8_t> Cache;
|
|
||||||
|
|
||||||
FResourceLump() = default;
|
|
||||||
|
|
||||||
virtual ~FResourceLump();
|
|
||||||
virtual FileReader *GetReader();
|
|
||||||
virtual FileReader NewReader();
|
|
||||||
virtual int GetFileOffset() { return -1; }
|
|
||||||
virtual int GetIndexNum() const { return 0; }
|
|
||||||
void LumpNameSetup(FString iname);
|
|
||||||
virtual FCompressedBuffer GetRawData();
|
|
||||||
|
|
||||||
void *CacheLump();
|
|
||||||
int ReleaseCache();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual int FillCache() { return -1; }
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class FResourceFile
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FileReader Reader;
|
|
||||||
FString FileName;
|
|
||||||
protected:
|
|
||||||
uint32_t NumLumps;
|
|
||||||
|
|
||||||
FResourceFile(const char *filename);
|
|
||||||
FResourceFile(const char *filename, FileReader &r);
|
|
||||||
|
|
||||||
// for archives that can contain directories
|
|
||||||
void PostProcessArchive(void *lumps, size_t lumpsize);
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32_t FirstLump;
|
|
||||||
|
|
||||||
int FilterLumps(FString filtername, void *lumps, size_t lumpsize, uint32_t max);
|
|
||||||
int FilterLumpsByGameType(int gametype, void *lumps, size_t lumpsize, uint32_t max);
|
|
||||||
bool FindPrefixRange(FString filter, void *lumps, size_t lumpsize, uint32_t max, uint32_t &start, uint32_t &end);
|
|
||||||
void JunkLeftoverFilters(void *lumps, size_t lumpsize, uint32_t max);
|
|
||||||
static FResourceFile *DoOpenResourceFile(const char *filename, FileReader &file, bool quiet, bool containeronly);
|
|
||||||
|
|
||||||
public:
|
|
||||||
static FResourceFile *OpenResourceFile(const char *filename, FileReader &file, bool quiet = false, bool containeronly = false);
|
|
||||||
static FResourceFile *OpenResourceFile(const char *filename, bool quiet = false, bool containeronly = false);
|
|
||||||
//static FResourceFile *OpenResourceFileFromLump(int lumpnum, bool quiet = false, bool containeronly = false);
|
|
||||||
static FResourceFile *OpenDirectory(const char *filename, bool quiet = false);
|
|
||||||
virtual ~FResourceFile();
|
|
||||||
// If this FResourceFile represents a directory, the Reader object is not usable so don't return it.
|
|
||||||
FileReader *GetReader() { return Reader.isOpen()? &Reader : nullptr; }
|
|
||||||
uint32_t LumpCount() const { return NumLumps; }
|
|
||||||
uint32_t GetFirstLump() const { return FirstLump; }
|
|
||||||
void SetFirstLump(uint32_t f) { FirstLump = f; }
|
|
||||||
|
|
||||||
|
|
||||||
virtual void FindStrifeTeaserVoices ();
|
|
||||||
virtual bool Open(bool quiet) = 0;
|
|
||||||
virtual FResourceLump *GetLump(int no) = 0;
|
|
||||||
FResourceLump *FindLump(const char *name);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FUncompressedLump : public FResourceLump
|
|
||||||
{
|
|
||||||
int Position;
|
|
||||||
|
|
||||||
virtual FileReader *GetReader();
|
|
||||||
virtual int FillCache();
|
|
||||||
virtual int GetFileOffset() { return Position; }
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Base class for uncompressed resource files (GRP, PAK and single lumps)
|
|
||||||
class FUncompressedFile : public FResourceFile
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
TArray<FUncompressedLump> Lumps;
|
|
||||||
|
|
||||||
FUncompressedFile(const char *filename);
|
|
||||||
FUncompressedFile(const char *filename, FileReader &r);
|
|
||||||
virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct FExternalLump : public FResourceLump
|
|
||||||
{
|
|
||||||
FString Filename;
|
|
||||||
|
|
||||||
FExternalLump(const char *_filename, int filesize = -1);
|
|
||||||
virtual int FillCache();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,80 +0,0 @@
|
||||||
#ifndef __W_ZIP
|
|
||||||
#define __W_ZIP
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
// With versions of GCC newer than 4.2, it appears it was determined that the
|
|
||||||
// cost of an unaligned pointer on PPC was high enough to add padding to the
|
|
||||||
// end of packed structs. For whatever reason __packed__ and pragma pack are
|
|
||||||
// handled differently in this regard. Note that this only needs to be applied
|
|
||||||
// to types which are used in arrays.
|
|
||||||
#define FORCE_PACKED __attribute__((__packed__))
|
|
||||||
#else
|
|
||||||
#define FORCE_PACKED
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#pragma pack(1)
|
|
||||||
// FZipCentralInfo
|
|
||||||
struct FZipEndOfCentralDirectory
|
|
||||||
{
|
|
||||||
uint32_t Magic;
|
|
||||||
uint16_t DiskNumber;
|
|
||||||
uint16_t FirstDisk;
|
|
||||||
uint16_t NumEntries;
|
|
||||||
uint16_t NumEntriesOnAllDisks;
|
|
||||||
uint32_t DirectorySize;
|
|
||||||
uint32_t DirectoryOffset;
|
|
||||||
uint16_t ZipCommentLength;
|
|
||||||
} FORCE_PACKED;
|
|
||||||
|
|
||||||
// FZipFileInfo
|
|
||||||
struct FZipCentralDirectoryInfo
|
|
||||||
{
|
|
||||||
uint32_t Magic;
|
|
||||||
uint8_t VersionMadeBy[2];
|
|
||||||
uint8_t VersionToExtract[2];
|
|
||||||
uint16_t Flags;
|
|
||||||
uint16_t Method;
|
|
||||||
uint16_t ModTime;
|
|
||||||
uint16_t ModDate;
|
|
||||||
uint32_t CRC32;
|
|
||||||
uint32_t CompressedSize;
|
|
||||||
uint32_t UncompressedSize;
|
|
||||||
uint16_t NameLength;
|
|
||||||
uint16_t ExtraLength;
|
|
||||||
uint16_t CommentLength;
|
|
||||||
uint16_t StartingDiskNumber;
|
|
||||||
uint16_t InternalAttributes;
|
|
||||||
uint32_t ExternalAttributes;
|
|
||||||
uint32_t LocalHeaderOffset;
|
|
||||||
// file name and other variable length info follows
|
|
||||||
} FORCE_PACKED;
|
|
||||||
|
|
||||||
// FZipLocalHeader
|
|
||||||
struct FZipLocalFileHeader
|
|
||||||
{
|
|
||||||
uint32_t Magic;
|
|
||||||
uint8_t VersionToExtract[2];
|
|
||||||
uint16_t Flags;
|
|
||||||
uint16_t Method;
|
|
||||||
uint16_t ModTime;
|
|
||||||
uint16_t ModDate;
|
|
||||||
uint32_t CRC32;
|
|
||||||
uint32_t CompressedSize;
|
|
||||||
uint32_t UncompressedSize;
|
|
||||||
uint16_t NameLength;
|
|
||||||
uint16_t ExtraLength;
|
|
||||||
// file name and other variable length info follows
|
|
||||||
} FORCE_PACKED;
|
|
||||||
|
|
||||||
|
|
||||||
#pragma pack()
|
|
||||||
|
|
||||||
#define ZIP_LOCALFILE MAKE_ID('P','K',3,4)
|
|
||||||
#define ZIP_CENTRALFILE MAKE_ID('P','K',1,2)
|
|
||||||
#define ZIP_ENDOFDIR MAKE_ID('P','K',5,6)
|
|
||||||
|
|
||||||
// File header flags.
|
|
||||||
#define ZF_ENCRYPTED 0x1
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in a new issue