diff --git a/stepmania/src/RageFile.cpp b/stepmania/src/RageFile.cpp index c7f9fafd3e..c5c5b9d1b8 100644 --- a/stepmania/src/RageFile.cpp +++ b/stepmania/src/RageFile.cpp @@ -1,3 +1,10 @@ +/* + * This provides an interface to open files in RageFileManager's namespace + * This is just a simple RageBasicFile wrapper on top of another RageBasicFile; + * when a file is open, is acts like the underlying RageBasicFile, except that + * a few extra sanity checks are made to check file modes. + */ + #include "global.h" #include "RageFile.h" #include "RageUtil.h" @@ -6,26 +13,15 @@ RageFile::RageFile() { m_File = NULL; - m_BufAvail = 0; - m_EOF = false; - m_FilePos = 0; } -RageFile::RageFile( const RageFile &cpy ) +RageFile::RageFile( const RageFile &cpy ): + RageBasicFile( cpy ) { - ResetBuf(); - /* This will copy the file driver, including its internal file pointer. */ m_File = FILEMAN->CopyFileObj( cpy.m_File ); m_Path = cpy.m_Path; m_Mode = cpy.m_Mode; - m_Error = cpy.m_Error; - m_EOF = cpy.m_EOF; - m_FilePos = cpy.m_FilePos; - memcpy( this->m_Buffer, cpy.m_Buffer, sizeof(m_Buffer) ); - m_pBuf = m_Buffer + (cpy.m_pBuf-cpy.m_Buffer); - - m_BufAvail = cpy.m_BufAvail; } CString RageFile::GetPath() const @@ -49,9 +45,6 @@ bool RageFile::Open( const CString& path, int mode ) FixSlashesInPlace(m_Path); m_Mode = mode; - m_EOF = false; - m_FilePos = 0; - ResetBuf(); if( (m_Mode&READ) && (m_Mode&WRITE) ) { @@ -83,291 +76,90 @@ void RageFile::Close() m_File = NULL; } -/* 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 RageFile::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_BufAvail; - ASSERT_M( iBufAvail >= 0, ssprintf("%p, %p, %i", m_pBuf, m_Buffer, (int) sizeof(m_Buffer) ) ); - const int size = m_File->Read( m_pBuf+m_BufAvail, iBufAvail ); - - if( size == -1 ) - SetError( m_File->GetError() ); - - if( size > 0 ) - m_BufAvail += size; - - return size; -} - -void RageFile::ResetBuf() -{ - m_BufAvail = 0; - m_pBuf = m_Buffer; -} - -/* 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. */ +#define ASSERT_OPEN ASSERT_M( IsOpen(), ssprintf("\"%s\" is not open.", m_Path.c_str()) ); +#define ASSERT_READ ASSERT_OPEN; ASSERT_M( !!(m_Mode&READ), ssprintf("\"%s\" is not open for reading", m_Path.c_str()) ); +#define ASSERT_WRITE ASSERT_OPEN; ASSERT_M( !!(m_Mode&WRITE), ssprintf("\"%s\" is not open for writing", m_Path.c_str()) ); int RageFile::GetLine( CString &out ) { - out = ""; - - if ( !IsOpen() ) - RageException::Throw("\"%s\" is not open.", m_Path.c_str()); - - if( !(m_Mode&READ) ) - RageException::Throw("\"%s\" is not open for reading", GetPath().c_str()); - - if( m_EOF ) - 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_BufAvail ); - 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_BufAvail && m_pBuf[m_BufAvail-1] == '\r' ) - { - ReAddCR = true; - --m_BufAvail; - } - - p = m_pBuf+m_BufAvail; /* 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_BufAvail -= used; - m_FilePos += used; - GotData = true; - m_pBuf = p; - } - } - - if( ReAddCR ) - { - ASSERT( m_BufAvail == 0 ); - m_pBuf = m_Buffer; - m_Buffer[m_BufAvail] = '\r'; - ++m_BufAvail; - } - - 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_EOF = true; - return 0; - } - if( size == -1 ) - return -1; // error - if( size == 0 ) - break; // EOF or error - } - return GotData? 1:0; + ASSERT_READ; + return m_File->GetLine( out ); } -// 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 RageFile::PutLine( const CString &str ) { - if( Write(str) == -1 ) - return -1; - return Write( CString(NEWLINE) ); + ASSERT_WRITE; + return m_File->PutLine( str ); +} + +bool RageFile::AtEOF() const +{ + ASSERT_READ; + return m_File->AtEOF(); +} + +void RageFile::ClearError() +{ + if( m_File != NULL ) + m_File->ClearError(); + m_sError = ""; +} + +CString RageFile::GetError() const +{ + if( m_File != NULL && m_File->GetError() != "" ) + return m_File->GetError(); + return m_sError; } -int RageFile::Read( void *buffer, size_t bytes ) +void RageFile::SetError( const CString &err ) { - if( !IsOpen() ) - RageException::Throw("\"%s\" is not open.", GetPath().c_str()); + if( m_File != NULL ) + m_File->ClearError(); + m_sError = err; +} - if( !(m_Mode&READ) ) - RageException::Throw("\"%s\" is not open for reading", GetPath().c_str()); - - int ret = 0; - - while( !m_EOF && bytes > 0 ) - { - /* Copy data out of the buffer first. */ - int FromBuffer = min( (int) bytes, m_BufAvail ); - memcpy( buffer, m_pBuf, FromBuffer ); - - ret += FromBuffer; - m_FilePos += FromBuffer; - bytes -= FromBuffer; - m_BufAvail -= FromBuffer; - m_pBuf += FromBuffer; - - buffer = (char *) buffer + FromBuffer; - - if( !bytes ) - break; - - ASSERT( !m_BufAvail ); - - /* We need more; either fill the buffer and keep going, or just read directly - * into the destination buffer. */ - if( bytes >= sizeof(m_Buffer) ) - { - /* We have a lot more to read, so don't waste time copying it into the - * buffer. */ - int FromFile = m_File->Read( buffer, bytes ); - if( FromFile == -1 ) - { - SetError( m_File->GetError() ); - return -1; - } - if( FromFile == 0 ) - m_EOF = true; - ret += FromFile; - m_FilePos += FromFile; - return ret; - } - - m_pBuf = m_Buffer; - int got = FillBuf(); - if( got < 0 ) - return got; - if( got == 0 ) - m_EOF = true; - } - - return ret; +int RageFile::Read( void *pBuffer, size_t iBytes ) +{ + ASSERT_READ; + return m_File->Read( pBuffer, iBytes ); } int RageFile::Seek( int offset ) { - if( !IsOpen() ) - RageException::Throw("\"%s\" is not open.", GetPath().c_str()); + ASSERT_READ; + return m_File->Seek( offset ); +} - if( !(m_Mode&READ) ) - RageException::Throw("\"%s\" is not open for reading; can't seek", GetPath().c_str()); - - m_EOF = false; - - ResetBuf(); - - int pos = m_File->Seek( offset ); - if( pos == -1 ) - { - SetError( m_File->GetError() ); - return -1; - } - - m_FilePos = pos; - return pos; +int RageFile::Tell() const +{ + ASSERT_READ; + return m_File->Tell(); } int RageFile::GetFileSize() const { - if( !IsOpen() ) - RageException::Throw("\"%s\" is not open.", GetPath().c_str()); - - if( !(m_Mode&READ) ) - RageException::Throw("\"%s\" is not open for reading; can't GetFileSize", GetPath().c_str()); - - /* GetFileSize() may need to do non-const-like things--the default implementation reads - * until the end of file to emulate it. However, it should always restore the state to - * the way it was, so pretend it's const. */ - int iRet = const_cast(m_File)->GetFileSize(); - ASSERT_M( iRet >= 0, ssprintf("%i", iRet) ); - return iRet; + ASSERT_READ; + return m_File->GetFileSize(); } int RageFile::Read( CString &buffer, int bytes ) { - buffer.erase( buffer.begin(), buffer.end() ); - buffer.reserve( bytes != -1? bytes: this->GetFileSize() ); - - int ret = 0; - char buf[4096]; - while( bytes == -1 || ret < bytes ) - { - int ToRead = sizeof(buf); - if( bytes != -1 ) - ToRead = min( ToRead, bytes-ret ); - - const int got = Read( buf, ToRead ); - if( got == 0 ) - break; - if( got == -1 ) - return -1; - - buffer.append( buf, got ); - ret += got; - } - - return ret; + ASSERT_READ; + return m_File->Read( buffer, bytes ); } int RageFile::Write( const void *buffer, size_t bytes ) { - if( !IsOpen() ) - RageException::Throw("\"%s\" is not open.", GetPath().c_str()); - - if( !(m_Mode&WRITE) ) - RageException::Throw("\"%s\" is not open for writing", GetPath().c_str()); - - int iRet = m_File->Write( buffer, bytes ); - if( iRet == -1 ) - { - SetError( m_File->GetError() ); - return -1; - } - return iRet; + ASSERT_WRITE; + return m_File->Write( buffer, bytes ); } int RageFile::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 ) - { - SetError( m_File->GetError() ); - return -1; - } - return ret / bytes; + ASSERT_WRITE; + return m_File->Write( buffer, bytes, nmemb ); } int RageFile::Flush() @@ -378,42 +170,22 @@ int RageFile::Flush() return -1; } - int iRet = m_File->Flush(); - if( iRet == -1 ) - { - SetError( m_File->GetError() ); - return -1; - } - return iRet; + return m_File->Flush(); } int RageFile::Read( void *buffer, size_t bytes, int nmemb ) { - const int ret = Read( buffer, bytes*nmemb ); - if( ret == -1 ) - return -1; - - /* If we're reading 10-byte blocks, and we got 27 bytes, we have 7 extra bytes. - * Seek back. */ - const int extra = ret % bytes; - Seek( Tell()-extra ); - - return ret/bytes; + ASSERT_READ; + return m_File->Read( buffer, bytes, nmemb ); } int RageFile::Seek( int offset, int whence ) { - switch( whence ) - { - case SEEK_CUR: - return Seek( Tell() + offset ); - case SEEK_END: - offset += GetFileSize(); - } - return Seek( (int) offset ); + ASSERT_READ; + return m_File->Seek( offset, whence ); } -void FileReading::ReadBytes( RageFile &f, void *buf, int size, CString &sError ) +void FileReading::ReadBytes( RageBasicFile &f, void *buf, int size, CString &sError ) { if( sError.size() != 0 ) return; @@ -425,7 +197,7 @@ void FileReading::ReadBytes( RageFile &f, void *buf, int size, CString &sError ) sError = "Unexpected end of file"; } -uint8_t FileReading::read_8( RageFile &f, CString &sError ) +uint8_t FileReading::read_8( RageBasicFile &f, CString &sError ) { uint8_t val; ReadBytes( f, &val, sizeof(uint8_t), sError ); @@ -435,7 +207,7 @@ uint8_t FileReading::read_8( RageFile &f, CString &sError ) return 0; } -uint16_t FileReading::read_u16_le( RageFile &f, CString &sError ) +uint16_t FileReading::read_u16_le( RageBasicFile &f, CString &sError ) { uint16_t val; ReadBytes( f, &val, sizeof(uint16_t), sError ); @@ -445,7 +217,7 @@ uint16_t FileReading::read_u16_le( RageFile &f, CString &sError ) return 0; } -int16_t FileReading::read_16_le( RageFile &f, CString &sError ) +int16_t FileReading::read_16_le( RageBasicFile &f, CString &sError ) { int16_t val; ReadBytes( f, &val, sizeof(int16_t), sError ); @@ -455,7 +227,7 @@ int16_t FileReading::read_16_le( RageFile &f, CString &sError ) return 0; } -uint32_t FileReading::read_u32_le( RageFile &f, CString &sError ) +uint32_t FileReading::read_u32_le( RageBasicFile &f, CString &sError ) { uint32_t val; ReadBytes( f, &val, sizeof(uint32_t), sError ); @@ -465,7 +237,7 @@ uint32_t FileReading::read_u32_le( RageFile &f, CString &sError ) return 0; } -int32_t FileReading::read_32_le( RageFile &f, CString &sError ) +int32_t FileReading::read_32_le( RageBasicFile &f, CString &sError ) { int32_t val; ReadBytes( f, &val, sizeof(int32_t), sError ); diff --git a/stepmania/src/RageFile.h b/stepmania/src/RageFile.h index d2feb04474..bc7f29a2b2 100644 --- a/stepmania/src/RageFile.h +++ b/stepmania/src/RageFile.h @@ -7,10 +7,55 @@ class RageFileObj; -class RageFile -{ - friend class RageFileObj; +/* 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 RageBasicFile +{ +public: + virtual ~RageBasicFile() { } + + 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; + + virtual int GetLine( CString &out ) = 0; + virtual int PutLine( const CString &str ) = 0; + + virtual int GetFileSize() const = 0; +}; + +/* This is the high-level interface, which interfaces with RageFileObj implementations + * and RageFileManager. */ +class RageFile: public RageBasicFile +{ public: enum { @@ -25,8 +70,8 @@ public: SLOW_FLUSH = 0x8 }; - RageFile(); - ~RageFile() { Close(); } + RageFile(); + ~RageFile() { Close(); } RageFile( const RageFile &cpy ); /* Use GetRealPath to get the path this file was opened with; use that if you @@ -38,17 +83,16 @@ public: const CString &GetRealPath() const { return m_Path; } CString GetPath() const; - bool Open( const CString& path, int mode = READ ); - void Close(); - + bool Open( const CString& path, int mode = READ ); + void Close(); bool IsOpen() const { return m_File != NULL; } - int GetOpenMode() const { return m_Mode; } - bool AtEOF() const { return m_EOF; } - CString GetError() const { return m_Error; } - void ClearError() { m_Error = ""; } - bool IsGood() const { return IsOpen() && !AtEOF() && m_Error.empty(); } - int Tell() const { return m_FilePos; } + bool AtEOF() const; + CString GetError() const; + void ClearError(); + bool IsGood() const { return IsOpen() && !AtEOF() && GetError().empty(); } + + int Tell() const; int Seek( int offset ); int GetFileSize() const; @@ -69,24 +113,13 @@ public: int PutLine( const CString &str ); protected: - void SetError( const CString &err ) { m_Error = err; } /* called by RageFileObj::SetError */ + void SetError( const CString &err ); private: - int FillBuf(); - void ResetBuf(); - RageFileObj *m_File; CString m_Path; + CString m_sError; int m_Mode; - - CString m_Error; - bool m_EOF; - int m_FilePos; - - enum { BSIZE = 1024 }; - char m_Buffer[BSIZE]; - char *m_pBuf; - int m_BufAvail; }; /* Convenience wrappers for reading binary files. */ @@ -94,12 +127,12 @@ namespace FileReading { /* On error, these set sError to the error message. If sError is already * non-empty, nothing happens. */ - void ReadBytes( RageFile &f, void *buf, int size, CString &sError ); - uint8_t read_8( RageFile &f, CString &sError ); - int16_t read_16_le( RageFile &f, CString &sError ); - uint16_t read_u16_le( RageFile &f, CString &sError ); - int32_t read_32_le( RageFile &f, CString &sError ); - uint32_t read_u32_le( RageFile &f, CString &sError ); + void ReadBytes( RageBasicFile &f, void *buf, int size, CString &sError ); + uint8_t read_8( RageBasicFile &f, CString &sError ); + int16_t read_16_le( RageBasicFile &f, CString &sError ); + uint16_t read_u16_le( RageBasicFile &f, CString &sError ); + int32_t read_32_le( RageBasicFile &f, CString &sError ); + uint32_t read_u32_le( RageBasicFile &f, CString &sError ); }; #endif diff --git a/stepmania/src/RageFileDriver.cpp b/stepmania/src/RageFileDriver.cpp index 7fad43e736..173f03cf85 100644 --- a/stepmania/src/RageFileDriver.cpp +++ b/stepmania/src/RageFileDriver.cpp @@ -91,14 +91,129 @@ RageFileDriver *MakeFileDriver( CString Type, CString Root ) } +RageFileObj::RageFileObj() +{ + ResetBuf(); + + m_iBufAvail = 0; + m_bEOF = false; + m_iFilePos = 0; +} + int RageFileObj::Seek( int iOffset ) { - return SeekInternal( 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 ) { - return ReadInternal( pBuffer, 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 ) @@ -106,11 +221,144 @@ 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; +} + /* * Copyright (c) 2003-2004 Glenn Maynard * All rights reserved. diff --git a/stepmania/src/RageFileDriver.h b/stepmania/src/RageFileDriver.h index a3f12004f0..2f81e68131 100644 --- a/stepmania/src/RageFileDriver.h +++ b/stepmania/src/RageFileDriver.h @@ -31,34 +31,35 @@ protected: FilenameDB *FDB; }; -class RageFileObj +class RageFileObj: public RageBasicFile { public: + RageFileObj(); virtual ~RageFileObj() { } - virtual CString GetError() const { return m_sError; } + CString GetError() const { return m_sError; } + void ClearError() { SetError(""); } - /* 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. */ + bool AtEOF() const { return m_bEOF; } + int Seek( int iOffset ); + int Seek( int offset, int whence ); + int Tell() const { return m_iFilePos; } - /* 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. */ int Read( void *pBuffer, size_t iBytes ); + int Read( CString &buffer, int bytes = -1 ); + int Read( void *buffer, size_t bytes, int nmemb ); - /* Write iSize bytes of data from pBuf. Return 0 on success, -1 on error. */ 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 ); - /* 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. */ int Flush(); - virtual int GetFileSize() = 0; + int GetLine( CString &out ); + int PutLine( const CString &str ); + + virtual int GetFileSize() const = 0; virtual CString GetDisplayPath() const { return ""; } virtual RageFileObj *Copy() const { FAIL_M( "Copying unimplemented" ); } @@ -68,8 +69,20 @@ protected: virtual int WriteInternal( const void *pBuffer, size_t iBytes ) = 0; virtual int FlushInternal() { return 0; } - virtual void SetError( const CString &sError ) { m_sError = sError; } + 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; }; /* This is used to register the driver, so RageFileManager can see it. */ diff --git a/stepmania/src/RageFileDriverDirect.cpp b/stepmania/src/RageFileDriverDirect.cpp index 6d9a37ba7e..c7c5b307b4 100644 --- a/stepmania/src/RageFileDriverDirect.cpp +++ b/stepmania/src/RageFileDriverDirect.cpp @@ -49,7 +49,7 @@ public: virtual int SeekInternal( int offset ); virtual RageFileObj *Copy() const; virtual CString GetDisplayPath() const { return path; } - virtual int GetFileSize(); + virtual int GetFileSize() const; }; @@ -365,7 +365,7 @@ int RageFileObjDirect::SeekInternal( int offset ) return lseek( fd, offset, SEEK_SET ); } -int RageFileObjDirect::GetFileSize() +int RageFileObjDirect::GetFileSize() const { const int OldPos = lseek( fd, 0, SEEK_CUR ); ASSERT_M( OldPos != -1, strerror(errno) ); diff --git a/stepmania/src/RageFileDriverMemory.cpp b/stepmania/src/RageFileDriverMemory.cpp index 1ffba908cc..9502e47f86 100644 --- a/stepmania/src/RageFileDriverMemory.cpp +++ b/stepmania/src/RageFileDriverMemory.cpp @@ -73,7 +73,7 @@ public: return m_iFilePos; } - int GetFileSize() + int GetFileSize() const { LockMut(m_pFile->m_Mutex); return m_pFile->m_sBuf.size(); diff --git a/stepmania/src/RageFileDriverZip.cpp b/stepmania/src/RageFileDriverZip.cpp index f9b8ae46b2..9e1a0c1ace 100644 --- a/stepmania/src/RageFileDriverZip.cpp +++ b/stepmania/src/RageFileDriverZip.cpp @@ -84,7 +84,7 @@ public: int ReadInternal( void *pBuffer, size_t iBytes ); int WriteInternal( const void *pBuffer, size_t iBytes ) { SetError( "Not implemented" ); return -1; } int SeekInternal( int iOffset ); - int GetFileSize() { return info.uncompr_size; } + int GetFileSize() const { return info.uncompr_size; } RageFileObj *Copy() const { RageException::Throw( "Loading ZIPs from deflated ZIPs is currently disabled; see RageFileObjZipDeflated" ); @@ -106,7 +106,7 @@ public: int WriteInternal( const void *pBuffer, size_t iBytes ) { SetError( "Not implemented" ); return -1; } int SeekInternal( int iOffset ); - int GetFileSize() { return info.uncompr_size; } + int GetFileSize() const { return info.uncompr_size; } RageFileObj *Copy() const {