Files
itgmania212121/stepmania/src/RageFile.cpp
T

508 lines
11 KiB
C++
Raw Normal View History

2003-11-27 07:46:36 +00:00
#include "global.h"
2003-07-22 07:47:27 +00:00
#include "RageFile.h"
#include "RageUtil.h"
2003-12-04 08:25:59 +00:00
#include "RageFileDriver.h"
2003-07-22 07:47:27 +00:00
2003-12-04 08:25:59 +00:00
RageFile::RageFile()
{
m_File = NULL;
2004-06-23 01:53:30 +00:00
m_BufAvail = 0;
2003-12-04 08:25:59 +00:00
m_EOF = false;
m_FilePos = 0;
}
2003-12-10 05:04:37 +00:00
RageFile::RageFile( const RageFile &cpy )
{
2003-12-10 05:48:31 +00:00
ResetBuf();
/* This will copy the file driver, including its internal file pointer. */
2003-12-11 07:13:20 +00:00
m_File = FILEMAN->CopyFileObj( cpy.m_File, *this );
2003-12-10 05:04:37 +00:00
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;
2004-06-23 05:15:47 +00:00
memcpy( this->m_Buffer, cpy.m_Buffer, sizeof(m_Buffer) );
m_pBuf = m_Buffer + (cpy.m_pBuf-cpy.m_Buffer);
2004-06-23 01:53:30 +00:00
m_BufAvail = cpy.m_BufAvail;
2003-12-10 05:04:37 +00:00
}
2003-12-04 08:25:59 +00:00
CString RageFile::GetPath() const
{
if ( !IsOpen() )
return "";
return m_File->GetDisplayPath();
}
bool RageFile::Open( const CString& path, int mode )
2003-11-27 07:46:36 +00:00
{
2003-12-04 08:25:59 +00:00
ASSERT( FILEMAN );
2003-12-03 23:50:53 +00:00
Close();
2003-12-04 08:25:59 +00:00
m_Path = path;
FixSlashesInPlace(m_Path);
m_Mode = mode;
m_EOF = false;
m_FilePos = 0;
2003-12-03 23:50:53 +00:00
ResetBuf();
if( (m_Mode&READ) && (m_Mode&WRITE) )
{
SetError( "Reading and writing are mutually exclusive" );
return false;
}
if( !(m_Mode&READ) && !(m_Mode&WRITE) )
{
SetError( "Neither reading nor writing specified" );
return false;
}
2003-12-04 08:25:59 +00:00
int error;
m_File = FILEMAN->Open( path, mode, *this, error );
if( m_File == NULL )
{
SetError( strerror(error) );
return false;
}
return true;
2003-11-27 07:46:36 +00:00
}
void RageFile::Close()
{
2003-12-11 05:32:35 +00:00
FILEMAN->Close( m_File );
2003-12-04 08:25:59 +00:00
m_File = NULL;
2003-07-22 07:47:27 +00:00
}
2004-06-23 01:53:30 +00:00
/* 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.) */
2004-06-25 18:30:33 +00:00
const int iBufAvail = sizeof(m_Buffer) - (m_pBuf-m_Buffer) - m_BufAvail;
2004-06-23 01:53:30 +00:00
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 > 0 )
m_BufAvail += size;
return size;
}
2003-12-03 23:50:53 +00:00
void RageFile::ResetBuf()
2003-11-27 07:46:36 +00:00
{
2004-06-23 01:53:30 +00:00
m_BufAvail = 0;
2003-12-03 23:50:53 +00:00
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. */
2003-12-04 08:25:59 +00:00
int RageFile::GetLine( CString &out )
2003-12-03 23:50:53 +00:00
{
out = "";
2003-12-04 08:25:59 +00:00
if ( !IsOpen() )
RageException::Throw("\"%s\" is not open.", m_Path.c_str());
if( !(m_Mode&READ) )
2003-12-04 08:25:59 +00:00
RageException::Throw("\"%s\" is not open for reading", GetPath().c_str());
2003-12-03 23:50:53 +00:00
2004-06-23 01:53:30 +00:00
if( m_EOF )
return 0;
2003-12-04 08:25:59 +00:00
bool GotData = false;
2003-12-03 23:50:53 +00:00
while( 1 )
{
bool done = false;
/* Find the end of the block we'll move to out. */
2004-06-23 01:53:30 +00:00
char *p = (char *) memchr( m_pBuf, '\n', m_BufAvail );
2003-12-04 08:25:59 +00:00
bool ReAddCR = false;
2003-12-03 23:50:53 +00:00
if( p == NULL )
2003-12-04 08:25:59 +00:00
{
/* 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. */
2004-06-25 19:09:44 +00:00
if( m_BufAvail && m_pBuf[m_BufAvail-1] == '\r' )
2003-12-04 08:25:59 +00:00
{
ReAddCR = true;
2004-06-23 01:53:30 +00:00
--m_BufAvail;
2003-12-04 08:25:59 +00:00
}
2004-06-23 01:53:30 +00:00
p = m_pBuf+m_BufAvail; /* everything */
2003-12-04 08:25:59 +00:00
}
2003-12-03 23:50:53 +00:00
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 */
2003-12-04 08:25:59 +00:00
const int used = p-m_pBuf;
if( used )
{
2004-06-23 01:53:30 +00:00
m_BufAvail -= used;
2003-12-04 08:25:59 +00:00
m_FilePos += used;
GotData = true;
m_pBuf = p;
}
}
if( ReAddCR )
{
2004-06-23 01:53:30 +00:00
ASSERT( m_BufAvail == 0 );
2003-12-04 08:25:59 +00:00
m_pBuf = m_Buffer;
2004-06-23 01:53:30 +00:00
m_Buffer[m_BufAvail] = '\r';
++m_BufAvail;
2003-12-03 23:50:53 +00:00
}
if( done )
break;
2003-12-04 08:25:59 +00:00
/* We need more data. */
2003-12-03 23:50:53 +00:00
m_pBuf = m_Buffer;
2004-06-23 01:53:30 +00:00
const int size = FillBuf();
2003-12-04 08:25:59 +00:00
/* 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;
}
2004-06-23 01:53:30 +00:00
if( size == -1 )
2003-12-04 08:25:59 +00:00
return -1; // error
if( size == 0 )
2003-12-03 23:50:53 +00:00
break; // EOF or error
}
2003-12-04 08:25:59 +00:00
return GotData? 1:0;
}
2004-05-04 00:01:24 +00:00
// 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)
2003-12-04 04:26:49 +00:00
#define NEWLINE "\r\n"
2004-05-04 00:01:24 +00:00
//#else
//#define NEWLINE "\n"
//#endif
2003-12-04 04:26:49 +00:00
int RageFile::PutLine( const CString &str )
{
2003-12-04 08:25:59 +00:00
if( Write(str) == -1 )
2003-12-04 04:26:49 +00:00
return -1;
2003-12-04 08:25:59 +00:00
return Write( CString(NEWLINE) );
2003-12-04 04:26:49 +00:00
}
2003-12-04 08:25:59 +00:00
2003-12-03 23:50:53 +00:00
int RageFile::Read( void *buffer, size_t bytes )
{
if( !IsOpen() )
2003-12-04 08:25:59 +00:00
RageException::Throw("\"%s\" is not open.", GetPath().c_str());
if( !(m_Mode&READ) )
2003-12-04 08:25:59 +00:00
RageException::Throw("\"%s\" is not open for reading", GetPath().c_str());
2003-12-03 23:50:53 +00:00
int ret = 0;
2004-06-23 01:53:30 +00:00
while( !m_EOF && bytes > 0 )
{
/* Copy data out of the buffer first. */
int FromBuffer = min( (int) bytes, m_BufAvail );
memcpy( buffer, m_pBuf, FromBuffer );
2003-12-04 08:25:59 +00:00
2004-06-23 01:53:30 +00:00
ret += FromBuffer;
m_FilePos += FromBuffer;
bytes -= FromBuffer;
m_BufAvail -= FromBuffer;
m_pBuf += FromBuffer;
buffer = (char *) buffer + FromBuffer;
2003-11-27 07:46:36 +00:00
2004-06-23 01:53:30 +00:00
if( !bytes )
break;
2004-06-23 05:15:47 +00:00
ASSERT( !m_BufAvail );
2004-06-23 01:53:30 +00:00
/* 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 < 0 )
return FromFile;
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 )
2003-12-04 08:25:59 +00:00
m_EOF = true;
2003-12-03 23:50:53 +00:00
}
2003-12-04 08:25:59 +00:00
2003-12-03 23:50:53 +00:00
return ret;
2003-11-27 07:46:36 +00:00
}
2003-12-04 08:25:59 +00:00
int RageFile::Seek( int offset )
2003-11-27 07:46:36 +00:00
{
2003-12-04 08:25:59 +00:00
if( !IsOpen() )
RageException::Throw("\"%s\" is not open.", GetPath().c_str());
if( !(m_Mode&READ) )
2003-12-04 08:25:59 +00:00
RageException::Throw("\"%s\" is not open for reading; can't seek", GetPath().c_str());
m_EOF = false;
ResetBuf();
2003-12-08 03:24:00 +00:00
if( offset == 0 )
{
m_File->Rewind();
2004-02-12 05:24:22 +00:00
m_FilePos = 0;
2003-12-08 03:24:00 +00:00
return 0;
}
2003-12-04 08:25:59 +00:00
int pos = m_File->Seek( offset );
if( pos == -1 )
return -1;
m_FilePos = pos;
return pos;
}
int RageFile::SeekCur( int offset )
{
ASSERT( offset >= 0 );
if( !IsOpen() )
RageException::Throw("\"%s\" is not open.", GetPath().c_str());
if( !(m_Mode&READ) )
2003-12-04 08:25:59 +00:00
RageException::Throw("\"%s\" is not open for reading; can't seek", GetPath().c_str());
if( !offset || m_EOF )
return m_FilePos;
2004-06-23 01:53:30 +00:00
int FromBuffer = min( offset, m_BufAvail );
2003-12-04 08:25:59 +00:00
m_FilePos += FromBuffer;
offset -= FromBuffer;
2004-06-23 01:53:30 +00:00
m_BufAvail -= FromBuffer;
2003-12-04 08:25:59 +00:00
m_pBuf += FromBuffer;
if( offset )
{
int pos = m_File->SeekCur( offset );
if( pos == -1 )
return -1;
m_FilePos = pos;
}
return m_FilePos;
}
2004-02-17 21:42:06 +00:00
int RageFile::GetFileSize() const
2003-12-04 08:25:59 +00:00
{
if( !IsOpen() )
RageException::Throw("\"%s\" is not open.", GetPath().c_str());
if( !(m_Mode&READ) )
2003-12-04 08:25:59 +00:00
RageException::Throw("\"%s\" is not open for reading; can't GetFileSize", GetPath().c_str());
2004-02-17 21:42:06 +00:00
/* 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<RageFileObj*>(m_File)->GetFileSize();
ASSERT_M( iRet >= 0, ssprintf("%i", iRet) );
return iRet;
2003-11-27 07:46:36 +00:00
}
2003-12-03 23:50:53 +00:00
2003-12-04 08:25:59 +00:00
void RageFile::Rewind()
{
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 Rewind", GetPath().c_str());
2003-12-04 08:25:59 +00:00
m_EOF = false;
2003-12-10 05:04:37 +00:00
m_FilePos = 0;
2003-12-04 08:25:59 +00:00
m_File->Rewind();
}
2003-12-21 09:17:39 +00:00
int RageFile::Read( CString &buffer, int bytes )
2003-12-10 05:04:37 +00:00
{
2003-12-21 18:35:00 +00:00
buffer.erase( buffer.begin(), buffer.end() );
2003-12-21 09:17:39 +00:00
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;
}
2003-12-10 05:04:37 +00:00
return ret;
}
2003-12-04 08:25:59 +00:00
2003-12-12 07:47:38 +00:00
int RageFile::Write( const void *buffer, size_t bytes )
{
if( !IsOpen() )
RageException::Throw("\"%s\" is not open.", GetPath().c_str());
if( !(m_Mode&WRITE) )
2003-12-12 07:47:38 +00:00
RageException::Throw("\"%s\" is not open for writing", GetPath().c_str());
return m_File->Write( buffer, bytes );
}
2003-12-04 21:10:16 +00:00
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 )
return -1;
return ret / bytes;
}
2003-12-12 07:47:38 +00:00
int RageFile::Flush()
{
if( !m_File )
{
SetError( "Not open" );
return -1;
}
2003-12-12 07:47:38 +00:00
return m_File->Flush();
}
2003-12-04 21:10:16 +00:00
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;
}
int RageFile::Seek( int offset, int whence )
{
switch( whence )
{
case SEEK_CUR:
return SeekCur( (int) offset );
case SEEK_END:
offset += GetFileSize();
}
return Seek( (int) offset );
}
2004-05-06 00:42:06 +00:00
void FileReading::ReadBytes( RageFile &f, void *buf, int size )
{
int ret = f.Read( buf, size );
if( ret == -1 )
throw FatalError( f.GetError() );
if( ret < size )
throw UnexpectedEOF();
}
uint8_t FileReading::read_8( RageFile &f )
{
uint8_t val;
ReadBytes( f, &val, sizeof(uint8_t) );
return val;
}
uint16_t FileReading::read_u16_le( RageFile &f )
{
uint16_t val;
ReadBytes( f, &val, sizeof(uint16_t) );
return Swap16LE( val );
}
int16_t FileReading::read_16_le( RageFile &f )
{
int16_t val;
ReadBytes( f, &val, sizeof(int16_t) );
return Swap16LE( val );
}
uint32_t FileReading::read_u32_le( RageFile &f )
{
uint32_t val;
ReadBytes( f, &val, sizeof(uint32_t) );
return Swap32LE( val );
}
int32_t FileReading::read_32_le( RageFile &f )
{
int32_t val;
ReadBytes( f, &val, sizeof(int32_t) );
return Swap32LE( val );
}
2004-05-06 00:42:06 +00:00
/*
* Copyright (c) 2003-2004 Glenn Maynard, Chris Danford, Steve Checkoway
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/