// 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(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(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(math::minimum(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(new_ofs) > m_size) return false; if (static_cast(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& 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& 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