RageFileBasic
This commit is contained in:
@@ -20,7 +20,7 @@ bool IniFile::ReadFile( const CString &sPath )
|
|||||||
return ReadFile( f );
|
return ReadFile( f );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IniFile::ReadFile( RageBasicFile &f )
|
bool IniFile::ReadFile( RageFileBasic &f )
|
||||||
{
|
{
|
||||||
CString keyname;
|
CString keyname;
|
||||||
while( 1 )
|
while( 1 )
|
||||||
@@ -74,7 +74,7 @@ bool IniFile::WriteFile( const CString &sPath )
|
|||||||
return IniFile::WriteFile( f );
|
return IniFile::WriteFile( f );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IniFile::WriteFile( RageBasicFile &f )
|
bool IniFile::WriteFile( RageFileBasic &f )
|
||||||
{
|
{
|
||||||
for( keymap::const_iterator k = keys.begin(); k != keys.end(); ++k )
|
for( keymap::const_iterator k = keys.begin(); k != keys.end(); ++k )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
class RageBasicFile;
|
class RageFileBasic;
|
||||||
|
|
||||||
class IniFile
|
class IniFile
|
||||||
{
|
{
|
||||||
@@ -37,9 +37,9 @@ public:
|
|||||||
const CString &GetError() const { return m_sError; }
|
const CString &GetError() const { return m_sError; }
|
||||||
|
|
||||||
bool ReadFile( const CString &sPath );
|
bool ReadFile( const CString &sPath );
|
||||||
bool ReadFile( RageBasicFile &sFile );
|
bool ReadFile( RageFileBasic &sFile );
|
||||||
bool WriteFile( const CString &sPath );
|
bool WriteFile( const CString &sPath );
|
||||||
bool WriteFile( RageBasicFile &sFile );
|
bool WriteFile( RageFileBasic &sFile );
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
int GetNumKeys() const;
|
int GetNumKeys() const;
|
||||||
|
|||||||
@@ -224,6 +224,7 @@ Helpers = SDL_SaveJPEG.cpp SDL_SaveJPEG.h \
|
|||||||
PCRE = pcre/chartables.c pcre/get.c pcre/internal.h pcre/maketables.c pcre/pcre.c pcre/pcre.h pcre/study.c
|
PCRE = pcre/chartables.c pcre/get.c pcre/internal.h pcre/maketables.c pcre/pcre.c pcre/pcre.h pcre/study.c
|
||||||
|
|
||||||
RageFile = \
|
RageFile = \
|
||||||
|
RageFileBasic.cpp RageFileBasic.h \
|
||||||
RageFile.cpp RageFile.h RageFileDriver.cpp RageFileDriver.h RageFileManager.cpp RageFileManager.h \
|
RageFile.cpp RageFile.h RageFileDriver.cpp RageFileDriver.h RageFileManager.cpp RageFileManager.h \
|
||||||
RageFileDriverDirect.cpp RageFileDriverDirect.h RageFileDriverDirectHelpers.cpp RageFileDriverDirectHelpers.h \
|
RageFileDriverDirect.cpp RageFileDriverDirect.h RageFileDriverDirectHelpers.cpp RageFileDriverDirectHelpers.h \
|
||||||
RageFileDriverMemory.cpp RageFileDriverMemory.h RageFileDriverZip.cpp RageFileDriverZip.h
|
RageFileDriverMemory.cpp RageFileDriverMemory.h RageFileDriverZip.cpp RageFileDriverZip.h
|
||||||
|
|||||||
+11
-10
@@ -1,11 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* This provides an interface to open files in RageFileManager's namespace
|
* This provides an interface to open files in RageFileManager's namespace
|
||||||
* This is just a simple RageBasicFile wrapper on top of another RageBasicFile;
|
* This is just a simple RageFileBasic wrapper on top of another RageFileBasic;
|
||||||
* when a file is open, is acts like the underlying RageBasicFile, except that
|
* when a file is open, is acts like the underlying RageFileBasic, except that
|
||||||
* a few extra sanity checks are made to check file modes.
|
* a few extra sanity checks are made to check file modes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
#include "RageFileBasic.h"
|
||||||
#include "RageFile.h"
|
#include "RageFile.h"
|
||||||
#include "RageUtil.h"
|
#include "RageUtil.h"
|
||||||
#include "RageFileDriver.h"
|
#include "RageFileDriver.h"
|
||||||
@@ -16,7 +17,7 @@ RageFile::RageFile()
|
|||||||
}
|
}
|
||||||
|
|
||||||
RageFile::RageFile( const RageFile &cpy ):
|
RageFile::RageFile( const RageFile &cpy ):
|
||||||
RageBasicFile( cpy )
|
RageFileBasic( cpy )
|
||||||
{
|
{
|
||||||
/* This will copy the file driver, including its internal file pointer. */
|
/* This will copy the file driver, including its internal file pointer. */
|
||||||
m_File = FILEMAN->CopyFileObj( cpy.m_File );
|
m_File = FILEMAN->CopyFileObj( cpy.m_File );
|
||||||
@@ -24,7 +25,7 @@ RageFile::RageFile( const RageFile &cpy ):
|
|||||||
m_Mode = cpy.m_Mode;
|
m_Mode = cpy.m_Mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
RageBasicFile *RageFile::Copy() const
|
RageFileBasic *RageFile::Copy() const
|
||||||
{
|
{
|
||||||
return new RageFile( *this );
|
return new RageFile( *this );
|
||||||
}
|
}
|
||||||
@@ -190,7 +191,7 @@ int RageFile::Seek( int offset, int whence )
|
|||||||
return m_File->Seek( offset, whence );
|
return m_File->Seek( offset, whence );
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileReading::ReadBytes( RageBasicFile &f, void *buf, int size, CString &sError )
|
void FileReading::ReadBytes( RageFileBasic &f, void *buf, int size, CString &sError )
|
||||||
{
|
{
|
||||||
if( sError.size() != 0 )
|
if( sError.size() != 0 )
|
||||||
return;
|
return;
|
||||||
@@ -202,7 +203,7 @@ void FileReading::ReadBytes( RageBasicFile &f, void *buf, int size, CString &sEr
|
|||||||
sError = "Unexpected end of file";
|
sError = "Unexpected end of file";
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t FileReading::read_8( RageBasicFile &f, CString &sError )
|
uint8_t FileReading::read_8( RageFileBasic &f, CString &sError )
|
||||||
{
|
{
|
||||||
uint8_t val;
|
uint8_t val;
|
||||||
ReadBytes( f, &val, sizeof(uint8_t), sError );
|
ReadBytes( f, &val, sizeof(uint8_t), sError );
|
||||||
@@ -212,7 +213,7 @@ uint8_t FileReading::read_8( RageBasicFile &f, CString &sError )
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t FileReading::read_u16_le( RageBasicFile &f, CString &sError )
|
uint16_t FileReading::read_u16_le( RageFileBasic &f, CString &sError )
|
||||||
{
|
{
|
||||||
uint16_t val;
|
uint16_t val;
|
||||||
ReadBytes( f, &val, sizeof(uint16_t), sError );
|
ReadBytes( f, &val, sizeof(uint16_t), sError );
|
||||||
@@ -222,7 +223,7 @@ uint16_t FileReading::read_u16_le( RageBasicFile &f, CString &sError )
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t FileReading::read_16_le( RageBasicFile &f, CString &sError )
|
int16_t FileReading::read_16_le( RageFileBasic &f, CString &sError )
|
||||||
{
|
{
|
||||||
int16_t val;
|
int16_t val;
|
||||||
ReadBytes( f, &val, sizeof(int16_t), sError );
|
ReadBytes( f, &val, sizeof(int16_t), sError );
|
||||||
@@ -232,7 +233,7 @@ int16_t FileReading::read_16_le( RageBasicFile &f, CString &sError )
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t FileReading::read_u32_le( RageBasicFile &f, CString &sError )
|
uint32_t FileReading::read_u32_le( RageFileBasic &f, CString &sError )
|
||||||
{
|
{
|
||||||
uint32_t val;
|
uint32_t val;
|
||||||
ReadBytes( f, &val, sizeof(uint32_t), sError );
|
ReadBytes( f, &val, sizeof(uint32_t), sError );
|
||||||
@@ -242,7 +243,7 @@ uint32_t FileReading::read_u32_le( RageBasicFile &f, CString &sError )
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t FileReading::read_32_le( RageBasicFile &f, CString &sError )
|
int32_t FileReading::read_32_le( RageFileBasic &f, CString &sError )
|
||||||
{
|
{
|
||||||
int32_t val;
|
int32_t val;
|
||||||
ReadBytes( f, &val, sizeof(int32_t), sError );
|
ReadBytes( f, &val, sizeof(int32_t), sError );
|
||||||
|
|||||||
+10
-60
@@ -5,61 +5,11 @@
|
|||||||
#ifndef RAGE_FILE_H
|
#ifndef RAGE_FILE_H
|
||||||
#define RAGE_FILE_H
|
#define RAGE_FILE_H
|
||||||
|
|
||||||
class RageFileObj;
|
#include "RageFileBasic.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 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;
|
|
||||||
|
|
||||||
/* This returns a descriptive path for the file, or "". */
|
|
||||||
virtual CString GetDisplayPath() const { return ""; }
|
|
||||||
|
|
||||||
virtual RageBasicFile *Copy() const = 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
|
/* This is the high-level interface, which interfaces with RageFileObj implementations
|
||||||
* and RageFileManager. */
|
* and RageFileManager. */
|
||||||
class RageFile: public RageBasicFile
|
class RageFile: public RageFileBasic
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum
|
enum
|
||||||
@@ -78,7 +28,7 @@ public:
|
|||||||
RageFile();
|
RageFile();
|
||||||
~RageFile() { Close(); }
|
~RageFile() { Close(); }
|
||||||
RageFile( const RageFile &cpy );
|
RageFile( const RageFile &cpy );
|
||||||
RageBasicFile *Copy() const;
|
RageFileBasic *Copy() const;
|
||||||
|
|
||||||
/* Use GetRealPath to get the path this file was opened with; use that if you
|
/* Use GetRealPath to get the path this file was opened with; use that if you
|
||||||
* want a path that will probably get you the same file again.
|
* want a path that will probably get you the same file again.
|
||||||
@@ -122,7 +72,7 @@ protected:
|
|||||||
void SetError( const CString &err );
|
void SetError( const CString &err );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RageBasicFile *m_File;
|
RageFileBasic *m_File;
|
||||||
CString m_Path;
|
CString m_Path;
|
||||||
CString m_sError;
|
CString m_sError;
|
||||||
int m_Mode;
|
int m_Mode;
|
||||||
@@ -133,12 +83,12 @@ namespace FileReading
|
|||||||
{
|
{
|
||||||
/* On error, these set sError to the error message. If sError is already
|
/* On error, these set sError to the error message. If sError is already
|
||||||
* non-empty, nothing happens. */
|
* non-empty, nothing happens. */
|
||||||
void ReadBytes( RageBasicFile &f, void *buf, int size, CString &sError );
|
void ReadBytes( RageFileBasic &f, void *buf, int size, CString &sError );
|
||||||
uint8_t read_8( RageBasicFile &f, CString &sError );
|
uint8_t read_8( RageFileBasic &f, CString &sError );
|
||||||
int16_t read_16_le( RageBasicFile &f, CString &sError );
|
int16_t read_16_le( RageFileBasic &f, CString &sError );
|
||||||
uint16_t read_u16_le( RageBasicFile &f, CString &sError );
|
uint16_t read_u16_le( RageFileBasic &f, CString &sError );
|
||||||
int32_t read_32_le( RageBasicFile &f, CString &sError );
|
int32_t read_32_le( RageFileBasic &f, CString &sError );
|
||||||
uint32_t read_u32_le( RageBasicFile &f, CString &sError );
|
uint32_t read_u32_le( RageFileBasic &f, CString &sError );
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -90,275 +90,6 @@ RageFileDriver *MakeFileDriver( CString Type, CString Root )
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2003-2004 Glenn Maynard
|
* Copyright (c) 2003-2004 Glenn Maynard
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class RageFileDriver
|
|||||||
public:
|
public:
|
||||||
RageFileDriver( FilenameDB *db ) { FDB = db; }
|
RageFileDriver( FilenameDB *db ) { FDB = db; }
|
||||||
virtual ~RageFileDriver();
|
virtual ~RageFileDriver();
|
||||||
virtual RageBasicFile *Open( const CString &path, int mode, int &err ) = 0;
|
virtual RageFileBasic *Open( const CString &path, int mode, int &err ) = 0;
|
||||||
virtual void GetDirListing( const CString &sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo );
|
virtual void GetDirListing( const CString &sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo );
|
||||||
virtual RageFileManager::FileType GetFileType( const CString &sPath );
|
virtual RageFileManager::FileType GetFileType( const CString &sPath );
|
||||||
virtual int GetFileSizeInBytes( const CString &sFilePath );
|
virtual int GetFileSizeInBytes( const CString &sFilePath );
|
||||||
@@ -31,60 +31,6 @@ protected:
|
|||||||
FilenameDB *FDB;
|
FilenameDB *FDB;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RageFileObj: public RageBasicFile
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RageFileObj();
|
|
||||||
virtual ~RageFileObj() { }
|
|
||||||
|
|
||||||
CString GetError() const { return m_sError; }
|
|
||||||
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 RageBasicFile *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;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This is used to register the driver, so RageFileManager can see it. */
|
/* This is used to register the driver, so RageFileManager can see it. */
|
||||||
struct FileDriverEntry
|
struct FileDriverEntry
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public:
|
|||||||
virtual int WriteInternal( const void *pBuffer, size_t iBytes );
|
virtual int WriteInternal( const void *pBuffer, size_t iBytes );
|
||||||
virtual int FlushInternal();
|
virtual int FlushInternal();
|
||||||
virtual int SeekInternal( int offset );
|
virtual int SeekInternal( int offset );
|
||||||
virtual RageBasicFile *Copy() const;
|
virtual RageFileBasic *Copy() const;
|
||||||
virtual CString GetDisplayPath() const { return path; }
|
virtual CString GetDisplayPath() const { return path; }
|
||||||
virtual int GetFileSize() const;
|
virtual int GetFileSize() const;
|
||||||
};
|
};
|
||||||
@@ -106,7 +106,7 @@ RageFileObj *MakeFileObjDirect( CString sPath, int mode, int &err )
|
|||||||
return new RageFileObjDirect( sPath, fd, mode );
|
return new RageFileObjDirect( sPath, fd, mode );
|
||||||
}
|
}
|
||||||
|
|
||||||
RageBasicFile *RageFileDriverDirect::Open( const CString &path, int mode, int &err )
|
RageFileBasic *RageFileDriverDirect::Open( const CString &path, int mode, int &err )
|
||||||
{
|
{
|
||||||
CString sPath = path;
|
CString sPath = path;
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ bool RageFileDriverDirect::Remove( const CString &path )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RageBasicFile *RageFileObjDirect::Copy() const
|
RageFileBasic *RageFileObjDirect::Copy() const
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
RageFileObj *ret = MakeFileObjDirect( path, m_iMode, err );
|
RageFileObj *ret = MakeFileObjDirect( path, m_iMode, err );
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class RageFileDriverDirect: public RageFileDriver
|
|||||||
public:
|
public:
|
||||||
RageFileDriverDirect( CString root );
|
RageFileDriverDirect( CString root );
|
||||||
|
|
||||||
RageBasicFile *Open( const CString &path, int mode, int &err );
|
RageFileBasic *Open( const CString &path, int mode, int &err );
|
||||||
bool Remove( const CString &sPath );
|
bool Remove( const CString &sPath );
|
||||||
bool Ready();
|
bool Ready();
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ int RageFileObjMem::ReadInternal( void *buffer, size_t bytes )
|
|||||||
int RageFileObjMem::WriteInternal( const void *buffer, size_t bytes )
|
int RageFileObjMem::WriteInternal( const void *buffer, size_t bytes )
|
||||||
{
|
{
|
||||||
m_pFile->m_Mutex.Lock();
|
m_pFile->m_Mutex.Lock();
|
||||||
m_pFile->m_sBuf.append( (const char *) buffer, bytes );
|
m_pFile->m_sBuf.replace( m_iFilePos, bytes, (const char *) buffer, bytes );
|
||||||
m_pFile->m_Mutex.Unlock();
|
m_pFile->m_Mutex.Unlock();
|
||||||
|
|
||||||
m_iFilePos += bytes;
|
m_iFilePos += bytes;
|
||||||
@@ -91,7 +91,7 @@ RageFileObjMem::RageFileObjMem( const RageFileObjMem &cpy ):
|
|||||||
RageFileObjMemFile::AddReference( m_pFile );
|
RageFileObjMemFile::AddReference( m_pFile );
|
||||||
}
|
}
|
||||||
|
|
||||||
RageBasicFile *RageFileObjMem::Copy() const
|
RageFileBasic *RageFileObjMem::Copy() const
|
||||||
{
|
{
|
||||||
RageFileObjMem *pRet = new RageFileObjMem( *this );
|
RageFileObjMem *pRet = new RageFileObjMem( *this );
|
||||||
return pRet;
|
return pRet;
|
||||||
@@ -117,7 +117,7 @@ RageFileDriverMem::~RageFileDriverMem()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RageBasicFile *RageFileDriverMem::Open( const CString &sPath, int mode, int &err )
|
RageFileBasic *RageFileDriverMem::Open( const CString &sPath, int mode, int &err )
|
||||||
{
|
{
|
||||||
LockMut(m_Mutex);
|
LockMut(m_Mutex);
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public:
|
|||||||
int WriteInternal( const void *buffer, size_t bytes );
|
int WriteInternal( const void *buffer, size_t bytes );
|
||||||
int SeekInternal( int offset );
|
int SeekInternal( int offset );
|
||||||
int GetFileSize() const;
|
int GetFileSize() const;
|
||||||
RageBasicFile *Copy() const;
|
RageFileBasic *Copy() const;
|
||||||
|
|
||||||
/* Retrieve the contents of this file. */
|
/* Retrieve the contents of this file. */
|
||||||
const CString &GetString() const;
|
const CString &GetString() const;
|
||||||
@@ -34,7 +34,7 @@ public:
|
|||||||
RageFileDriverMem();
|
RageFileDriverMem();
|
||||||
~RageFileDriverMem();
|
~RageFileDriverMem();
|
||||||
|
|
||||||
RageBasicFile *Open( const CString &sPath, int mode, int &err );
|
RageFileBasic *Open( const CString &sPath, int mode, int &err );
|
||||||
void FlushDirCache( const CString &sPath ) { }
|
void FlushDirCache( const CString &sPath ) { }
|
||||||
|
|
||||||
bool Remove( const CString &sPath );
|
bool Remove( const CString &sPath );
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public:
|
|||||||
int WriteInternal( const void *pBuffer, size_t iBytes ) { SetError( "Not implemented" ); return -1; }
|
int WriteInternal( const void *pBuffer, size_t iBytes ) { SetError( "Not implemented" ); return -1; }
|
||||||
int SeekInternal( int iOffset );
|
int SeekInternal( int iOffset );
|
||||||
int GetFileSize() const { return info.uncompr_size; }
|
int GetFileSize() const { return info.uncompr_size; }
|
||||||
RageBasicFile *Copy() const
|
RageFileBasic *Copy() const
|
||||||
{
|
{
|
||||||
RageException::Throw( "Loading ZIPs from deflated ZIPs is currently disabled; see RageFileObjZipDeflated" );
|
RageException::Throw( "Loading ZIPs from deflated ZIPs is currently disabled; see RageFileObjZipDeflated" );
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ public:
|
|||||||
int SeekInternal( int iOffset );
|
int SeekInternal( int iOffset );
|
||||||
int GetFileSize() const { return m_iFileSize; }
|
int GetFileSize() const { return m_iFileSize; }
|
||||||
|
|
||||||
RageBasicFile *Copy() const
|
RageFileBasic *Copy() const
|
||||||
{
|
{
|
||||||
RageFileObjZipStored *pRet = new RageFileObjZipStored( zip, m_iOffset, m_iFileSize );
|
RageFileObjZipStored *pRet = new RageFileObjZipStored( zip, m_iOffset, m_iFileSize );
|
||||||
pRet->m_iFilePos = m_iFilePos;
|
pRet->m_iFilePos = m_iFilePos;
|
||||||
@@ -383,7 +383,7 @@ RageFileDriverZip::~RageFileDriverZip()
|
|||||||
delete Files[i];
|
delete Files[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
RageBasicFile *RageFileDriverZip::Open( const CString &path, int mode, int &err )
|
RageFileBasic *RageFileDriverZip::Open( const CString &path, int mode, int &err )
|
||||||
{
|
{
|
||||||
if( mode == RageFile::WRITE )
|
if( mode == RageFile::WRITE )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public:
|
|||||||
RageFileDriverZip( CString path );
|
RageFileDriverZip( CString path );
|
||||||
virtual ~RageFileDriverZip();
|
virtual ~RageFileDriverZip();
|
||||||
|
|
||||||
RageBasicFile *Open( const CString &path, int mode, int &err );
|
RageFileBasic *Open( const CString &path, int mode, int &err );
|
||||||
void FlushDirCache( const CString &sPath );
|
void FlushDirCache( const CString &sPath );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ static RageEvent *g_Mutex;
|
|||||||
CString InitialWorkingDirectory;
|
CString InitialWorkingDirectory;
|
||||||
CString DirOfExecutable;
|
CString DirOfExecutable;
|
||||||
|
|
||||||
typedef map< const RageBasicFile *, RageFileDriver * > FileReferences;
|
typedef map< const RageFileBasic *, RageFileDriver * > FileReferences;
|
||||||
static FileReferences g_Refs;
|
static FileReferences g_Refs;
|
||||||
|
|
||||||
struct LoadedDriver
|
struct LoadedDriver
|
||||||
@@ -102,7 +102,7 @@ class RageFileDriverMountpoints: public RageFileDriver
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RageFileDriverMountpoints(): RageFileDriver( new FilenameDB ) { }
|
RageFileDriverMountpoints(): RageFileDriver( new FilenameDB ) { }
|
||||||
RageBasicFile *Open( const CString &path, int mode, int &err )
|
RageFileBasic *Open( const CString &path, int mode, int &err )
|
||||||
{
|
{
|
||||||
err = (mode == RageFile::WRITE)? ERROR_WRITING_NOT_SUPPORTED:ENOENT;
|
err = (mode == RageFile::WRITE)? ERROR_WRITING_NOT_SUPPORTED:ENOENT;
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -593,11 +593,11 @@ static bool SortBySecond( const pair<int,int> &a, const pair<int,int> &b )
|
|||||||
return a.second < b.second;
|
return a.second < b.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddReference( const RageBasicFile *obj, RageFileDriver *driver )
|
void AddReference( const RageFileBasic *obj, RageFileDriver *driver )
|
||||||
{
|
{
|
||||||
LockMut( *g_Mutex );
|
LockMut( *g_Mutex );
|
||||||
|
|
||||||
pair< const RageBasicFile *, RageFileDriver * > ref;
|
pair< const RageFileBasic *, RageFileDriver * > ref;
|
||||||
ref.first = obj;
|
ref.first = obj;
|
||||||
ref.second = driver;
|
ref.second = driver;
|
||||||
|
|
||||||
@@ -607,7 +607,7 @@ void AddReference( const RageBasicFile *obj, RageFileDriver *driver )
|
|||||||
ASSERT_M( ret.second, ssprintf( "RemoveReference: Duplicate reference (%s)", obj->GetDisplayPath().c_str() ) );
|
ASSERT_M( ret.second, ssprintf( "RemoveReference: Duplicate reference (%s)", obj->GetDisplayPath().c_str() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveReference( const RageBasicFile *obj )
|
void RemoveReference( const RageFileBasic *obj )
|
||||||
{
|
{
|
||||||
LockMut( *g_Mutex );
|
LockMut( *g_Mutex );
|
||||||
|
|
||||||
@@ -638,7 +638,7 @@ static bool PathUsesSlowFlush( const CString &sPath )
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Used only by RageFile: */
|
/* Used only by RageFile: */
|
||||||
RageBasicFile *RageFileManager::Open( CString sPath, int mode, int &err )
|
RageFileBasic *RageFileManager::Open( CString sPath, int mode, int &err )
|
||||||
{
|
{
|
||||||
err = ENOENT;
|
err = ENOENT;
|
||||||
|
|
||||||
@@ -662,7 +662,7 @@ RageBasicFile *RageFileManager::Open( CString sPath, int mode, int &err )
|
|||||||
if( path.size() == 0 )
|
if( path.size() == 0 )
|
||||||
continue;
|
continue;
|
||||||
int error;
|
int error;
|
||||||
RageBasicFile *ret = ld.driver->Open( path, mode, error );
|
RageFileBasic *ret = ld.driver->Open( path, mode, error );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
{
|
||||||
AddReference( ret, ld.driver );
|
AddReference( ret, ld.driver );
|
||||||
@@ -680,15 +680,15 @@ RageBasicFile *RageFileManager::Open( CString sPath, int mode, int &err )
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy a RageBasicFile for a new RageFile. */
|
/* Copy a RageFileBasic for a new RageFile. */
|
||||||
RageBasicFile *RageFileManager::CopyFileObj( const RageBasicFile *cpy )
|
RageFileBasic *RageFileManager::CopyFileObj( const RageFileBasic *cpy )
|
||||||
{
|
{
|
||||||
LockMut( *g_Mutex );
|
LockMut( *g_Mutex );
|
||||||
|
|
||||||
FileReferences::const_iterator it = g_Refs.find( cpy );
|
FileReferences::const_iterator it = g_Refs.find( cpy );
|
||||||
ASSERT_M( it != g_Refs.end(), ssprintf( "CopyFileObj: Missing reference (%s)", cpy->GetDisplayPath().c_str() ) );
|
ASSERT_M( it != g_Refs.end(), ssprintf( "CopyFileObj: Missing reference (%s)", cpy->GetDisplayPath().c_str() ) );
|
||||||
|
|
||||||
RageBasicFile *ret = cpy->Copy();
|
RageFileBasic *ret = cpy->Copy();
|
||||||
|
|
||||||
/* It's from the same driver as the original. */
|
/* It's from the same driver as the original. */
|
||||||
AddReference( ret, it->second );
|
AddReference( ret, it->second );
|
||||||
@@ -696,7 +696,7 @@ RageBasicFile *RageFileManager::CopyFileObj( const RageBasicFile *cpy )
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
RageBasicFile *RageFileManager::OpenForWriting( CString sPath, int mode, int &err )
|
RageFileBasic *RageFileManager::OpenForWriting( CString sPath, int mode, int &err )
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The value for a driver to open a file is the number of directories and/or files
|
* The value for a driver to open a file is the number of directories and/or files
|
||||||
@@ -748,7 +748,7 @@ RageBasicFile *RageFileManager::OpenForWriting( CString sPath, int mode, int &er
|
|||||||
ASSERT( path.size() );
|
ASSERT( path.size() );
|
||||||
|
|
||||||
int error;
|
int error;
|
||||||
RageBasicFile *ret = ld.driver->Open( path, mode, error );
|
RageFileBasic *ret = ld.driver->Open( path, mode, error );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
{
|
||||||
AddReference( ret, ld.driver );
|
AddReference( ret, ld.driver );
|
||||||
@@ -769,7 +769,7 @@ RageBasicFile *RageFileManager::OpenForWriting( CString sPath, int mode, int &er
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RageFileManager::Close( RageBasicFile *obj )
|
void RageFileManager::Close( RageFileBasic *obj )
|
||||||
{
|
{
|
||||||
if( obj == NULL )
|
if( obj == NULL )
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -44,12 +44,12 @@ public:
|
|||||||
void FlushDirCache( CString sPath );
|
void FlushDirCache( CString sPath );
|
||||||
|
|
||||||
/* Used only by RageFile: */
|
/* Used only by RageFile: */
|
||||||
RageBasicFile *Open( CString sPath, int mode, int &err );
|
RageFileBasic *Open( CString sPath, int mode, int &err );
|
||||||
void Close( RageBasicFile *obj );
|
void Close( RageFileBasic *obj );
|
||||||
RageBasicFile *CopyFileObj( const RageBasicFile *cpy );
|
RageFileBasic *CopyFileObj( const RageFileBasic *cpy );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RageBasicFile *OpenForWriting( CString sPath, int mode, int &err );
|
RageFileBasic *OpenForWriting( CString sPath, int mode, int &err );
|
||||||
};
|
};
|
||||||
|
|
||||||
extern RageFileManager *FILEMAN;
|
extern RageFileManager *FILEMAN;
|
||||||
|
|||||||
Reference in New Issue
Block a user