- moved file system implementation to 'common'.
This commit is contained in:
parent
80c6d5b276
commit
05d8856fe0
177 changed files with 179 additions and 177 deletions
435
src/common/filesystem/ancientzip.cpp
Normal file
435
src/common/filesystem/ancientzip.cpp
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
/*
|
||||
** ancientzip.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2010-2011 Randy Heit
|
||||
** 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
** Based in information from
|
||||
**
|
||||
gunzip.c by Pasi Ojala, a1bert@iki.fi
|
||||
http://www.iki.fi/a1bert/
|
||||
|
||||
A hopefully easier to understand guide to GZip
|
||||
(deflate) decompression routine than the GZip
|
||||
source code.
|
||||
|
||||
*/
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "ancientzip.h"
|
||||
|
||||
/****************************************************************
|
||||
Bit-I/O variables and routines/macros
|
||||
|
||||
These routines work in the bit level because the target
|
||||
environment does not have a barrel shifter. Trying to
|
||||
handle several bits at once would've only made the code
|
||||
slower.
|
||||
|
||||
If the environment supports multi-bit shifts, you should
|
||||
write these routines again (see e.g. the GZIP sources).
|
||||
|
||||
[RH] Since the target environment is not a C64, I did as
|
||||
suggested and rewrote these using zlib as a reference.
|
||||
|
||||
****************************************************************/
|
||||
|
||||
#define READBYTE(c) \
|
||||
do { \
|
||||
c = 0; \
|
||||
if (InLeft) { \
|
||||
InLeft--; \
|
||||
if (bs < be) \
|
||||
c = ReadBuf[bs++]; \
|
||||
else { \
|
||||
be = (decltype(be))In->Read(&ReadBuf, sizeof(ReadBuf)); \
|
||||
c = ReadBuf[0]; \
|
||||
bs = 1; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Get a byte of input into the bit accumulator. */
|
||||
#define PULLBYTE() \
|
||||
do { \
|
||||
unsigned char next; \
|
||||
READBYTE(next); \
|
||||
Hold += (unsigned int)(next) << Bits; \
|
||||
Bits += 8; \
|
||||
} while (0)
|
||||
|
||||
/* Assure that there are at least n bits in the bit accumulator. */
|
||||
#define NEEDBITS(n) \
|
||||
do { \
|
||||
while (Bits < (unsigned)(n)) \
|
||||
PULLBYTE(); \
|
||||
} while (0)
|
||||
|
||||
/* Return the low n bits of the bit accumulator (n < 16) */
|
||||
#define BITS(n) \
|
||||
((unsigned)Hold & ((1U << (n)) - 1))
|
||||
|
||||
/* Remove n bits from the bit accumulator */
|
||||
#define DROPBITS(n) \
|
||||
do { \
|
||||
Hold >>= (n); \
|
||||
Bits -= (unsigned)(n); \
|
||||
} while (0)
|
||||
|
||||
#define READBITS(c, a) \
|
||||
do { \
|
||||
NEEDBITS(a); \
|
||||
c = BITS(a); \
|
||||
DROPBITS(a); \
|
||||
} while (0)
|
||||
|
||||
/****************************************************************
|
||||
Shannon-Fano tree routines
|
||||
****************************************************************/
|
||||
|
||||
static const unsigned char BitReverse4[] = {
|
||||
0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e,
|
||||
0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07, 0x0f
|
||||
};
|
||||
|
||||
#define FIRST_BIT_LEN 8
|
||||
#define REST_BIT_LEN 4
|
||||
|
||||
void FZipExploder::InsertCode(TArray<HuffNode> &decoder, unsigned int pos, int bits, unsigned short code, int len, unsigned char value)
|
||||
{
|
||||
assert(len > 0);
|
||||
unsigned int node = pos + (code & ((1 << bits) - 1));
|
||||
|
||||
if (len > bits)
|
||||
{ // This code uses more bits than this level has room for. Store the bottom bits
|
||||
// in this table, then proceed to the next one.
|
||||
unsigned int child = decoder[node].ChildTable;
|
||||
if (child == 0)
|
||||
{ // Need to create child table.
|
||||
child = InitTable(decoder, 1 << REST_BIT_LEN);
|
||||
decoder[node].ChildTable = child;
|
||||
decoder[node].Length = bits;
|
||||
decoder[node].Value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(decoder[node].Length == bits);
|
||||
assert(decoder[node].Value == 0);
|
||||
}
|
||||
InsertCode(decoder, child, REST_BIT_LEN, code >> bits, len - bits, value);
|
||||
}
|
||||
else
|
||||
{ // If this code uses fewer bits than this level of the table, it is
|
||||
// inserted repeatedly for each value that matches it at its lower
|
||||
// bits.
|
||||
for (int i = 1 << (bits - len); --i >= 0; node += 1 << len)
|
||||
{
|
||||
decoder[node].Length = len;
|
||||
decoder[node].Value = value;
|
||||
assert(decoder[node].ChildTable == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int FZipExploder::InitTable(TArray<HuffNode> &decoder, int numspots)
|
||||
{
|
||||
unsigned int start = decoder.Size();
|
||||
decoder.Reserve(numspots);
|
||||
memset(&decoder[start], 0, sizeof(HuffNode)*numspots);
|
||||
return start;
|
||||
}
|
||||
|
||||
int FZipExploder::buildercmp(const void *a, const void *b)
|
||||
{
|
||||
const TableBuilder *v1 = (const TableBuilder *)a;
|
||||
const TableBuilder *v2 = (const TableBuilder *)b;
|
||||
int d = v1->Length - v2->Length;
|
||||
if (d == 0) {
|
||||
d = v1->Value - v2->Value;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
int FZipExploder::BuildDecoder(TArray<HuffNode> &decoder, TableBuilder *values, int numvals)
|
||||
{
|
||||
int i;
|
||||
|
||||
qsort(values, numvals, sizeof(*values), buildercmp);
|
||||
|
||||
// Generate the Shannon-Fano tree:
|
||||
unsigned short code = 0;
|
||||
unsigned short code_increment = 0;
|
||||
unsigned short last_bit_length = 0;
|
||||
for (i = numvals - 1; i >= 0; --i)
|
||||
{
|
||||
code += code_increment;
|
||||
if (values[i].Length != last_bit_length)
|
||||
{
|
||||
last_bit_length = values[i].Length;
|
||||
code_increment = 1 << (16 - last_bit_length);
|
||||
}
|
||||
// Reverse the order of the bits in the code before storing it.
|
||||
values[i].Code = BitReverse4[code >> 12] |
|
||||
(BitReverse4[(code >> 8) & 0xf] << 4) |
|
||||
(BitReverse4[(code >> 4) & 0xf] << 8) |
|
||||
(BitReverse4[code & 0xf] << 12);
|
||||
}
|
||||
|
||||
// Insert each code into the hierarchical table. The top level is FIRST_BIT_LEN bits,
|
||||
// and the other levels are REST_BIT_LEN bits. If a code does not completely fill
|
||||
// a level, every permutation for the remaining bits is filled in to
|
||||
// match this one.
|
||||
InitTable(decoder, 1 << FIRST_BIT_LEN); // Set up the top level.
|
||||
for (i = 0; i < numvals; ++i)
|
||||
{
|
||||
InsertCode(decoder, 0, FIRST_BIT_LEN, values[i].Code, values[i].Length, values[i].Value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int FZipExploder::DecodeSFValue(const TArray<HuffNode> &decoder)
|
||||
{
|
||||
unsigned int bits = FIRST_BIT_LEN, table = 0, code;
|
||||
const HuffNode *pos;
|
||||
do
|
||||
{
|
||||
NEEDBITS(bits);
|
||||
code = BITS(bits);
|
||||
bits = REST_BIT_LEN;
|
||||
pos = &decoder[table + code];
|
||||
DROPBITS(pos->Length);
|
||||
table = pos->ChildTable;
|
||||
}
|
||||
while (table != 0);
|
||||
return pos->Value;
|
||||
}
|
||||
|
||||
|
||||
int FZipExploder::DecodeSF(TArray<HuffNode> &decoder, int numvals)
|
||||
{
|
||||
TableBuilder builder[256];
|
||||
unsigned char a, c;
|
||||
int i, n, v = 0;
|
||||
|
||||
READBYTE(c);
|
||||
n = c + 1;
|
||||
for (i = 0; i < n; i++) {
|
||||
int nv, bl;
|
||||
READBYTE(a);
|
||||
nv = ((a >> 4) & 15) + 1;
|
||||
bl = (a & 15) + 1;
|
||||
while (nv--) {
|
||||
builder[v].Length = bl;
|
||||
builder[v].Value = v;
|
||||
v++;
|
||||
}
|
||||
}
|
||||
if (v != numvals)
|
||||
return 1; /* bad table */
|
||||
return BuildDecoder(decoder, builder, v);
|
||||
}
|
||||
|
||||
int FZipExploder::Explode(unsigned char *out, unsigned int outsize,
|
||||
FileReader &in, unsigned int insize,
|
||||
int flags)
|
||||
{
|
||||
int c, i, minMatchLen = 3, len, dist;
|
||||
int lowDistanceBits;
|
||||
unsigned int bIdx = 0;
|
||||
|
||||
Hold = 0;
|
||||
Bits = 0;
|
||||
In = ∈
|
||||
InLeft = insize;
|
||||
bs = be = 0;
|
||||
|
||||
if ((flags & 4)) {
|
||||
/* 3 trees: literals, lengths, distance top 6 */
|
||||
minMatchLen = 3;
|
||||
if (DecodeSF(LiteralDecoder, 256))
|
||||
return 1;
|
||||
} else {
|
||||
/* 2 trees: lengths, distance top 6 */
|
||||
minMatchLen = 2;
|
||||
}
|
||||
if (DecodeSF(LengthDecoder, 64))
|
||||
return 1;
|
||||
if (DecodeSF(DistanceDecoder, 64))
|
||||
return 1;
|
||||
|
||||
lowDistanceBits = (flags & 2) ? /* 8k dictionary */ 7 : /* 4k dictionary */ 6;
|
||||
while (bIdx < outsize)
|
||||
{
|
||||
READBITS(c, 1);
|
||||
if (c) {
|
||||
/* literal data */
|
||||
if ((flags & 4)) {
|
||||
c = DecodeSFValue(LiteralDecoder);
|
||||
} else {
|
||||
READBITS(c, 8);
|
||||
}
|
||||
out[bIdx++] = c;
|
||||
} else {
|
||||
READBITS(dist, lowDistanceBits);
|
||||
c = DecodeSFValue(DistanceDecoder);
|
||||
dist |= (c << lowDistanceBits);
|
||||
len = DecodeSFValue(LengthDecoder);
|
||||
if (len == 63) {
|
||||
READBITS(c, 8);
|
||||
len += c;
|
||||
}
|
||||
len += minMatchLen;
|
||||
dist++;
|
||||
if (bIdx + len > outsize) {
|
||||
throw CExplosionError("Not enough output space");
|
||||
}
|
||||
if ((unsigned int)dist > bIdx) {
|
||||
/* Anything before the first input byte is zero. */
|
||||
int zeros = dist - bIdx;
|
||||
if (len < zeros)
|
||||
zeros = len;
|
||||
for(i = zeros; i; i--)
|
||||
out[bIdx++] = 0;
|
||||
len -= zeros;
|
||||
}
|
||||
for(i = len; i; i--, bIdx++) {
|
||||
out[bIdx] = out[bIdx - dist];
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* HSIZE is defined as 2^13 (8192) in unzip.h */
|
||||
#define HSIZE 8192
|
||||
#define BOGUSCODE 256
|
||||
#define CODE_MASK (HSIZE - 1) /* 0x1fff (lower bits are parent's index) */
|
||||
#define FREE_CODE HSIZE /* 0x2000 (code is unused or was cleared) */
|
||||
#define HAS_CHILD (HSIZE << 1) /* 0x4000 (code has a child--do not clear) */
|
||||
|
||||
int ShrinkLoop(unsigned char *out, unsigned int outsize, FileReader &_In, unsigned int InLeft)
|
||||
{
|
||||
FileReader *In = &_In;
|
||||
unsigned char ReadBuf[256];
|
||||
unsigned short Parent[HSIZE];
|
||||
unsigned char Value[HSIZE], Stack[HSIZE];
|
||||
unsigned char *newstr;
|
||||
int len;
|
||||
int KwKwK, codesize = 9; /* start at 9 bits/code */
|
||||
int code, oldcode, freecode, curcode;
|
||||
unsigned int Bits = 0, Hold = 0;
|
||||
unsigned int size = 0;
|
||||
unsigned int bs = 0, be = 0;
|
||||
|
||||
freecode = BOGUSCODE;
|
||||
for (code = 0; code < BOGUSCODE; code++)
|
||||
{
|
||||
Value[code] = code;
|
||||
Parent[code] = BOGUSCODE;
|
||||
}
|
||||
for (code = BOGUSCODE+1; code < HSIZE; code++)
|
||||
Parent[code] = FREE_CODE;
|
||||
|
||||
READBITS(oldcode, codesize);
|
||||
if (size < outsize) {
|
||||
out[size++] = oldcode;
|
||||
}
|
||||
|
||||
while (size < outsize)
|
||||
{
|
||||
READBITS(code, codesize);
|
||||
if (code == BOGUSCODE) { /* possible to have consecutive escapes? */
|
||||
READBITS(code, codesize);
|
||||
if (code == 1) {
|
||||
codesize++;
|
||||
} else if (code == 2) {
|
||||
/* clear leafs (nodes with no children) */
|
||||
/* first loop: mark each parent as such */
|
||||
for (code = BOGUSCODE+1; code < HSIZE; ++code) {
|
||||
curcode = (Parent[code] & CODE_MASK);
|
||||
|
||||
if (curcode > BOGUSCODE)
|
||||
Parent[curcode] |= HAS_CHILD; /* set parent's child-bit */
|
||||
}
|
||||
|
||||
/* second loop: clear all nodes *not* marked as parents; reset flag bits */
|
||||
for (code = BOGUSCODE+1; code < HSIZE; ++code) {
|
||||
if (Parent[code] & HAS_CHILD) { /* just clear child-bit */
|
||||
Parent[code] &= ~HAS_CHILD;
|
||||
} else { /* leaf: lose it */
|
||||
Parent[code] = FREE_CODE;
|
||||
}
|
||||
}
|
||||
freecode = BOGUSCODE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
newstr = &Stack[HSIZE-1];
|
||||
curcode = code;
|
||||
|
||||
if (Parent[curcode] == FREE_CODE) {
|
||||
KwKwK = 1;
|
||||
newstr--; /* last character will be same as first character */
|
||||
curcode = oldcode;
|
||||
len = 1;
|
||||
} else {
|
||||
KwKwK = 0;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
do {
|
||||
*newstr-- = Value[curcode];
|
||||
len++;
|
||||
curcode = (Parent[curcode] & CODE_MASK);
|
||||
} while (curcode != BOGUSCODE);
|
||||
|
||||
newstr++;
|
||||
if (KwKwK) {
|
||||
Stack[HSIZE-1] = *newstr;
|
||||
}
|
||||
|
||||
do {
|
||||
freecode++;
|
||||
} while (Parent[freecode] != FREE_CODE);
|
||||
|
||||
Parent[freecode] = oldcode;
|
||||
Value[freecode] = *newstr;
|
||||
oldcode = code;
|
||||
|
||||
while (len--) {
|
||||
out[size++] = *newstr++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
50
src/common/filesystem/ancientzip.h
Normal file
50
src/common/filesystem/ancientzip.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#include "files.h"
|
||||
#include "engineerrors.h"
|
||||
|
||||
class FZipExploder
|
||||
{
|
||||
unsigned int Hold, Bits;
|
||||
FileReader *In;
|
||||
unsigned int InLeft;
|
||||
|
||||
/****************************************************************
|
||||
Shannon-Fano tree structures, variables and related routines
|
||||
****************************************************************/
|
||||
|
||||
struct HuffNode
|
||||
{
|
||||
unsigned char Value;
|
||||
unsigned char Length;
|
||||
unsigned short ChildTable;
|
||||
};
|
||||
|
||||
struct TableBuilder
|
||||
{
|
||||
unsigned char Value;
|
||||
unsigned char Length;
|
||||
unsigned short Code;
|
||||
};
|
||||
|
||||
TArray<HuffNode> LiteralDecoder;
|
||||
TArray<HuffNode> DistanceDecoder;
|
||||
TArray<HuffNode> LengthDecoder;
|
||||
unsigned char ReadBuf[256];
|
||||
unsigned int bs, be;
|
||||
|
||||
static int buildercmp(const void *a, const void *b);
|
||||
void InsertCode(TArray<HuffNode> &decoder, unsigned int pos, int bits, unsigned short code, int len, unsigned char value);
|
||||
unsigned int InitTable(TArray<HuffNode> &decoder, int numspots);
|
||||
int BuildDecoder(TArray<HuffNode> &decoder, TableBuilder *values, int numvals);
|
||||
int DecodeSFValue(const TArray<HuffNode> ¤tTree);
|
||||
int DecodeSF(TArray<HuffNode> &decoder, int numvals);
|
||||
public:
|
||||
int Explode(unsigned char *out, unsigned int outsize, FileReader &in, unsigned int insize, int flags);
|
||||
};
|
||||
|
||||
class CExplosionError : CRecoverableError
|
||||
{
|
||||
public:
|
||||
CExplosionError(const char *message) : CRecoverableError(message) {}
|
||||
};
|
||||
|
||||
int ShrinkLoop(unsigned char *out, unsigned int outsize, FileReader &in, unsigned int insize);
|
||||
381
src/common/filesystem/file_7z.cpp
Normal file
381
src/common/filesystem/file_7z.cpp
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
** file_7z.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2009 Randy Heit
|
||||
** 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
**
|
||||
*/
|
||||
|
||||
// Note that 7z made the unwise decision to include windows.h :(
|
||||
#include "7z.h"
|
||||
#include "7zCrc.h"
|
||||
|
||||
#include "resourcefile.h"
|
||||
#include "cmdlib.h"
|
||||
#include "printf.h"
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
//
|
||||
// Interface classes to 7z library
|
||||
//
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
extern ISzAlloc g_Alloc;
|
||||
|
||||
struct CZDFileInStream
|
||||
{
|
||||
ISeekInStream s;
|
||||
FileReader &File;
|
||||
|
||||
CZDFileInStream(FileReader &_file)
|
||||
: File(_file)
|
||||
{
|
||||
s.Read = Read;
|
||||
s.Seek = Seek;
|
||||
}
|
||||
|
||||
static SRes Read(const ISeekInStream *pp, void *buf, size_t *size)
|
||||
{
|
||||
CZDFileInStream *p = (CZDFileInStream *)pp;
|
||||
auto numread = p->File.Read(buf, (long)*size);
|
||||
if (numread < 0)
|
||||
{
|
||||
*size = 0;
|
||||
return SZ_ERROR_READ;
|
||||
}
|
||||
*size = numread;
|
||||
return SZ_OK;
|
||||
}
|
||||
|
||||
static SRes Seek(const ISeekInStream *pp, Int64 *pos, ESzSeek origin)
|
||||
{
|
||||
CZDFileInStream *p = (CZDFileInStream *)pp;
|
||||
FileReader::ESeek move_method;
|
||||
int res;
|
||||
if (origin == SZ_SEEK_SET)
|
||||
{
|
||||
move_method = FileReader::SeekSet;
|
||||
}
|
||||
else if (origin == SZ_SEEK_CUR)
|
||||
{
|
||||
move_method = FileReader::SeekCur;
|
||||
}
|
||||
else if (origin == SZ_SEEK_END)
|
||||
{
|
||||
move_method = FileReader::SeekEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
res = (int)p->File.Seek((long)*pos, move_method);
|
||||
*pos = p->File.Tell();
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
struct C7zArchive
|
||||
{
|
||||
CSzArEx DB;
|
||||
CZDFileInStream ArchiveStream;
|
||||
CLookToRead2 LookStream;
|
||||
Byte StreamBuffer[1<<14];
|
||||
UInt32 BlockIndex;
|
||||
Byte *OutBuffer;
|
||||
size_t OutBufferSize;
|
||||
|
||||
C7zArchive(FileReader &file) : ArchiveStream(file)
|
||||
{
|
||||
if (g_CrcTable[1] == 0)
|
||||
{
|
||||
CrcGenerateTable();
|
||||
}
|
||||
file.Seek(0, FileReader::SeekSet);
|
||||
LookToRead2_CreateVTable(&LookStream, false);
|
||||
LookStream.realStream = &ArchiveStream.s;
|
||||
LookToRead2_Init(&LookStream);
|
||||
LookStream.bufSize = sizeof(StreamBuffer);
|
||||
LookStream.buf = StreamBuffer;
|
||||
SzArEx_Init(&DB);
|
||||
BlockIndex = 0xFFFFFFFF;
|
||||
OutBuffer = NULL;
|
||||
OutBufferSize = 0;
|
||||
}
|
||||
|
||||
~C7zArchive()
|
||||
{
|
||||
if (OutBuffer != NULL)
|
||||
{
|
||||
IAlloc_Free(&g_Alloc, OutBuffer);
|
||||
}
|
||||
SzArEx_Free(&DB, &g_Alloc);
|
||||
}
|
||||
|
||||
SRes Open()
|
||||
{
|
||||
return SzArEx_Open(&DB, &LookStream.vt, &g_Alloc, &g_Alloc);
|
||||
}
|
||||
|
||||
SRes Extract(UInt32 file_index, char *buffer)
|
||||
{
|
||||
size_t offset, out_size_processed;
|
||||
SRes res = SzArEx_Extract(&DB, &LookStream.vt, file_index,
|
||||
&BlockIndex, &OutBuffer, &OutBufferSize,
|
||||
&offset, &out_size_processed,
|
||||
&g_Alloc, &g_Alloc);
|
||||
if (res == SZ_OK)
|
||||
{
|
||||
memcpy(buffer, OutBuffer + offset, out_size_processed);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
//==========================================================================
|
||||
//
|
||||
// Zip Lump
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct F7ZLump : public FResourceLump
|
||||
{
|
||||
int Position;
|
||||
|
||||
virtual int FillCache();
|
||||
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 7-zip file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class F7ZFile : public FResourceFile
|
||||
{
|
||||
friend struct F7ZLump;
|
||||
|
||||
F7ZLump *Lumps;
|
||||
C7zArchive *Archive;
|
||||
|
||||
public:
|
||||
F7ZFile(const char * filename, FileReader &filer);
|
||||
bool Open(bool quiet, LumpFilterInfo* filter);
|
||||
virtual ~F7ZFile();
|
||||
virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 7Z file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
F7ZFile::F7ZFile(const char * filename, FileReader &filer)
|
||||
: FResourceFile(filename, filer)
|
||||
{
|
||||
Lumps = NULL;
|
||||
Archive = NULL;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Open it
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool F7ZFile::Open(bool quiet, LumpFilterInfo *filter)
|
||||
{
|
||||
Archive = new C7zArchive(Reader);
|
||||
int skipped = 0;
|
||||
SRes res;
|
||||
|
||||
res = Archive->Open();
|
||||
if (res != SZ_OK)
|
||||
{
|
||||
delete Archive;
|
||||
Archive = NULL;
|
||||
if (!quiet)
|
||||
{
|
||||
Printf("\n" TEXTCOLOR_RED "%s: ", FileName.GetChars());
|
||||
if (res == SZ_ERROR_UNSUPPORTED)
|
||||
{
|
||||
Printf("Decoder does not support this archive\n");
|
||||
}
|
||||
else if (res == SZ_ERROR_MEM)
|
||||
{
|
||||
Printf("Cannot allocate memory\n");
|
||||
}
|
||||
else if (res == SZ_ERROR_CRC)
|
||||
{
|
||||
Printf("CRC error\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("error #%d\n", res);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CSzArEx* const archPtr = &Archive->DB;
|
||||
|
||||
NumLumps = archPtr->NumFiles;
|
||||
Lumps = new F7ZLump[NumLumps];
|
||||
|
||||
F7ZLump *lump_p = Lumps;
|
||||
TArray<UInt16> nameUTF16;
|
||||
TArray<char> nameASCII;
|
||||
|
||||
for (uint32_t i = 0; i < NumLumps; ++i)
|
||||
{
|
||||
// skip Directories
|
||||
if (SzArEx_IsDir(archPtr, i))
|
||||
{
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t nameLength = SzArEx_GetFileNameUtf16(archPtr, i, NULL);
|
||||
|
||||
if (0 == nameLength)
|
||||
{
|
||||
++skipped;
|
||||
continue;
|
||||
}
|
||||
|
||||
nameUTF16.Resize((unsigned)nameLength);
|
||||
nameASCII.Resize((unsigned)nameLength);
|
||||
SzArEx_GetFileNameUtf16(archPtr, i, &nameUTF16[0]);
|
||||
for (size_t c = 0; c < nameLength; ++c)
|
||||
{
|
||||
nameASCII[c] = static_cast<char>(nameUTF16[c]);
|
||||
}
|
||||
FixPathSeperator(&nameASCII[0]);
|
||||
|
||||
FString name = &nameASCII[0];
|
||||
name.ToLower();
|
||||
|
||||
lump_p->LumpNameSetup(name);
|
||||
lump_p->LumpSize = static_cast<int>(SzArEx_GetFileSize(archPtr, i));
|
||||
lump_p->Owner = this;
|
||||
lump_p->Flags = LUMPF_FULLPATH|LUMPF_COMPRESSED;
|
||||
lump_p->Position = i;
|
||||
lump_p->CheckEmbedded();
|
||||
lump_p++;
|
||||
}
|
||||
// Resize the lump record array to its actual size
|
||||
NumLumps -= skipped;
|
||||
|
||||
if (NumLumps > 0)
|
||||
{
|
||||
// Quick check for unsupported compression method
|
||||
|
||||
TArray<char> temp;
|
||||
temp.Resize(Lumps[0].LumpSize);
|
||||
|
||||
if (SZ_OK != Archive->Extract(Lumps[0].Position, &temp[0]))
|
||||
{
|
||||
if (!quiet) Printf("\n%s: unsupported 7z/LZMA file!\n", FileName.GetChars());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GenerateHash();
|
||||
PostProcessArchive(&Lumps[0], sizeof(F7ZLump), filter);
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
F7ZFile::~F7ZFile()
|
||||
{
|
||||
if (Lumps != NULL)
|
||||
{
|
||||
delete[] Lumps;
|
||||
}
|
||||
if (Archive != NULL)
|
||||
{
|
||||
delete Archive;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Fills the lump cache and performs decompression
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int F7ZLump::FillCache()
|
||||
{
|
||||
Cache = new char[LumpSize];
|
||||
static_cast<F7ZFile*>(Owner)->Archive->Extract(Position, Cache);
|
||||
RefCount = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// File open
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FResourceFile *Check7Z(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter)
|
||||
{
|
||||
char head[k7zSignatureSize];
|
||||
|
||||
if (file.GetLength() >= k7zSignatureSize)
|
||||
{
|
||||
file.Seek(0, FileReader::SeekSet);
|
||||
file.Read(&head, k7zSignatureSize);
|
||||
file.Seek(0, FileReader::SeekSet);
|
||||
if (!memcmp(head, k7zSignature, k7zSignatureSize))
|
||||
{
|
||||
FResourceFile *rf = new F7ZFile(filename, file);
|
||||
if (rf->Open(quiet, filter)) return rf;
|
||||
|
||||
file = std::move(rf->Reader); // to avoid destruction of reader
|
||||
delete rf;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
262
src/common/filesystem/file_directory.cpp
Normal file
262
src/common/filesystem/file_directory.cpp
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
** file_directory.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2008-2009 Randy Heit
|
||||
** 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 <sys/stat.h>
|
||||
|
||||
#include "resourcefile.h"
|
||||
#include "cmdlib.h"
|
||||
#include "printf.h"
|
||||
#include "findfile.h"
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Zip Lump
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct FDirectoryLump : public FResourceLump
|
||||
{
|
||||
FileReader NewReader() override;
|
||||
int FillCache() override;
|
||||
|
||||
FString mFullPath;
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Zip file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FDirectory : public FResourceFile
|
||||
{
|
||||
TArray<FDirectoryLump> Lumps;
|
||||
const bool nosubdir;
|
||||
|
||||
int AddDirectory(const char *dirpath);
|
||||
void AddEntry(const char *fullpath, int size);
|
||||
|
||||
public:
|
||||
FDirectory(const char * dirname, bool nosubdirflag = false);
|
||||
bool Open(bool quiet, LumpFilterInfo* filter);
|
||||
virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FDirectory::FDirectory(const char * directory, bool nosubdirflag)
|
||||
: FResourceFile(NULL), nosubdir(nosubdirflag)
|
||||
{
|
||||
FString dirname;
|
||||
|
||||
#ifdef _WIN32
|
||||
directory = _fullpath(NULL, directory, _MAX_PATH);
|
||||
#else
|
||||
// Todo for Linux: Resolve the path before using it
|
||||
#endif
|
||||
dirname = directory;
|
||||
#ifdef _WIN32
|
||||
free((void *)directory);
|
||||
#endif
|
||||
dirname.Substitute("\\", "/");
|
||||
if (dirname[dirname.Len()-1] != '/') dirname += '/';
|
||||
FileName = dirname;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Windows version
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FDirectory::AddDirectory(const char *dirpath)
|
||||
{
|
||||
void * handle;
|
||||
int count = 0;
|
||||
|
||||
FString dirmatch = dirpath;
|
||||
findstate_t find;
|
||||
dirmatch += '*';
|
||||
|
||||
handle = I_FindFirst(dirmatch.GetChars(), &find);
|
||||
if (handle == ((void *)(-1)))
|
||||
{
|
||||
Printf("Could not scan '%s': %s\n", dirpath, strerror(errno));
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
// I_FindName only returns the file's name and not its full path
|
||||
auto attr = I_FindAttr(&find);
|
||||
if (attr & FA_HIDDEN)
|
||||
{
|
||||
// Skip hidden files and directories. (Prevents SVN bookkeeping
|
||||
// info from being included.)
|
||||
continue;
|
||||
}
|
||||
FString fi = I_FindName(&find);
|
||||
if (attr & FA_DIREC)
|
||||
{
|
||||
if (nosubdir || (fi[0] == '.' &&
|
||||
(fi[1] == '\0' ||
|
||||
(fi[1] == '.' && fi[2] == '\0'))))
|
||||
{
|
||||
// Do not record . and .. directories.
|
||||
continue;
|
||||
}
|
||||
FString newdir = dirpath;
|
||||
newdir << fi << '/';
|
||||
count += AddDirectory(newdir);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strstr(fi, ".orig") || strstr(fi, ".bak"))
|
||||
{
|
||||
// We shouldn't add backup files to the file system
|
||||
continue;
|
||||
}
|
||||
size_t size = 0;
|
||||
FString fn = FString(dirpath) + fi;
|
||||
if (GetFileInfo(fn, &size, nullptr))
|
||||
{
|
||||
AddEntry(fn, (int)size);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
} while (I_FindNext (handle, &find) == 0);
|
||||
I_FindClose (handle);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FDirectory::Open(bool quiet, LumpFilterInfo* filter)
|
||||
{
|
||||
NumLumps = AddDirectory(FileName);
|
||||
PostProcessArchive(&Lumps[0], sizeof(FDirectoryLump), filter);
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FDirectory::AddEntry(const char *fullpath, int size)
|
||||
{
|
||||
FDirectoryLump *lump_p = &Lumps[Lumps.Reserve(1)];
|
||||
|
||||
// Store the full path here so that we can access the file later, even if it is from a filter directory.
|
||||
lump_p->mFullPath = fullpath;
|
||||
|
||||
// [mxd] Convert name to lowercase
|
||||
FString name = fullpath + strlen(FileName);
|
||||
name.ToLower();
|
||||
|
||||
// The lump's name is only the part relative to the main directory
|
||||
lump_p->LumpNameSetup(name);
|
||||
lump_p->LumpSize = size;
|
||||
lump_p->Owner = this;
|
||||
lump_p->Flags = 0;
|
||||
lump_p->CheckEmbedded();
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FileReader FDirectoryLump::NewReader()
|
||||
{
|
||||
FileReader fr;
|
||||
fr.OpenFile(mFullPath);
|
||||
return fr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FDirectoryLump::FillCache()
|
||||
{
|
||||
FileReader fr;
|
||||
Cache = new char[LumpSize];
|
||||
if (!fr.OpenFile(mFullPath))
|
||||
{
|
||||
memset(Cache, 0, LumpSize);
|
||||
return 0;
|
||||
}
|
||||
fr.Read(Cache, LumpSize);
|
||||
RefCount = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// File open
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FResourceFile *CheckDir(const char *filename, bool quiet, bool nosubdirflag, LumpFilterInfo* filter)
|
||||
{
|
||||
FResourceFile *rf = new FDirectory(filename, nosubdirflag);
|
||||
if (rf->Open(quiet, filter)) return rf;
|
||||
delete rf;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
152
src/common/filesystem/file_grp.cpp
Normal file
152
src/common/filesystem/file_grp.cpp
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
** file_grp.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 "resourcefile.h"
|
||||
#include "printf.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct GrpInfo
|
||||
{
|
||||
uint32_t Magic[3];
|
||||
uint32_t NumLumps;
|
||||
};
|
||||
|
||||
struct GrpLump
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
char Name[12];
|
||||
uint32_t Size;
|
||||
};
|
||||
char NameWithZero[13];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Build GRP file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FGrpFile : public FUncompressedFile
|
||||
{
|
||||
public:
|
||||
FGrpFile(const char * filename, FileReader &file);
|
||||
bool Open(bool quiet, LumpFilterInfo* filter);
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Initializes a Build GRP file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FGrpFile::FGrpFile(const char *filename, FileReader &file)
|
||||
: FUncompressedFile(filename, file)
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Open it
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FGrpFile::Open(bool quiet, LumpFilterInfo*)
|
||||
{
|
||||
GrpInfo header;
|
||||
|
||||
Reader.Read(&header, sizeof(header));
|
||||
NumLumps = LittleLong(header.NumLumps);
|
||||
|
||||
GrpLump *fileinfo = new GrpLump[NumLumps];
|
||||
Reader.Read (fileinfo, NumLumps * sizeof(GrpLump));
|
||||
|
||||
Lumps.Resize(NumLumps);
|
||||
|
||||
int Position = sizeof(GrpInfo) + NumLumps * sizeof(GrpLump);
|
||||
|
||||
for(uint32_t i = 0; i < NumLumps; i++)
|
||||
{
|
||||
Lumps[i].Owner = this;
|
||||
Lumps[i].Position = Position;
|
||||
Lumps[i].LumpSize = LittleLong(fileinfo[i].Size);
|
||||
Position += fileinfo[i].Size;
|
||||
Lumps[i].Flags = 0;
|
||||
fileinfo[i].NameWithZero[12] = '\0'; // Be sure filename is null-terminated
|
||||
Lumps[i].LumpNameSetup(fileinfo[i].NameWithZero);
|
||||
}
|
||||
GenerateHash();
|
||||
delete[] fileinfo;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// File open
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FResourceFile *CheckGRP(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter)
|
||||
{
|
||||
char head[12];
|
||||
|
||||
if (file.GetLength() >= 12)
|
||||
{
|
||||
file.Seek(0, FileReader::SeekSet);
|
||||
file.Read(&head, 12);
|
||||
file.Seek(0, FileReader::SeekSet);
|
||||
if (!memcmp(head, "KenSilverman", 12))
|
||||
{
|
||||
FResourceFile *rf = new FGrpFile(filename, file);
|
||||
if (rf->Open(quiet, filter)) return rf;
|
||||
|
||||
file = std::move(rf->Reader); // to avoid destruction of reader
|
||||
delete rf;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
103
src/common/filesystem/file_lump.cpp
Normal file
103
src/common/filesystem/file_lump.cpp
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
** file_lump.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** 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 "resourcefile.h"
|
||||
#include "cmdlib.h"
|
||||
#include "printf.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Single lump
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FLumpFile : public FUncompressedFile
|
||||
{
|
||||
public:
|
||||
FLumpFile(const char * filename, FileReader &file);
|
||||
bool Open(bool quiet, LumpFilterInfo* filter);
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FLumpFile::FLumpFile
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FLumpFile::FLumpFile(const char *filename, FileReader &file)
|
||||
: FUncompressedFile(filename, file)
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Open it
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FLumpFile::Open(bool quiet, LumpFilterInfo*)
|
||||
{
|
||||
FString name(ExtractFileBase(FileName, true));
|
||||
|
||||
Lumps.Resize(1);
|
||||
Lumps[0].LumpNameSetup(name);
|
||||
Lumps[0].Owner = this;
|
||||
Lumps[0].Position = 0;
|
||||
Lumps[0].LumpSize = (int)Reader.GetLength();
|
||||
Lumps[0].Flags = 0;
|
||||
NumLumps = 1;
|
||||
if (!quiet)
|
||||
{
|
||||
Printf("\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// File open
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FResourceFile *CheckLump(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter)
|
||||
{
|
||||
// always succeeds
|
||||
FResourceFile *rf = new FLumpFile(filename, file);
|
||||
if (rf->Open(quiet, filter)) return rf;
|
||||
file = std::move(rf->Reader); // to avoid destruction of reader
|
||||
delete rf;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
146
src/common/filesystem/file_pak.cpp
Normal file
146
src/common/filesystem/file_pak.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
** file_pak.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** 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 "resourcefile.h"
|
||||
#include "printf.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct dpackfile_t
|
||||
{
|
||||
char name[56];
|
||||
int filepos, filelen;
|
||||
} ;
|
||||
|
||||
struct dpackheader_t
|
||||
{
|
||||
int ident; // == IDPAKHEADER
|
||||
int dirofs;
|
||||
int dirlen;
|
||||
} ;
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Wad file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FPakFile : public FUncompressedFile
|
||||
{
|
||||
public:
|
||||
FPakFile(const char * filename, FileReader &file);
|
||||
bool Open(bool quiet, LumpFilterInfo* filter);
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FWadFile::FWadFile
|
||||
//
|
||||
// Initializes a WAD file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FPakFile::FPakFile(const char *filename, FileReader &file)
|
||||
: FUncompressedFile(filename, file)
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Open it
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FPakFile::Open(bool quiet, LumpFilterInfo* filter)
|
||||
{
|
||||
dpackheader_t header;
|
||||
|
||||
Reader.Read(&header, sizeof(header));
|
||||
NumLumps = LittleLong(header.dirlen) / sizeof(dpackfile_t);
|
||||
header.dirofs = LittleLong(header.dirofs);
|
||||
|
||||
TArray<dpackfile_t> fileinfo(NumLumps, true);
|
||||
Reader.Seek (header.dirofs, FileReader::SeekSet);
|
||||
Reader.Read (fileinfo.Data(), NumLumps * sizeof(dpackfile_t));
|
||||
|
||||
Lumps.Resize(NumLumps);
|
||||
|
||||
for(uint32_t i = 0; i < NumLumps; i++)
|
||||
{
|
||||
Lumps[i].LumpNameSetup(fileinfo[i].name);
|
||||
Lumps[i].Flags = LUMPF_FULLPATH;
|
||||
Lumps[i].Owner = this;
|
||||
Lumps[i].Position = LittleLong(fileinfo[i].filepos);
|
||||
Lumps[i].LumpSize = LittleLong(fileinfo[i].filelen);
|
||||
Lumps[i].CheckEmbedded();
|
||||
}
|
||||
GenerateHash();
|
||||
PostProcessArchive(&Lumps[0], sizeof(Lumps[0]), filter);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// File open
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FResourceFile *CheckPak(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter)
|
||||
{
|
||||
char head[4];
|
||||
|
||||
if (file.GetLength() >= 12)
|
||||
{
|
||||
file.Seek(0, FileReader::SeekSet);
|
||||
file.Read(&head, 4);
|
||||
file.Seek(0, FileReader::SeekSet);
|
||||
if (!memcmp(head, "PACK", 4))
|
||||
{
|
||||
FResourceFile *rf = new FPakFile(filename, file);
|
||||
if (rf->Open(quiet, filter)) return rf;
|
||||
|
||||
file = std::move(rf->Reader); // to avoid destruction of reader
|
||||
delete rf;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
262
src/common/filesystem/file_rff.cpp
Normal file
262
src/common/filesystem/file_rff.cpp
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
** file_rff.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 "resourcefile.h"
|
||||
#include "templates.h"
|
||||
#include "printf.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct RFFInfo
|
||||
{
|
||||
// Should be "RFF\x18"
|
||||
uint32_t Magic;
|
||||
uint32_t Version;
|
||||
uint32_t DirOfs;
|
||||
uint32_t NumLumps;
|
||||
};
|
||||
|
||||
struct RFFLump
|
||||
{
|
||||
uint32_t DontKnow1[4];
|
||||
uint32_t FilePos;
|
||||
uint32_t Size;
|
||||
uint32_t DontKnow2;
|
||||
uint32_t Time;
|
||||
uint8_t Flags;
|
||||
char Extension[3];
|
||||
char Name[8];
|
||||
uint32_t IndexNum; // Used by .sfx, possibly others
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Blood RFF lump (uncompressed lump with encryption)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct FRFFLump : public FUncompressedLump
|
||||
{
|
||||
virtual FileReader *GetReader();
|
||||
virtual int FillCache();
|
||||
|
||||
uint32_t IndexNum;
|
||||
|
||||
int GetIndexNum() const { return IndexNum; }
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// BloodCrypt
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void BloodCrypt (void *data, int key, int len)
|
||||
{
|
||||
int p = (uint8_t)key, i;
|
||||
|
||||
for (i = 0; i < len; ++i)
|
||||
{
|
||||
((uint8_t *)data)[i] ^= (unsigned char)(p+(i>>1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Blood RFF file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FRFFFile : public FResourceFile
|
||||
{
|
||||
FRFFLump *Lumps;
|
||||
|
||||
public:
|
||||
FRFFFile(const char * filename, FileReader &file);
|
||||
virtual ~FRFFFile();
|
||||
virtual bool Open(bool quiet, LumpFilterInfo* filter);
|
||||
virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; }
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Initializes a Blood RFF file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FRFFFile::FRFFFile(const char *filename, FileReader &file)
|
||||
: FResourceFile(filename, file)
|
||||
{
|
||||
Lumps = NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Initializes a Blood RFF file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FRFFFile::Open(bool quiet, LumpFilterInfo*)
|
||||
{
|
||||
RFFLump *lumps;
|
||||
RFFInfo header;
|
||||
|
||||
Reader.Read(&header, sizeof(header));
|
||||
|
||||
NumLumps = LittleLong(header.NumLumps);
|
||||
header.DirOfs = LittleLong(header.DirOfs);
|
||||
lumps = new RFFLump[header.NumLumps];
|
||||
Reader.Seek (header.DirOfs, FileReader::SeekSet);
|
||||
Reader.Read (lumps, header.NumLumps * sizeof(RFFLump));
|
||||
BloodCrypt (lumps, header.DirOfs, header.NumLumps * sizeof(RFFLump));
|
||||
|
||||
Lumps = new FRFFLump[NumLumps];
|
||||
|
||||
for (uint32_t i = 0; i < NumLumps; ++i)
|
||||
{
|
||||
Lumps[i].Position = LittleLong(lumps[i].FilePos);
|
||||
Lumps[i].LumpSize = LittleLong(lumps[i].Size);
|
||||
Lumps[i].Owner = this;
|
||||
if (lumps[i].Flags & 0x10)
|
||||
{
|
||||
Lumps[i].Flags |= LUMPF_COMPRESSED; // flags the lump as not directly usable
|
||||
}
|
||||
Lumps[i].IndexNum = LittleLong(lumps[i].IndexNum);
|
||||
// Rearrange the name and extension to construct the fullname.
|
||||
char name[13];
|
||||
strncpy(name, lumps[i].Name, 8);
|
||||
name[8] = 0;
|
||||
size_t len = strlen(name);
|
||||
assert(len + 4 <= 12);
|
||||
name[len+0] = '.';
|
||||
name[len+1] = lumps[i].Extension[0];
|
||||
name[len+2] = lumps[i].Extension[1];
|
||||
name[len+3] = lumps[i].Extension[2];
|
||||
name[len+4] = 0;
|
||||
Lumps[i].LumpNameSetup(name);
|
||||
}
|
||||
delete[] lumps;
|
||||
GenerateHash();
|
||||
return true;
|
||||
}
|
||||
|
||||
FRFFFile::~FRFFFile()
|
||||
{
|
||||
if (Lumps != NULL)
|
||||
{
|
||||
delete[] Lumps;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Get reader (only returns non-NULL if not encrypted)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FileReader *FRFFLump::GetReader()
|
||||
{
|
||||
// Don't return the reader if this lump is encrypted
|
||||
// In that case always force caching of the lump
|
||||
if (!(Flags & LUMPF_COMPRESSED))
|
||||
{
|
||||
return FUncompressedLump::GetReader();
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Fills the lump cache and performs decryption
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FRFFLump::FillCache()
|
||||
{
|
||||
int res = FUncompressedLump::FillCache();
|
||||
|
||||
if (Flags & LUMPF_COMPRESSED)
|
||||
{
|
||||
int cryptlen = MIN<int> (LumpSize, 256);
|
||||
uint8_t *data = (uint8_t *)Cache;
|
||||
|
||||
for (int i = 0; i < cryptlen; ++i)
|
||||
{
|
||||
data[i] ^= i >> 1;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// File open
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FResourceFile *CheckRFF(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter)
|
||||
{
|
||||
char head[4];
|
||||
|
||||
if (file.GetLength() >= 16)
|
||||
{
|
||||
file.Seek(0, FileReader::SeekSet);
|
||||
file.Read(&head, 4);
|
||||
file.Seek(0, FileReader::SeekSet);
|
||||
if (!memcmp(head, "RFF\x1a", 4))
|
||||
{
|
||||
FResourceFile *rf = new FRFFFile(filename, file);
|
||||
if (rf->Open(quiet, filter)) return rf;
|
||||
|
||||
file = std::move(rf->Reader); // to avoid destruction of reader
|
||||
delete rf;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
484
src/common/filesystem/file_wad.cpp
Normal file
484
src/common/filesystem/file_wad.cpp
Normal file
|
|
@ -0,0 +1,484 @@
|
|||
/*
|
||||
** file_wad.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 <ctype.h>
|
||||
#include "resourcefile.h"
|
||||
#include "v_text.h"
|
||||
#include "filesystem.h"
|
||||
#include "engineerrors.h"
|
||||
|
||||
|
||||
struct wadinfo_t
|
||||
{
|
||||
// Should be "IWAD" or "PWAD".
|
||||
uint32_t Magic;
|
||||
uint32_t NumLumps;
|
||||
uint32_t InfoTableOfs;
|
||||
};
|
||||
|
||||
struct wadlump_t
|
||||
{
|
||||
uint32_t FilePos;
|
||||
uint32_t Size;
|
||||
char Name[8];
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Wad Lump (with console doom LZSS support)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FWadFileLump : public FResourceLump
|
||||
{
|
||||
public:
|
||||
bool Compressed;
|
||||
int Position;
|
||||
int Namespace;
|
||||
|
||||
int GetNamespace() const override { return Namespace; }
|
||||
|
||||
int GetFileOffset() { return Position; }
|
||||
FileReader *GetReader()
|
||||
{
|
||||
if(!Compressed)
|
||||
{
|
||||
Owner->Reader.Seek(Position, FileReader::SeekSet);
|
||||
return &Owner->Reader;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
int FillCache()
|
||||
{
|
||||
if(!Compressed)
|
||||
{
|
||||
const char * buffer = Owner->Reader.GetBuffer();
|
||||
|
||||
if (buffer != NULL)
|
||||
{
|
||||
// This is an in-memory file so the cache can point directly to the file's data.
|
||||
Cache = const_cast<char*>(buffer) + Position;
|
||||
RefCount = -1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Owner->Reader.Seek(Position, FileReader::SeekSet);
|
||||
Cache = new char[LumpSize];
|
||||
|
||||
if(Compressed)
|
||||
{
|
||||
FileReader lzss;
|
||||
if (lzss.OpenDecompressor(Owner->Reader, LumpSize, METHOD_LZSS, false, [](const char* err) { I_Error("%s", err); }))
|
||||
{
|
||||
lzss.Read(Cache, LumpSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
Owner->Reader.Read(Cache, LumpSize);
|
||||
|
||||
RefCount = 1;
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Wad file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FWadFile : public FResourceFile
|
||||
{
|
||||
TArray<FWadFileLump> Lumps;
|
||||
|
||||
bool IsMarker(int lump, const char *marker);
|
||||
void SetNamespace(const char *startmarker, const char *endmarker, namespace_t space, bool flathack=false);
|
||||
void SkinHack ();
|
||||
|
||||
public:
|
||||
FWadFile(const char * filename, FileReader &file);
|
||||
FResourceLump *GetLump(int lump) { return &Lumps[lump]; }
|
||||
bool Open(bool quiet, LumpFilterInfo* filter);
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FWadFile::FWadFile
|
||||
//
|
||||
// Initializes a WAD file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FWadFile::FWadFile(const char *filename, FileReader &file)
|
||||
: FResourceFile(filename, file)
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Open it
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FWadFile::Open(bool quiet, LumpFilterInfo*)
|
||||
{
|
||||
wadinfo_t header;
|
||||
uint32_t InfoTableOfs;
|
||||
bool isBigEndian = false; // Little endian is assumed until proven otherwise
|
||||
auto wadSize = Reader.GetLength();
|
||||
|
||||
Reader.Read(&header, sizeof(header));
|
||||
NumLumps = LittleLong(header.NumLumps);
|
||||
InfoTableOfs = LittleLong(header.InfoTableOfs);
|
||||
|
||||
// Check to see if the little endian interpretation is valid
|
||||
// This should be sufficient to detect big endian wads.
|
||||
if (InfoTableOfs + NumLumps*sizeof(wadlump_t) > (unsigned)wadSize)
|
||||
{
|
||||
NumLumps = BigLong(header.NumLumps);
|
||||
InfoTableOfs = BigLong(header.InfoTableOfs);
|
||||
isBigEndian = true;
|
||||
|
||||
// Check again to detect broken wads
|
||||
if (InfoTableOfs + NumLumps*sizeof(wadlump_t) > (unsigned)wadSize)
|
||||
{
|
||||
I_Error("Cannot load broken WAD file %s\n", FileName.GetChars());
|
||||
}
|
||||
}
|
||||
|
||||
TArray<wadlump_t> fileinfo(NumLumps, true);
|
||||
Reader.Seek (InfoTableOfs, FileReader::SeekSet);
|
||||
Reader.Read (fileinfo.Data(), NumLumps * sizeof(wadlump_t));
|
||||
|
||||
Lumps.Resize(NumLumps);
|
||||
|
||||
for(uint32_t i = 0; i < NumLumps; i++)
|
||||
{
|
||||
char n[9];
|
||||
uppercopy(n, fileinfo[i].Name);
|
||||
n[8] = 0;
|
||||
// This needs to be done differently. We cannot simply assume that all lumps where the first character's high bit is set are compressed without verification.
|
||||
// This requires explicit toggling for precisely the files that need it.
|
||||
#if 0
|
||||
Lumps[i].Compressed = !(gameinfo.flags & GI_SHAREWARE) && (n[0] & 0x80) == 0x80;
|
||||
#else
|
||||
Lumps[i].Compressed = false;
|
||||
#endif
|
||||
n[0] &= ~0x80;
|
||||
Lumps[i].LumpNameSetup(n);
|
||||
|
||||
Lumps[i].Owner = this;
|
||||
Lumps[i].Position = isBigEndian ? BigLong(fileinfo[i].FilePos) : LittleLong(fileinfo[i].FilePos);
|
||||
Lumps[i].LumpSize = isBigEndian ? BigLong(fileinfo[i].Size) : LittleLong(fileinfo[i].Size);
|
||||
Lumps[i].Namespace = ns_global;
|
||||
Lumps[i].Flags = Lumps[i].Compressed ? LUMPF_COMPRESSED | LUMPF_SHORTNAME : LUMPF_SHORTNAME;
|
||||
|
||||
// Check if the lump is within the WAD file and print a warning if not.
|
||||
if (Lumps[i].Position + Lumps[i].LumpSize > wadSize || Lumps[i].Position < 0 || Lumps[i].LumpSize < 0)
|
||||
{
|
||||
if (Lumps[i].LumpSize != 0)
|
||||
{
|
||||
Printf(PRINT_HIGH, "%s: Lump %s contains invalid positioning info and will be ignored\n", FileName.GetChars(), Lumps[i].getName());
|
||||
Lumps[i].LumpNameSetup("");
|
||||
}
|
||||
Lumps[i].LumpSize = Lumps[i].Position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GenerateHash(); // Do this before the lump processing below.
|
||||
|
||||
if (!quiet) // don't bother with namespaces in quiet mode. We won't need them.
|
||||
{
|
||||
SetNamespace("S_START", "S_END", ns_sprites);
|
||||
SetNamespace("F_START", "F_END", ns_flats, true);
|
||||
SetNamespace("C_START", "C_END", ns_colormaps);
|
||||
SetNamespace("A_START", "A_END", ns_acslibrary);
|
||||
SetNamespace("TX_START", "TX_END", ns_newtextures);
|
||||
SetNamespace("V_START", "V_END", ns_strifevoices);
|
||||
SetNamespace("HI_START", "HI_END", ns_hires);
|
||||
SetNamespace("VX_START", "VX_END", ns_voxels);
|
||||
SkinHack();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// IsMarker
|
||||
//
|
||||
// (from BOOM)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
inline bool FWadFile::IsMarker(int lump, const char *marker)
|
||||
{
|
||||
if (Lumps[lump].getName()[0] == marker[0])
|
||||
{
|
||||
return (!strcmp(Lumps[lump].getName(), marker) ||
|
||||
(marker[1] == '_' && !strcmp(Lumps[lump].getName() +1, marker)));
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// SetNameSpace
|
||||
//
|
||||
// Sets namespace information for the lumps. It always looks for the first
|
||||
// x_START and the last x_END lump, except when loading flats. In this case
|
||||
// F_START may be absent and if that is the case all lumps with a size of
|
||||
// 4096 will be flagged appropriately.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
// This class was supposed to be local in the function but GCC
|
||||
// does not like that.
|
||||
struct Marker
|
||||
{
|
||||
int markertype;
|
||||
unsigned int index;
|
||||
};
|
||||
|
||||
void FWadFile::SetNamespace(const char *startmarker, const char *endmarker, namespace_t space, bool flathack)
|
||||
{
|
||||
bool warned = false;
|
||||
int numstartmarkers = 0, numendmarkers = 0;
|
||||
unsigned int i;
|
||||
TArray<Marker> markers;
|
||||
|
||||
for(i = 0; i < NumLumps; i++)
|
||||
{
|
||||
if (IsMarker(i, startmarker))
|
||||
{
|
||||
Marker m = { 0, i };
|
||||
markers.Push(m);
|
||||
numstartmarkers++;
|
||||
}
|
||||
else if (IsMarker(i, endmarker))
|
||||
{
|
||||
Marker m = { 1, i };
|
||||
markers.Push(m);
|
||||
numendmarkers++;
|
||||
}
|
||||
}
|
||||
|
||||
if (numstartmarkers == 0)
|
||||
{
|
||||
if (numendmarkers == 0) return; // no markers found
|
||||
|
||||
Printf(TEXTCOLOR_YELLOW"WARNING: %s marker without corresponding %s found.\n", endmarker, startmarker);
|
||||
|
||||
|
||||
if (flathack)
|
||||
{
|
||||
// We have found no F_START but one or more F_END markers.
|
||||
// mark all lumps before the last F_END marker as potential flats.
|
||||
unsigned int end = markers[markers.Size()-1].index;
|
||||
for(unsigned int i = 0; i < end; i++)
|
||||
{
|
||||
if (Lumps[i].LumpSize == 4096)
|
||||
{
|
||||
// We can't add this to the flats namespace but
|
||||
// it needs to be flagged for the texture manager.
|
||||
DPrintf(DMSG_NOTIFY, "Marking %s as potential flat\n", Lumps[i].getName());
|
||||
Lumps[i].Flags |= LUMPF_MAYBEFLAT;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (i < markers.Size())
|
||||
{
|
||||
int start, end;
|
||||
if (markers[i].markertype != 0)
|
||||
{
|
||||
Printf(TEXTCOLOR_YELLOW"WARNING: %s marker without corresponding %s found.\n", endmarker, startmarker);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
start = i++;
|
||||
|
||||
// skip over subsequent x_START markers
|
||||
while (i < markers.Size() && markers[i].markertype == 0)
|
||||
{
|
||||
Printf(TEXTCOLOR_YELLOW"WARNING: duplicate %s marker found.\n", startmarker);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// same for x_END markers
|
||||
while (i < markers.Size()-1 && (markers[i].markertype == 1 && markers[i+1].markertype == 1))
|
||||
{
|
||||
Printf(TEXTCOLOR_YELLOW"WARNING: duplicate %s marker found.\n", endmarker);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// We found a starting marker but no end marker. Ignore this block.
|
||||
if (i >= markers.Size())
|
||||
{
|
||||
Printf(TEXTCOLOR_YELLOW"WARNING: %s marker without corresponding %s found.\n", startmarker, endmarker);
|
||||
end = NumLumps;
|
||||
}
|
||||
else
|
||||
{
|
||||
end = markers[i++].index;
|
||||
}
|
||||
|
||||
// we found a marked block
|
||||
DPrintf(DMSG_NOTIFY, "Found %s block at (%d-%d)\n", startmarker, markers[start].index, end);
|
||||
for(int j = markers[start].index + 1; j < end; j++)
|
||||
{
|
||||
if (Lumps[j].Namespace != ns_global)
|
||||
{
|
||||
if (!warned)
|
||||
{
|
||||
Printf(TEXTCOLOR_YELLOW"WARNING: Overlapping namespaces found (lump %d)\n", j);
|
||||
}
|
||||
warned = true;
|
||||
}
|
||||
else if (space == ns_sprites && Lumps[j].LumpSize < 8)
|
||||
{
|
||||
// sf 26/10/99:
|
||||
// ignore sprite lumps smaller than 8 bytes (the smallest possible)
|
||||
// in size -- this was used by some dmadds wads
|
||||
// as an 'empty' graphics resource
|
||||
DPrintf(DMSG_WARNING, " Skipped empty sprite %s (lump %d)\n", Lumps[j].getName(), j);
|
||||
}
|
||||
else
|
||||
{
|
||||
Lumps[j].Namespace = space;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// W_SkinHack
|
||||
//
|
||||
// Tests a wad file to see if it contains an S_SKIN marker. If it does,
|
||||
// every lump in the wad is moved into a new namespace. Because skins are
|
||||
// only supposed to replace player sprites, sounds, or faces, this should
|
||||
// not be a problem. Yes, there are skins that replace more than that, but
|
||||
// they are such a pain, and breaking them like this was done on purpose.
|
||||
// This also renames any S_SKINxx lumps to just S_SKIN.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FWadFile::SkinHack ()
|
||||
{
|
||||
// this being static is not a problem. The only relevant thing is that each skin gets a different number.
|
||||
static int namespc = ns_firstskin;
|
||||
bool skinned = false;
|
||||
bool hasmap = false;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < NumLumps; i++)
|
||||
{
|
||||
FResourceLump *lump = &Lumps[i];
|
||||
|
||||
if (!strnicmp(lump->getName(), "S_SKIN", 6))
|
||||
{ // Wad has at least one skin.
|
||||
lump->LumpNameSetup("S_SKIN");
|
||||
if (!skinned)
|
||||
{
|
||||
skinned = true;
|
||||
uint32_t j;
|
||||
|
||||
for (j = 0; j < NumLumps; j++)
|
||||
{
|
||||
Lumps[j].Namespace = namespc;
|
||||
}
|
||||
namespc++;
|
||||
}
|
||||
}
|
||||
if ((lump->getName()[0] == 'M' &&
|
||||
lump->getName()[1] == 'A' &&
|
||||
lump->getName()[2] == 'P' &&
|
||||
lump->getName()[3] >= '0' && lump->getName()[3] <= '9' &&
|
||||
lump->getName()[4] >= '0' && lump->getName()[4] <= '9' &&
|
||||
lump->getName()[5] >= '\0')
|
||||
||
|
||||
(lump->getName()[0] == 'E' &&
|
||||
lump->getName()[1] >= '0' && lump->getName()[1] <= '9' &&
|
||||
lump->getName()[2] == 'M' &&
|
||||
lump->getName()[3] >= '0' && lump->getName()[3] <= '9' &&
|
||||
lump->getName()[4] >= '\0'))
|
||||
{
|
||||
hasmap = true;
|
||||
}
|
||||
}
|
||||
if (skinned && hasmap)
|
||||
{
|
||||
Printf (TEXTCOLOR_BLUE
|
||||
"The maps in %s will not be loaded because it has a skin.\n"
|
||||
TEXTCOLOR_BLUE
|
||||
"You should remove the skin from the wad to play these maps.\n",
|
||||
FileName.GetChars());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// File open
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FResourceFile *CheckWad(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter)
|
||||
{
|
||||
char head[4];
|
||||
|
||||
if (file.GetLength() >= 12)
|
||||
{
|
||||
file.Seek(0, FileReader::SeekSet);
|
||||
file.Read(&head, 4);
|
||||
file.Seek(0, FileReader::SeekSet);
|
||||
if (!memcmp(head, "IWAD", 4) || !memcmp(head, "PWAD", 4))
|
||||
{
|
||||
FResourceFile *rf = new FWadFile(filename, file);
|
||||
if (rf->Open(quiet, filter)) return rf;
|
||||
|
||||
file = std::move(rf->Reader); // to avoid destruction of reader
|
||||
delete rf;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
158
src/common/filesystem/file_whres.cpp
Normal file
158
src/common/filesystem/file_whres.cpp
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
** file_whres.cpp
|
||||
**
|
||||
** reads a Witchaven/TekWar sound resource file
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2009-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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
**
|
||||
*/
|
||||
|
||||
#include "resourcefile.h"
|
||||
#include "printf.h"
|
||||
#include "cmdlib.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct whresentry
|
||||
{
|
||||
int filepospage, filelen, priority;
|
||||
} ;
|
||||
|
||||
struct dpackheader_t
|
||||
{
|
||||
int ident; // == IDPAKHEADER
|
||||
int dirofs;
|
||||
int dirlen;
|
||||
} ;
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Wad file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FWHResFile : public FUncompressedFile
|
||||
{
|
||||
FString basename;
|
||||
public:
|
||||
FWHResFile(const char * filename, FileReader &file);
|
||||
bool Open(bool quiet, LumpFilterInfo* filter);
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FWadFile::FWadFile
|
||||
//
|
||||
// Initializes a WAD file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FWHResFile::FWHResFile(const char *filename, FileReader &file)
|
||||
: FUncompressedFile(filename, file)
|
||||
{
|
||||
basename = ExtractFileBase(filename, false);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Open it
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FWHResFile::Open(bool quiet, LumpFilterInfo*)
|
||||
{
|
||||
int directory[1024];
|
||||
|
||||
Reader.Seek(-4096, FileReader::SeekEnd);
|
||||
Reader.Read(directory, 4096);
|
||||
|
||||
int nl =1024/3;
|
||||
Lumps.Resize(nl);
|
||||
|
||||
|
||||
int i = 0;
|
||||
for(int k = 0; k < nl; k++)
|
||||
{
|
||||
int offset = LittleLong(directory[k*3]) * 4096;
|
||||
int length = LittleLong(directory[k*3+1]);
|
||||
if (length <= 0) break;
|
||||
FStringf synthname("%s/%04d", basename.GetChars(), k);
|
||||
Lumps[i].LumpNameSetup(synthname);
|
||||
Lumps[i].Owner = this;
|
||||
Lumps[i].Position = offset;
|
||||
Lumps[i].LumpSize = length;
|
||||
i++;
|
||||
}
|
||||
NumLumps = i;
|
||||
Lumps.Clamp(NumLumps);
|
||||
Lumps.ShrinkToFit();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// File open
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FResourceFile *CheckWHRes(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter)
|
||||
{
|
||||
if (file.GetLength() >= 8192) // needs to be at least 8192 to contain one file and the directory.
|
||||
{
|
||||
int directory[1024];
|
||||
int nl =1024/3;
|
||||
|
||||
file.Seek(-4096, FileReader::SeekEnd);
|
||||
file.Read(directory, 4096);
|
||||
|
||||
int checkpos = 0;
|
||||
for(int k = 0; k < nl; k++)
|
||||
{
|
||||
int offset = LittleLong(directory[k*3]);
|
||||
int length = LittleLong(directory[k*3+1]);
|
||||
if (length <= 0 && offset == 0) break;
|
||||
if (offset != checkpos || length <= 0) return nullptr;
|
||||
checkpos += (length+4095) / 4096;
|
||||
}
|
||||
FResourceFile *rf = new FWHResFile(filename, file);
|
||||
if (rf->Open(quiet, filter)) return rf;
|
||||
file = std::move(rf->Reader); // to avoid destruction of reader
|
||||
delete rf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
645
src/common/filesystem/file_zip.cpp
Normal file
645
src/common/filesystem/file_zip.cpp
Normal file
|
|
@ -0,0 +1,645 @@
|
|||
/*
|
||||
** 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 "file_zip.h"
|
||||
#include "cmdlib.h"
|
||||
#include "templates.h"
|
||||
#include "printf.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) { I_Error("%s", 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 (CRecoverableError &err)
|
||||
{
|
||||
Printf("%s\n", err.GetMessage());
|
||||
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 = 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 = 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, LumpFilterInfo* filter)
|
||||
{
|
||||
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.IndexOf("/") < 0 || (filter && filter->reservedFolders.Find(name) < filter->reservedFolders.Size());
|
||||
if (isspecial) break;
|
||||
name0 = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (name.IndexOf(name0) != 0)
|
||||
{
|
||||
name0 = "";
|
||||
break;
|
||||
}
|
||||
else if (!foundspeciallump && filter)
|
||||
{
|
||||
// at least one of the more common definition lumps must be present.
|
||||
for (auto &p : filter->requiredPrefixes)
|
||||
{
|
||||
if (name.IndexOf(name0 + p) == 0)
|
||||
{
|
||||
foundspeciallump = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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_FULLPATH;
|
||||
lump_p->NeedFileStart = true;
|
||||
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->CheckEmbedded();
|
||||
|
||||
lump_p++;
|
||||
}
|
||||
// Resize the lump record array to its actual size
|
||||
NumLumps -= skipped;
|
||||
free(directory);
|
||||
|
||||
GenerateHash();
|
||||
PostProcessArchive(&Lumps[0], sizeof(FZipLump), filter);
|
||||
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 (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;
|
||||
NeedFileStart = false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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 (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 (NeedFileStart) SetLumpAddress();
|
||||
const char *buffer;
|
||||
|
||||
if (Method == METHOD_STORED && (buffer = Owner->Reader.GetBuffer()) != NULL)
|
||||
{
|
||||
// This is an in-memory file so the cache can point directly to the file's data.
|
||||
Cache = const_cast<char*>(buffer) + Position;
|
||||
RefCount = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Owner->Reader.Seek(Position, FileReader::SeekSet);
|
||||
Cache = new char[LumpSize];
|
||||
UncompressZipLump(Cache, Owner->Reader, Method, LumpSize, CompressedSize, GPFlags);
|
||||
RefCount = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FZipLump::GetFileOffset()
|
||||
{
|
||||
if (Method != METHOD_STORED) return -1;
|
||||
if (NeedFileStart) SetLumpAddress();
|
||||
return Position;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// File open
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FResourceFile *CheckZip(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter)
|
||||
{
|
||||
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, filter)) 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;
|
||||
}
|
||||
49
src/common/filesystem/file_zip.h
Normal file
49
src/common/filesystem/file_zip.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef __FILE_ZIP_H
|
||||
#define __FILE_ZIP_H
|
||||
|
||||
#include "resourcefile.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Zip Lump
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct FZipLump : public FResourceLump
|
||||
{
|
||||
uint16_t GPFlags;
|
||||
uint8_t Method;
|
||||
bool NeedFileStart;
|
||||
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, LumpFilterInfo* filter);
|
||||
virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
1566
src/common/filesystem/filesystem.cpp
Normal file
1566
src/common/filesystem/filesystem.cpp
Normal file
File diff suppressed because it is too large
Load diff
200
src/common/filesystem/filesystem.h
Normal file
200
src/common/filesystem/filesystem.h
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// WAD I/O functions.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __W_WAD__
|
||||
#define __W_WAD__
|
||||
|
||||
#include "files.h"
|
||||
#include "tarray.h"
|
||||
#include "cmdlib.h"
|
||||
#include "zstring.h"
|
||||
#include "resourcefile.h"
|
||||
|
||||
class FResourceFile;
|
||||
struct FResourceLump;
|
||||
class FTexture;
|
||||
|
||||
union LumpShortName
|
||||
{
|
||||
char String[9];
|
||||
|
||||
uint32_t dword; // These are for accessing the first 4 or 8 chars of
|
||||
uint64_t qword; // Name as a unit without breaking strict aliasing rules
|
||||
};
|
||||
|
||||
|
||||
// A lump 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;
|
||||
};
|
||||
|
||||
class FileSystem
|
||||
{
|
||||
public:
|
||||
FileSystem ();
|
||||
~FileSystem ();
|
||||
|
||||
// The wadnum for the IWAD
|
||||
int GetIwadNum() { return IwadIndex; }
|
||||
void SetIwadNum(int x) { IwadIndex = x; }
|
||||
|
||||
int GetMaxIwadNum() { return MaxIwadIndex; }
|
||||
void SetMaxIwadNum(int x) { MaxIwadIndex = x; }
|
||||
|
||||
void InitSingleFile(const char *filename, bool quiet = false);
|
||||
void InitMultipleFiles (TArray<FString> &filenames, bool quiet = false, LumpFilterInfo* filter = nullptr);
|
||||
void AddFile (const char *filename, FileReader *wadinfo, bool quiet, LumpFilterInfo* filter);
|
||||
int CheckIfResourceFileLoaded (const char *name) noexcept;
|
||||
|
||||
const char *GetResourceFileName (int filenum) const noexcept;
|
||||
const char *GetResourceFileFullName (int wadnum) const noexcept;
|
||||
|
||||
int GetFirstEntry(int wadnum) const noexcept;
|
||||
int GetLastEntry(int wadnum) const noexcept;
|
||||
int GetEntryCount(int wadnum) const noexcept;
|
||||
|
||||
int CheckNumForName (const char *name, int namespc);
|
||||
int CheckNumForName (const char *name, int namespc, int wadfile, bool exact = true);
|
||||
int GetNumForName (const char *name, int namespc);
|
||||
|
||||
inline int CheckNumForName (const uint8_t *name) { return CheckNumForName ((const char *)name, ns_global); }
|
||||
inline int CheckNumForName (const char *name) { return CheckNumForName (name, ns_global); }
|
||||
inline int CheckNumForName (const FString &name) { return CheckNumForName (name.GetChars()); }
|
||||
inline int CheckNumForName (const uint8_t *name, int ns) { return CheckNumForName ((const char *)name, ns); }
|
||||
inline int GetNumForName (const char *name) { return GetNumForName (name, ns_global); }
|
||||
inline int GetNumForName (const FString &name) { return GetNumForName (name.GetChars(), ns_global); }
|
||||
inline int GetNumForName (const uint8_t *name) { return GetNumForName ((const char *)name); }
|
||||
inline int GetNumForName (const uint8_t *name, int ns) { return GetNumForName ((const char *)name, ns); }
|
||||
|
||||
int CheckNumForFullName (const char *name, bool trynormal = false, int namespc = ns_global, bool ignoreext = false);
|
||||
int CheckNumForFullName (const char *name, int wadfile);
|
||||
int GetNumForFullName (const char *name);
|
||||
int FindFile(const char* name)
|
||||
{
|
||||
return CheckNumForFullName(name);
|
||||
}
|
||||
LumpShortName& GetShortName(int i); // may only be called before the hash chains are set up.
|
||||
bool CreatePathlessCopy(const char* name, int id, int flags);
|
||||
|
||||
inline int CheckNumForFullName(const FString &name, bool trynormal = false, int namespc = ns_global) { return CheckNumForFullName(name.GetChars(), trynormal, namespc); }
|
||||
inline int CheckNumForFullName (const FString &name, int wadfile) { return CheckNumForFullName(name.GetChars(), wadfile); }
|
||||
inline int GetNumForFullName (const FString &name) { return GetNumForFullName(name.GetChars()); }
|
||||
|
||||
void SetLinkedTexture(int lump, FTexture *tex);
|
||||
FTexture *GetLinkedTexture(int lump);
|
||||
|
||||
|
||||
void ReadFile (int lump, void *dest);
|
||||
TArray<uint8_t> GetFileData(int lump, int pad = 0); // reads lump into a writable buffer and optionally adds some padding at the end. (FileData isn't writable!)
|
||||
FileData ReadFile (int lump);
|
||||
FileData ReadFile (const char *name) { return ReadFile (GetNumForName (name)); }
|
||||
|
||||
FileReader OpenFileReader(int lump); // opens a reader that redirects to the containing file's one.
|
||||
FileReader ReopenFileReader(int lump, bool alwayscache = false); // opens an independent reader.
|
||||
FileReader OpenFileReader(const char* name);
|
||||
|
||||
int FindLump (const char *name, int *lastlump, bool anyns=false); // [RH] Find lumps with duplication
|
||||
int FindLumpMulti (const char **names, int *lastlump, bool anyns = false, int *nameindex = NULL); // same with multiple possible names
|
||||
bool CheckFileName (int lump, const char *name); // [RH] True if lump's name == name
|
||||
|
||||
int FindFileWithExtensions(const char* name, const char* const* exts, int count);
|
||||
int FindResource(int resid, const char* type, int filenum) const noexcept;
|
||||
int GetResource(int resid, const char* type, int filenum) const;
|
||||
|
||||
|
||||
static uint32_t LumpNameHash (const char *name); // [RH] Create hash key from an 8-char name
|
||||
|
||||
int FileLength (int lump) const;
|
||||
int GetFileOffset (int lump); // [RH] Returns offset of lump in the wadfile
|
||||
int GetFileFlags (int lump); // Return the flags for this lump
|
||||
void GetFileShortName (char *to, int lump) const; // [RH] Copies the lump name to to using uppercopy
|
||||
void GetFileShortName (FString &to, int lump) const;
|
||||
const char* GetFileShortName(int lump) const;
|
||||
const char *GetFileFullName (int lump, bool returnshort = true) const; // [RH] Returns the lump's full name
|
||||
FString GetFileFullPath (int lump) const; // [RH] Returns wad's name + lump's full name
|
||||
int GetFileContainer (int lump) const; // [RH] Returns wadnum for a specified lump
|
||||
int GetFileNamespace (int lump) const; // [RH] Returns the namespace a lump belongs to
|
||||
void SetFileNamespace(int lump, int ns);
|
||||
int GetResourceId(int lump) const; // Returns the RFF index number for this lump
|
||||
const char* GetResourceType(int lump) const;
|
||||
bool CheckFileName (int lump, const char *name) const; // [RH] Returns true if the names match
|
||||
unsigned GetFilesInFolder(const char *path, TArray<FolderEntry> &result, bool atomic) const;
|
||||
|
||||
int GetNumEntries() const
|
||||
{
|
||||
return NumEntries;
|
||||
}
|
||||
|
||||
int GetNumWads() const
|
||||
{
|
||||
return Files.Size();
|
||||
}
|
||||
|
||||
void AddLump(FResourceLump* lump);
|
||||
int AddExternalFile(const char *filename);
|
||||
int AddFromBuffer(const char* name, const char* type, char* data, int size, int id, int flags);
|
||||
FileReader* GetFileReader(int wadnum); // Gets a FileReader object to the entire WAD
|
||||
void InitHashChains();
|
||||
|
||||
protected:
|
||||
|
||||
struct LumpRecord;
|
||||
|
||||
TArray<FResourceFile *> Files;
|
||||
TArray<LumpRecord> FileInfo;
|
||||
|
||||
TArray<uint32_t> Hashes; // one allocation for all hash lists.
|
||||
uint32_t *FirstLumpIndex; // [RH] Hashing stuff moved out of lumpinfo structure
|
||||
uint32_t *NextLumpIndex;
|
||||
|
||||
uint32_t *FirstLumpIndex_FullName; // The same information for fully qualified paths from .zips
|
||||
uint32_t *NextLumpIndex_FullName;
|
||||
|
||||
uint32_t *FirstLumpIndex_NoExt; // The same information for fully qualified paths from .zips
|
||||
uint32_t *NextLumpIndex_NoExt;
|
||||
|
||||
uint32_t* FirstLumpIndex_ResId; // The same information for fully qualified paths from .zips
|
||||
uint32_t* NextLumpIndex_ResId;
|
||||
|
||||
uint32_t NumEntries = 0; // Not necessarily the same as FileInfo.Size()
|
||||
uint32_t NumWads;
|
||||
|
||||
int IwadIndex = -1;
|
||||
int MaxIwadIndex = -1;
|
||||
|
||||
private:
|
||||
void DeleteAll();
|
||||
void MoveLumpsInFolder(const char *);
|
||||
|
||||
};
|
||||
|
||||
extern FileSystem fileSystem;
|
||||
|
||||
#endif
|
||||
688
src/common/filesystem/resourcefile.cpp
Normal file
688
src/common/filesystem/resourcefile.cpp
Normal file
|
|
@ -0,0 +1,688 @@
|
|||
/*
|
||||
** 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 "md5.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->Lock();
|
||||
bufptr = src->Cache;
|
||||
}
|
||||
|
||||
~FLumpReader()
|
||||
{
|
||||
source->Unlock();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Base class for resource lumps
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FResourceLump::~FResourceLump()
|
||||
{
|
||||
if (Cache != NULL && RefCount >= 0)
|
||||
{
|
||||
delete [] Cache;
|
||||
Cache = NULL;
|
||||
}
|
||||
Owner = NULL;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sets up the lump name information for anything not coming from a WAD file.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FResourceLump::LumpNameSetup(FString iname)
|
||||
{
|
||||
// this causes interference with real Dehacked lumps.
|
||||
if (!iname.CompareNoCase("dehacked.exe"))
|
||||
{
|
||||
iname = "";
|
||||
}
|
||||
|
||||
FullName = iname;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Checks for embedded resource files
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static bool IsWadInFolder(const FResourceFile* const archive, const char* const resPath)
|
||||
{
|
||||
// Checks a special case when <somefile.wad> was put in
|
||||
// <myproject> directory inside <myproject.zip>
|
||||
|
||||
if (NULL == archive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FString dirName = ExtractFileBase(archive->FileName);
|
||||
const FString fileName = ExtractFileBase(resPath, true);
|
||||
const FString filePath = dirName + '/' + fileName;
|
||||
|
||||
return 0 == filePath.CompareNoCase(resPath);
|
||||
}
|
||||
|
||||
void FResourceLump::CheckEmbedded()
|
||||
{
|
||||
// Checks for embedded archives
|
||||
const char *c = strstr(FullName, ".wad");
|
||||
if (c && strlen(c) == 4 && (!strchr(FullName, '/') || IsWadInFolder(Owner, FullName)))
|
||||
{
|
||||
Flags |= LUMPF_EMBEDDED;
|
||||
}
|
||||
/* later
|
||||
else
|
||||
{
|
||||
if (c==NULL) c = strstr(Name, ".zip");
|
||||
if (c==NULL) c = strstr(Name, ".pk3");
|
||||
if (c==NULL) c = strstr(Name, ".7z");
|
||||
if (c==NULL) c = strstr(Name, ".pak");
|
||||
if (c && strlen(c) <= 4)
|
||||
{
|
||||
// Mark all embedded archives in any directory
|
||||
Flags |= LUMPF_EMBEDDED;
|
||||
memset(Name, 0, 8);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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, Lock(), LumpSize);
|
||||
Unlock();
|
||||
cbuf.mCRC32 = crc32(0, (uint8_t*)cbuf.mBuffer, LumpSize);
|
||||
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::Lock()
|
||||
{
|
||||
if (Cache != NULL)
|
||||
{
|
||||
if (RefCount > 0) RefCount++;
|
||||
}
|
||||
else if (LumpSize > 0)
|
||||
{
|
||||
FillCache();
|
||||
}
|
||||
return Cache;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Decrements reference counter and frees lump if counter reaches 0
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FResourceLump::Unlock()
|
||||
{
|
||||
if (LumpSize > 0 && RefCount > 0)
|
||||
{
|
||||
if (--RefCount == 0)
|
||||
{
|
||||
delete [] Cache;
|
||||
Cache = NULL;
|
||||
}
|
||||
}
|
||||
return RefCount;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Opens a resource file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
typedef FResourceFile * (*CheckFunc)(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter);
|
||||
|
||||
FResourceFile *CheckWad(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter);
|
||||
FResourceFile *CheckGRP(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter);
|
||||
FResourceFile *CheckRFF(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter);
|
||||
FResourceFile *CheckPak(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter);
|
||||
FResourceFile *CheckZip(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter);
|
||||
FResourceFile *Check7Z(const char *filename, FileReader &file, bool quiet, LumpFilterInfo* filter);
|
||||
FResourceFile *CheckLump(const char *filename,FileReader &file, bool quiet, LumpFilterInfo* filter);
|
||||
FResourceFile *CheckDir(const char *filename, bool quiet, bool nosub, LumpFilterInfo* filter);
|
||||
|
||||
static CheckFunc funcs[] = { CheckWad, CheckZip, Check7Z, CheckPak, CheckGRP, CheckRFF, CheckLump };
|
||||
|
||||
FResourceFile *FResourceFile::DoOpenResourceFile(const char *filename, FileReader &file, bool quiet, bool containeronly, LumpFilterInfo* filter)
|
||||
{
|
||||
for(size_t i = 0; i < countof(funcs) - containeronly; i++)
|
||||
{
|
||||
FResourceFile *resfile = funcs[i](filename, file, quiet, filter);
|
||||
if (resfile != NULL) return resfile;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FResourceFile *FResourceFile::OpenResourceFile(const char *filename, FileReader &file, bool quiet, bool containeronly, LumpFilterInfo* filter)
|
||||
{
|
||||
return DoOpenResourceFile(filename, file, quiet, containeronly, filter);
|
||||
}
|
||||
|
||||
|
||||
FResourceFile *FResourceFile::OpenResourceFile(const char *filename, bool quiet, bool containeronly, LumpFilterInfo* filter)
|
||||
{
|
||||
FileReader file;
|
||||
if (!file.OpenFile(filename)) return nullptr;
|
||||
return DoOpenResourceFile(filename, file, quiet, containeronly, filter);
|
||||
}
|
||||
|
||||
FResourceFile *FResourceFile::OpenDirectory(const char *filename, bool quiet, LumpFilterInfo* filter)
|
||||
{
|
||||
return CheckDir(filename, quiet, false, filter);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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 stricmp(rec1->getName(), rec2->getName());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FResourceFile :: GenerateHash
|
||||
//
|
||||
// Generates a hash identifier for use in file identification.
|
||||
// Potential uses are mod-wide compatibility settings or localization add-ons.
|
||||
// This only hashes the lump directory but not the actual content
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FResourceFile::GenerateHash()
|
||||
{
|
||||
// hash the lump directory after sorting
|
||||
|
||||
Hash.Format(("%08X-%04X-"), (unsigned)Reader.GetLength(), NumLumps);
|
||||
|
||||
MD5Context md5;
|
||||
|
||||
uint8_t digest[16];
|
||||
for(uint32_t i = 0; i < NumLumps; i++)
|
||||
{
|
||||
auto lump = GetLump(i);
|
||||
md5.Update((const uint8_t*)lump->FullName.GetChars(), (unsigned)lump->FullName.Len() + 1);
|
||||
md5.Update((const uint8_t*)&lump->LumpSize, 4);
|
||||
}
|
||||
md5.Final(digest);
|
||||
for (auto c : digest)
|
||||
{
|
||||
Hash.AppendFormat("%02X", c);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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, LumpFilterInfo *filter)
|
||||
{
|
||||
// Entries in archives are sorted alphabetically
|
||||
qsort(lumps, NumLumps, lumpsize, lumpcmp);
|
||||
if (!filter) return;
|
||||
|
||||
// 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(filter, lumps, lumpsize, max);
|
||||
|
||||
long len;
|
||||
int lastpos = -1;
|
||||
FString file;
|
||||
FString LumpFilter = filter->dotFilter;
|
||||
if (LumpFilter.IndexOf('.') < 0)
|
||||
{
|
||||
max -= FilterLumps(LumpFilter, lumps, lumpsize, max);
|
||||
}
|
||||
else while ((len = LumpFilter.IndexOf('.', lastpos+1)) > 0)
|
||||
{
|
||||
max -= FilterLumps(LumpFilter.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(LumpFilterInfo *filter, void *lumps, size_t lumpsize, uint32_t max)
|
||||
{
|
||||
if (filter == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
for (auto &fstring : filter->gameTypeFilter)
|
||||
{
|
||||
count += FilterLumps(fstring, 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 = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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()
|
||||
{
|
||||
const char * buffer = Owner->Reader.GetBuffer();
|
||||
|
||||
if (buffer != NULL)
|
||||
{
|
||||
// This is an in-memory file so the cache can point directly to the file's data.
|
||||
Cache = const_cast<char*>(buffer) + Position;
|
||||
RefCount = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Owner->Reader.Seek(Position, FileReader::SeekSet);
|
||||
Cache = new char[LumpSize];
|
||||
Owner->Reader.Read(Cache, 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 = new char[LumpSize];
|
||||
FileReader f;
|
||||
|
||||
if (f.OpenFile(Filename))
|
||||
{
|
||||
f.Read(Cache, LumpSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(Cache, 0, LumpSize);
|
||||
}
|
||||
RefCount = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
224
src/common/filesystem/resourcefile.h
Normal file
224
src/common/filesystem/resourcefile.h
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
|
||||
|
||||
#ifndef __RESFILE_H
|
||||
#define __RESFILE_H
|
||||
|
||||
#include "files.h"
|
||||
|
||||
struct LumpFilterInfo
|
||||
{
|
||||
TArray<FString> gameTypeFilter; // this can contain multiple entries
|
||||
FString dotFilter;
|
||||
|
||||
// The following are for checking if the root directory of a zip can be removed.
|
||||
TArray<FString> reservedFolders;
|
||||
TArray<FString> requiredPrefixes;
|
||||
std::function<void()> postprocessFunc;
|
||||
};
|
||||
|
||||
class FResourceFile;
|
||||
class FTexture;
|
||||
|
||||
// [RH] Namespaces from BOOM.
|
||||
// These are needed here in the low level part so that WAD files can be properly set up.
|
||||
typedef enum {
|
||||
ns_hidden = -1,
|
||||
|
||||
ns_global = 0,
|
||||
ns_sprites,
|
||||
ns_flats,
|
||||
ns_colormaps,
|
||||
ns_acslibrary,
|
||||
ns_newtextures,
|
||||
ns_bloodraw, // no longer used - kept for ZScript.
|
||||
ns_bloodsfx, // no longer used - kept for ZScript.
|
||||
ns_bloodmisc, // no longer used - kept for ZScript.
|
||||
ns_strifevoices,
|
||||
ns_hires,
|
||||
ns_voxels,
|
||||
|
||||
// These namespaces are only used to mark lumps in special subdirectories
|
||||
// so that their contents doesn't interfere with the global namespace.
|
||||
// searching for data in these namespaces works differently for lumps coming
|
||||
// from Zips or other files.
|
||||
ns_specialzipdirectory,
|
||||
ns_sounds,
|
||||
ns_patches,
|
||||
ns_graphics,
|
||||
ns_music,
|
||||
|
||||
ns_firstskin,
|
||||
} namespace_t;
|
||||
|
||||
enum ELumpFlags
|
||||
{
|
||||
LUMPF_MAYBEFLAT = 1, // might be a flat outside F_START/END
|
||||
LUMPF_FULLPATH = 2, // contains a full path. This will trigger extended namespace checks when looking up short names.
|
||||
LUMPF_EMBEDDED = 4, // marks an embedded resource file for later processing.
|
||||
LUMPF_SHORTNAME = 8, // the stored name is a short extension-less name
|
||||
LUMPF_COMPRESSED = 16, // compressed or encrypted, i.e. cannot be read with the container file's reader.
|
||||
};
|
||||
|
||||
// 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;
|
||||
friend class FWadFile; // this still needs direct access.
|
||||
|
||||
int LumpSize;
|
||||
int RefCount;
|
||||
protected:
|
||||
FString FullName;
|
||||
public:
|
||||
uint8_t Flags;
|
||||
char * Cache;
|
||||
FResourceFile * Owner;
|
||||
|
||||
FResourceLump()
|
||||
{
|
||||
Cache = NULL;
|
||||
Owner = NULL;
|
||||
Flags = 0;
|
||||
RefCount = 0;
|
||||
}
|
||||
|
||||
virtual ~FResourceLump();
|
||||
virtual FileReader *GetReader();
|
||||
virtual FileReader NewReader();
|
||||
virtual int GetFileOffset() { return -1; }
|
||||
virtual int GetIndexNum() const { return 0; }
|
||||
virtual int GetNamespace() const { return 0; }
|
||||
void LumpNameSetup(FString iname);
|
||||
void CheckEmbedded();
|
||||
virtual FCompressedBuffer GetRawData();
|
||||
|
||||
void *Lock(); // validates the cache and increases the refcount.
|
||||
int Unlock(); // decreases the refcount and frees the buffer
|
||||
|
||||
unsigned Size() const{ return LumpSize; }
|
||||
int LockCount() const { return RefCount; }
|
||||
const char* getName() { return FullName.GetChars(); }
|
||||
|
||||
protected:
|
||||
virtual int FillCache() { return -1; }
|
||||
|
||||
};
|
||||
|
||||
class FResourceFile
|
||||
{
|
||||
public:
|
||||
FileReader Reader;
|
||||
FString FileName;
|
||||
protected:
|
||||
uint32_t NumLumps;
|
||||
FString Hash;
|
||||
|
||||
FResourceFile(const char *filename);
|
||||
FResourceFile(const char *filename, FileReader &r);
|
||||
|
||||
// for archives that can contain directories
|
||||
void GenerateHash();
|
||||
void PostProcessArchive(void *lumps, size_t lumpsize, LumpFilterInfo *filter);
|
||||
|
||||
private:
|
||||
uint32_t FirstLump;
|
||||
|
||||
int FilterLumps(FString filtername, void *lumps, size_t lumpsize, uint32_t max);
|
||||
int FilterLumpsByGameType(LumpFilterInfo *filter, 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, LumpFilterInfo* filter);
|
||||
|
||||
public:
|
||||
static FResourceFile *OpenResourceFile(const char *filename, FileReader &file, bool quiet = false, bool containeronly = false, LumpFilterInfo* filter = nullptr);
|
||||
static FResourceFile *OpenResourceFile(const char *filename, bool quiet = false, bool containeronly = false, LumpFilterInfo* filter = nullptr);
|
||||
static FResourceFile *OpenDirectory(const char *filename, bool quiet = false, LumpFilterInfo* filter = nullptr);
|
||||
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 GetFirstEntry() const { return FirstLump; }
|
||||
void SetFirstLump(uint32_t f) { FirstLump = f; }
|
||||
const FString &GetHash() const { return Hash; }
|
||||
|
||||
|
||||
virtual bool Open(bool quiet, LumpFilterInfo* filter) = 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 (WAD, 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();
|
||||
|
||||
};
|
||||
|
||||
struct FMemoryLump : public FResourceLump
|
||||
{
|
||||
FMemoryLump(const void* data, int length)
|
||||
{
|
||||
RefCount = INT_MAX / 2;
|
||||
LumpSize = length;
|
||||
Cache = new char[length];
|
||||
memcpy(Cache, data, length);
|
||||
}
|
||||
|
||||
virtual int FillCache() override
|
||||
{
|
||||
RefCount = INT_MAX / 2; // Make sure it never counts down to 0 by resetting it to something high each time it is used.
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
70
src/common/filesystem/w_zip.h
Normal file
70
src/common/filesystem/w_zip.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#ifndef __W_ZIP
|
||||
#define __W_ZIP
|
||||
|
||||
#include "basics.h"
|
||||
|
||||
#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…
Add table
Add a link
Reference in a new issue