nuclear@0: /************************************************************************** nuclear@0: nuclear@0: Filename : OVR_FileFILE.cpp nuclear@0: Content : File wrapper class implementation (Win32) nuclear@0: nuclear@0: Created : April 5, 1999 nuclear@0: Authors : Michael Antonov nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: **************************************************************************/ nuclear@0: nuclear@0: #define GFILE_CXX nuclear@0: nuclear@0: #include "OVR_Types.h" nuclear@0: #include "OVR_Log.h" nuclear@0: nuclear@0: // Standard C library (Captain Obvious guarantees!) nuclear@0: #include nuclear@0: #ifndef OVR_OS_WINCE nuclear@0: #include nuclear@0: #endif nuclear@0: nuclear@0: #include "OVR_SysFile.h" nuclear@0: nuclear@0: #ifndef OVR_OS_WINCE nuclear@0: #include nuclear@0: #endif nuclear@0: nuclear@0: namespace OVR { nuclear@0: nuclear@0: // ***** File interface nuclear@0: nuclear@0: // ***** FILEFile - C streams file nuclear@0: nuclear@0: static int SFerror () nuclear@0: { nuclear@0: if (errno == ENOENT) nuclear@0: return FileConstants::Error_FileNotFound; nuclear@0: else if (errno == EACCES || errno == EPERM) nuclear@0: return FileConstants::Error_Access; nuclear@0: else if (errno == ENOSPC) nuclear@0: return FileConstants::Error_DiskFull; nuclear@0: else nuclear@0: return FileConstants::Error_IOError; nuclear@0: }; nuclear@0: nuclear@0: #if defined(OVR_OS_WIN32) nuclear@0: #define WIN32_LEAN_AND_MEAN nuclear@0: #include "windows.h" nuclear@0: // A simple helper class to disable/enable system error mode, if necessary nuclear@0: // Disabling happens conditionally only if a drive name is involved nuclear@0: class SysErrorModeDisabler nuclear@0: { nuclear@0: BOOL Disabled; nuclear@0: UINT OldMode; nuclear@0: public: nuclear@0: SysErrorModeDisabler(const char* pfileName) nuclear@0: { nuclear@0: if (pfileName && (pfileName[0]!=0) && pfileName[1]==':') nuclear@0: { nuclear@0: Disabled = TRUE; nuclear@0: OldMode = ::SetErrorMode(SEM_FAILCRITICALERRORS); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: Disabled = 0; nuclear@0: OldMode = 0; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: ~SysErrorModeDisabler() nuclear@0: { nuclear@0: if (Disabled) nuclear@0: ::SetErrorMode(OldMode); nuclear@0: } nuclear@0: }; nuclear@0: #else nuclear@0: class SysErrorModeDisabler nuclear@0: { nuclear@0: public: nuclear@0: SysErrorModeDisabler(const char* pfileName) { OVR_UNUSED(pfileName); } nuclear@0: }; nuclear@0: #endif // OVR_OS_WIN32 nuclear@0: nuclear@0: nuclear@0: // This macro enables verification of I/O results after seeks against a pre-loaded nuclear@0: // full file buffer copy. This is generally not necessary, but can been used to debug nuclear@0: // memory corruptions; we've seen this fail due to EAX2/DirectSound corrupting memory nuclear@0: // under FMOD with XP64 (32-bit) and Realtek HA Audio driver. nuclear@0: //#define GFILE_VERIFY_SEEK_ERRORS nuclear@0: nuclear@0: nuclear@0: // This is the simplest possible file implementation, it wraps around the descriptor nuclear@0: // This file is delegated to by SysFile. nuclear@0: nuclear@0: class FILEFile : public File nuclear@0: { nuclear@0: protected: nuclear@0: nuclear@0: // Allocated filename nuclear@0: String FileName; nuclear@0: nuclear@0: // File handle & open mode nuclear@0: bool Opened; nuclear@0: FILE* fs; nuclear@0: int OpenFlags; nuclear@0: // Error code for last request nuclear@0: int ErrorCode; nuclear@0: nuclear@0: int LastOp; nuclear@0: nuclear@0: #ifdef OVR_FILE_VERIFY_SEEK_ERRORS nuclear@0: uint8_t* pFileTestBuffer; nuclear@0: unsigned FileTestLength; nuclear@0: unsigned TestPos; // File pointer position during tests. nuclear@0: #endif nuclear@0: nuclear@0: public: nuclear@0: nuclear@0: FILEFile() : nuclear@0: FileName(), nuclear@0: Opened(false), nuclear@0: fs(NULL), nuclear@0: OpenFlags(0), nuclear@0: ErrorCode(0), nuclear@0: LastOp(0) nuclear@0: #ifdef OVR_FILE_VERIFY_SEEK_ERRORS nuclear@0: ,pFileTestBuffer(NULL) nuclear@0: ,FileTestLength(0) nuclear@0: ,TestPos(0) nuclear@0: #endif nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: // Initialize file by opening it nuclear@0: FILEFile(const String& fileName, int flags, int Mode); nuclear@0: nuclear@0: // The 'pfileName' should be encoded as UTF-8 to support international file names. nuclear@0: FILEFile(const char* pfileName, int flags, int Mode); nuclear@0: nuclear@0: ~FILEFile() nuclear@0: { nuclear@0: if (Opened) nuclear@0: Close(); nuclear@0: } nuclear@0: nuclear@0: virtual const char* GetFilePath(); nuclear@0: nuclear@0: // ** File Information nuclear@0: virtual bool IsValid(); nuclear@0: virtual bool IsWritable(); nuclear@0: nuclear@0: // Return position / file size nuclear@0: virtual int Tell(); nuclear@0: virtual int64_t LTell(); nuclear@0: virtual int GetLength(); nuclear@0: virtual int64_t LGetLength(); nuclear@0: nuclear@0: // virtual bool Stat(FileStats *pfs); nuclear@0: virtual int GetErrorCode(); nuclear@0: nuclear@0: // ** Stream implementation & I/O nuclear@0: virtual int Write(const uint8_t *pbuffer, int numBytes); nuclear@0: virtual int Read(uint8_t *pbuffer, int numBytes); nuclear@0: virtual int SkipBytes(int numBytes); nuclear@0: virtual int BytesAvailable(); nuclear@0: virtual bool Flush(); nuclear@0: virtual int Seek(int offset, int origin); nuclear@0: virtual int64_t LSeek(int64_t offset, int origin); nuclear@0: nuclear@0: virtual int CopyFromStream(File *pStream, int byteSize); nuclear@0: virtual bool Close(); nuclear@0: private: nuclear@0: void init(); nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // Initialize file by opening it nuclear@0: FILEFile::FILEFile(const String& fileName, int flags, int mode) nuclear@0: : FileName(fileName), OpenFlags(flags) nuclear@0: { nuclear@0: OVR_UNUSED(mode); nuclear@0: init(); nuclear@0: } nuclear@0: nuclear@0: // The 'pfileName' should be encoded as UTF-8 to support international file names. nuclear@0: FILEFile::FILEFile(const char* pfileName, int flags, int mode) nuclear@0: : FileName(pfileName), OpenFlags(flags) nuclear@0: { nuclear@0: OVR_UNUSED(mode); nuclear@0: init(); nuclear@0: } nuclear@0: nuclear@0: void FILEFile::init() nuclear@0: { nuclear@0: // Open mode for file's open nuclear@0: const char *omode = "rb"; nuclear@0: nuclear@0: if (OpenFlags & Open_Truncate) nuclear@0: { nuclear@0: if (OpenFlags & Open_Read) nuclear@0: omode = "w+b"; nuclear@0: else nuclear@0: omode = "wb"; nuclear@0: } nuclear@0: else if (OpenFlags & Open_Create) nuclear@0: { nuclear@0: if (OpenFlags & Open_Read) nuclear@0: omode = "a+b"; nuclear@0: else nuclear@0: omode = "ab"; nuclear@0: } nuclear@0: else if (OpenFlags & Open_Write) nuclear@0: omode = "r+b"; nuclear@0: nuclear@0: #if defined(OVR_OS_MS) nuclear@0: SysErrorModeDisabler disabler(FileName.ToCStr()); nuclear@0: #endif nuclear@0: nuclear@0: #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) nuclear@0: wchar_t womode[16]; nuclear@0: wchar_t *pwFileName = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(FileName.ToCStr())+1) * sizeof(wchar_t)); nuclear@0: UTF8Util::DecodeString(pwFileName, FileName.ToCStr()); nuclear@0: OVR_ASSERT(strlen(omode) < sizeof(womode)/sizeof(womode[0])); nuclear@0: UTF8Util::DecodeString(womode, omode); nuclear@0: _wfopen_s(&fs, pwFileName, womode); nuclear@0: OVR_FREE(pwFileName); nuclear@0: #else nuclear@0: fs = fopen(FileName.ToCStr(), omode); nuclear@0: #endif nuclear@0: if (fs) nuclear@0: rewind (fs); nuclear@0: Opened = (fs != NULL); nuclear@0: // Set error code nuclear@0: if (!Opened) nuclear@0: ErrorCode = SFerror(); nuclear@0: else nuclear@0: { nuclear@0: // If we are testing file seek correctness, pre-load the entire file so nuclear@0: // that we can do comparison tests later. nuclear@0: #ifdef OVR_FILE_VERIFY_SEEK_ERRORS nuclear@0: TestPos = 0; nuclear@0: fseek(fs, 0, SEEK_END); nuclear@0: FileTestLength = ftell(fs); nuclear@0: fseek(fs, 0, SEEK_SET); nuclear@0: pFileTestBuffer = (uint8_t*)OVR_ALLOC(FileTestLength); nuclear@0: if (pFileTestBuffer) nuclear@0: { nuclear@0: OVR_ASSERT(FileTestLength == (unsigned)Read(pFileTestBuffer, FileTestLength)); nuclear@0: Seek(0, Seek_Set); nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: ErrorCode = 0; nuclear@0: } nuclear@0: LastOp = 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: const char* FILEFile::GetFilePath() nuclear@0: { nuclear@0: return FileName.ToCStr(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ** File Information nuclear@0: bool FILEFile::IsValid() nuclear@0: { nuclear@0: return Opened; nuclear@0: } nuclear@0: bool FILEFile::IsWritable() nuclear@0: { nuclear@0: return IsValid() && (OpenFlags&Open_Write); nuclear@0: } nuclear@0: /* nuclear@0: bool FILEFile::IsRecoverable() nuclear@0: { nuclear@0: return IsValid() && ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC); nuclear@0: } nuclear@0: */ nuclear@0: nuclear@0: // Return position / file size nuclear@0: int FILEFile::Tell() nuclear@0: { nuclear@0: int pos = (int)ftell (fs); nuclear@0: if (pos < 0) nuclear@0: ErrorCode = SFerror(); nuclear@0: return pos; nuclear@0: } nuclear@0: nuclear@0: int64_t FILEFile::LTell() nuclear@0: { nuclear@0: int64_t pos = ftell(fs); nuclear@0: if (pos < 0) nuclear@0: ErrorCode = SFerror(); nuclear@0: return pos; nuclear@0: } nuclear@0: nuclear@0: int FILEFile::GetLength() nuclear@0: { nuclear@0: int pos = Tell(); nuclear@0: if (pos >= 0) nuclear@0: { nuclear@0: Seek (0, Seek_End); nuclear@0: int size = Tell(); nuclear@0: Seek (pos, Seek_Set); nuclear@0: return size; nuclear@0: } nuclear@0: return -1; nuclear@0: } nuclear@0: int64_t FILEFile::LGetLength() nuclear@0: { nuclear@0: int64_t pos = LTell(); nuclear@0: if (pos >= 0) nuclear@0: { nuclear@0: LSeek (0, Seek_End); nuclear@0: int64_t size = LTell(); nuclear@0: LSeek (pos, Seek_Set); nuclear@0: return size; nuclear@0: } nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: int FILEFile::GetErrorCode() nuclear@0: { nuclear@0: return ErrorCode; nuclear@0: } nuclear@0: nuclear@0: // ** Stream implementation & I/O nuclear@0: int FILEFile::Write(const uint8_t *pbuffer, int numBytes) nuclear@0: { nuclear@0: if (LastOp && LastOp != Open_Write) nuclear@0: fflush(fs); nuclear@0: LastOp = Open_Write; nuclear@0: int written = (int) fwrite(pbuffer, 1, numBytes, fs); nuclear@0: if (written < numBytes) nuclear@0: ErrorCode = SFerror(); nuclear@0: nuclear@0: #ifdef OVR_FILE_VERIFY_SEEK_ERRORS nuclear@0: if (written > 0) nuclear@0: TestPos += written; nuclear@0: #endif nuclear@0: nuclear@0: return written; nuclear@0: } nuclear@0: nuclear@0: int FILEFile::Read(uint8_t *pbuffer, int numBytes) nuclear@0: { nuclear@0: if (LastOp && LastOp != Open_Read) nuclear@0: fflush(fs); nuclear@0: LastOp = Open_Read; nuclear@0: int read = (int) fread(pbuffer, 1, numBytes, fs); nuclear@0: if (read < numBytes) nuclear@0: ErrorCode = SFerror(); nuclear@0: nuclear@0: #ifdef OVR_FILE_VERIFY_SEEK_ERRORS nuclear@0: if (read > 0) nuclear@0: { nuclear@0: // Read-in data must match our pre-loaded buffer data! nuclear@0: uint8_t* pcompareBuffer = pFileTestBuffer + TestPos; nuclear@0: for (int i=0; i< read; i++) nuclear@0: { nuclear@0: OVR_ASSERT(pcompareBuffer[i] == pbuffer[i]); nuclear@0: } nuclear@0: nuclear@0: //OVR_ASSERT(!memcmp(pFileTestBuffer + TestPos, pbuffer, read)); nuclear@0: TestPos += read; nuclear@0: OVR_ASSERT(ftell(fs) == (int)TestPos); nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: return read; nuclear@0: } nuclear@0: nuclear@0: // Seeks ahead to skip bytes nuclear@0: int FILEFile::SkipBytes(int numBytes) nuclear@0: { nuclear@0: int64_t pos = LTell(); nuclear@0: int64_t newPos = LSeek(numBytes, Seek_Cur); nuclear@0: nuclear@0: // Return -1 for major error nuclear@0: if ((pos==-1) || (newPos==-1)) nuclear@0: { nuclear@0: return -1; nuclear@0: } nuclear@0: //ErrorCode = ((NewPos-Pos) int(sizeof(buff))) ? int(sizeof(buff)) : byteSize; nuclear@0: nuclear@0: szRead = pstream->Read(buff, szRequest); nuclear@0: szWritten = 0; nuclear@0: if (szRead > 0) nuclear@0: szWritten = Write(buff, szRead); nuclear@0: nuclear@0: count += szWritten; nuclear@0: byteSize -= szWritten; nuclear@0: if (szWritten < szRequest) nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: delete[] buff; nuclear@0: nuclear@0: return count; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: bool FILEFile::Close() nuclear@0: { nuclear@0: #ifdef OVR_FILE_VERIFY_SEEK_ERRORS nuclear@0: if (pFileTestBuffer) nuclear@0: { nuclear@0: OVR_FREE(pFileTestBuffer); nuclear@0: pFileTestBuffer = 0; nuclear@0: FileTestLength = 0; nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: bool closeRet = !fclose(fs); nuclear@0: nuclear@0: if (!closeRet) nuclear@0: { nuclear@0: ErrorCode = SFerror(); nuclear@0: return 0; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: Opened = 0; nuclear@0: fs = 0; nuclear@0: ErrorCode = 0; nuclear@0: } nuclear@0: nuclear@0: // Handle safe truncate nuclear@0: /* nuclear@0: if ((OpenFlags & OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC) nuclear@0: { nuclear@0: // Delete original file (if it existed) nuclear@0: DWORD oldAttributes = FileUtilWin32::GetFileAttributes(FileName); nuclear@0: if (oldAttributes!=0xFFFFFFFF) nuclear@0: if (!FileUtilWin32::DeleteFile(FileName)) nuclear@0: { nuclear@0: // Try to remove the readonly attribute nuclear@0: FileUtilWin32::SetFileAttributes(FileName, oldAttributes & (~FILE_ATTRIBUTE_READONLY) ); nuclear@0: // And delete the file again nuclear@0: if (!FileUtilWin32::DeleteFile(FileName)) nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: // Rename temp file to real filename nuclear@0: if (!FileUtilWin32::MoveFile(TempName, FileName)) nuclear@0: { nuclear@0: //ErrorCode = errno; nuclear@0: return 0; nuclear@0: } nuclear@0: } nuclear@0: */ nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: /* nuclear@0: bool FILEFile::CloseCancel() nuclear@0: { nuclear@0: bool closeRet = (bool)::CloseHandle(fd); nuclear@0: nuclear@0: if (!closeRet) nuclear@0: { nuclear@0: //ErrorCode = errno; nuclear@0: return 0; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: Opened = 0; nuclear@0: fd = INVALID_HANDLE_VALUE; nuclear@0: ErrorCode = 0; nuclear@0: } nuclear@0: nuclear@0: // Handle safe truncate (delete tmp file, leave original unchanged) nuclear@0: if ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC) nuclear@0: if (!FileUtilWin32::DeleteFile(TempName)) nuclear@0: { nuclear@0: //ErrorCode = errno; nuclear@0: return 0; nuclear@0: } nuclear@0: return 1; nuclear@0: } nuclear@0: */ nuclear@0: nuclear@0: Ptr FileFILEOpen(const String& path, int flags, int mode) nuclear@0: { nuclear@0: Ptr result = *new FILEFile(path, flags, mode); nuclear@0: return result; nuclear@0: } nuclear@0: nuclear@0: // Helper function: obtain file information time. nuclear@0: bool SysFile::GetFileStat(FileStat* pfileStat, const String& path) nuclear@0: { nuclear@0: #if defined(OVR_OS_MS) nuclear@0: // 64-bit implementation on Windows. nuclear@0: struct __stat64 fileStat; nuclear@0: // Stat returns 0 for success. nuclear@0: wchar_t *pwpath = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(path.ToCStr())+1)*sizeof(wchar_t)); nuclear@0: UTF8Util::DecodeString(pwpath, path.ToCStr()); nuclear@0: nuclear@0: int ret = _wstat64(pwpath, &fileStat); nuclear@0: OVR_FREE(pwpath); nuclear@0: if (ret) return false; nuclear@0: #else nuclear@0: struct stat fileStat; nuclear@0: // Stat returns 0 for success. nuclear@0: if (stat(path, &fileStat) != 0) nuclear@0: return false; nuclear@0: #endif nuclear@0: pfileStat->AccessTime = fileStat.st_atime; nuclear@0: pfileStat->ModifyTime = fileStat.st_mtime; nuclear@0: pfileStat->FileSize = fileStat.st_size; nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: } // Namespace OVR