2023-08-19 16:11:38 +00:00
/*
* * files . h
* * Implements classes for reading from files or memory blocks
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 1998 - 2008 Randy Heit
2023-12-10 12:30:50 +00:00
* * Copyright 2005 - 2023 Christoph Oelckers
2023-08-19 16:11:38 +00:00
* * 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>
2023-08-23 18:36:19 +00:00
# include <vector>
2023-08-19 16:11:38 +00:00
# include "fs_swap.h"
# include "tarray.h"
2023-08-23 18:36:19 +00:00
namespace FileSys {
2023-08-19 16:11:38 +00:00
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 ,
2023-10-07 16:44:31 +00:00
METHOD_XZ = 95 ,
2023-08-19 16:11:38 +00:00
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 ;
2023-12-10 12:30:50 +00:00
// 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 ;
}
} ;
2023-08-19 16:11:38 +00:00
class FileReaderInterface
{
public :
2023-09-23 07:56:27 +00:00
ptrdiff_t Length = - 1 ;
2023-08-19 16:11:38 +00:00
virtual ~ FileReaderInterface ( ) { }
2023-09-23 07:56:27 +00:00
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 ;
2023-08-19 16:11:38 +00:00
virtual const char * GetBuffer ( ) const { return nullptr ; }
2023-09-23 07:56:27 +00:00
ptrdiff_t GetLength ( ) const { return Length ; }
2023-08-19 16:11:38 +00:00
} ;
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 ;
}
2023-12-10 12:30:50 +00:00
bool OpenFile ( const char * filename , Size start = 0 , Size length = - 1 , bool buffered = false ) ;
2023-08-19 16:11:38 +00:00
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.
2023-12-10 12:30:50 +00:00
bool OpenMemoryArray ( std : : vector < uint8_t > & data ) ; // take the given array
bool OpenMemoryArray ( ResourceData & data ) ; // take the given array
2023-08-19 16:11:38 +00:00
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 )
{
2023-09-23 07:56:27 +00:00
return mReader - > Seek ( offset , origin ) ;
2023-08-19 16:11:38 +00:00
}
Size Read ( void * buffer , Size len ) const
{
2023-09-23 07:56:27 +00:00
return mReader - > Read ( buffer , len ) ;
2023-08-19 16:11:38 +00:00
}
2023-12-10 12:30:50 +00:00
ResourceData Read ( size_t len )
2023-08-19 16:11:38 +00:00
{
2023-12-10 12:30:50 +00:00
ResourceData buffer ;
2023-08-23 18:36:19 +00:00
if ( len > 0 )
{
2023-12-10 12:30:50 +00:00
Size length = mReader - > Read ( buffer . allocate ( len ) , len ) ;
if ( ( size_t ) length < len ) buffer . allocate ( length ) ;
2023-08-23 18:36:19 +00:00
}
2023-08-19 16:11:38 +00:00
return buffer ;
}
2023-12-10 12:30:50 +00:00
ResourceData Read ( )
2023-08-19 16:11:38 +00:00
{
return Read ( GetLength ( ) ) ;
}
2023-12-10 12:30:50 +00:00
ResourceData ReadPadded ( size_t padding )
2023-08-19 16:11:38 +00:00
{
auto len = GetLength ( ) ;
2023-12-10 12:30:50 +00:00
ResourceData buffer ;
2023-08-23 18:36:19 +00:00
if ( len > 0 )
{
2023-12-10 12:30:50 +00:00
auto p = ( char * ) buffer . allocate ( len + padding ) ;
Size length = mReader - > Read ( p , len ) ;
2023-08-23 18:36:19 +00:00
if ( length < len ) buffer . clear ( ) ;
2023-12-10 12:30:50 +00:00
else memset ( p + len , 0 , padding ) ;
2023-08-23 18:36:19 +00:00
}
2023-08-19 16:11:38 +00:00
return buffer ;
}
char * Gets ( char * strbuf , Size len )
{
2023-09-23 07:56:27 +00:00
return mReader - > Gets ( strbuf , len ) ;
2023-08-19 16:11:38 +00:00
}
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 ) ;
2023-08-23 18:36:19 +00:00
return byteswap : : LittleShort ( v ) ;
2023-08-19 16:11:38 +00:00
}
int16_t ReadInt16 ( )
{
return ( int16_t ) ReadUInt16 ( ) ;
}
int16_t ReadUInt16BE ( )
{
uint16_t v = 0 ;
Read ( & v , 2 ) ;
2023-08-23 18:36:19 +00:00
return byteswap : : BigShort ( v ) ;
2023-08-19 16:11:38 +00:00
}
int16_t ReadInt16BE ( )
{
return ( int16_t ) ReadUInt16BE ( ) ;
}
uint32_t ReadUInt32 ( )
{
uint32_t v = 0 ;
Read ( & v , 4 ) ;
2023-08-23 18:36:19 +00:00
return byteswap : : LittleLong ( v ) ;
2023-08-19 16:11:38 +00:00
}
int32_t ReadInt32 ( )
{
return ( int32_t ) ReadUInt32 ( ) ;
}
uint32_t ReadUInt32BE ( )
{
uint32_t v = 0 ;
Read ( & v , 4 ) ;
2023-08-23 18:36:19 +00:00
return byteswap : : BigLong ( v ) ;
2023-08-19 16:11:38 +00:00
}
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 ) ;
2023-09-23 07:56:27 +00:00
virtual ptrdiff_t Tell ( ) ;
virtual ptrdiff_t Seek ( ptrdiff_t offset , int mode ) ;
2023-08-19 16:11:38 +00:00
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 :
2023-12-10 12:30:50 +00:00
std : : vector < unsigned char > mBuffer ;
2023-08-19 16:11:38 +00:00
public :
BufferWriter ( ) { }
virtual size_t Write ( const void * buffer , size_t len ) override ;
2023-12-10 12:30:50 +00:00
std : : vector < unsigned char > * GetBuffer ( ) { return & mBuffer ; }
std : : vector < unsigned char > & & TakeBuffer ( ) { return std : : move ( mBuffer ) ; }
2023-08-19 16:11:38 +00:00
} ;
2023-08-23 18:36:19 +00:00
}
2023-08-19 16:11:38 +00:00
# endif