mirror of
https://github.com/ZDoom/Raze.git
synced 2024-12-13 22:21:14 +00:00
448 lines
9.4 KiB
C++
448 lines
9.4 KiB
C++
/*
|
|
** files.h
|
|
** Implements classes for reading from files or memory blocks
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 1998-2008 Randy Heit
|
|
** Copyright 2005-2023 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.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
#ifndef FILES_H
|
|
#define FILES_H
|
|
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdarg.h>
|
|
#include <functional>
|
|
#include <vector>
|
|
#include "fs_swap.h"
|
|
|
|
#include "tarray.h"
|
|
|
|
namespace FileSys {
|
|
|
|
class FileSystemException : public std::exception
|
|
{
|
|
protected:
|
|
static const int MAX_FSERRORTEXT = 1024;
|
|
char m_Message[MAX_FSERRORTEXT];
|
|
|
|
public:
|
|
FileSystemException(const char* error, ...)
|
|
{
|
|
va_list argptr;
|
|
va_start(argptr, error);
|
|
vsnprintf(m_Message, MAX_FSERRORTEXT, error, argptr);
|
|
va_end(argptr);
|
|
}
|
|
FileSystemException(const char* error, va_list argptr)
|
|
{
|
|
vsnprintf(m_Message, MAX_FSERRORTEXT, error, argptr);
|
|
}
|
|
char const* what() const noexcept override
|
|
{
|
|
return m_Message;
|
|
}
|
|
};
|
|
|
|
// Zip compression methods, extended by some internal types to be passed to OpenDecompressor
|
|
enum
|
|
{
|
|
METHOD_STORED = 0,
|
|
METHOD_SHRINK = 1,
|
|
METHOD_IMPLODE = 6,
|
|
METHOD_DEFLATE = 8,
|
|
METHOD_BZIP2 = 12,
|
|
METHOD_LZMA = 14,
|
|
METHOD_XZ = 95,
|
|
METHOD_PPMD = 98,
|
|
METHOD_LZSS = 1337, // not used in Zips - this is for Console Doom compression
|
|
METHOD_ZLIB = 1338, // Zlib stream with header, used by compressed nodes.
|
|
METHOD_TRANSFEROWNER = 0x8000,
|
|
};
|
|
|
|
class FileReader;
|
|
|
|
// an opaque memory buffer to the file's content. Can either own the memory or just point to an external buffer.
|
|
class ResourceData
|
|
{
|
|
void* memory;
|
|
size_t length;
|
|
bool owned;
|
|
|
|
public:
|
|
using value_type = uint8_t;
|
|
ResourceData() { memory = nullptr; length = 0; owned = true; }
|
|
const void* data() const { return memory; }
|
|
size_t size() const { return length; }
|
|
const char* string() const { return (const char*)memory; }
|
|
const uint8_t* bytes() const { return (const uint8_t*)memory; }
|
|
|
|
ResourceData& operator = (const ResourceData& copy)
|
|
{
|
|
if (owned && memory) free(memory);
|
|
length = copy.length;
|
|
owned = copy.owned;
|
|
if (owned)
|
|
{
|
|
memory = malloc(length);
|
|
memcpy(memory, copy.memory, length);
|
|
}
|
|
else memory = copy.memory;
|
|
return *this;
|
|
}
|
|
|
|
ResourceData& operator = (ResourceData&& copy) noexcept
|
|
{
|
|
if (owned && memory) free(memory);
|
|
length = copy.length;
|
|
owned = copy.owned;
|
|
memory = copy.memory;
|
|
copy.memory = nullptr;
|
|
copy.length = 0;
|
|
copy.owned = true;
|
|
return *this;
|
|
}
|
|
|
|
ResourceData(const ResourceData& copy)
|
|
{
|
|
memory = nullptr;
|
|
*this = copy;
|
|
}
|
|
|
|
~ResourceData()
|
|
{
|
|
if (owned && memory) free(memory);
|
|
}
|
|
|
|
void* allocate(size_t len)
|
|
{
|
|
if (!owned) memory = nullptr;
|
|
length = len;
|
|
owned = true;
|
|
memory = realloc(memory, length);
|
|
return memory;
|
|
}
|
|
|
|
void set(const void* mem, size_t len)
|
|
{
|
|
memory = (void*)mem;
|
|
length = len;
|
|
owned = false;
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
if (owned && memory) free(memory);
|
|
memory = nullptr;
|
|
length = 0;
|
|
owned = true;
|
|
}
|
|
|
|
};
|
|
|
|
class FileReaderInterface
|
|
{
|
|
public:
|
|
ptrdiff_t Length = -1;
|
|
virtual ~FileReaderInterface() {}
|
|
virtual ptrdiff_t Tell () const = 0;
|
|
virtual ptrdiff_t Seek (ptrdiff_t offset, int origin) = 0;
|
|
virtual ptrdiff_t Read (void *buffer, ptrdiff_t len) = 0;
|
|
virtual char *Gets(char *strbuf, ptrdiff_t len) = 0;
|
|
virtual const char *GetBuffer() const { return nullptr; }
|
|
ptrdiff_t GetLength () const { return Length; }
|
|
};
|
|
|
|
struct FResourceLump;
|
|
|
|
class FileReader
|
|
{
|
|
friend struct FResourceLump; // needs access to the private constructor.
|
|
|
|
FileReaderInterface *mReader = nullptr;
|
|
|
|
FileReader(const FileReader &r) = delete;
|
|
FileReader &operator=(const FileReader &r) = delete;
|
|
|
|
public:
|
|
|
|
explicit FileReader(FileReaderInterface *r)
|
|
{
|
|
mReader = r;
|
|
}
|
|
|
|
enum ESeek
|
|
{
|
|
SeekSet = SEEK_SET,
|
|
SeekCur = SEEK_CUR,
|
|
SeekEnd = SEEK_END
|
|
};
|
|
|
|
typedef ptrdiff_t Size; // let's not use 'long' here.
|
|
|
|
FileReader() {}
|
|
|
|
FileReader(FileReader &&r)
|
|
{
|
|
mReader = r.mReader;
|
|
r.mReader = nullptr;
|
|
}
|
|
|
|
FileReader& operator =(FileReader &&r)
|
|
{
|
|
Close();
|
|
mReader = r.mReader;
|
|
r.mReader = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
// This is for wrapping the actual reader for custom access where a managed FileReader won't work.
|
|
FileReaderInterface* GetInterface()
|
|
{
|
|
auto i = mReader;
|
|
mReader = nullptr;
|
|
return i;
|
|
}
|
|
|
|
|
|
~FileReader()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
bool isOpen() const
|
|
{
|
|
return mReader != nullptr;
|
|
}
|
|
|
|
void Close()
|
|
{
|
|
if (mReader != nullptr) delete mReader;
|
|
mReader = nullptr;
|
|
}
|
|
|
|
bool OpenFile(const char *filename, Size start = 0, Size length = -1, bool buffered = false);
|
|
bool OpenFilePart(FileReader &parent, Size start, Size length);
|
|
bool OpenMemory(const void *mem, Size length); // read directly from the buffer
|
|
bool OpenMemoryArray(const void *mem, Size length); // read from a copy of the buffer.
|
|
bool OpenMemoryArray(std::vector<uint8_t>& data); // take the given array
|
|
bool OpenMemoryArray(ResourceData& data); // take the given array
|
|
bool OpenMemoryArray(std::function<bool(std::vector<uint8_t>&)> getter); // read contents to a buffer and return a reader to it
|
|
bool OpenDecompressor(FileReader &parent, Size length, int method, bool seekable, bool exceptions = false); // creates a decompressor stream. 'seekable' uses a buffered version so that the Seek and Tell methods can be used.
|
|
|
|
Size Tell() const
|
|
{
|
|
return mReader->Tell();
|
|
}
|
|
|
|
Size Seek(Size offset, ESeek origin)
|
|
{
|
|
return mReader->Seek(offset, origin);
|
|
}
|
|
|
|
Size Read(void *buffer, Size len) const
|
|
{
|
|
return mReader->Read(buffer, len);
|
|
}
|
|
|
|
ResourceData Read(size_t len)
|
|
{
|
|
ResourceData buffer;
|
|
if (len > 0)
|
|
{
|
|
Size length = mReader->Read(buffer.allocate(len), len);
|
|
if ((size_t)length < len) buffer.allocate(length);
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
ResourceData Read()
|
|
{
|
|
return Read(GetLength());
|
|
}
|
|
|
|
ResourceData ReadPadded(size_t padding)
|
|
{
|
|
auto len = GetLength();
|
|
ResourceData buffer;
|
|
|
|
if (len > 0)
|
|
{
|
|
auto p = (char*)buffer.allocate(len + padding);
|
|
Size length = mReader->Read(p, len);
|
|
if (length < len) buffer.clear();
|
|
else memset(p + len, 0, padding);
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
char *Gets(char *strbuf, Size len)
|
|
{
|
|
return mReader->Gets(strbuf, len);
|
|
}
|
|
|
|
const char *GetBuffer()
|
|
{
|
|
return mReader->GetBuffer();
|
|
}
|
|
|
|
Size GetLength() const
|
|
{
|
|
return mReader->GetLength();
|
|
}
|
|
|
|
uint8_t ReadUInt8()
|
|
{
|
|
uint8_t v = 0;
|
|
Read(&v, 1);
|
|
return v;
|
|
}
|
|
|
|
int8_t ReadInt8()
|
|
{
|
|
int8_t v = 0;
|
|
Read(&v, 1);
|
|
return v;
|
|
}
|
|
|
|
|
|
uint16_t ReadUInt16()
|
|
{
|
|
uint16_t v = 0;
|
|
Read(&v, 2);
|
|
return byteswap::LittleShort(v);
|
|
}
|
|
|
|
int16_t ReadInt16()
|
|
{
|
|
return (int16_t)ReadUInt16();
|
|
}
|
|
|
|
int16_t ReadUInt16BE()
|
|
{
|
|
uint16_t v = 0;
|
|
Read(&v, 2);
|
|
return byteswap::BigShort(v);
|
|
}
|
|
|
|
int16_t ReadInt16BE()
|
|
{
|
|
return (int16_t)ReadUInt16BE();
|
|
}
|
|
|
|
uint32_t ReadUInt32()
|
|
{
|
|
uint32_t v = 0;
|
|
Read(&v, 4);
|
|
return byteswap::LittleLong(v);
|
|
}
|
|
|
|
int32_t ReadInt32()
|
|
{
|
|
return (int32_t)ReadUInt32();
|
|
}
|
|
|
|
uint32_t ReadUInt32BE()
|
|
{
|
|
uint32_t v = 0;
|
|
Read(&v, 4);
|
|
return byteswap::BigLong(v);
|
|
}
|
|
|
|
int32_t ReadInt32BE()
|
|
{
|
|
return (int32_t)ReadUInt32BE();
|
|
}
|
|
|
|
uint64_t ReadUInt64()
|
|
{
|
|
uint64_t v = 0;
|
|
Read(&v, 8);
|
|
// Prove to me that there's a relevant 64 bit Big Endian architecture and I fix this! :P
|
|
return v;
|
|
}
|
|
|
|
|
|
friend class FileSystem;
|
|
};
|
|
|
|
|
|
class FileWriter
|
|
{
|
|
protected:
|
|
bool OpenDirect(const char *filename);
|
|
|
|
public:
|
|
FileWriter(FILE *f = nullptr) // if passed, this writer will take over the file.
|
|
{
|
|
File = f;
|
|
}
|
|
virtual ~FileWriter()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
static FileWriter *Open(const char *filename);
|
|
|
|
virtual size_t Write(const void *buffer, size_t len);
|
|
virtual ptrdiff_t Tell();
|
|
virtual ptrdiff_t Seek(ptrdiff_t offset, int mode);
|
|
size_t Printf(const char *fmt, ...);
|
|
|
|
virtual void Close()
|
|
{
|
|
if (File != NULL) fclose(File);
|
|
File = nullptr;
|
|
}
|
|
|
|
protected:
|
|
|
|
FILE *File;
|
|
|
|
protected:
|
|
bool CloseOnDestruct;
|
|
};
|
|
|
|
class BufferWriter : public FileWriter
|
|
{
|
|
protected:
|
|
std::vector<unsigned char> mBuffer;
|
|
public:
|
|
|
|
BufferWriter() {}
|
|
virtual size_t Write(const void *buffer, size_t len) override;
|
|
std::vector<unsigned char> *GetBuffer() { return &mBuffer; }
|
|
std::vector<unsigned char>&& TakeBuffer() { return std::move(mBuffer); }
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|