/********************************************************************** Filename : GFile_sys.cpp Content : GFile wrapper class implementation (Win32) Created : April 5, 1999 Authors : Michael Antonov History : 8/27/2001 MA Reworked file and directory interface Copyright : (c) 1999-2006 Scaleform Corp. All Rights Reserved. Licensees may use this file in accordance with the valid Scaleform Commercial License Agreement provided with the software. This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE. **********************************************************************/ #define GFILE_CXX #include "GTypes.h" // Standard C library #include #ifndef GFC_OS_WINCE #include #endif #include "GSysFile.h" #ifndef GFC_OS_WINCE #include #endif // unistd provides stat() function #ifdef GFC_OS_PS3 #include #endif #include "GHeapNew.h" // ***** GFile interface // ***** GFILEFile - C streams file static int GFC_error () { #if !defined(GFC_OS_WINCE) && !defined(GFC_CC_RENESAS) if (errno == ENOENT) return GFileConstants::Error_FileNotFound; else if (errno == EACCES || errno == EPERM) return GFileConstants::Error_Access; else if (errno == ENOSPC) return GFileConstants::Error_DiskFull; else #endif return GFileConstants::Error_IOError; }; #if defined(GFC_OS_WIN32) && !RSG_DURANGO #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 // GFC_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 GSysFile. class GFILEFile : public GFile { protected: // Allocated filename GString FileName; // File handle & open mode bool Opened; FILE* fs; SInt OpenFlags; // Error code for last request SInt ErrorCode; SInt LastOp; #ifdef GFILE_VERIFY_SEEK_ERRORS UByte* pFileTestBuffer; UInt FileTestLength; UInt TestPos; // File pointer position during tests. #endif public: GFILEFile() { Opened = 0; FileName = ""; #ifdef GFILE_VERIFY_SEEK_ERRORS pFileTestBuffer =0; FileTestLength =0; TestPos =0; #endif } // Initialize file by opening it GFILEFile(const GString& fileName, SInt flags, SInt Mode); // The 'pfileName' should be encoded as UTF-8 to support international file names. GFILEFile(const char* pfileName, SInt flags, SInt Mode); ~GFILEFile() { if (Opened) Close(); } virtual const char* GetFilePath(); // ** File Information virtual bool IsValid(); virtual bool IsWritable(); //virtual bool IsRecoverable(); // Return position / file size virtual SInt Tell(); virtual SInt64 LTell(); virtual SInt GetLength(); virtual SInt64 LGetLength(); // virtual bool Stat(GFileStats *pfs); virtual SInt GetErrorCode(); // ** GFxStream implementation & I/O virtual SInt Write(const UByte *pbuffer, SInt numBytes); virtual SInt Read(UByte *pbuffer, SInt numBytes); virtual SInt SkipBytes(SInt numBytes); virtual SInt BytesAvailable(); virtual bool Flush(); virtual SInt Seek(SInt offset, SInt origin); virtual SInt64 LSeek(SInt64 offset, SInt origin); virtual bool ChangeSize(SInt newSize); virtual SInt CopyFromStream(GFile *pStream, SInt byteSize); virtual bool Close(); //virtual bool CloseCancel(); private: void init(); }; // Initialize file by opening it GFILEFile::GFILEFile(const GString& fileName, SInt flags, SInt mode) : FileName(fileName), OpenFlags(flags) { GUNUSED(mode); init(); } // The 'pfileName' should be encoded as UTF-8 to support international file names. GFILEFile::GFILEFile(const char* pfileName, SInt flags, SInt mode) : FileName(pfileName), OpenFlags(flags) { GUNUSED(mode); init(); } void GFILEFile::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 GFC_OS_WIN32 SysErrorModeDisabler disabler(FileName.ToCStr()); #endif #if defined(GFC_CC_MSVC) && (GFC_CC_MSVC >= 1400) && !defined(GFC_OS_WINCE) wchar_t womode[16]; wchar_t *pwFileName = (wchar_t*)GALLOC((GUTF8Util::GetLength(FileName.ToCStr())+1) * sizeof(wchar_t), GStat_Default_Mem); GUTF8Util::DecodeString(pwFileName, FileName.ToCStr()); GASSERT(strlen(omode) < sizeof(womode)/sizeof(womode[0])); GUTF8Util::DecodeString(womode, omode); _wfopen_s(&fs, pwFileName, womode); GFREE(pwFileName); #else fs = fopen(FileName.ToCStr(), omode); #endif #ifdef GFC_OS_WINCE if (fs) fseek(fs, 0, SEEK_SET); #else if (fs) rewind (fs); #endif Opened = (fs != NULL); // Set error code if (!Opened) ErrorCode = GFC_error(); else { // If we are testing file seek correctness, pre-load the entire file so // that we can do comparison tests later. #ifdef GFILE_VERIFY_SEEK_ERRORS TestPos = 0; fseek(fs, 0, SEEK_END); FileTestLength = ftell(fs); fseek(fs, 0, SEEK_SET); pFileTestBuffer = (UByte*)GHEAP_AUTO_ALLOC(this, FileTestLength); if (pFileTestBuffer) { GASSERT(FileTestLength == (UInt)Read(pFileTestBuffer, FileTestLength)); Seek(0, Seek_Set); } #endif ErrorCode = 0; } LastOp = 0; } const char* GFILEFile::GetFilePath() { return FileName.ToCStr(); } // ** File Information bool GFILEFile::IsValid() { return Opened; } bool GFILEFile::IsWritable() { return IsValid() && (OpenFlags&Open_Write); } /* bool GFILEFile::IsRecoverable() { return IsValid() && ((OpenFlags&GFC_FO_SAFETRUNC) == GFC_FO_SAFETRUNC); } */ // Return position / file size SInt GFILEFile::Tell() { int pos = ftell (fs); if (pos < 0) ErrorCode = GFC_error(); return pos; } SInt64 GFILEFile::LTell() { int pos = ftell(fs); if (pos < 0) ErrorCode = GFC_error(); return pos; } SInt GFILEFile::GetLength() { SInt pos = Tell(); if (pos >= 0) { Seek (0, Seek_End); SInt size = Tell(); Seek (pos, Seek_Set); return size; } return -1; } SInt64 GFILEFile::LGetLength() { return GetLength(); } SInt GFILEFile::GetErrorCode() { return ErrorCode; } // ** GFxStream implementation & I/O SInt GFILEFile::Write(const UByte *pbuffer, SInt numBytes) { if (LastOp && LastOp != Open_Write) fflush(fs); LastOp = Open_Write; int written = (int) fwrite(pbuffer, 1, numBytes, fs); if (written < numBytes) ErrorCode = GFC_error(); #ifdef GFILE_VERIFY_SEEK_ERRORS if (written > 0) TestPos += written; #endif return written; } SInt GFILEFile::Read(UByte *pbuffer, SInt numBytes) { if (LastOp && LastOp != Open_Read) fflush(fs); LastOp = Open_Read; int read = (int) fread(pbuffer, 1, numBytes, fs); if (read < numBytes) ErrorCode = GFC_error(); #ifdef GFILE_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++) { GASSERT(pcompareBuffer[i] == pbuffer[i]); } //GASSERT(!memcmp(pFileTestBuffer + TestPos, pbuffer, read)); TestPos += read; GASSERT(ftell(fs) == (int)TestPos); } #endif return read; } // Seeks ahead to skip bytes SInt GFILEFile::SkipBytes(SInt 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) SInt(sizeof(buff))) ? SInt(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 GFILEFile::Close() { #ifdef GFILE_VERIFY_SEEK_ERRORS if (pFileTestBuffer) { GFREE(pFileTestBuffer); pFileTestBuffer = 0; FileTestLength = 0; } #endif bool closeRet = !fclose(fs); if (!closeRet) { ErrorCode = GFC_error(); return 0; } else { Opened = 0; fs = 0; ErrorCode = 0; } // Handle safe truncate /* if ((OpenFlags & GFC_FO_SAFETRUNC) == GFC_FO_SAFETRUNC) { // Delete original file (if it existed) DWORD oldAttributes = GFileUtilWin32::GetFileAttributes(FileName); if (oldAttributes!=0xFFFFFFFF) if (!GFileUtilWin32::DeleteFile(FileName)) { // Try to remove the readonly attribute GFileUtilWin32::SetFileAttributes(FileName, oldAttributes & (~FILE_ATTRIBUTE_READONLY) ); // And delete the file again if (!GFileUtilWin32::DeleteFile(FileName)) return 0; } // Rename temp file to real filename if (!GFileUtilWin32::MoveFile(TempName, FileName)) { //ErrorCode = errno; return 0; } } */ return 1; } /* bool GFILEFile::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&GFC_FO_SAFETRUNC) == GFC_FO_SAFETRUNC) if (!GFileUtilWin32::DeleteFile(TempName)) { //ErrorCode = errno; return 0; } return 1; } */ GFile *GFileFILEOpen(const GString& path, SInt flags, SInt mode) { return GNEW GFILEFile(path, flags, mode); } #if !defined(GFC_OS_PS2) && !defined(GFC_OS_PSP) && !defined(GFC_OS_WII) // Helper function: obtain file information time. bool GSysFile::GetFileStat(GFileStat* pfileStat, const GString& path) { #if defined(GFC_OS_WINCE) return false; #else #if defined(GFC_OS_WIN32) // 64-bit implementation on Windows. struct __stat64 fileStat; // Stat returns 0 for success. wchar_t *pwpath = (wchar_t*)GALLOC((GUTF8Util::GetLength(path.ToCStr())+1)*sizeof(wchar_t), GStat_Default_Mem); GUTF8Util::DecodeString(pwpath, path.ToCStr()); int ret = _wstat64(pwpath, &fileStat); GFREE(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; #endif } #endif