qzdoom/src/files.cpp

475 lines
9.7 KiB
C++
Raw Normal View History

2016-03-01 15:47:10 +00:00
/*
** files.cpp
** Implements classes for reading from files or memory blocks
**
**---------------------------------------------------------------------------
** Copyright 1998-2008 Randy Heit
** Copyright 2005-2008 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 "files.h"
#include "i_system.h"
#include "templates.h"
#include "m_misc.h"
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// StdFileReader
2016-03-01 15:47:10 +00:00
//
// reads data from an stdio FILE* or part of it.
2016-03-01 15:47:10 +00:00
//
//==========================================================================
class StdFileReader : public FileReaderInterface
2016-03-01 15:47:10 +00:00
{
FILE *File = nullptr;
long Length = 0;
long StartPos = 0;
long FilePos = 0;
2016-03-01 15:47:10 +00:00
public:
StdFileReader()
{}
~StdFileReader()
2016-03-01 15:47:10 +00:00
{
if (File != nullptr)
{
fclose(File);
}
File = nullptr;
2016-03-01 15:47:10 +00:00
}
bool Open(const char *filename, long startpos = 0, long len = -1)
2016-03-01 15:47:10 +00:00
{
File = fopen(filename, "rb");
if (File == nullptr) return false;
FilePos = startpos;
StartPos = startpos;
Length = CalcFileLen();
if (len >= 0 && len < Length) Length = len;
if (startpos > 0) Seek(startpos, SEEK_SET);
return true;
2016-03-01 15:47:10 +00:00
}
long Tell() const override
2016-03-01 15:47:10 +00:00
{
return FilePos - StartPos;
2016-03-01 15:47:10 +00:00
}
long Seek(long offset, int origin) override
2016-03-01 15:47:10 +00:00
{
if (origin == SEEK_SET)
2016-03-01 15:47:10 +00:00
{
offset += StartPos;
2016-03-01 15:47:10 +00:00
}
else if (origin == SEEK_CUR)
2016-03-01 15:47:10 +00:00
{
offset += FilePos;
2016-03-01 15:47:10 +00:00
}
else if (origin == SEEK_END)
2016-03-01 15:47:10 +00:00
{
offset += StartPos + Length;
2016-03-01 15:47:10 +00:00
}
if (offset < StartPos || offset >= StartPos + Length) return -1; // out of scope
2016-03-01 15:47:10 +00:00
if (0 == fseek(File, offset, SEEK_SET))
{
FilePos = offset;
return 0;
}
return -1;
2016-03-01 15:47:10 +00:00
}
long Read(void *buffer, long len) override
2016-03-01 15:47:10 +00:00
{
assert(len >= 0);
if (len <= 0) return 0;
if (FilePos + len > StartPos + Length)
2016-03-01 15:47:10 +00:00
{
len = Length - FilePos + StartPos;
2016-03-01 15:47:10 +00:00
}
len = (long)fread(buffer, 1, len, File);
FilePos += len;
return len;
2016-03-01 15:47:10 +00:00
}
char *Gets(char *strbuf, int len) override
2016-03-01 15:47:10 +00:00
{
if (len <= 0 || FilePos >= StartPos + Length) return NULL;
char *p = fgets(strbuf, len, File);
if (p != NULL)
{
int old = FilePos;
FilePos = ftell(File);
if (FilePos - StartPos > Length)
{
strbuf[Length - old + StartPos] = 0;
}
}
return p;
2016-03-01 15:47:10 +00:00
}
private:
long CalcFileLen() const
2016-03-01 15:47:10 +00:00
{
long endpos;
fseek(File, 0, SEEK_END);
endpos = ftell(File);
fseek(File, 0, SEEK_SET);
return endpos;
2016-03-01 15:47:10 +00:00
}
};
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// FileReaderRedirect
2016-03-01 15:47:10 +00:00
//
// like the above, but uses another File reader as its backing data
2016-03-01 15:47:10 +00:00
//
//==========================================================================
class FileReaderRedirect : public FileReaderInterface
2016-03-01 15:47:10 +00:00
{
FileRdr *mReader = nullptr;
long Length = 0;
long StartPos = 0;
long FilePos = 0;
2016-03-01 15:47:10 +00:00
public:
FileReaderRedirect(FileRdr &parent, long start, long length)
2016-03-01 15:47:10 +00:00
{
mReader = &parent;
FilePos = start;
StartPos = start;
Length = length;
Seek(0, SEEK_SET);
2016-03-01 15:47:10 +00:00
}
virtual long Tell() const override
{
return FilePos - StartPos;
}
2016-03-01 15:47:10 +00:00
virtual long Seek(long offset, int origin)
{
switch (origin)
{
case SEEK_SET:
offset += StartPos;
break;
2016-03-01 15:47:10 +00:00
case SEEK_END:
offset += StartPos + Length;
break;
2016-03-01 15:47:10 +00:00
case SEEK_CUR:
offset += (long)mReader->Tell();
break;
}
if (offset < StartPos || offset >= StartPos + Length) return -1; // out of scope
if (mReader->Seek(offset, FileRdr::SeekSet) == 0)
2016-03-01 15:47:10 +00:00
{
FilePos = offset;
return 0;
2016-03-01 15:47:10 +00:00
}
return -1;
2016-03-01 15:47:10 +00:00
}
virtual long Read(void *buffer, long len)
2016-03-01 15:47:10 +00:00
{
assert(len >= 0);
if (len <= 0) return 0;
if (FilePos + len > StartPos + Length)
{
len = Length - FilePos + StartPos;
}
len = (long)mReader->Read(buffer, len);
FilePos += len;
return len;
2016-03-01 15:47:10 +00:00
}
virtual char *Gets(char *strbuf, int len)
2016-03-01 15:47:10 +00:00
{
if (len <= 0 || FilePos >= StartPos + Length) return NULL;
char *p = mReader->Gets(strbuf, len);
if (p != NULL)
{
int old = FilePos;
FilePos = (long)mReader->Tell();
if (FilePos - StartPos > Length)
{
strbuf[Length - old + StartPos] = 0;
}
}
return p;
2016-03-01 15:47:10 +00:00
}
};
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// MemoryReader
2016-03-01 15:47:10 +00:00
//
// reads data from a block of memory
2016-03-01 15:47:10 +00:00
//
//==========================================================================
long MemoryReader::Tell() const
2016-03-01 15:47:10 +00:00
{
return FilePos;
}
2016-03-01 15:47:10 +00:00
long MemoryReader::Seek(long offset, int origin)
2016-03-01 15:47:10 +00:00
{
switch (origin)
2016-03-01 15:47:10 +00:00
{
case SEEK_CUR:
offset += FilePos;
break;
2016-03-01 15:47:10 +00:00
case SEEK_END:
offset += Length;
break;
2016-03-01 15:47:10 +00:00
}
if (offset < 0 || offset >= Length) return -1;
FilePos = clamp<long>(offset, 0, Length);
return 0;
2016-03-01 15:47:10 +00:00
}
long MemoryReader::Read(void *buffer, long len)
2016-03-01 15:47:10 +00:00
{
if (len>Length - FilePos) len = Length - FilePos;
if (len<0) len = 0;
memcpy(buffer, bufptr + FilePos, len);
FilePos += len;
return len;
2016-03-01 15:47:10 +00:00
}
char *MemoryReader::Gets(char *strbuf, int len)
2016-03-01 15:47:10 +00:00
{
if (len>Length - FilePos) len = Length - FilePos;
if (len <= 0) return NULL;
2016-03-01 15:47:10 +00:00
char *p = strbuf;
while (len > 1)
2016-03-01 15:47:10 +00:00
{
if (bufptr[FilePos] == 0)
2016-03-01 15:47:10 +00:00
{
FilePos++;
break;
2016-03-01 15:47:10 +00:00
}
if (bufptr[FilePos] != '\r')
2016-03-01 15:47:10 +00:00
{
*p++ = bufptr[FilePos];
len--;
if (bufptr[FilePos] == '\n')
2016-03-01 15:47:10 +00:00
{
FilePos++;
break;
2016-03-01 15:47:10 +00:00
}
}
FilePos++;
2016-03-01 15:47:10 +00:00
}
if (p == strbuf) return NULL;
*p++ = 0;
return strbuf;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// MemoryArrayReader
2016-03-01 15:47:10 +00:00
//
// reads data from an array of memory
2016-03-01 15:47:10 +00:00
//
//==========================================================================
class MemoryArrayReader : public MemoryReader
2016-03-01 15:47:10 +00:00
{
TArray<uint8_t> buf;
2016-03-01 15:47:10 +00:00
public:
MemoryArrayReader(const char *buffer, long length)
2016-03-01 15:47:10 +00:00
{
if (length > 0)
{
buf.Resize(length);
memcpy(&buf[0], buffer, length);
}
Length = length;
FilePos = 0;
bufptr = (const char *)&buf[0];
}
2016-03-01 15:47:10 +00:00
TArray<uint8_t> &GetArray() { return buf; }
2016-03-01 15:47:10 +00:00
void UpdateLength() { Length = buf.Size(); }
};
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// FileReader
2016-03-01 15:47:10 +00:00
//
// this wraps the different reader types in an object with value semantics.
2016-03-01 15:47:10 +00:00
//
//==========================================================================
bool FileRdr::OpenFile(const char *filename, FileRdr::Size start, FileRdr::Size length)
2016-03-01 15:47:10 +00:00
{
auto reader = new StdFileReader;
if (!reader->Open(filename, (long)start, (long)length)) return false;
Close();
mReader = reader;
return true;
2016-03-01 15:47:10 +00:00
}
bool FileRdr::OpenFilePart(FileRdr &parent, FileRdr::Size start, FileRdr::Size length)
2016-03-01 15:47:10 +00:00
{
auto reader = new FileReaderRedirect(parent, (long)start, (long)length);
Close();
mReader = reader;
return true;
2016-03-01 15:47:10 +00:00
}
bool FileRdr::OpenMemory(const void *mem, FileRdr::Size length)
2016-03-01 15:47:10 +00:00
{
Close();
mReader = new MemoryReader((const char *)mem, (long)length);
return true;
2016-03-01 15:47:10 +00:00
}
bool FileRdr::OpenMemoryArray(const void *mem, FileRdr::Size length)
2016-03-01 15:47:10 +00:00
{
Close();
mReader = new MemoryArrayReader((const char *)mem, (long)length);
return true;
2016-03-01 15:47:10 +00:00
}
bool FileRdr::OpenMemoryArray(std::function<bool(TArray<uint8_t>&)> getter)
2016-03-01 15:47:10 +00:00
{
auto reader = new MemoryArrayReader(nullptr, 0);
if (getter(reader->GetArray()))
{
Close();
reader->UpdateLength();
mReader = reader;
return true;
}
else
{
// This will keep the old buffer, if one existed
delete reader;
return false;
}
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// FileWriter (the motivation here is to have a buffer writing subclass)
//
//==========================================================================
bool FileWriter::OpenDirect(const char *filename)
{
File = fopen(filename, "wb");
return (File != NULL);
}
FileWriter *FileWriter::Open(const char *filename)
{
FileWriter *fwrit = new FileWriter();
if (fwrit->OpenDirect(filename))
{
return fwrit;
}
delete fwrit;
return NULL;
}
size_t FileWriter::Write(const void *buffer, size_t len)
{
if (File != NULL)
{
return fwrite(buffer, 1, len, File);
}
else
{
return 0;
}
}
long FileWriter::Tell()
{
if (File != NULL)
{
return ftell(File);
}
else
{
return 0;
}
}
long FileWriter::Seek(long offset, int mode)
{
if (File != NULL)
{
return fseek(File, offset, mode);
}
else
{
return 0;
}
}
size_t FileWriter::Printf(const char *fmt, ...)
{
va_list ap;
FString out;
va_start(ap, fmt);
out.VFormat(fmt, ap);
va_end(ap);
return Write(out.GetChars(), out.Len());
}
size_t BufferWriter::Write(const void *buffer, size_t len)
{
unsigned int ofs = mBuffer.Reserve((unsigned)len);
memcpy(&mBuffer[ofs], buffer, len);
return len;
}