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