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