mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-10 22:38:34 +02:00
571 lines
14 KiB
C++
Executable file
571 lines
14 KiB
C++
Executable file
/**************************************************************************
|
|
|
|
Filename : OVR_File.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
|
|
|
|
// Standard C library (Captain Obvious guarantees!)
|
|
#include <stdio.h>
|
|
|
|
#include "OVR_File.h"
|
|
|
|
namespace OVR {
|
|
|
|
// Buffered file adds buffering to an existing file
|
|
// FILEBUFFER_SIZE defines the size of internal buffer, while
|
|
// FILEBUFFER_TOLERANCE controls the amount of data we'll effectively try to buffer
|
|
#define FILEBUFFER_SIZE (8192-8)
|
|
#define FILEBUFFER_TOLERANCE 4096
|
|
|
|
// ** Constructor/Destructor
|
|
|
|
// Hidden constructor
|
|
// Not supposed to be used
|
|
BufferedFile::BufferedFile() : DelegatedFile(0)
|
|
{
|
|
pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE);
|
|
BufferMode = NoBuffer;
|
|
FilePos = 0;
|
|
Pos = 0;
|
|
DataSize = 0;
|
|
}
|
|
|
|
// Takes another file as source
|
|
BufferedFile::BufferedFile(File *pfile) : DelegatedFile(pfile)
|
|
{
|
|
pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE);
|
|
BufferMode = NoBuffer;
|
|
FilePos = pfile->LTell();
|
|
Pos = 0;
|
|
DataSize = 0;
|
|
}
|
|
|
|
|
|
// Destructor
|
|
BufferedFile::~BufferedFile()
|
|
{
|
|
// Flush in case there's data
|
|
if (pFile)
|
|
FlushBuffer();
|
|
// Get rid of buffer
|
|
if (pBuffer)
|
|
OVR_FREE(pBuffer);
|
|
}
|
|
|
|
/*
|
|
bool BufferedFile::VCopy(const Object &source)
|
|
{
|
|
if (!DelegatedFile::VCopy(source))
|
|
return 0;
|
|
|
|
// Data members
|
|
BufferedFile *psource = (BufferedFile*)&source;
|
|
|
|
// Buffer & the mode it's in
|
|
pBuffer = psource->pBuffer;
|
|
BufferMode = psource->BufferMode;
|
|
Pos = psource->Pos;
|
|
DataSize = psource->DataSize;
|
|
return 1;
|
|
}
|
|
*/
|
|
|
|
// Initializes buffering to a certain mode
|
|
bool BufferedFile::SetBufferMode(BufferModeType mode)
|
|
{
|
|
if (!pBuffer)
|
|
return false;
|
|
if (mode == BufferMode)
|
|
return true;
|
|
|
|
FlushBuffer();
|
|
|
|
// Can't set write mode if we can't write
|
|
if ((mode==WriteBuffer) && (!pFile || !pFile->IsWritable()) )
|
|
return 0;
|
|
|
|
// And SetMode
|
|
BufferMode = mode;
|
|
Pos = 0;
|
|
DataSize = 0;
|
|
return 1;
|
|
}
|
|
|
|
// Flushes buffer
|
|
void BufferedFile::FlushBuffer()
|
|
{
|
|
switch(BufferMode)
|
|
{
|
|
case WriteBuffer:
|
|
// Write data in buffer
|
|
FilePos += pFile->Write(pBuffer,Pos);
|
|
Pos = 0;
|
|
break;
|
|
|
|
case ReadBuffer:
|
|
// Seek back & reset buffer data
|
|
if ((DataSize-Pos)>0)
|
|
FilePos = pFile->LSeek(-(int)(DataSize-Pos), Seek_Cur);
|
|
DataSize = 0;
|
|
Pos = 0;
|
|
break;
|
|
default:
|
|
// not handled!
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Reloads data for ReadBuffer
|
|
void BufferedFile::LoadBuffer()
|
|
{
|
|
if (BufferMode == ReadBuffer)
|
|
{
|
|
// We should only reload once all of pre-loaded buffer is consumed.
|
|
OVR_ASSERT(Pos == DataSize);
|
|
|
|
// WARNING: Right now LoadBuffer() assumes the buffer's empty
|
|
int sz = pFile->Read(pBuffer,FILEBUFFER_SIZE);
|
|
DataSize = sz<0 ? 0 : (unsigned)sz;
|
|
Pos = 0;
|
|
FilePos += DataSize;
|
|
}
|
|
}
|
|
|
|
|
|
// ** Overridden functions
|
|
|
|
// We override all the functions that can possibly
|
|
// require buffer mode switch, flush, or extra calculations
|
|
|
|
// Tell() requires buffer adjustment
|
|
int BufferedFile::Tell()
|
|
{
|
|
if (BufferMode == ReadBuffer)
|
|
return int (FilePos - DataSize + Pos);
|
|
|
|
int pos = pFile->Tell();
|
|
// Adjust position based on buffer mode & data
|
|
if (pos!=-1)
|
|
{
|
|
OVR_ASSERT(BufferMode != ReadBuffer);
|
|
if (BufferMode == WriteBuffer)
|
|
pos += Pos;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
SInt64 BufferedFile::LTell()
|
|
{
|
|
if (BufferMode == ReadBuffer)
|
|
return FilePos - DataSize + Pos;
|
|
|
|
SInt64 pos = pFile->LTell();
|
|
if (pos!=-1)
|
|
{
|
|
OVR_ASSERT(BufferMode != ReadBuffer);
|
|
if (BufferMode == WriteBuffer)
|
|
pos += Pos;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
int BufferedFile::GetLength()
|
|
{
|
|
int len = pFile->GetLength();
|
|
// If writing through buffer, file length may actually be bigger
|
|
if ((len!=-1) && (BufferMode==WriteBuffer))
|
|
{
|
|
int currPos = pFile->Tell() + Pos;
|
|
if (currPos>len)
|
|
len = currPos;
|
|
}
|
|
return len;
|
|
}
|
|
SInt64 BufferedFile::LGetLength()
|
|
{
|
|
SInt64 len = pFile->LGetLength();
|
|
// If writing through buffer, file length may actually be bigger
|
|
if ((len!=-1) && (BufferMode==WriteBuffer))
|
|
{
|
|
SInt64 currPos = pFile->LTell() + Pos;
|
|
if (currPos>len)
|
|
len = currPos;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/*
|
|
bool BufferedFile::Stat(FileStats *pfs)
|
|
{
|
|
// Have to fix up length is stat
|
|
if (pFile->Stat(pfs))
|
|
{
|
|
if (BufferMode==WriteBuffer)
|
|
{
|
|
SInt64 currPos = pFile->LTell() + Pos;
|
|
if (currPos > pfs->Size)
|
|
{
|
|
pfs->Size = currPos;
|
|
// ??
|
|
pfs->Blocks = (pfs->Size+511) >> 9;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
*/
|
|
|
|
int BufferedFile::Write(const UByte *psourceBuffer, int numBytes)
|
|
{
|
|
if ( (BufferMode==WriteBuffer) || SetBufferMode(WriteBuffer))
|
|
{
|
|
// If not data space in buffer, flush
|
|
if ((FILEBUFFER_SIZE-(int)Pos)<numBytes)
|
|
{
|
|
FlushBuffer();
|
|
// If bigger then tolerance, just write directly
|
|
if (numBytes>FILEBUFFER_TOLERANCE)
|
|
{
|
|
int sz = pFile->Write(psourceBuffer,numBytes);
|
|
if (sz > 0)
|
|
FilePos += sz;
|
|
return sz;
|
|
}
|
|
}
|
|
|
|
// Enough space in buffer.. so copy to it
|
|
memcpy(pBuffer+Pos, psourceBuffer, numBytes);
|
|
Pos += numBytes;
|
|
return numBytes;
|
|
}
|
|
int sz = pFile->Write(psourceBuffer,numBytes);
|
|
if (sz > 0)
|
|
FilePos += sz;
|
|
return sz;
|
|
}
|
|
|
|
int BufferedFile::Read(UByte *pdestBuffer, int numBytes)
|
|
{
|
|
if ( (BufferMode==ReadBuffer) || SetBufferMode(ReadBuffer))
|
|
{
|
|
// Data in buffer... copy it
|
|
if ((int)(DataSize-Pos) >= numBytes)
|
|
{
|
|
memcpy(pdestBuffer, pBuffer+Pos, numBytes);
|
|
Pos += numBytes;
|
|
return numBytes;
|
|
}
|
|
|
|
// Not enough data in buffer, copy buffer
|
|
int readBytes = DataSize-Pos;
|
|
memcpy(pdestBuffer, pBuffer+Pos, readBytes);
|
|
numBytes -= readBytes;
|
|
pdestBuffer += readBytes;
|
|
Pos = DataSize;
|
|
|
|
// Don't reload buffer if more then tolerance
|
|
// (No major advantage, and we don't want to write a loop)
|
|
if (numBytes>FILEBUFFER_TOLERANCE)
|
|
{
|
|
numBytes = pFile->Read(pdestBuffer,numBytes);
|
|
if (numBytes > 0)
|
|
{
|
|
FilePos += numBytes;
|
|
Pos = DataSize = 0;
|
|
}
|
|
return readBytes + ((numBytes==-1) ? 0 : numBytes);
|
|
}
|
|
|
|
// Reload the buffer
|
|
// WARNING: Right now LoadBuffer() assumes the buffer's empty
|
|
LoadBuffer();
|
|
if ((int)(DataSize-Pos) < numBytes)
|
|
numBytes = (int)DataSize-Pos;
|
|
|
|
memcpy(pdestBuffer, pBuffer+Pos, numBytes);
|
|
Pos += numBytes;
|
|
return numBytes + readBytes;
|
|
|
|
/*
|
|
// Alternative Read implementation. The one above is probably better
|
|
// due to FILEBUFFER_TOLERANCE.
|
|
int total = 0;
|
|
|
|
do {
|
|
int bufferBytes = (int)(DataSize-Pos);
|
|
int copyBytes = (bufferBytes > numBytes) ? numBytes : bufferBytes;
|
|
|
|
memcpy(pdestBuffer, pBuffer+Pos, copyBytes);
|
|
numBytes -= copyBytes;
|
|
pdestBuffer += copyBytes;
|
|
Pos += copyBytes;
|
|
total += copyBytes;
|
|
|
|
if (numBytes == 0)
|
|
break;
|
|
LoadBuffer();
|
|
|
|
} while (DataSize > 0);
|
|
|
|
return total;
|
|
*/
|
|
}
|
|
int sz = pFile->Read(pdestBuffer,numBytes);
|
|
if (sz > 0)
|
|
FilePos += sz;
|
|
return sz;
|
|
}
|
|
|
|
|
|
int BufferedFile::SkipBytes(int numBytes)
|
|
{
|
|
int skippedBytes = 0;
|
|
|
|
// Special case for skipping a little data in read buffer
|
|
if (BufferMode==ReadBuffer)
|
|
{
|
|
skippedBytes = (((int)DataSize-(int)Pos) >= numBytes) ? numBytes : (DataSize-Pos);
|
|
Pos += skippedBytes;
|
|
numBytes -= skippedBytes;
|
|
}
|
|
|
|
if (numBytes)
|
|
{
|
|
numBytes = pFile->SkipBytes(numBytes);
|
|
// Make sure we return the actual number skipped, or error
|
|
if (numBytes!=-1)
|
|
{
|
|
skippedBytes += numBytes;
|
|
FilePos += numBytes;
|
|
Pos = DataSize = 0;
|
|
}
|
|
else if (skippedBytes <= 0)
|
|
skippedBytes = -1;
|
|
}
|
|
return skippedBytes;
|
|
}
|
|
|
|
int BufferedFile::BytesAvailable()
|
|
{
|
|
int available = pFile->BytesAvailable();
|
|
// Adjust available size based on buffers
|
|
switch(BufferMode)
|
|
{
|
|
case ReadBuffer:
|
|
available += DataSize-Pos;
|
|
break;
|
|
case WriteBuffer:
|
|
available -= Pos;
|
|
if (available<0)
|
|
available= 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return available;
|
|
}
|
|
|
|
bool BufferedFile::Flush()
|
|
{
|
|
FlushBuffer();
|
|
return pFile->Flush();
|
|
}
|
|
|
|
// Seeking could be optimized better..
|
|
int BufferedFile::Seek(int offset, int origin)
|
|
{
|
|
if (BufferMode == ReadBuffer)
|
|
{
|
|
if (origin == Seek_Cur)
|
|
{
|
|
// Seek can fall either before or after Pos in the buffer,
|
|
// but it must be within bounds.
|
|
if (((unsigned(offset) + Pos)) <= DataSize)
|
|
{
|
|
Pos += offset;
|
|
return int (FilePos - DataSize + Pos);
|
|
}
|
|
|
|
// Lightweight buffer "Flush". We do this to avoid an extra seek
|
|
// back operation which would take place if we called FlushBuffer directly.
|
|
origin = Seek_Set;
|
|
OVR_ASSERT(((FilePos - DataSize + Pos) + (UInt64)offset) < ~(UInt64)0);
|
|
offset = (int)(FilePos - DataSize + Pos) + offset;
|
|
Pos = DataSize = 0;
|
|
}
|
|
else if (origin == Seek_Set)
|
|
{
|
|
if (((unsigned)offset - (FilePos-DataSize)) <= DataSize)
|
|
{
|
|
OVR_ASSERT((FilePos-DataSize) < ~(UInt64)0);
|
|
Pos = (unsigned)offset - (unsigned)(FilePos-DataSize);
|
|
return offset;
|
|
}
|
|
Pos = DataSize = 0;
|
|
}
|
|
else
|
|
{
|
|
FlushBuffer();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FlushBuffer();
|
|
}
|
|
|
|
/*
|
|
// Old Seek Logic
|
|
if (origin == Seek_Cur && offset + Pos < DataSize)
|
|
{
|
|
//OVR_ASSERT((FilePos - DataSize) >= (FilePos - DataSize + Pos + offset));
|
|
Pos += offset;
|
|
OVR_ASSERT(int (Pos) >= 0);
|
|
return int (FilePos - DataSize + Pos);
|
|
}
|
|
else if (origin == Seek_Set && unsigned(offset) >= FilePos - DataSize && unsigned(offset) < FilePos)
|
|
{
|
|
Pos = unsigned(offset - FilePos + DataSize);
|
|
OVR_ASSERT(int (Pos) >= 0);
|
|
return int (FilePos - DataSize + Pos);
|
|
}
|
|
|
|
FlushBuffer();
|
|
*/
|
|
|
|
|
|
FilePos = pFile->Seek(offset,origin);
|
|
return int (FilePos);
|
|
}
|
|
|
|
SInt64 BufferedFile::LSeek(SInt64 offset, int origin)
|
|
{
|
|
if (BufferMode == ReadBuffer)
|
|
{
|
|
if (origin == Seek_Cur)
|
|
{
|
|
// Seek can fall either before or after Pos in the buffer,
|
|
// but it must be within bounds.
|
|
if (((unsigned(offset) + Pos)) <= DataSize)
|
|
{
|
|
Pos += (unsigned)offset;
|
|
return SInt64(FilePos - DataSize + Pos);
|
|
}
|
|
|
|
// Lightweight buffer "Flush". We do this to avoid an extra seek
|
|
// back operation which would take place if we called FlushBuffer directly.
|
|
origin = Seek_Set;
|
|
offset = (SInt64)(FilePos - DataSize + Pos) + offset;
|
|
Pos = DataSize = 0;
|
|
}
|
|
else if (origin == Seek_Set)
|
|
{
|
|
if (((UInt64)offset - (FilePos-DataSize)) <= DataSize)
|
|
{
|
|
Pos = (unsigned)((UInt64)offset - (FilePos-DataSize));
|
|
return offset;
|
|
}
|
|
Pos = DataSize = 0;
|
|
}
|
|
else
|
|
{
|
|
FlushBuffer();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FlushBuffer();
|
|
}
|
|
|
|
/*
|
|
OVR_ASSERT(BufferMode != NoBuffer);
|
|
|
|
if (origin == Seek_Cur && offset + Pos < DataSize)
|
|
{
|
|
Pos += int (offset);
|
|
return FilePos - DataSize + Pos;
|
|
}
|
|
else if (origin == Seek_Set && offset >= SInt64(FilePos - DataSize) && offset < SInt64(FilePos))
|
|
{
|
|
Pos = unsigned(offset - FilePos + DataSize);
|
|
return FilePos - DataSize + Pos;
|
|
}
|
|
|
|
FlushBuffer();
|
|
*/
|
|
|
|
FilePos = pFile->LSeek(offset,origin);
|
|
return FilePos;
|
|
}
|
|
|
|
int BufferedFile::CopyFromStream(File *pstream, int byteSize)
|
|
{
|
|
// We can't rely on overridden Write()
|
|
// because delegation doesn't override virtual pointers
|
|
// So, just re-implement
|
|
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;
|
|
}
|
|
|
|
// Closing files
|
|
bool BufferedFile::Close()
|
|
{
|
|
switch(BufferMode)
|
|
{
|
|
case WriteBuffer:
|
|
FlushBuffer();
|
|
break;
|
|
case ReadBuffer:
|
|
// No need to seek back on close
|
|
BufferMode = NoBuffer;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return pFile->Close();
|
|
}
|
|
|
|
|
|
// ***** Global path helpers
|
|
|
|
// Find trailing short filename in a path.
|
|
const char* OVR_CDECL GetShortFilename(const char* purl)
|
|
{
|
|
UPInt len = OVR_strlen(purl);
|
|
for (UPInt i=len; i>0; i--)
|
|
if (purl[i]=='\\' || purl[i]=='/')
|
|
return purl+i+1;
|
|
return purl;
|
|
}
|
|
|
|
} // OVR
|
|
|