242 lines
5.6 KiB
C
242 lines
5.6 KiB
C
|
// File: crn_cfile_stream.h
|
||
|
// See Copyright Notice and license at the end of inc/crnlib.h
|
||
|
#pragma once
|
||
|
#include "crn_data_stream.h"
|
||
|
|
||
|
namespace crnlib
|
||
|
{
|
||
|
class cfile_stream : public data_stream
|
||
|
{
|
||
|
public:
|
||
|
cfile_stream() : data_stream(), m_pFile(NULL), m_size(0), m_ofs(0), m_has_ownership(false)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
cfile_stream(FILE* pFile, const char* pFilename, uint attribs, bool has_ownership) :
|
||
|
data_stream(), m_pFile(NULL), m_size(0), m_ofs(0), m_has_ownership(false)
|
||
|
{
|
||
|
open(pFile, pFilename, attribs, has_ownership);
|
||
|
}
|
||
|
|
||
|
cfile_stream(const char* pFilename, uint attribs = cDataStreamReadable | cDataStreamSeekable, bool open_existing = false) :
|
||
|
data_stream(), m_pFile(NULL), m_size(0), m_ofs(0), m_has_ownership(false)
|
||
|
{
|
||
|
open(pFilename, attribs, open_existing);
|
||
|
}
|
||
|
|
||
|
virtual ~cfile_stream()
|
||
|
{
|
||
|
close();
|
||
|
}
|
||
|
|
||
|
virtual bool close()
|
||
|
{
|
||
|
clear_error();
|
||
|
|
||
|
if (m_opened)
|
||
|
{
|
||
|
bool status = true;
|
||
|
if (m_has_ownership)
|
||
|
{
|
||
|
if (EOF == fclose(m_pFile))
|
||
|
status = false;
|
||
|
}
|
||
|
|
||
|
m_pFile = NULL;
|
||
|
m_opened = false;
|
||
|
m_size = 0;
|
||
|
m_ofs = 0;
|
||
|
m_has_ownership = false;
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool open(FILE* pFile, const char* pFilename, uint attribs, bool has_ownership)
|
||
|
{
|
||
|
CRNLIB_ASSERT(pFile);
|
||
|
CRNLIB_ASSERT(pFilename);
|
||
|
|
||
|
close();
|
||
|
|
||
|
set_name(pFilename);
|
||
|
m_pFile = pFile;
|
||
|
m_has_ownership = has_ownership;
|
||
|
m_attribs = static_cast<uint16>(attribs);
|
||
|
|
||
|
m_ofs = crn_ftell(m_pFile);
|
||
|
crn_fseek(m_pFile, 0, SEEK_END);
|
||
|
m_size = crn_ftell(m_pFile);
|
||
|
crn_fseek(m_pFile, m_ofs, SEEK_SET);
|
||
|
|
||
|
m_opened = true;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool open(const char* pFilename, uint attribs = cDataStreamReadable | cDataStreamSeekable, bool open_existing = false)
|
||
|
{
|
||
|
CRNLIB_ASSERT(pFilename);
|
||
|
|
||
|
close();
|
||
|
|
||
|
m_attribs = static_cast<uint16>(attribs);
|
||
|
|
||
|
const char* pMode;
|
||
|
if ((is_readable()) && (is_writable()))
|
||
|
pMode = open_existing ? "r+b" : "w+b";
|
||
|
else if (is_writable())
|
||
|
pMode = open_existing ? "ab" : "wb";
|
||
|
else if (is_readable())
|
||
|
pMode = "rb";
|
||
|
else
|
||
|
{
|
||
|
set_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
FILE* pFile = NULL;
|
||
|
crn_fopen(&pFile, pFilename, pMode);
|
||
|
m_has_ownership = true;
|
||
|
|
||
|
if (!pFile)
|
||
|
{
|
||
|
set_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// TODO: Change stream class to support UCS2 filenames.
|
||
|
|
||
|
return open(pFile, pFilename, attribs, true);
|
||
|
}
|
||
|
|
||
|
FILE* get_file() const { return m_pFile; }
|
||
|
|
||
|
virtual uint read(void* pBuf, uint len)
|
||
|
{
|
||
|
CRNLIB_ASSERT(pBuf && (len <= 0x7FFFFFFF));
|
||
|
|
||
|
if (!m_opened || (!is_readable()) || (!len))
|
||
|
return 0;
|
||
|
|
||
|
len = static_cast<uint>(math::minimum<uint64>(len, get_remaining()));
|
||
|
|
||
|
if (fread(pBuf, 1, len, m_pFile) != len)
|
||
|
{
|
||
|
set_error();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
m_ofs += len;
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
virtual uint write(const void* pBuf, uint len)
|
||
|
{
|
||
|
CRNLIB_ASSERT(pBuf && (len <= 0x7FFFFFFF));
|
||
|
|
||
|
if (!m_opened || (!is_writable()) || (!len))
|
||
|
return 0;
|
||
|
|
||
|
if (fwrite(pBuf, 1, len, m_pFile) != len)
|
||
|
{
|
||
|
set_error();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
m_ofs += len;
|
||
|
m_size = math::maximum(m_size, m_ofs);
|
||
|
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
virtual bool flush()
|
||
|
{
|
||
|
if ((!m_opened) || (!is_writable()))
|
||
|
return false;
|
||
|
|
||
|
if (EOF == fflush(m_pFile))
|
||
|
{
|
||
|
set_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
virtual uint64 get_size()
|
||
|
{
|
||
|
if (!m_opened)
|
||
|
return 0;
|
||
|
|
||
|
return m_size;
|
||
|
}
|
||
|
|
||
|
virtual uint64 get_remaining()
|
||
|
{
|
||
|
if (!m_opened)
|
||
|
return 0;
|
||
|
|
||
|
CRNLIB_ASSERT(m_ofs <= m_size);
|
||
|
return m_size - m_ofs;
|
||
|
}
|
||
|
|
||
|
virtual uint64 get_ofs()
|
||
|
{
|
||
|
if (!m_opened)
|
||
|
return 0;
|
||
|
|
||
|
return m_ofs;
|
||
|
}
|
||
|
|
||
|
virtual bool seek(int64 ofs, bool relative)
|
||
|
{
|
||
|
if ((!m_opened) || (!is_seekable()))
|
||
|
return false;
|
||
|
|
||
|
int64 new_ofs = relative ? (m_ofs + ofs) : ofs;
|
||
|
if (new_ofs < 0)
|
||
|
return false;
|
||
|
else if (static_cast<uint64>(new_ofs) > m_size)
|
||
|
return false;
|
||
|
|
||
|
if (static_cast<uint64>(new_ofs) != m_ofs)
|
||
|
{
|
||
|
if (crn_fseek(m_pFile, new_ofs, SEEK_SET) != 0)
|
||
|
{
|
||
|
set_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_ofs = new_ofs;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool read_file_into_array(const char* pFilename, vector<uint8>& buf)
|
||
|
{
|
||
|
cfile_stream in_stream(pFilename);
|
||
|
if (!in_stream.is_opened())
|
||
|
return false;
|
||
|
return in_stream.read_array(buf);
|
||
|
}
|
||
|
|
||
|
static bool write_array_to_file(const char* pFilename, const vector<uint8>& buf)
|
||
|
{
|
||
|
cfile_stream out_stream(pFilename, cDataStreamWritable|cDataStreamSeekable);
|
||
|
if (!out_stream.is_opened())
|
||
|
return false;
|
||
|
return out_stream.write_array(buf);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
FILE* m_pFile;
|
||
|
uint64 m_size, m_ofs;
|
||
|
bool m_has_ownership;
|
||
|
};
|
||
|
|
||
|
} // namespace crnlib
|