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