RageBasicFile -> RageFileBasic (sorts better), move it and RageFileObj (probably to be renamed) into a separate file since they're no longer "file driver"-specific
This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
#include "global.h"
|
||||
#include "RageFileBasic.h"
|
||||
#include "RageUtil.h"
|
||||
|
||||
RageFileObj::RageFileObj()
|
||||
{
|
||||
ResetBuf();
|
||||
|
||||
m_iBufAvail = 0;
|
||||
m_bEOF = false;
|
||||
m_iFilePos = 0;
|
||||
}
|
||||
|
||||
int RageFileObj::Seek( int iOffset )
|
||||
{
|
||||
m_bEOF = false;
|
||||
|
||||
ResetBuf();
|
||||
|
||||
int iPos = SeekInternal( iOffset );
|
||||
if( iPos != -1 )
|
||||
m_iFilePos = iPos;
|
||||
return iPos;
|
||||
}
|
||||
|
||||
int RageFileObj::Seek( int offset, int whence )
|
||||
{
|
||||
switch( whence )
|
||||
{
|
||||
case SEEK_CUR:
|
||||
return Seek( Tell() + offset );
|
||||
case SEEK_END:
|
||||
offset += GetFileSize();
|
||||
}
|
||||
return Seek( (int) offset );
|
||||
}
|
||||
|
||||
int RageFileObj::Read( void *pBuffer, size_t iBytes )
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
while( !m_bEOF && iBytes > 0 )
|
||||
{
|
||||
/* Copy data out of the buffer first. */
|
||||
int FromBuffer = min( (int) iBytes, m_iBufAvail );
|
||||
memcpy( pBuffer, m_pBuf, FromBuffer );
|
||||
|
||||
ret += FromBuffer;
|
||||
m_iFilePos += FromBuffer;
|
||||
iBytes -= FromBuffer;
|
||||
m_iBufAvail -= FromBuffer;
|
||||
m_pBuf += FromBuffer;
|
||||
|
||||
pBuffer = (char *) pBuffer + FromBuffer;
|
||||
|
||||
if( !iBytes )
|
||||
break;
|
||||
|
||||
ASSERT( m_iBufAvail == 0 );
|
||||
|
||||
/* We need more; either fill the buffer and keep going, or just read directly
|
||||
* into the destination buffer. */
|
||||
if( iBytes >= sizeof(m_Buffer) )
|
||||
{
|
||||
/* We have a lot more to read, so don't waste time copying it into the
|
||||
* buffer. */
|
||||
int iFromFile = this->ReadInternal( pBuffer, iBytes );
|
||||
if( iFromFile == -1 )
|
||||
return -1;
|
||||
|
||||
if( iFromFile == 0 )
|
||||
m_bEOF = true;
|
||||
ret += iFromFile;
|
||||
m_iFilePos += iFromFile;
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_pBuf = m_Buffer;
|
||||
int got = FillBuf();
|
||||
if( got < 0 )
|
||||
return got;
|
||||
if( got == 0 )
|
||||
m_bEOF = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int RageFileObj::Read( CString &sBuffer, int iBytes )
|
||||
{
|
||||
sBuffer.erase( sBuffer.begin(), sBuffer.end() );
|
||||
sBuffer.reserve( iBytes != -1? iBytes: this->GetFileSize() );
|
||||
|
||||
int iRet = 0;
|
||||
char buf[4096];
|
||||
while( iBytes == -1 || iRet < iBytes )
|
||||
{
|
||||
int ToRead = sizeof(buf);
|
||||
if( iBytes != -1 )
|
||||
ToRead = min( ToRead, iBytes-iRet );
|
||||
|
||||
const int iGot = Read( buf, ToRead );
|
||||
if( iGot == 0 )
|
||||
break;
|
||||
if( iGot == -1 )
|
||||
return -1;
|
||||
|
||||
sBuffer.append( buf, iGot );
|
||||
iRet += iGot;
|
||||
}
|
||||
|
||||
return iRet;
|
||||
}
|
||||
|
||||
int RageFileObj::Read( void *buffer, size_t bytes, int nmemb )
|
||||
{
|
||||
const int iRet = Read( buffer, bytes*nmemb );
|
||||
if( iRet == -1 )
|
||||
return -1;
|
||||
|
||||
/* If we're reading 10-byte blocks, and we got 27 bytes, we have 7 extra bytes.
|
||||
* Seek back. XXX: seeking is very slow for eg. deflated ZIPs. If the block is
|
||||
* small enough, we may be able to stuff the extra data into the buffer. */
|
||||
const int iExtra = iRet % bytes;
|
||||
Seek( Tell()-iExtra );
|
||||
|
||||
return iRet/bytes;
|
||||
}
|
||||
|
||||
int RageFileObj::Write( const void *pBuffer, size_t iBytes )
|
||||
{
|
||||
return WriteInternal( pBuffer, iBytes );
|
||||
}
|
||||
|
||||
int RageFileObj::Write( const void *buffer, size_t bytes, int nmemb )
|
||||
{
|
||||
/* Simple write. We never return partial writes. */
|
||||
int ret = Write( buffer, bytes*nmemb ) / bytes;
|
||||
if( ret == -1 )
|
||||
return -1;
|
||||
return ret / bytes;
|
||||
}
|
||||
|
||||
int RageFileObj::Flush()
|
||||
{
|
||||
return FlushInternal();
|
||||
}
|
||||
|
||||
|
||||
/* Read up to the next \n, and return it in out. Strip the \n. If the \n is
|
||||
* preceded by a \r (DOS newline), strip that, too. */
|
||||
int RageFileObj::GetLine( CString &out )
|
||||
{
|
||||
out = "";
|
||||
|
||||
if( m_bEOF )
|
||||
return 0;
|
||||
|
||||
bool GotData = false;
|
||||
while( 1 )
|
||||
{
|
||||
bool done = false;
|
||||
|
||||
/* Find the end of the block we'll move to out. */
|
||||
char *p = (char *) memchr( m_pBuf, '\n', m_iBufAvail );
|
||||
bool ReAddCR = false;
|
||||
if( p == NULL )
|
||||
{
|
||||
/* Hack: If the last character of the buffer is \r, then it's likely that an
|
||||
* \r\n has been split across buffers. Move everything else, then move the
|
||||
* \r to the beginning of the buffer and handle it the next time around the loop. */
|
||||
if( m_iBufAvail && m_pBuf[m_iBufAvail-1] == '\r' )
|
||||
{
|
||||
ReAddCR = true;
|
||||
--m_iBufAvail;
|
||||
}
|
||||
|
||||
p = m_pBuf+m_iBufAvail; /* everything */
|
||||
}
|
||||
else
|
||||
done = true;
|
||||
|
||||
if( p >= m_pBuf )
|
||||
{
|
||||
char *RealEnd = p;
|
||||
if( done && p > m_pBuf && p[-1] == '\r' )
|
||||
--RealEnd; /* not including \r */
|
||||
out.append( m_pBuf, RealEnd );
|
||||
|
||||
if( done )
|
||||
++p; /* skip \n */
|
||||
|
||||
const int used = p-m_pBuf;
|
||||
if( used )
|
||||
{
|
||||
m_iBufAvail -= used;
|
||||
m_iFilePos += used;
|
||||
GotData = true;
|
||||
m_pBuf = p;
|
||||
}
|
||||
}
|
||||
|
||||
if( ReAddCR )
|
||||
{
|
||||
ASSERT( m_iBufAvail == 0 );
|
||||
m_pBuf = m_Buffer;
|
||||
m_Buffer[m_iBufAvail] = '\r';
|
||||
++m_iBufAvail;
|
||||
}
|
||||
|
||||
if( done )
|
||||
break;
|
||||
|
||||
/* We need more data. */
|
||||
m_pBuf = m_Buffer;
|
||||
|
||||
const int size = FillBuf();
|
||||
|
||||
/* If we've read data already, then don't mark EOF yet. Wait until the
|
||||
* next time we're called. */
|
||||
if( size == 0 && !GotData )
|
||||
{
|
||||
m_bEOF = true;
|
||||
return 0;
|
||||
}
|
||||
if( size == -1 )
|
||||
return -1; // error
|
||||
if( size == 0 )
|
||||
break; // EOF or error
|
||||
}
|
||||
return GotData? 1:0;
|
||||
}
|
||||
|
||||
// Always use "\r\n". Even though the program may be running on Unix, the
|
||||
// files written to a memory card are likely to be edited using Windows.
|
||||
//#if defined(_WIN32)
|
||||
#define NEWLINE "\r\n"
|
||||
//#else
|
||||
//#define NEWLINE "\n"
|
||||
//#endif
|
||||
|
||||
int RageFileObj::PutLine( const CString &str )
|
||||
{
|
||||
if( Write(str) == -1 )
|
||||
return -1;
|
||||
return Write( CString(NEWLINE) );
|
||||
}
|
||||
|
||||
/* Fill the internal buffer. This never marks EOF, since this is an internal, hidden
|
||||
* read; EOF should only be set as a result of a real read. (That is, disabling buffering
|
||||
* shouldn't cause the results of AtEOF to change.) */
|
||||
int RageFileObj::FillBuf()
|
||||
{
|
||||
/* The buffer starts at m_Buffer; any data in it starts at m_pBuf; space between
|
||||
* the two is old data that we've read. (Don't mangle that data; we can use it
|
||||
* for seeking backwards.) */
|
||||
const int iBufAvail = sizeof(m_Buffer) - (m_pBuf-m_Buffer) - m_iBufAvail;
|
||||
ASSERT_M( iBufAvail >= 0, ssprintf("%p, %p, %i", m_pBuf, m_Buffer, (int) sizeof(m_Buffer) ) );
|
||||
const int size = this->ReadInternal( m_pBuf+m_iBufAvail, iBufAvail );
|
||||
|
||||
if( size > 0 )
|
||||
m_iBufAvail += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void RageFileObj::ResetBuf()
|
||||
{
|
||||
m_iBufAvail = 0;
|
||||
m_pBuf = m_Buffer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
#ifndef RAGE_FILE_BASIC_H
|
||||
#define RAGE_FILE_BASIC_H
|
||||
|
||||
/* This is a simple file I/O interface. Although most of these operations
|
||||
* are straightforward, there are several of them; most of the time, you'll
|
||||
* only want to implement RageFileObj. */
|
||||
class RageFileBasic
|
||||
{
|
||||
public:
|
||||
virtual ~RageFileBasic() { }
|
||||
|
||||
virtual CString GetError() const = 0;
|
||||
virtual void ClearError() = 0;
|
||||
virtual bool AtEOF() const = 0;
|
||||
|
||||
/* Seek to the given absolute offset. Return to the position actually
|
||||
* seeked to; if the position given was beyond the end of the file, the
|
||||
* return value will be the size of the file. */
|
||||
virtual int Seek( int iOffset ) = 0;
|
||||
virtual int Seek( int offset, int whence ) = 0;
|
||||
virtual int Tell() const = 0;
|
||||
|
||||
/* Read at most iSize bytes into pBuf. Return the number of bytes read,
|
||||
* 0 on end of stream, or -1 on error. Note that reading less than iSize
|
||||
* does not necessarily mean that the end of the stream has been reached;
|
||||
* keep reading until 0 is returned. */
|
||||
virtual int Read( void *pBuffer, size_t iBytes ) = 0;
|
||||
virtual int Read( CString &buffer, int bytes = -1 ) = 0;
|
||||
virtual int Read( void *buffer, size_t bytes, int nmemb ) = 0;
|
||||
|
||||
/* Write iSize bytes of data from pBuf. Return 0 on success, -1 on error. */
|
||||
virtual int Write( const void *pBuffer, size_t iBytes ) = 0;
|
||||
virtual int Write( const CString &sString ) = 0;
|
||||
virtual int Write( const void *buffer, size_t bytes, int nmemb ) = 0;
|
||||
|
||||
/* Due to buffering, writing may not happen by the end of a Write() call, so not
|
||||
* all errors may be returned by it. Data will be flushed when the stream (or its
|
||||
* underlying object) is destroyed, but errors can no longer be returned. Call
|
||||
* Flush() to flush pending data, in order to check for errors. */
|
||||
virtual int Flush() = 0;
|
||||
|
||||
/* This returns a descriptive path for the file, or "". */
|
||||
virtual CString GetDisplayPath() const { return ""; }
|
||||
|
||||
virtual RageFileBasic *Copy() const = 0;
|
||||
|
||||
virtual int GetLine( CString &out ) = 0;
|
||||
virtual int PutLine( const CString &str ) = 0;
|
||||
|
||||
virtual int GetFileSize() const = 0;
|
||||
};
|
||||
|
||||
class RageFileObj: public RageFileBasic
|
||||
{
|
||||
public:
|
||||
RageFileObj();
|
||||
virtual ~RageFileObj() { }
|
||||
|
||||
virtual CString GetError() const { return m_sError; }
|
||||
virtual void ClearError() { SetError(""); }
|
||||
|
||||
bool AtEOF() const { return m_bEOF; }
|
||||
|
||||
int Seek( int iOffset );
|
||||
int Seek( int offset, int whence );
|
||||
int Tell() const { return m_iFilePos; }
|
||||
|
||||
int Read( void *pBuffer, size_t iBytes );
|
||||
int Read( CString &buffer, int bytes = -1 );
|
||||
int Read( void *buffer, size_t bytes, int nmemb );
|
||||
|
||||
int Write( const void *pBuffer, size_t iBytes );
|
||||
int Write( const CString &sString ) { return Write( sString.data(), sString.size() ); }
|
||||
int Write( const void *buffer, size_t bytes, int nmemb );
|
||||
|
||||
int Flush();
|
||||
|
||||
int GetLine( CString &out );
|
||||
int PutLine( const CString &str );
|
||||
|
||||
virtual int GetFileSize() const = 0;
|
||||
virtual CString GetDisplayPath() const { return ""; }
|
||||
virtual RageFileBasic *Copy() const { FAIL_M( "Copying unimplemented" ); }
|
||||
|
||||
protected:
|
||||
virtual int SeekInternal( int iOffset ) { FAIL_M( "Seeking unimplemented" ); }
|
||||
virtual int ReadInternal( void *pBuffer, size_t iBytes ) = 0;
|
||||
virtual int WriteInternal( const void *pBuffer, size_t iBytes ) = 0;
|
||||
virtual int FlushInternal() { return 0; }
|
||||
|
||||
void SetError( const CString &sError ) { m_sError = sError; }
|
||||
CString m_sError;
|
||||
|
||||
private:
|
||||
int FillBuf();
|
||||
void ResetBuf();
|
||||
|
||||
bool m_bEOF;
|
||||
int m_iFilePos;
|
||||
|
||||
enum { BSIZE = 1024 };
|
||||
char m_Buffer[BSIZE];
|
||||
char *m_pBuf;
|
||||
int m_iBufAvail;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user