#include "Athena/FileReader.hpp" #if _WIN32 #include "win32_largefilewrapper.h" #elif __APPLE__ #include "osx_largefilewrapper.h" #elif GEKKO #include "gekko_support.h" #include "osx_largefilewrapper.h" #endif namespace Athena { namespace io { FileReader::FileReader(const std::string& filename, atInt32 cacheSize) : m_fileHandle(nullptr), m_cacheData(nullptr), m_offset(0) { #if _WIN32 m_filename = utility::utf8ToWide(filename); #else m_filename = filename; #endif open(); setCacheSize(cacheSize); } FileReader::FileReader(const std::wstring& filename, atInt32 cacheSize) : m_fileHandle(nullptr), m_cacheData(nullptr), m_offset(0) { #if _WIN32 m_filename = filename; #else m_filename = utility::wideToUtf8(filename); #endif open(); setCacheSize(cacheSize); } FileReader::~FileReader() { if (isOpen()) close(); } void FileReader::open() { #if _WIN32 m_fileHandle = _wfopen(m_filename.c_str(), L"rb"); #else m_fileHandle = fopen(m_filename.c_str(), "rb"); #endif if (!m_fileHandle) { std::string _filename = filename(); atError("File not found '%s'", _filename.c_str()); setError(); return; } // reset error m_hasError = false; } void FileReader::close() { if (!m_fileHandle) { atError("Cannot close an unopened stream"); setError(); return; } fclose(m_fileHandle); m_fileHandle = NULL; return; } void FileReader::seek(atInt64 pos, SeekOrigin origin) { if (!isOpen()) return; // check block position if (m_blockSize > 0) { atUint64 oldOff = m_offset; switch(origin) { case SeekOrigin::Begin: m_offset = pos; break; case SeekOrigin::Current: m_offset += pos; break; case SeekOrigin::End: m_offset = length() - pos; break; } if (m_offset > length()) { oldOff = m_offset; atError("Unable to seek in file"); return; } size_t block = m_offset / m_blockSize; if (block != m_curBlock) { fseeko64(m_fileHandle, block * m_blockSize, SEEK_SET); fread(m_cacheData.get(), 1, m_blockSize, m_fileHandle); m_curBlock = (atInt32)block; } } else if (fseeko64(m_fileHandle, pos, (int)origin) != 0) atError("Unable to seek in file"); } atUint64 FileReader::position() const { if (!isOpen()) { atError("File not open"); return 0; } if (m_blockSize > 0) return m_offset; else return ftello64(m_fileHandle); } atUint64 FileReader::length() const { if (!isOpen()) { atError("File not open"); return 0; } #if _WIN32 return utility::fileSize(utility::wideToUtf8(m_filename)); #else return utility::fileSize(m_filename); #endif } atUint64 FileReader::readUBytesToBuf(void* buf, atUint64 len) { if (!isOpen()) { atError("File not open for reading"); setError(); return 0; } if (m_blockSize <= 0) return fread(buf, 1, len, m_fileHandle); else { size_t block = m_offset / m_blockSize; atUint64 cacheOffset = m_offset % m_blockSize; atUint64 cacheSize; atUint64 rem = len; atUint8* dst = (atUint8*)buf; while (rem) { if (block != m_curBlock) { fseeko64(m_fileHandle, block * m_blockSize, SEEK_SET); fread(m_cacheData.get(), 1, m_blockSize, m_fileHandle); m_curBlock = (atInt32)block; } cacheSize = rem; if (cacheSize + cacheOffset > m_blockSize) cacheSize = m_blockSize - cacheOffset; memcpy(dst, m_cacheData.get() + cacheOffset, cacheSize); dst += cacheSize; rem -= cacheSize; cacheOffset = 0; ++block; } m_offset += len; return dst - (atUint8*)buf; } } void FileReader::setCacheSize(const atInt32 blockSize) { m_blockSize = blockSize; if (m_blockSize > length()) m_blockSize = (atInt32)length(); m_curBlock = -1; if (m_blockSize > 0) m_cacheData.reset(new atUint8[m_blockSize]); } } // io } // Athena