Finished importing the good bits from LW's library
This commit is contained in:
parent
b9d77922ed
commit
4ab3dc1b29
82 changed files with 177 additions and 24084 deletions
src
Console-Unix.hhConsole.ccConsoleLogWriter.ccCwdFileLogger.ccDynLib.ccGameLoop.ccLW.ccLog.ccMessages.ccModInterface.ccMods.ccSRDBinary.ccSRDData.ccSRDDefinitions.ccSRDIO.ccSRDPPCommands.ccSRDParser.ccSRDParserConfig.ccSRDPreproc.ccSRDText.ccTemplateInstantiation.ccTextFileLogger.ccVFS.ccVFSDrivers.ccfiles.mk
|
@ -1,667 +0,0 @@
|
|||
/******************************************************************************/
|
||||
/* CONSOLE SUPPORT - UNIX IMPLEMENTATION **************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/Console.hh>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <pty.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
// List of unsupported terminals
|
||||
char const* const BadTerminals_[] = {
|
||||
"dumb" , "cons25" , "emacs" ,
|
||||
nullptr
|
||||
};
|
||||
|
||||
// Extended input sequences
|
||||
char const* const InputSequences_[] = {
|
||||
"A" , // Up
|
||||
"B" , // Down
|
||||
"C" , // Right
|
||||
"D" , // Left
|
||||
"H" , // Home
|
||||
"F" , // End
|
||||
"3~" , // Delete
|
||||
"1;5C" , // C-Right
|
||||
"1;5D" , // C-Left
|
||||
"15~" , // F5
|
||||
"17~" , // F6
|
||||
"18~" , // F7
|
||||
"19~" , // F8
|
||||
"20~" , // F9
|
||||
"21~" , // F10
|
||||
"23~" , // F11
|
||||
"24~" , // F12
|
||||
"1;2P" , // S-F1
|
||||
"1;2Q" , // S-F2
|
||||
"1;2R" , // S-F3
|
||||
"1;2S" , // S-F4
|
||||
"15;2~" , // S-F5
|
||||
"17;2~" , // S-F6
|
||||
"18;2~" , // S-F7
|
||||
"19;2~" , // S-F8
|
||||
"20;2~" , // S-F9
|
||||
"21;2~" , // S-F10
|
||||
"23;2~" , // S-F11
|
||||
"24;2~" , // S-F12
|
||||
"1;5P" , // C-F1
|
||||
"1;5Q" , // C-F2
|
||||
"1;5R" , // C-F3
|
||||
"1;5S" , // C-F4
|
||||
"15;5~" , // C-F5
|
||||
"17;5~" , // C-F6
|
||||
"18;5~" , // C-F7
|
||||
"19;5~" , // C-F8
|
||||
"20;5~" , // C-F9
|
||||
"21;5~" , // C-F10
|
||||
"23;5~" , // C-F11
|
||||
"24;5~" , // C-F12
|
||||
"1;6P" , // C-S-F1
|
||||
"1;6Q" , // C-S-F2
|
||||
"1;6R" , // C-S-F3
|
||||
"1;6S" , // C-S-F4
|
||||
"15;6~" , // C-S-F5
|
||||
"17;6~" , // C-S-F6
|
||||
"18;6~" , // C-S-F7
|
||||
"19;6~" , // C-S-F8
|
||||
"20;6~" , // C-S-F9
|
||||
"21;6~" , // C-S-F10
|
||||
"23;6~" , // C-S-F11
|
||||
"24;6~" , // C-S-F12
|
||||
nullptr
|
||||
};
|
||||
const lw::E_ConsoleKey InputKeys_[] = {
|
||||
lw::E_ConsoleKey::UP ,
|
||||
lw::E_ConsoleKey::DOWN ,
|
||||
lw::E_ConsoleKey::RIGHT ,
|
||||
lw::E_ConsoleKey::LEFT ,
|
||||
lw::E_ConsoleKey::HOME ,
|
||||
lw::E_ConsoleKey::END ,
|
||||
lw::E_ConsoleKey::DELETE ,
|
||||
lw::E_ConsoleKey::WNEXT ,
|
||||
lw::E_ConsoleKey::WPREV ,
|
||||
lw::E_ConsoleKey::F5 ,
|
||||
lw::E_ConsoleKey::F6 ,
|
||||
lw::E_ConsoleKey::F7 ,
|
||||
lw::E_ConsoleKey::F8 ,
|
||||
lw::E_ConsoleKey::F9 ,
|
||||
lw::E_ConsoleKey::F10 ,
|
||||
lw::E_ConsoleKey::F11 ,
|
||||
lw::E_ConsoleKey::F12 ,
|
||||
lw::E_ConsoleKey::S_F1 ,
|
||||
lw::E_ConsoleKey::S_F2 ,
|
||||
lw::E_ConsoleKey::S_F3 ,
|
||||
lw::E_ConsoleKey::S_F4 ,
|
||||
lw::E_ConsoleKey::S_F5 ,
|
||||
lw::E_ConsoleKey::S_F6 ,
|
||||
lw::E_ConsoleKey::S_F7 ,
|
||||
lw::E_ConsoleKey::S_F8 ,
|
||||
lw::E_ConsoleKey::S_F9 ,
|
||||
lw::E_ConsoleKey::S_F10 ,
|
||||
lw::E_ConsoleKey::S_F11 ,
|
||||
lw::E_ConsoleKey::S_F12 ,
|
||||
lw::E_ConsoleKey::C_F1 ,
|
||||
lw::E_ConsoleKey::C_F2 ,
|
||||
lw::E_ConsoleKey::C_F3 ,
|
||||
lw::E_ConsoleKey::C_F4 ,
|
||||
lw::E_ConsoleKey::C_F5 ,
|
||||
lw::E_ConsoleKey::C_F6 ,
|
||||
lw::E_ConsoleKey::C_F7 ,
|
||||
lw::E_ConsoleKey::C_F8 ,
|
||||
lw::E_ConsoleKey::C_F9 ,
|
||||
lw::E_ConsoleKey::C_F10 ,
|
||||
lw::E_ConsoleKey::C_F11 ,
|
||||
lw::E_ConsoleKey::C_F12 ,
|
||||
lw::E_ConsoleKey::CS_F1 ,
|
||||
lw::E_ConsoleKey::CS_F2 ,
|
||||
lw::E_ConsoleKey::CS_F3 ,
|
||||
lw::E_ConsoleKey::CS_F4 ,
|
||||
lw::E_ConsoleKey::CS_F5 ,
|
||||
lw::E_ConsoleKey::CS_F6 ,
|
||||
lw::E_ConsoleKey::CS_F7 ,
|
||||
lw::E_ConsoleKey::CS_F8 ,
|
||||
lw::E_ConsoleKey::CS_F9 ,
|
||||
lw::E_ConsoleKey::CS_F10 ,
|
||||
lw::E_ConsoleKey::CS_F11 ,
|
||||
lw::E_ConsoleKey::CS_F12 ,
|
||||
};
|
||||
|
||||
// Various control codes
|
||||
const char CCGetTermSizeStart_[] = "\033[s\033[99999C\033[99999A";
|
||||
const char CCGetTermSizeEnd_[] = "\033[u";
|
||||
const char CCGetCursorPos_[] = "\033[6n";
|
||||
const char CCClearScreen_[] = "\033[H\033[2J";
|
||||
const char CCNextLine_[] = "\n\033[99999D\033[K";
|
||||
const char CCClearLine_[] = "\033[99999D\033[K";
|
||||
const char CCClearLineUp_[] = "\033[99999D\033[K\033[A";
|
||||
|
||||
// Are we running in an unsupported terminal?
|
||||
inline bool IsBadTerminal_( )
|
||||
{
|
||||
char const* const t( getenv( "TERM" ) );
|
||||
if ( t == nullptr ) {
|
||||
return true;
|
||||
}
|
||||
for ( auto i = 0 ; BadTerminals_[ i ] ; i ++ ) {
|
||||
if ( !strcasecmp( t , BadTerminals_[ i ] ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
class T_ConsoleImpl_
|
||||
{
|
||||
private:
|
||||
bool ok_;
|
||||
termios initialTerm_;
|
||||
lw::E_ConsoleInteractionMode mode_;
|
||||
bool failed_;
|
||||
bool interrupted_;
|
||||
|
||||
int sizePolling_;
|
||||
uint32_t rows_ , cols_;
|
||||
|
||||
lw::T_StringBuilder styleSeq_;
|
||||
uint8_t keyBuf_[ 16 ];
|
||||
uint32_t kbPos_;
|
||||
|
||||
public:
|
||||
T_ConsoleImpl_( );
|
||||
~T_ConsoleImpl_( );
|
||||
|
||||
bool initConsole( );
|
||||
void shutdownConsole( );
|
||||
bool failed( ) const;
|
||||
|
||||
bool resized( uint32_t& rows , uint32_t& cols ) const;
|
||||
bool getTerminalSize( uint32_t& rows , uint32_t& cols );
|
||||
bool getCursorPosition( uint32_t& column , uint32_t& row );
|
||||
|
||||
bool readKey( lw::E_ConsoleKey& key , uint32_t& character );
|
||||
|
||||
bool clearScreen( );
|
||||
void setTextStyle( uint8_t style , lw::T_TextColor color );
|
||||
void printCharacter( lw::T_Character c );
|
||||
void nextLine( );
|
||||
void clearLine( );
|
||||
void clearLines( uint32_t count );
|
||||
void moveCursor( int32_t x , int32_t y );
|
||||
|
||||
private:
|
||||
bool initTTY( );
|
||||
bool checkInput( lw::E_ConsoleKey& key , uint32_t& character );
|
||||
|
||||
bool getTerminalSize( );
|
||||
bool writeSequence( char const* sequence );
|
||||
bool writeSequence( char const* sequence , uint32_t size );
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_ConsoleImpl_::T_ConsoleImpl_( )
|
||||
{
|
||||
// Buffer size:
|
||||
// * "ESC[0m" always => 4
|
||||
// * 2 per enabled style => 6
|
||||
// * color: "38;2;" + 3 unsigned 8-bit numbers + 2 ";" separators => 16
|
||||
// That would be 26, but let's go for 32 instead
|
||||
styleSeq_.ensureCapacity( 32 );
|
||||
}
|
||||
|
||||
T_ConsoleImpl_::~T_ConsoleImpl_( )
|
||||
{
|
||||
shutdownConsole( );
|
||||
}
|
||||
|
||||
bool T_ConsoleImpl_::initConsole( )
|
||||
{
|
||||
failed_ = false;
|
||||
kbPos_ = 0;
|
||||
ok_ = isatty( STDIN_FILENO ) && !IsBadTerminal_( ) && initTTY( );
|
||||
if ( ok_ && getTerminalSize( ) ) {
|
||||
sizePolling_ = 0;
|
||||
}
|
||||
return ok_ && !failed_;
|
||||
}
|
||||
|
||||
void T_ConsoleImpl_::shutdownConsole( )
|
||||
{
|
||||
if ( ok_ ) {
|
||||
setTextStyle( 0 , lw::E_TextColor::WHITE );
|
||||
tcsetattr( STDIN_FILENO , TCSAFLUSH , &initialTerm_ );
|
||||
}
|
||||
}
|
||||
|
||||
inline bool T_ConsoleImpl_::failed( ) const
|
||||
{
|
||||
return failed_;
|
||||
}
|
||||
|
||||
bool T_ConsoleImpl_::resized( uint32_t& rows , uint32_t& cols ) const
|
||||
{
|
||||
if ( cols_ != cols || rows_ != rows ) {
|
||||
cols = cols_;
|
||||
rows = rows_;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool T_ConsoleImpl_::getTerminalSize( uint32_t& rows , uint32_t& cols )
|
||||
{
|
||||
if ( !getTerminalSize( ) ) {
|
||||
return false;
|
||||
}
|
||||
rows = rows_;
|
||||
cols = cols_;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
bool T_ConsoleImpl_::readKey( lw::E_ConsoleKey& key , uint32_t& character )
|
||||
{
|
||||
if ( failed_ ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
assert( kbPos_ < sizeof( keyBuf_ ) );
|
||||
const auto n( read( STDIN_FILENO , &keyBuf_[ kbPos_ ] , 1 ) );
|
||||
if ( n != 1 ) {
|
||||
failed_ = failed_ || ( n == -1 );
|
||||
if ( n == 0 && kbPos_ == 0 ) {
|
||||
sizePolling_ = ( sizePolling_ + 1 ) % 20;
|
||||
if ( sizePolling_ == 0 ) {
|
||||
getTerminalSize( );
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
kbPos_ ++;
|
||||
} while ( !checkInput( key , character ) );
|
||||
kbPos_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool T_ConsoleImpl_::checkInput( lw::E_ConsoleKey& key , uint32_t& character )
|
||||
{
|
||||
assert( kbPos_ > 0 );
|
||||
|
||||
const uint8_t first( keyBuf_[ 0 ] );
|
||||
if ( kbPos_ == 1 ) {
|
||||
// Escape or UTF-8 input
|
||||
if ( first == 0x1b || first > 0x7f ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ( first ) {
|
||||
case 0x01: // Ctrl+A
|
||||
key = lw::E_ConsoleKey::HOME;
|
||||
return true;
|
||||
|
||||
case 0x02: // Ctrl+B
|
||||
key = lw::E_ConsoleKey::LEFT;
|
||||
return true;
|
||||
|
||||
case 0x03: // Ctrl+C
|
||||
key = lw::E_ConsoleKey::INTERRUPT;
|
||||
return true;
|
||||
|
||||
case 0x04: // Ctrl+D
|
||||
key = lw::E_ConsoleKey::EXIT;
|
||||
return true;
|
||||
|
||||
case 0x05: // Ctrl+E
|
||||
key = lw::E_ConsoleKey::END;
|
||||
return true;
|
||||
|
||||
case 0x06: // Ctrl+F
|
||||
key = lw::E_ConsoleKey::RIGHT;
|
||||
return true;
|
||||
|
||||
case 0x08: // Ctrl+H
|
||||
case 0x7f:
|
||||
key = lw::E_ConsoleKey::BACKSPACE;
|
||||
return true;
|
||||
|
||||
case 0x09: // Ctrl+I/Tab
|
||||
key = lw::E_ConsoleKey::CHARACTER;
|
||||
character = ' ';
|
||||
return true;
|
||||
|
||||
case 0x0b: // Ctrl+K
|
||||
key = lw::E_ConsoleKey::KILL_REST;
|
||||
return true;
|
||||
|
||||
case 0x0c: // Ctrl+L
|
||||
key = lw::E_ConsoleKey::CLEAR;
|
||||
return true;
|
||||
|
||||
case 0x0d: // Ctrl+M/Enter
|
||||
key = lw::E_ConsoleKey::ENTER;
|
||||
return true;
|
||||
|
||||
case 0x0e: // Ctrl+N
|
||||
key = lw::E_ConsoleKey::DOWN;
|
||||
return true;
|
||||
|
||||
case 0x10: // Ctrl+P
|
||||
key = lw::E_ConsoleKey::UP;
|
||||
return true;
|
||||
|
||||
case 0x15: // Ctrl+U
|
||||
key = lw::E_ConsoleKey::KILL_LINE;
|
||||
return true;
|
||||
|
||||
case 0x17: // Ctrl+W
|
||||
key = lw::E_ConsoleKey::KILL_WORD;
|
||||
return true;
|
||||
|
||||
case 0x19: // Ctrl+Y
|
||||
key = lw::E_ConsoleKey::PASTE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Skip other control characters
|
||||
if ( first < 32 ) {
|
||||
kbPos_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
key = lw::E_ConsoleKey::CHARACTER;
|
||||
character = first;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for UTF-8 input
|
||||
if ( first != 0x1b ) {
|
||||
uint32_t l( 0 );
|
||||
if ( !lw::UTF8BufferInfo( (char const*) keyBuf_ , kbPos_ , l ) ) {
|
||||
if ( kbPos_ > 4 ) {
|
||||
kbPos_ = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if ( l != 1 ) {
|
||||
kbPos_ = 0;
|
||||
return false;
|
||||
} else {
|
||||
key = lw::E_ConsoleKey::CHARACTER;
|
||||
character = lw::UTF8GetCodepoint( (char const*) keyBuf_ );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Escape sequences
|
||||
const uint8_t second( keyBuf_[ 1 ] );
|
||||
if ( kbPos_ == 2 ) {
|
||||
switch ( second ) {
|
||||
case 0x7f:
|
||||
key = lw::E_ConsoleKey::KILL_WORD;
|
||||
return true;
|
||||
|
||||
case 'f':
|
||||
case 'F':
|
||||
key = lw::E_ConsoleKey::WNEXT;
|
||||
return true;
|
||||
|
||||
case 'b':
|
||||
case 'B':
|
||||
key = lw::E_ConsoleKey::WPREV;
|
||||
return true;
|
||||
|
||||
default:
|
||||
kbPos_ = 0;
|
||||
case 'O':
|
||||
case '[':
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ESC-O-x sequences
|
||||
if ( second == 'O' ) {
|
||||
switch ( keyBuf_[ 2 ] ) {
|
||||
case 'F':
|
||||
key = lw::E_ConsoleKey::END;
|
||||
return true;
|
||||
case 'H':
|
||||
key = lw::E_ConsoleKey::HOME;
|
||||
return true;
|
||||
case 'P':
|
||||
key = lw::E_ConsoleKey::F1;
|
||||
return true;
|
||||
case 'Q':
|
||||
key = lw::E_ConsoleKey::F2;
|
||||
return true;
|
||||
case 'R':
|
||||
key = lw::E_ConsoleKey::F3;
|
||||
return true;
|
||||
case 'S':
|
||||
key = lw::E_ConsoleKey::F4;
|
||||
return true;
|
||||
}
|
||||
kbPos_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extended sequences - ESC-[, ends with ~ or uppercase letter
|
||||
const uint8_t last( keyBuf_[ kbPos_ - 1 ] );
|
||||
if ( last != '~' && ( last < 'A' || last > 'Z' ) ) {
|
||||
if ( kbPos_ == sizeof( keyBuf_ ) - 1 ) {
|
||||
kbPos_ = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the list
|
||||
uint32_t idx( 0 );
|
||||
keyBuf_[ kbPos_ ] = 0;
|
||||
while ( InputSequences_[ idx ] != nullptr ) {
|
||||
if ( !strcmp( (char const*) keyBuf_ + 2 , InputSequences_[ idx ] ) ) {
|
||||
key = InputKeys_[ idx ];
|
||||
return true;
|
||||
}
|
||||
idx ++;
|
||||
}
|
||||
kbPos_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
bool T_ConsoleImpl_::clearScreen( )
|
||||
{
|
||||
return writeSequence( CCClearScreen_ , sizeof( CCClearScreen_ ) - 1 );
|
||||
}
|
||||
|
||||
void T_ConsoleImpl_::setTextStyle( uint8_t style , lw::T_TextColor color )
|
||||
{
|
||||
styleSeq_.clear( );
|
||||
styleSeq_ << "\033[0";
|
||||
if ( ( style & uint8_t( lw::E_TextStyle::BOLD ) ) != 0 ) {
|
||||
styleSeq_ << ";1";
|
||||
}
|
||||
if ( ( style & uint8_t( lw::E_TextStyle::ITALIC ) ) != 0 ) {
|
||||
styleSeq_ << ";3";
|
||||
}
|
||||
if ( ( style & uint8_t( lw::E_TextStyle::UNDERLINE ) ) != 0 ) {
|
||||
styleSeq_ << ";4";
|
||||
}
|
||||
if ( color.type == lw::E_TextColor::CUSTOM ) {
|
||||
styleSeq_ << ";38;2;" << color.r
|
||||
<< ';' << color.g
|
||||
<< ';' << color.b;
|
||||
} else {
|
||||
styleSeq_ << ';' << ( 30 + uint8_t( color.type ) );
|
||||
}
|
||||
styleSeq_ << 'm';
|
||||
writeSequence( styleSeq_.data( ) , styleSeq_.size( ) );
|
||||
}
|
||||
|
||||
void T_ConsoleImpl_::printCharacter( lw::T_Character c )
|
||||
{
|
||||
char buf[ 8 ];
|
||||
uint32_t l( UTF8PutCodepoint( buf , 7 , c ) );
|
||||
if ( l == 0 ) {
|
||||
return;
|
||||
}
|
||||
writeSequence( buf , l );
|
||||
}
|
||||
|
||||
void T_ConsoleImpl_::nextLine( )
|
||||
{
|
||||
writeSequence( CCNextLine_ , sizeof( CCNextLine_ ) - 1 );
|
||||
}
|
||||
|
||||
void T_ConsoleImpl_::clearLine( )
|
||||
{
|
||||
writeSequence( CCClearLine_ , sizeof( CCClearLine_ ) - 1 );
|
||||
}
|
||||
|
||||
void T_ConsoleImpl_::clearLines( uint32_t count )
|
||||
{
|
||||
assert( count > 0 );
|
||||
while ( --count ) {
|
||||
writeSequence( CCClearLineUp_ , sizeof( CCClearLineUp_ ) - 1 );
|
||||
}
|
||||
clearLine( );
|
||||
}
|
||||
|
||||
void T_ConsoleImpl_::moveCursor( int32_t x , int32_t y )
|
||||
{
|
||||
styleSeq_.clear( );
|
||||
if ( x ) {
|
||||
styleSeq_ << "\033["
|
||||
<< ( x > 0 ? x : -x )
|
||||
<< ( x > 0 ? 'C' : 'D' );
|
||||
}
|
||||
if ( y ) {
|
||||
styleSeq_ << "\033["
|
||||
<< ( y > 0 ? y : -y )
|
||||
<< ( y > 0 ? 'B' : 'A' );
|
||||
}
|
||||
if ( styleSeq_.size( ) ) {
|
||||
writeSequence( styleSeq_.data( ) , styleSeq_.size( ) );
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
bool T_ConsoleImpl_::initTTY( )
|
||||
{
|
||||
if ( tcgetattr( STDIN_FILENO , &initialTerm_ ) == -1 ) {
|
||||
failed_ = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
termios rawTerm( initialTerm_ );
|
||||
rawTerm.c_iflag &= ~( BRKINT | ICRNL | INPCK | ISTRIP | IXON );
|
||||
rawTerm.c_oflag &= ~OPOST;
|
||||
rawTerm.c_cflag |= CS8;
|
||||
rawTerm.c_lflag &= ~( ECHO | ICANON | IEXTEN | ISIG );
|
||||
rawTerm.c_cc[ VMIN ] = 0;
|
||||
rawTerm.c_cc[ VTIME ] = 0;
|
||||
|
||||
if ( tcsetattr( STDIN_FILENO , TCSAFLUSH , &rawTerm ) >= 0 ) {
|
||||
return getTerminalSize( );
|
||||
} else {
|
||||
failed_ = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
bool T_ConsoleImpl_::getTerminalSize( )
|
||||
{
|
||||
// Try obtaining the size using ioctl
|
||||
winsize ws;
|
||||
if ( ioctl( STDIN_FILENO , TIOCGWINSZ , &ws ) != -1 && ws.ws_col != 0 ) {
|
||||
rows_ = ws.ws_row;
|
||||
cols_ = ws.ws_col;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, we need to move the cursor to the bottom/right corner, then
|
||||
// get its position.
|
||||
return writeSequence( CCGetTermSizeStart_ , sizeof( CCGetTermSizeStart_ ) - 1 )
|
||||
&& getCursorPosition( cols_ , rows_ )
|
||||
&& writeSequence( CCGetTermSizeEnd_ , sizeof( CCGetTermSizeEnd_ ) - 1 );
|
||||
}
|
||||
|
||||
bool T_ConsoleImpl_::getCursorPosition( uint32_t& column , uint32_t& row )
|
||||
{
|
||||
if ( failed_ || tcflush( STDIN_FILENO , TCIFLUSH ) != 0 ) {
|
||||
failed_ = true;
|
||||
return false;
|
||||
}
|
||||
if ( !writeSequence( CCGetCursorPos_ , sizeof( CCGetCursorPos_ ) - 1 ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[ 32 ];
|
||||
size_t i( 0 );
|
||||
while ( i < sizeof( buf ) ) {
|
||||
if ( read( STDIN_FILENO , buf + i , 1 ) != 1 ) {
|
||||
i = sizeof( buf );
|
||||
break;
|
||||
}
|
||||
if ( buf[ i ] == 'R' ) {
|
||||
break;
|
||||
}
|
||||
i ++;
|
||||
}
|
||||
|
||||
if ( i == sizeof( buf ) || i < 6 || buf[ 0 ] != 27 || buf[ 1 ] != '[' ) {
|
||||
failed_ = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
buf[ i ] = 0;
|
||||
if ( sscanf( buf + 2 , "%d;%d" , &row , &column ) != 2 ) {
|
||||
failed_ = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool T_ConsoleImpl_::writeSequence( char const* sequence )
|
||||
{
|
||||
if ( failed_ ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int n( strlen( sequence ) );
|
||||
if ( write( STDOUT_FILENO , sequence , n ) != n ) {
|
||||
failed_ = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool T_ConsoleImpl_::writeSequence( char const* sequence , uint32_t size )
|
||||
{
|
||||
if ( failed_ ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( write( STDOUT_FILENO , sequence , size ) != size ) {
|
||||
failed_ = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
1346
src/Console.cc
1346
src/Console.cc
File diff suppressed because it is too large
Load diff
|
@ -1,83 +0,0 @@
|
|||
#include <lw/lib/BuiltinLoggers.hh>
|
||||
using namespace lw;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
char const* const V_Name_ = "console";
|
||||
inline T_String Name_( ) { return T_String::Pooled( V_Name_ ); }
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*= T_ConsoleLogWriterFactory ================================================*/
|
||||
|
||||
T_ConsoleLogWriterFactory::T_ConsoleLogWriterFactory( T_Console& console )
|
||||
: A_LogWriterFactory( Name_( ) ) , console_( console )
|
||||
{ }
|
||||
|
||||
RP_LogWriterConfiguration T_ConsoleLogWriterFactory::createConfiguration( T_String const& name ) const
|
||||
{
|
||||
RP_LogWriterConfiguration p( new T_LogWriterConfiguration( Name_( ) ) );
|
||||
p->setName( name );
|
||||
return p;
|
||||
}
|
||||
|
||||
OP_LogWriter T_ConsoleLogWriterFactory::createLogWriter( OP_LogWriterConfiguration&& configuration ) const
|
||||
{
|
||||
T_ConsoleLogWriter* p( new T_ConsoleLogWriter( std::move( configuration ) , console_ ) );
|
||||
return OwnRawPointer( p );
|
||||
}
|
||||
|
||||
|
||||
/*= T_ConsoleLogWriter =======================================================*/
|
||||
|
||||
T_ConsoleLogWriter::T_ConsoleLogWriter( OP_LogWriterConfiguration&& configuration , T_Console& console )
|
||||
: A_LogWriter( std::move( configuration ) ) , console_( console )
|
||||
{ }
|
||||
|
||||
void T_ConsoleLogWriter::log( T_LogTimestamp const& timestamp ,
|
||||
E_LogLevel level , T_LogPath const& path ,
|
||||
T_LogStringData const& data , uint32_t size )
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
char timeBuffer[ 128 ];
|
||||
std::time_t tst( T_LogTimestamp::clock::to_time_t( timestamp ) );
|
||||
std::strftime( timeBuffer , 128 , "%Y-%m-%d %H:%M:%S" , std::gmtime( &tst ) );
|
||||
const auto ms( ( duration_cast< milliseconds >( timestamp - T_LogTimestamp( ) ) ).count( ) % 1000 );
|
||||
|
||||
T_TextBuilder sb;
|
||||
|
||||
sb << timeBuffer << '.';
|
||||
if ( ms < 100 ) {
|
||||
sb << '0';
|
||||
if ( ms < 10 ) {
|
||||
sb << '0';
|
||||
}
|
||||
}
|
||||
sb << ms << ' ' << path.toString( ) << " - "
|
||||
<< E_TextStyle::UNDERLINE;
|
||||
switch ( level ) {
|
||||
case E_LogLevel::TRACE:
|
||||
case E_LogLevel::DEBUG:
|
||||
case E_LogLevel::INFO:
|
||||
sb << E_TextColor::CYAN;
|
||||
break;
|
||||
case E_LogLevel::NOTICE:
|
||||
break;
|
||||
case E_LogLevel::WARNING:
|
||||
sb << E_TextColor::YELLOW;
|
||||
break;
|
||||
case E_LogLevel::CRITICAL:
|
||||
sb << E_TextStyle::BOLD;
|
||||
case E_LogLevel::ERROR:
|
||||
sb << E_TextColor::RED;
|
||||
break;
|
||||
}
|
||||
sb << level;
|
||||
sb.reset( );
|
||||
sb << " - " << T_String( &( (*data) [ 0 ] ) , size );
|
||||
|
||||
console_.putLine( std::move( sb ) );
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
/******************************************************************************/
|
||||
/* LOGGING SYSTEM - BUILT-IN LOGGERS - NON-VFS FILE LOG WRITER ****************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/BuiltinLoggers.hh>
|
||||
#include <lw/lib/SRDParser.hh>
|
||||
using namespace lw;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
char const* const V_Name_ = "preinit-file";
|
||||
inline T_String Name_( ) { return T_String::Pooled( V_Name_ ); }
|
||||
|
||||
bool CFLCPath_( T_SRDParserData const& data )
|
||||
{
|
||||
auto const& ptok( (*data.input)[ 1 ] );
|
||||
auto lconf( data.targetData->value< RP_LogWriterConfiguration >( ) );
|
||||
( dynamic_cast< T_CWDFileLogWriterCfg* >( lconf ) )->setPath( ptok.stringValue( ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CFLCAppend_( T_SRDParserData const& data )
|
||||
{
|
||||
auto lconf( data.targetData->value< RP_LogWriterConfiguration >( ) );
|
||||
( dynamic_cast< T_CWDFileLogWriterCfg* >( lconf ) )->setAppend( true );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CFLCTruncate_( T_SRDParserData const& data )
|
||||
{
|
||||
auto lconf( data.targetData->value< RP_LogWriterConfiguration >( ) );
|
||||
( dynamic_cast< T_CWDFileLogWriterCfg* >( lconf ) )->setAppend( false );
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*= T_CWDFileLogWriterFactory ==================================================*/
|
||||
|
||||
T_CWDFileLogWriterFactory::T_CWDFileLogWriterFactory( )
|
||||
: A_LogWriterFactory( Name_( ) )
|
||||
{ }
|
||||
|
||||
RP_LogWriterConfiguration T_CWDFileLogWriterFactory::createConfiguration( T_String const& name ) const
|
||||
{
|
||||
RP_LogWriterConfiguration p( new T_CWDFileLogWriterCfg( ) );
|
||||
p->setName( name );
|
||||
return p;
|
||||
}
|
||||
|
||||
OP_LogWriter T_CWDFileLogWriterFactory::createLogWriter( OP_LogWriterConfiguration&& configuration ) const
|
||||
{
|
||||
T_CWDFileLogWriter* p( new T_CWDFileLogWriter( std::move( configuration ) ) );
|
||||
return OwnRawPointer( p );
|
||||
}
|
||||
|
||||
void T_CWDFileLogWriterFactory::initializeSyntax( T_SRDParserDefs& , T_SRDContext& main ) const
|
||||
{
|
||||
using namespace lw::SRD;
|
||||
main << ( Rule( ) << "file" << Text( ) << CFLCPath_ );
|
||||
main << ( Rule( ) << "append" << CFLCAppend_ );
|
||||
main << ( Rule( ) << "truncate" << CFLCTruncate_ );
|
||||
}
|
||||
|
||||
|
||||
/*= T_CWDFileLogWriterCfg ======================================================*/
|
||||
|
||||
T_CWDFileLogWriterCfg::T_CWDFileLogWriterCfg( )
|
||||
: T_LogWriterConfiguration( Name_( ) )
|
||||
{ }
|
||||
|
||||
T_CWDFileLogWriterCfg::T_CWDFileLogWriterCfg( T_CWDFileLogWriterCfg const& source )
|
||||
: T_LogWriterConfiguration( source ) , path_( source.path_ ) ,
|
||||
append_( source.append_ )
|
||||
{ }
|
||||
|
||||
OP_LogWriterConfiguration T_CWDFileLogWriterCfg::clone( )
|
||||
{
|
||||
T_CWDFileLogWriterCfg* ptr( new T_CWDFileLogWriterCfg( *this ) );
|
||||
return OwnRawPointer( ptr );
|
||||
}
|
||||
|
||||
void T_CWDFileLogWriterCfg::check( T_SRDErrors& errors , T_SRDList const& input )
|
||||
{
|
||||
T_LogWriterConfiguration::check( errors , input );
|
||||
if ( !path_ ) {
|
||||
errors.add( "no file selected" , input[ 0 ] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*= T_CWDFileLogWriter =========================================================*/
|
||||
|
||||
T_CWDFileLogWriter::T_CWDFileLogWriter( OP_LogWriterConfiguration&& configuration )
|
||||
: A_LogWriter( std::move( configuration ) )
|
||||
{ }
|
||||
|
||||
void T_CWDFileLogWriter::log( T_LogTimestamp const& timestamp ,
|
||||
E_LogLevel level , T_LogPath const& path ,
|
||||
T_LogStringData const& data , uint32_t size )
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
char timeBuffer[ 128 ];
|
||||
std::time_t tst( T_LogTimestamp::clock::to_time_t( timestamp ) );
|
||||
std::strftime( timeBuffer , 128 , "%Y-%m-%d %H:%M:%S" , std::gmtime( &tst ) );
|
||||
const auto ms( ( duration_cast< milliseconds >( timestamp - T_LogTimestamp( ) ) ).count( ) % 1000 );
|
||||
|
||||
T_StringBuilder sb;
|
||||
|
||||
sb << timeBuffer << '.';
|
||||
if ( ms < 100 ) {
|
||||
sb << '0';
|
||||
if ( ms < 10 ) {
|
||||
sb << '0';
|
||||
}
|
||||
}
|
||||
sb << ms << ' ' << path.toString( ) << " - " << level << ": ";
|
||||
sb.append( &( (*data) [ 0 ] ) , size );
|
||||
sb << '\n';
|
||||
|
||||
auto const& cfg( configuration< T_CWDFileLogWriterCfg >( ) );
|
||||
auto const& p( cfg.path( ) );
|
||||
if ( !file_ ) {
|
||||
file_ = NewOwned< T_File >( p , cfg.append( ) ? E_FileMode::READ_WRITE : E_FileMode::OVERWRITE );
|
||||
if ( !file_ ) {
|
||||
disable( );
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
file_->open( );
|
||||
} catch ( X_StreamError const& ) {
|
||||
disable( );
|
||||
file_.clear( );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
file_->position( 0 , true );
|
||||
file_->write( sb.data( ) , sb.size( ) );
|
||||
file_->flush( );
|
||||
} catch ( X_StreamError ) {
|
||||
disable( );
|
||||
file_.clear( );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,9 +2,9 @@
|
|||
/* DYNAMIC LIBRARIES **********************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/DynLib.hh>
|
||||
#include <lw/lib/Threading.hh>
|
||||
using namespace lw;
|
||||
#include <ebcl/DynLib.hh>
|
||||
#include <ebcl/Threading.hh>
|
||||
using namespace ebcl;
|
||||
|
||||
#ifdef _WIN32
|
||||
# error "Not implemented"
|
||||
|
|
140
src/GameLoop.cc
140
src/GameLoop.cc
|
@ -1,140 +0,0 @@
|
|||
/******************************************************************************/
|
||||
/* GAME'S MAIN LOOP ***********************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/GameLoop.hh>
|
||||
#include <lw/lib/Threading.hh>
|
||||
#include <lw/lib/Log.hh>
|
||||
#include <lw/lib/LW.hh>
|
||||
using namespace lw;
|
||||
|
||||
|
||||
/*= T_GameLoopPrivate_ =======================================================*/
|
||||
|
||||
namespace {
|
||||
struct T_GameLoopPrivate_
|
||||
{
|
||||
T_Logger logger{ "/core/loop" };
|
||||
|
||||
OP_Thread thread;
|
||||
bool active = false;
|
||||
bool forceShutdown = false;
|
||||
|
||||
T_Mutex mutex;
|
||||
T_Condition cond;
|
||||
T_RingBuffer< T_UIMessage > messages{ 64 };
|
||||
|
||||
/* Game loop */
|
||||
void run( ) noexcept;
|
||||
|
||||
/* Wait for the next message, no timeout */
|
||||
bool nextMessage(
|
||||
T_UIMessage& message ) noexcept;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
inline void T_GameLoopPrivate_::run( ) noexcept
|
||||
{
|
||||
const bool trace( logger.hasLevel( E_LogLevel::TRACE ) );
|
||||
logger.debug( ) << "Game loop thread starting";
|
||||
|
||||
while ( !forceShutdown ) {
|
||||
T_UIMessage message;
|
||||
if ( !nextMessage( message ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto mt( message.type( ) );
|
||||
if ( trace ) {
|
||||
logger.trace( ) << "Got message " << mt;
|
||||
}
|
||||
|
||||
switch ( mt ) {
|
||||
|
||||
case lw::E_GameUIMessage::QUIT:
|
||||
// FIXME quit properly
|
||||
LW::ui( ).putMessage( E_GameLoopMessage::TERMINATED );
|
||||
forceShutdown = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.debug( ) << "Unhandled message " << mt;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
active = false;
|
||||
logger.debug( ) << "Game loop thread exiting";
|
||||
}
|
||||
|
||||
inline bool T_GameLoopPrivate_::nextMessage(
|
||||
T_UIMessage& message ) noexcept
|
||||
{
|
||||
T_ExclusiveLock lock( mutex );
|
||||
cond.wait( lock , [this]() {
|
||||
return messages.size( ) != 0 || forceShutdown;
|
||||
} );
|
||||
return messages.readNext( message )
|
||||
&& message.hasMessage( );
|
||||
}
|
||||
|
||||
|
||||
/*= T_GameLoop ===============================================================*/
|
||||
|
||||
#define M_PRIVATE_ \
|
||||
auto& pi( p< T_GameLoopPrivate_ >( ) );
|
||||
|
||||
T_GameLoop::T_GameLoop( ) noexcept
|
||||
: A_PrivateImplementation( new T_GameLoopPrivate_( ) )
|
||||
{ }
|
||||
|
||||
bool T_GameLoop::active( ) const noexcept
|
||||
{
|
||||
M_PRIVATE_;
|
||||
return bool( pi.thread ) && !pi.active;
|
||||
}
|
||||
|
||||
void T_GameLoop::start( ) noexcept
|
||||
{
|
||||
M_PRIVATE_;
|
||||
pi.logger.trace( ) << "Starting main loop thread";
|
||||
if ( pi.thread ) {
|
||||
pi.logger.warning( )
|
||||
<< "The main loop thread is already active?!";
|
||||
}
|
||||
pi.active = true;
|
||||
pi.thread = NewOwned< T_Thread >( [&]{
|
||||
pi.run( );
|
||||
} );
|
||||
}
|
||||
|
||||
void T_GameLoop::shutdown( ) noexcept
|
||||
{
|
||||
M_PRIVATE_;
|
||||
if ( !pi.thread ) {
|
||||
pi.logger.trace( )
|
||||
<< "The main loop thread is not present";
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pi.active ) {
|
||||
pi.logger.notice( ) << "Main loop is still active at shutdown!";
|
||||
pi.forceShutdown = true;
|
||||
|
||||
T_ExclusiveLock lock( pi.mutex );
|
||||
pi.cond.notify_one( );
|
||||
}
|
||||
pi.thread->join( );
|
||||
pi.thread.clear( );
|
||||
}
|
||||
|
||||
void T_GameLoop::putMessage( T_UIMessage&& message ) noexcept
|
||||
{
|
||||
M_PRIVATE_;
|
||||
T_ExclusiveLock lock( pi.mutex );
|
||||
pi.messages.put( std::move( message ) );
|
||||
pi.cond.notify_one( );
|
||||
}
|
603
src/LW.cc
603
src/LW.cc
|
@ -1,603 +0,0 @@
|
|||
/******************************************************************************/
|
||||
/* MAIN CLASS *****************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/LW.hh>
|
||||
#include <lw/lib/BuiltinLoggers.hh>
|
||||
#include <lw/lib/SRDBinary.hh>
|
||||
#include <lw/lib/SRDParser.hh>
|
||||
#include <lw/lib/SRDText.hh>
|
||||
#include <lw/lib/MemoryStreams.hh>
|
||||
#include <lw/lib/VFSDrivers.hh>
|
||||
using namespace lw;
|
||||
|
||||
|
||||
/*= LIBRARY DATA =============================================================*/
|
||||
|
||||
namespace {
|
||||
#ifdef LW_BUILD
|
||||
# include "lib-rom.hh"
|
||||
#else
|
||||
const uint8_t lw_library_rom[] = { 0 };
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
|
||||
/*= INTERNALS ================================================================*/
|
||||
|
||||
namespace {
|
||||
|
||||
/* Command line arguments */
|
||||
struct T_CommandLine_
|
||||
{
|
||||
T_String homePath;
|
||||
T_Array< T_String > extraPaths;
|
||||
T_String ui;
|
||||
};
|
||||
|
||||
struct T_LWInternals_
|
||||
{
|
||||
T_LWComponents& components;
|
||||
|
||||
const T_String executablePath_;
|
||||
T_CommandLine_ arguments_;
|
||||
|
||||
OP_LoggingSystem loggingSystem;
|
||||
T_Logger lPreinit;
|
||||
T_Logger logger;
|
||||
T_Logger lShutdown;
|
||||
|
||||
bool uiInitialised;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
T_LWInternals_( T_String const& executablePath ,
|
||||
T_LWComponents& components ) noexcept;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
[[noreturn]] void fatalError( T_Logger& logger , char const* text );
|
||||
|
||||
/* Load a logging configuration file */
|
||||
OP_LoggingConfiguration loadLoggingConfigFile(
|
||||
T_Logger& logger ,
|
||||
char const* path ) noexcept;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/* Pre-initialisation - console, logging, VFS, mods manager */
|
||||
void preInit( T_String const& commandLine );
|
||||
|
||||
OP_LoggingConfiguration getInitialLoggingConfig( );
|
||||
void parseCommandLine(
|
||||
T_String const& commandLine );
|
||||
OP_LoggingConfiguration loadPreinitLoggingConfig( );
|
||||
OP_LoggingConfiguration getDefaultPreinitLoggingConfig( );
|
||||
T_ModsManagerConfiguration getModsManagerConfiguration( );
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
void init( );
|
||||
void run( );
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
void shutdown( );
|
||||
OP_LoggingConfiguration loadShutdownLoggingConfig( );
|
||||
OP_LoggingConfiguration getDefaultShutdownLoggingConfig( );
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
inline T_LWInternals_::T_LWInternals_(
|
||||
T_String const& executablePath ,
|
||||
T_LWComponents& components ) noexcept
|
||||
: components( components ) ,
|
||||
executablePath_( executablePath ) ,
|
||||
lPreinit( "/core/preinit" ) ,
|
||||
logger( "/core/init" ) ,
|
||||
lShutdown( "/core/shutdown" )
|
||||
{ }
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
void T_LWInternals_::fatalError( T_Logger& logger , char const* message )
|
||||
{
|
||||
logger.critical( ) << message;
|
||||
// XXX UI-specific error display if possible
|
||||
throw X_FatalError( );
|
||||
}
|
||||
|
||||
OP_LoggingConfiguration T_LWInternals_::loadLoggingConfigFile(
|
||||
T_Logger& logger ,
|
||||
char const* const path ) noexcept
|
||||
{
|
||||
T_StringBuilder sb;
|
||||
sb << path << ".srd";
|
||||
const T_String srd( std::move( sb ) );
|
||||
sb << path << ".srb";
|
||||
const T_String srb( std::move( sb ) );
|
||||
|
||||
auto& vfs( LW::vfs( ) );
|
||||
const bool useSRD( vfs.typeOf( srd ) == E_VFSEntryType::FILE );
|
||||
if ( !useSRD && vfs.typeOf( srb ) != E_VFSEntryType::FILE ) {
|
||||
return {};
|
||||
}
|
||||
T_String const& pstr( useSRD ? srd : srb );
|
||||
|
||||
auto cfg( LW::logWriters( ).getParserConfiguration( ) );
|
||||
T_SRDParser parser( cfg );
|
||||
OP_SRDReader reader( ([&]() -> OP_SRDReader {
|
||||
if ( useSRD ) {
|
||||
return NewOwned< T_SRDTextReader >( parser );
|
||||
} else {
|
||||
return NewOwned< T_SRDBinaryReader >( parser );
|
||||
}
|
||||
})( ) );
|
||||
try {
|
||||
OP_InputStream input( vfs.read( pstr ) );
|
||||
if ( !input ) {
|
||||
throw X_StreamError( E_StreamError::UNAVAILABLE );
|
||||
}
|
||||
reader->read( pstr , *input );
|
||||
return NewOwned< T_LoggingConfiguration >(
|
||||
std::move( *parser.getData< SP_LoggingConfiguration >( ) ) );
|
||||
|
||||
} catch ( X_StreamError const& e ) {
|
||||
logger.warning( ) << "could not load '"
|
||||
<< pstr << "': " << e.what( );
|
||||
} catch ( X_SRDErrors const& errors ) {
|
||||
errors.log( lPreinit );
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
/*= PRE-INITIALISATION =======================================================*/
|
||||
|
||||
void T_LWInternals_::preInit(
|
||||
T_String const& commandLine )
|
||||
{
|
||||
// Initialise the console, if we can
|
||||
components.console = NewOwned< T_Console >( );
|
||||
components.console->mode( E_ConsoleMode::OPTIONAL );
|
||||
|
||||
// Register basic log writers
|
||||
components.logWriters = NewOwned< T_LogWriterRegistry >( );
|
||||
LW::logWriters( ).add< T_CWDFileLogWriterFactory >( ).automatic( false );
|
||||
LW::logWriters( ).add< T_ConsoleLogWriterFactory >( LW::console( ) ).automatic( false );
|
||||
|
||||
// Initialise the logging system with a temporary configuration
|
||||
loggingSystem = NewOwned< T_LoggingSystem >(
|
||||
LW::logWriters( ) ,
|
||||
getInitialLoggingConfig( ) );
|
||||
|
||||
// Parse command line arguments
|
||||
if ( commandLine ) {
|
||||
parseCommandLine( commandLine );
|
||||
}
|
||||
|
||||
// Initialise the VFS
|
||||
try {
|
||||
if ( arguments_.homePath ) {
|
||||
components.vfs = NewOwned< T_VFS >( arguments_.homePath );
|
||||
} else {
|
||||
components.vfs = NewOwned< T_VFS >( );
|
||||
}
|
||||
} catch ( X_VFSInitialisationFailure const& ) {
|
||||
fatalError( lPreinit , "Could not initialise the VFS." );
|
||||
}
|
||||
|
||||
// Add the VFS-based log writer, then try reconfiguring
|
||||
LW::logWriters( ).add< T_TextFileLogWriterFactory >( LW::vfs( ) ).automatic( false );
|
||||
loggingSystem->reconfigure( loadPreinitLoggingConfig( ) );
|
||||
|
||||
// Finish configuring the VFS
|
||||
lPreinit.debug( ) << "Adding install path '" << executablePath_
|
||||
<< "' to the VFS";
|
||||
{
|
||||
auto ipvfsd( LW::vfs( ).addDriver< T_VFSFilesystemDriver >(
|
||||
executablePath_ ) );
|
||||
if ( !ipvfsd ) {
|
||||
fatalError( lPreinit , "Could not initialise the VFS." );
|
||||
} else {
|
||||
ipvfsd.automatic( false );
|
||||
}
|
||||
}
|
||||
const uint32_t nExtraPaths( arguments_.extraPaths.size( ) );
|
||||
for ( uint32_t i = 0 ; i < nExtraPaths ; i ++ ) {
|
||||
auto const& path( arguments_.extraPaths[ i ] );
|
||||
lPreinit.trace( ) << "Adding extra path '" << path << "' to the VFS";
|
||||
auto vfsd( LW::vfs( ).addDriver< T_VFSFilesystemDriver >( path ) );
|
||||
if ( vfsd ) {
|
||||
vfsd.automatic( false );
|
||||
} else {
|
||||
lPreinit.warning( ) << "unable to add directory '"
|
||||
<< path << "' to the VFS";
|
||||
}
|
||||
}
|
||||
lPreinit.debug( ) << "Adding library data to the VFS";
|
||||
LW::vfs( ).addDriver( LW::getLibData( ) ).automatic( false );
|
||||
|
||||
// Create the global preprocessor configuration
|
||||
lPreinit.trace( ) << "Creating SRD preprocessor configuration";
|
||||
components.ppConfig = NewOwned< T_SRDPreprocessorConfig >( );
|
||||
components.ppConfig->addBuiltinCommands( );
|
||||
components.ppConfig->addVFSCommands( *components.vfs );
|
||||
|
||||
// Initialise the mods manager
|
||||
lPreinit.trace( ) << "Initialising mods manager";
|
||||
components.mods = NewOwned< T_ModsManager >(
|
||||
getModsManagerConfiguration( ) );
|
||||
auto& mods( LW::mods( ) );
|
||||
if ( !mods.scanForMods( ) ) {
|
||||
// TODO later - switch to installer
|
||||
fatalError( lPreinit , "No mods found" );
|
||||
}
|
||||
if ( !mods.resolveDependencies( ) ) {
|
||||
// TODO later - switch to installer
|
||||
fatalError( lPreinit ,
|
||||
"Could not find a valid set of mods "
|
||||
"matching the current configuration" );
|
||||
}
|
||||
|
||||
// Pre-initialise mods
|
||||
mods.preinitCommon( );
|
||||
components.ui = arguments_.ui
|
||||
? mods.preinitUIMods( arguments_.ui )
|
||||
: mods.preinitUIMods( );
|
||||
if ( !components.ui ) {
|
||||
fatalError( lPreinit , "Unable to start the user interface." );
|
||||
}
|
||||
uiInitialised = false;
|
||||
|
||||
// Create the main loop object
|
||||
components.gameLoop = NewOwned< T_GameLoop >( );
|
||||
lPreinit.trace( ) << "UI & game loop created";
|
||||
|
||||
// Load main logging configuration
|
||||
auto lc( loadLoggingConfigFile( lPreinit , "/logging" ) );
|
||||
if ( lc ) {
|
||||
loggingSystem->reconfigure( std::move( lc ) );
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
OP_LoggingConfiguration T_LWInternals_::getInitialLoggingConfig( )
|
||||
{
|
||||
auto logCfg( NewOwned< T_LoggingConfiguration >( ) );
|
||||
logCfg->setMinLevelFor( T_LogPath( ) , E_LogLevel::NOTICE );
|
||||
{
|
||||
const T_String console( T_String::Pooled( "console" ) );
|
||||
logCfg->addLogWriter( T_LogPath( ) , console );
|
||||
auto writerConfig( LW::logWriters( )
|
||||
.get( console )
|
||||
->createConfiguration( console ) );
|
||||
logCfg->putLogWriter( OwnRawPointer( writerConfig ) );
|
||||
}
|
||||
{
|
||||
const T_String tfw( "preinit-file" );
|
||||
T_CWDFileLogWriterCfg* writerConfig(
|
||||
dynamic_cast< T_CWDFileLogWriterCfg *>(
|
||||
LW::logWriters( )
|
||||
.get( tfw )
|
||||
->createConfiguration( tfw ) ) );
|
||||
writerConfig->setPath( T_String::Pooled( "legacyworlds-preinit.log" ) );
|
||||
writerConfig->setAppend( false );
|
||||
logCfg->putLogWriter( OwnRawPointer( writerConfig ) );
|
||||
logCfg->addLogWriter( T_LogPath( ) , tfw );
|
||||
}
|
||||
return logCfg;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
void T_LWInternals_::parseCommandLine(
|
||||
T_String const& commandLine )
|
||||
{
|
||||
const T_SRDParserConfig clpConfig( ([]( T_CommandLine_* cl ) {
|
||||
using namespace lw::SRD;
|
||||
T_SRDParserDefs defs( "default" );
|
||||
|
||||
defs << OnStart( [cl]( T_SRDParserData const& d ) -> bool {
|
||||
*( d.currentData ) = cl;
|
||||
return true;
|
||||
} );
|
||||
|
||||
defs.context( "default" )
|
||||
<< ( Rule( ) << "ui" << Text( )
|
||||
<< []( T_SRDParserData const& d ) -> bool {
|
||||
auto cl( d.currentData->value< T_CommandLine_* >( ) );
|
||||
if ( cl->ui ) {
|
||||
d.errors.add( "duplicate UI identifier" , (*d.input)[ 0 ] );
|
||||
} else {
|
||||
cl->ui = (*d.input)[ 1 ].stringValue( );
|
||||
}
|
||||
return true;
|
||||
} )
|
||||
<< ( Rule( ) << "home" << Text( )
|
||||
<< []( T_SRDParserData const& d ) -> bool {
|
||||
auto cl( d.currentData->value< T_CommandLine_* >( ) );
|
||||
if ( cl->homePath ) {
|
||||
d.errors.add( "duplicate home directory" , (*d.input)[ 0 ] );
|
||||
} else {
|
||||
cl->homePath = (*d.input)[ 1 ].stringValue( );
|
||||
}
|
||||
return true;
|
||||
} )
|
||||
<< ( Rule( ) << "extra-paths" << ( AtLeast( 1 ) << Text( ) )
|
||||
<< []( T_SRDParserData const& d ) -> bool {
|
||||
auto cl( d.currentData->value< T_CommandLine_* >( ) );
|
||||
auto const& input( *d.input );
|
||||
const auto nPaths( input.size( ) );
|
||||
for ( uint32_t i = 1 ; i < nPaths ; i ++ ) {
|
||||
auto const& tok( input[ i ] );
|
||||
if ( cl->extraPaths.contains( tok.stringValue( ) ) ) {
|
||||
d.errors.add( "duplicate data path" , tok );
|
||||
} else {
|
||||
cl->extraPaths.add( tok.stringValue( ) );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
|
||||
return T_SRDParserConfig( defs );
|
||||
})( &arguments_ ) );
|
||||
|
||||
T_SRDParser parser( clpConfig );
|
||||
T_SRDTextReader reader( parser );
|
||||
T_MemoryInputStream input( commandLine.data( ) , commandLine.size( ) );
|
||||
try {
|
||||
reader.read( T_String( "command line" ) , input );
|
||||
} catch ( X_SRDErrors const& errors ) {
|
||||
errors.log( lPreinit );
|
||||
fatalError( lPreinit , "Invalid command line arguments" );
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
OP_LoggingConfiguration T_LWInternals_::loadPreinitLoggingConfig( )
|
||||
{
|
||||
auto rv( loadLoggingConfigFile( lPreinit , "/logging-pre" ) );
|
||||
if ( !rv ) {
|
||||
return getDefaultPreinitLoggingConfig( );
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
OP_LoggingConfiguration T_LWInternals_::getDefaultPreinitLoggingConfig( )
|
||||
{
|
||||
auto logCfg( NewOwned< T_LoggingConfiguration >( ) );
|
||||
{
|
||||
const T_String console( T_String::Pooled( "console" ) );
|
||||
auto writerConfig( LW::logWriters( )
|
||||
.get( console )
|
||||
->createConfiguration( console ) );
|
||||
writerConfig->setMinLevel( E_LogLevel::NOTICE );
|
||||
logCfg->putLogWriter( OwnRawPointer( writerConfig ) );
|
||||
logCfg->addLogWriter( T_LogPath( ) , console );
|
||||
}
|
||||
{
|
||||
const T_String tfw( "text-file" );
|
||||
T_TextFileLogWriterCfg* writerConfig(
|
||||
dynamic_cast< T_TextFileLogWriterCfg *>(
|
||||
LW::logWriters( )
|
||||
.get( tfw )
|
||||
->createConfiguration( tfw ) ) );
|
||||
writerConfig->setMinLevel( E_LogLevel::INFO );
|
||||
writerConfig->setPath( "/preinit.log" );
|
||||
writerConfig->setAppend( false );
|
||||
logCfg->putLogWriter( OwnRawPointer( writerConfig ) );
|
||||
logCfg->addLogWriter( T_LogPath( ) , tfw );
|
||||
}
|
||||
return logCfg;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_ModsManagerConfiguration T_LWInternals_::getModsManagerConfiguration( )
|
||||
{
|
||||
using T_MMC = T_ModsManagerConfiguration;
|
||||
using SP_MMC = SP_ModsManagerConfiguration;
|
||||
|
||||
const T_String mmcfgf( "/mods.srd" );
|
||||
auto& vfs( LW::vfs( ) );
|
||||
if ( vfs.typeOf( mmcfgf ) == E_VFSEntryType::FILE ) {
|
||||
auto pconf( T_MMC::GetParserConfig( ) );
|
||||
T_SRDParser parser( pconf );
|
||||
T_SRDTextReader reader( parser );
|
||||
try {
|
||||
OP_InputStream input( vfs.read( mmcfgf ) );
|
||||
if ( !input ) {
|
||||
throw X_StreamError( E_StreamError::UNAVAILABLE );
|
||||
}
|
||||
reader.read( mmcfgf , *input );
|
||||
return std::move( *parser.getData< SP_MMC >( ) );
|
||||
|
||||
} catch ( X_StreamError const& e ) {
|
||||
lPreinit.warning( ) << "could not load '" << mmcfgf << "': "
|
||||
<< e.what( );
|
||||
} catch ( X_SRDErrors const& errors ) {
|
||||
errors.log( lPreinit );
|
||||
}
|
||||
}
|
||||
lPreinit.debug( ) << "Using default mods configuration";
|
||||
return T_MMC::DefaultConfiguration( );
|
||||
}
|
||||
|
||||
|
||||
/*= INITIALISATION ===========================================================*/
|
||||
|
||||
void T_LWInternals_::init( )
|
||||
{
|
||||
// Initialise all the mods
|
||||
const uint32_t nLoaded( components.mods->modsCount( ) );
|
||||
uint32_t initialised = 0;
|
||||
components.mods->initialise(
|
||||
[&]( RPC_ModInfo mi ) -> F_UpdateInitProgress {
|
||||
const T_ProgressInfoPart main{
|
||||
([&](){
|
||||
T_StringBuilder sb( "Initializing mod " );
|
||||
sb << mi->identifier.name;
|
||||
return T_String{ std::move( sb ) };
|
||||
})( ) , initialised + 1 , nLoaded + 2
|
||||
};
|
||||
initialised ++;
|
||||
components.ui->setInitProgress(
|
||||
T_ProgressInfo{ main } );
|
||||
|
||||
return [&]( T_ProgressInfoPart part ) {
|
||||
components.ui->setInitProgress(
|
||||
T_ProgressInfo{ main , part } );
|
||||
};
|
||||
} );
|
||||
|
||||
// Initialise UI
|
||||
const T_ProgressInfoPart uiInitMain{
|
||||
T_String( "Initializing user interface" ) ,
|
||||
initialised + 1 , nLoaded + 2
|
||||
};
|
||||
components.ui->setInitProgress( T_ProgressInfo{ uiInitMain } );
|
||||
if ( ! components.ui->init( [&]( T_ProgressInfoPart part ) {
|
||||
components.ui->setInitProgress(
|
||||
T_ProgressInfo{ uiInitMain , part } );
|
||||
}) ) {
|
||||
fatalError( logger , "Failed to initialize user interface" );
|
||||
}
|
||||
uiInitialised = true;
|
||||
|
||||
// Start game loop
|
||||
components.gameLoop->start( );
|
||||
}
|
||||
|
||||
/*FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME*/
|
||||
|
||||
void T_LWInternals_::run( )
|
||||
{
|
||||
components.ui->run( );
|
||||
}
|
||||
|
||||
|
||||
/*= SHUTDOWN =================================================================*/
|
||||
|
||||
void T_LWInternals_::shutdown( )
|
||||
{
|
||||
lShutdown.trace( ) << "Starting shutdown";
|
||||
|
||||
// Shutdown game loop if it's still active
|
||||
if ( components.gameLoop ) {
|
||||
components.gameLoop->shutdown( );
|
||||
}
|
||||
|
||||
// If the UI is present and initialised, shut it down
|
||||
if ( components.ui && uiInitialised ) {
|
||||
components.ui->shutdown( );
|
||||
uiInitialised = false;
|
||||
}
|
||||
|
||||
// Shut down mods
|
||||
if ( components.mods ) {
|
||||
components.mods->shutdown( );
|
||||
}
|
||||
|
||||
// Reset logging configuration
|
||||
lShutdown.trace( ) << "Resetting logger configuration";
|
||||
loggingSystem->reconfigure( loadShutdownLoggingConfig( ) );
|
||||
|
||||
// Terminate the UI
|
||||
if ( components.ui ) {
|
||||
components.ui->postshutdown( );
|
||||
components.ui.clear( );
|
||||
components.gameLoop.clear( );
|
||||
lShutdown.trace( ) << "UI and game loop terminated";
|
||||
}
|
||||
|
||||
// Clear mods
|
||||
if ( components.mods ) {
|
||||
components.mods->unload( );
|
||||
components.mods.clear( );
|
||||
lShutdown.trace( ) << "Mods unloaded";
|
||||
}
|
||||
|
||||
// Clear remaining components
|
||||
lShutdown.trace( ) << "Terminating core components";
|
||||
components.ppConfig.clear( );
|
||||
loggingSystem.clear( );
|
||||
components.logWriters.clear( );
|
||||
components.console.clear( );
|
||||
components.vfs.clear( );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
OP_LoggingConfiguration T_LWInternals_::loadShutdownLoggingConfig( )
|
||||
{
|
||||
auto rv( loadLoggingConfigFile( lShutdown , "/logging-post" ) );
|
||||
if ( !rv ) {
|
||||
return getDefaultShutdownLoggingConfig( );
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
OP_LoggingConfiguration T_LWInternals_::getDefaultShutdownLoggingConfig( )
|
||||
{
|
||||
auto logCfg( NewOwned< T_LoggingConfiguration >( ) );
|
||||
{
|
||||
const T_String console( T_String::Pooled( "console" ) );
|
||||
auto writerConfig( LW::logWriters( )
|
||||
.get( console )
|
||||
->createConfiguration( console ) );
|
||||
writerConfig->setMinLevel( E_LogLevel::NOTICE );
|
||||
logCfg->putLogWriter( OwnRawPointer( writerConfig ) );
|
||||
logCfg->addLogWriter( T_LogPath( ) , console );
|
||||
}
|
||||
{
|
||||
const T_String tfw( "text-file" );
|
||||
T_TextFileLogWriterCfg* writerConfig(
|
||||
dynamic_cast< T_TextFileLogWriterCfg *>(
|
||||
LW::logWriters( )
|
||||
.get( tfw )
|
||||
->createConfiguration( tfw ) ) );
|
||||
writerConfig->setMinLevel( E_LogLevel::INFO );
|
||||
writerConfig->setPath( "/shutdown.log" );
|
||||
writerConfig->setAppend( false );
|
||||
logCfg->putLogWriter( OwnRawPointer( writerConfig ) );
|
||||
logCfg->addLogWriter( T_LogPath( ) , tfw );
|
||||
}
|
||||
return logCfg;
|
||||
}
|
||||
|
||||
|
||||
/*= MAIN CLASS ===============================================================*/
|
||||
|
||||
LW* LW::instance_ = nullptr;
|
||||
|
||||
LW::LW( T_String const& commandLine , T_String const& executablePath )
|
||||
{
|
||||
assert( instance_ == nullptr );
|
||||
instance_ = this;
|
||||
T_LWInternals_ pi( executablePath , components );
|
||||
try {
|
||||
pi.preInit( commandLine );
|
||||
pi.init( );
|
||||
pi.run( );
|
||||
} catch ( X_FatalError const& ) {
|
||||
pi.shutdown( );
|
||||
components.~T_LWComponents( );
|
||||
instance_ = nullptr;
|
||||
throw;
|
||||
}
|
||||
pi.shutdown( );
|
||||
instance_ = nullptr;
|
||||
}
|
||||
|
||||
OP_VFSDriver LW::getLibData( ) noexcept
|
||||
{
|
||||
return NewOwned< T_VFSRomDriver >(
|
||||
lw_library_rom , sizeof( lw_library_rom ) );
|
||||
}
|
1041
src/Log.cc
1041
src/Log.cc
File diff suppressed because it is too large
Load diff
|
@ -1,86 +0,0 @@
|
|||
/******************************************************************************/
|
||||
/* UI<=>GAME MESSAGES *********************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/Messages.hh>
|
||||
|
||||
using namespace lw;
|
||||
|
||||
#define M_ENUM_OUT_( Type , Value ) \
|
||||
case Type::Value: obj << #Value; break
|
||||
|
||||
|
||||
/*= A_GameView ===============================================================*/
|
||||
|
||||
A_GameView::~A_GameView( )
|
||||
{ }
|
||||
|
||||
|
||||
/*= A_ViewBuilder ============================================================*/
|
||||
|
||||
A_ViewBuilder::~A_ViewBuilder( )
|
||||
{ }
|
||||
|
||||
|
||||
/*= E_GameState ==============================================================*/
|
||||
|
||||
namespace lw {
|
||||
M_LSHIFT_OP( T_StringBuilder , E_GameState )
|
||||
{
|
||||
switch ( value ) {
|
||||
M_ENUM_OUT_( E_GameState , NO_GAME );
|
||||
M_ENUM_OUT_( E_GameState , GAME_PAUSED );
|
||||
M_ENUM_OUT_( E_GameState , GAME_SLOW );
|
||||
M_ENUM_OUT_( E_GameState , GAME_NORMAL );
|
||||
M_ENUM_OUT_( E_GameState , GAME_FAST );
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
} // namespace lw
|
||||
|
||||
|
||||
/*= E_GameUIMessage ==========================================================*/
|
||||
|
||||
namespace lw {
|
||||
M_LSHIFT_OP( T_StringBuilder , E_GameUIMessage )
|
||||
{
|
||||
switch ( value ) {
|
||||
M_ENUM_OUT_( E_GameUIMessage , NEW );
|
||||
M_ENUM_OUT_( E_GameUIMessage , LOAD );
|
||||
M_ENUM_OUT_( E_GameUIMessage , SAVE );
|
||||
M_ENUM_OUT_( E_GameUIMessage , STOP );
|
||||
M_ENUM_OUT_( E_GameUIMessage , QUIT );
|
||||
M_ENUM_OUT_( E_GameUIMessage , ABORT );
|
||||
M_ENUM_OUT_( E_GameUIMessage , DELETE );
|
||||
M_ENUM_OUT_( E_GameUIMessage , COPY_OR_RENAME );
|
||||
M_ENUM_OUT_( E_GameUIMessage , SET_SPEED );
|
||||
M_ENUM_OUT_( E_GameUIMessage , STEPS );
|
||||
M_ENUM_OUT_( E_GameUIMessage , SET_VIEW );
|
||||
M_ENUM_OUT_( E_GameUIMessage , VIEW_DISPLAYED );
|
||||
M_ENUM_OUT_( E_GameUIMessage , QUERY );
|
||||
M_ENUM_OUT_( E_GameUIMessage , COMMAND );
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
} // namespace lw
|
||||
|
||||
|
||||
/*= E_GameLoopMessage ========================================================*/
|
||||
|
||||
namespace lw {
|
||||
M_LSHIFT_OP( T_StringBuilder , E_GameLoopMessage )
|
||||
{
|
||||
switch ( value ) {
|
||||
M_ENUM_OUT_( E_GameLoopMessage , TERMINATED );
|
||||
M_ENUM_OUT_( E_GameLoopMessage , PROGRESS );
|
||||
M_ENUM_OUT_( E_GameLoopMessage , DONE );
|
||||
M_ENUM_OUT_( E_GameLoopMessage , STATE_CHANGED );
|
||||
M_ENUM_OUT_( E_GameLoopMessage , VIEW_AVAILABLE );
|
||||
M_ENUM_OUT_( E_GameLoopMessage , QUERY_RESPONSE );
|
||||
M_ENUM_OUT_( E_GameLoopMessage , COMMAND_OK );
|
||||
M_ENUM_OUT_( E_GameLoopMessage , COMMAND_SYNTAX );
|
||||
M_ENUM_OUT_( E_GameLoopMessage , COMMAND_ERROR );
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
} // namespace lw
|
|
@ -1,20 +0,0 @@
|
|||
/******************************************************************************/
|
||||
/* MODDING SYSTEM INTERFACES **************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/ModInterface.hh>
|
||||
using namespace lw;
|
||||
|
||||
|
||||
/*= A_ModBase ===============================================================*/
|
||||
|
||||
A_ModBase::~A_ModBase( ) noexcept
|
||||
{ }
|
||||
|
||||
|
||||
/*= A_NativeMod ==============================================================*/
|
||||
|
||||
OP_UserInterface A_NativeMod::getUserInterface( ) const noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
2646
src/Mods.cc
2646
src/Mods.cc
File diff suppressed because it is too large
Load diff
|
@ -2,8 +2,8 @@
|
|||
/* SRD PARSER AND PREPROCESSOR - BINARY FORM **********************************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/SRDBinary.hh>
|
||||
using namespace lw;
|
||||
#include <ebcl/SRDBinary.hh>
|
||||
using namespace ebcl;
|
||||
|
||||
|
||||
/*= MAGIC NUMBERS, VERSIONS AND TAGS =========================================*/
|
||||
|
@ -36,7 +36,7 @@ enum class E_V1Tag_ : uint8_t {
|
|||
} // namespace
|
||||
|
||||
|
||||
namespace lw {
|
||||
namespace ebcl {
|
||||
|
||||
// Writer for version numbers
|
||||
template< >
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
/* SRD PARSER AND PREPROCESSOR - DATA REPRESENTATION **************************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/SRDData.hh>
|
||||
#include <lw/lib/Alloc.hh>
|
||||
#include <lw/lib/Log.hh>
|
||||
using namespace lw;
|
||||
#include <ebcl/SRDData.hh>
|
||||
#include <ebcl/Alloc.hh>
|
||||
using namespace ebcl;
|
||||
|
||||
|
||||
/*= T_SRDLocation ============================================================*/
|
||||
|
@ -58,7 +57,7 @@ T_SRDLocation::T_SRDLocation(
|
|||
swap( *this , other );
|
||||
}
|
||||
|
||||
namespace lw {
|
||||
namespace ebcl {
|
||||
M_DECLARE_SWAP( T_SRDLocation )
|
||||
{
|
||||
using std::swap;
|
||||
|
@ -149,34 +148,10 @@ char const* X_SRDErrors::what( ) const noexcept
|
|||
return "SRD read/parse errors";
|
||||
}
|
||||
|
||||
#ifndef LW_MINLIB
|
||||
void X_SRDErrors::log( T_Logger& logger ) const
|
||||
{
|
||||
const auto nErrors( errors.size( ) );
|
||||
for ( size_t i = 0 ; i < nErrors ; i ++ ) {
|
||||
auto sb( logger.error( ) );
|
||||
auto const& e( errors[ i ] );
|
||||
auto const& l( e.location( ) );
|
||||
|
||||
if ( l.unknown( ) ) {
|
||||
sb << "unknown location";
|
||||
} else {
|
||||
sb << l.source( );
|
||||
if ( l.binary( ) ) {
|
||||
sb << ", b" << l.byte( );
|
||||
} else {
|
||||
sb << ", l" << l.line( ) << ", c" << l.character( );
|
||||
}
|
||||
}
|
||||
sb << ": " << e.error( );
|
||||
}
|
||||
}
|
||||
#endif // LW_MINLIB
|
||||
|
||||
|
||||
/*= T_SRDToken =============================================================*/
|
||||
|
||||
T_StringBuilder& lw::operator<< ( T_StringBuilder& sb , E_SRDTokenType tt )
|
||||
T_StringBuilder& ebcl::operator<< ( T_StringBuilder& sb , E_SRDTokenType tt )
|
||||
{
|
||||
static char const* const C_TOKEN_TYPES_[] = {
|
||||
"(...)" ,
|
||||
|
@ -425,7 +400,7 @@ T_SRDToken& T_SRDToken::operator= ( T_SRDToken const& other )
|
|||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
void lw::swap( T_SRDToken& lhs , T_SRDToken& rhs ) noexcept
|
||||
void ebcl::swap( T_SRDToken& lhs , T_SRDToken& rhs ) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
swap( lhs.type_ , rhs.type_ );
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
/******************************************************************************/
|
||||
|
||||
|
||||
#include <lw/lib/SRDDefinitions.hh>
|
||||
using namespace lw;
|
||||
#include <ebcl/SRDDefinitions.hh>
|
||||
using namespace ebcl;
|
||||
|
||||
|
||||
/*= T_SRDEnum ================================================================*/
|
||||
|
@ -52,7 +52,7 @@ uint32_t T_SRDEnum::operator[] ( char const* word ) const
|
|||
|
||||
/*= T_SRDInputItem ===========================================================*/
|
||||
|
||||
void lw::swap( T_SRDInputItem& lhs , T_SRDInputItem& rhs ) noexcept
|
||||
void ebcl::swap( T_SRDInputItem& lhs , T_SRDInputItem& rhs ) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
swap( lhs.type_ , rhs.type_ );
|
||||
|
@ -80,7 +80,7 @@ T_SRDInputItem& T_SRDInputItem::operator<< ( T_SRDInputItem item )
|
|||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_StringBuilder& lw::operator<< ( T_StringBuilder& sb , T_SRDInputItem const& item )
|
||||
T_StringBuilder& ebcl::operator<< ( T_StringBuilder& sb , T_SRDInputItem const& item )
|
||||
{
|
||||
switch ( item.type( ) ) {
|
||||
|
||||
|
@ -166,7 +166,7 @@ T_SRDInputRule& T_SRDInputRule::operator<< ( T_SRDInputItem item )
|
|||
return *this;
|
||||
}
|
||||
|
||||
T_StringBuilder& lw::operator<< ( T_StringBuilder& sb , T_SRDInputRule const& item )
|
||||
T_StringBuilder& ebcl::operator<< ( T_StringBuilder& sb , T_SRDInputRule const& item )
|
||||
{
|
||||
auto const& seq( item.rule( ) );
|
||||
sb << '(';
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
/******************************************************************************/
|
||||
|
||||
|
||||
#include <lw/lib/SRDIO.hh>
|
||||
using namespace lw;
|
||||
#include <ebcl/SRDIO.hh>
|
||||
using namespace ebcl;
|
||||
|
||||
|
||||
/*= X_SRDWriterError =========================================================*/
|
||||
|
@ -36,7 +36,7 @@ A_SRDWriter& A_SRDWriter::putToken( T_SRDToken const& token )
|
|||
case E_SRDTokenType::LIST:
|
||||
startList( );
|
||||
putList( token.list( ) );
|
||||
|
||||
// fall through
|
||||
case E_SRDTokenType::END:
|
||||
return endList( );
|
||||
|
||||
|
|
2518
src/SRDPPCommands.cc
2518
src/SRDPPCommands.cc
File diff suppressed because it is too large
Load diff
|
@ -3,8 +3,8 @@
|
|||
/******************************************************************************/
|
||||
|
||||
|
||||
#include <lw/lib/SRDParser.hh>
|
||||
using namespace lw;
|
||||
#include <ebcl/SRDParser.hh>
|
||||
using namespace ebcl;
|
||||
|
||||
|
||||
/*= T_SRDParserData ==========================================================*/
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
/* SRD - PARSER CONFIGURATION *************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/SRDParserConfig.hh>
|
||||
using namespace lw;
|
||||
#include <ebcl/SRDParserConfig.hh>
|
||||
using namespace ebcl;
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
1416
src/SRDPreproc.cc
1416
src/SRDPreproc.cc
File diff suppressed because it is too large
Load diff
|
@ -3,8 +3,8 @@
|
|||
/******************************************************************************/
|
||||
|
||||
|
||||
#include <lw/lib/SRDText.hh>
|
||||
using namespace lw;
|
||||
#include <ebcl/SRDText.hh>
|
||||
using namespace ebcl;
|
||||
|
||||
|
||||
/*= T_SRDLexerPrivate_ =======================================================*/
|
||||
|
@ -787,7 +787,7 @@ inline void T_SRDLexerPrivate_::processEnd( )
|
|||
|
||||
case E_State_::COMMENT_ML:
|
||||
pushComment( );
|
||||
|
||||
// fall through
|
||||
case E_State_::COMMENT_ML_START:
|
||||
error( "unterminated multi-line comment" );
|
||||
break;
|
||||
|
@ -798,7 +798,7 @@ inline void T_SRDLexerPrivate_::processEnd( )
|
|||
|
||||
case E_State_::NB_FRAC_PART_M:
|
||||
error( "fractional part expected" );
|
||||
|
||||
// fall through
|
||||
case E_State_::NB_FRAC_PART:
|
||||
case E_State_::NB_EXP:
|
||||
pushFloat( );
|
||||
|
@ -814,7 +814,7 @@ inline void T_SRDLexerPrivate_::processEnd( )
|
|||
error( "incomplete word" );
|
||||
stringBuffer_.clear( );
|
||||
stringBuffer_ << "invalid";
|
||||
|
||||
// fall through
|
||||
case E_State_::WORD:
|
||||
pushWord( );
|
||||
break;
|
||||
|
@ -899,7 +899,7 @@ void T_SRDTextReader::read( T_String const& name , A_InputStream& input )
|
|||
|
||||
/*= TEXT LEXING HELPERS ======================================================*/
|
||||
|
||||
T_SRDList lw::SRDFromText( T_String const& name , T_String const& string , bool structured )
|
||||
T_SRDList ebcl::SRDFromText( T_String const& name , T_String const& string , bool structured )
|
||||
{
|
||||
T_SRDMemoryTarget mt( structured );
|
||||
T_SRDErrors errors;
|
||||
|
@ -919,7 +919,7 @@ T_SRDList lw::SRDFromText( T_String const& name , T_String const& string , bool
|
|||
return mt.list( );
|
||||
}
|
||||
|
||||
T_SRDList lw::SRDFromText( T_String const& name , char const* string , bool structured )
|
||||
T_SRDList ebcl::SRDFromText( T_String const& name , char const* string , bool structured )
|
||||
{
|
||||
T_SRDMemoryTarget mt( structured );
|
||||
T_SRDErrors errors;
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include <ebcl/Utilities.hh>
|
||||
#include <ebcl/Arrays.hh>
|
||||
#include <ebcl/Strings.hh>
|
||||
#include <ebcl/SRDData.hh>
|
||||
#include <ebcl/SRDDefinitions.hh>
|
||||
|
||||
namespace ebcl {
|
||||
|
||||
|
@ -17,6 +19,8 @@ template struct T_Comparator< uint32_t >;
|
|||
template class T_Array< uint32_t >;
|
||||
template class T_Array< bool >;
|
||||
template class T_Array< T_String >;
|
||||
template class T_Array< T_SRDInputItem >;
|
||||
template class T_Array< T_SRDToken >;
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
/******************************************************************************/
|
||||
/* LOGGING SYSTEM - BUILT-IN LOGGERS - TEXT FILE LOGGER ***********************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/BuiltinLoggers.hh>
|
||||
#include <lw/lib/SRDParser.hh>
|
||||
using namespace lw;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
char const* const V_Name_ = "text-file";
|
||||
inline T_String Name_( ) { return T_String::Pooled( V_Name_ ); }
|
||||
|
||||
bool TFLCPath_( T_SRDParserData const& data )
|
||||
{
|
||||
auto const& ptok( (*data.input)[ 1 ] );
|
||||
T_VFSPath path( ptok.stringValue( ) );
|
||||
path = path.normalize( );
|
||||
if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
|
||||
data.errors.add( "invalid path" , ptok );
|
||||
} else {
|
||||
auto lconf( data.targetData->value< RP_LogWriterConfiguration >( ) );
|
||||
( dynamic_cast< T_TextFileLogWriterCfg* >( lconf ) )->setPath( std::move( path ) );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TFLCAppend_( T_SRDParserData const& data )
|
||||
{
|
||||
auto lconf( data.targetData->value< RP_LogWriterConfiguration >( ) );
|
||||
( dynamic_cast< T_TextFileLogWriterCfg* >( lconf ) )->setAppend( true );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TFLCTruncate_( T_SRDParserData const& data )
|
||||
{
|
||||
auto lconf( data.targetData->value< RP_LogWriterConfiguration >( ) );
|
||||
( dynamic_cast< T_TextFileLogWriterCfg* >( lconf ) )->setAppend( false );
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*= T_TextFileLogWriterFactory ==================================================*/
|
||||
|
||||
T_TextFileLogWriterFactory::T_TextFileLogWriterFactory( T_VFS& vfs )
|
||||
: A_LogWriterFactory( Name_( ) ) , vfs_( vfs )
|
||||
{ }
|
||||
|
||||
RP_LogWriterConfiguration T_TextFileLogWriterFactory::createConfiguration( T_String const& name ) const
|
||||
{
|
||||
RP_LogWriterConfiguration p( new T_TextFileLogWriterCfg( ) );
|
||||
p->setName( name );
|
||||
return p;
|
||||
}
|
||||
|
||||
OP_LogWriter T_TextFileLogWriterFactory::createLogWriter( OP_LogWriterConfiguration&& configuration ) const
|
||||
{
|
||||
T_TextFileLogWriter* p( new T_TextFileLogWriter( std::move( configuration ) , vfs_ ) );
|
||||
return OwnRawPointer( p );
|
||||
}
|
||||
|
||||
void T_TextFileLogWriterFactory::initializeSyntax( T_SRDParserDefs& , T_SRDContext& main ) const
|
||||
{
|
||||
using namespace lw::SRD;
|
||||
main << ( Rule( ) << "file" << Text( ) << TFLCPath_ );
|
||||
main << ( Rule( ) << "append" << TFLCAppend_ );
|
||||
main << ( Rule( ) << "truncate" << TFLCTruncate_ );
|
||||
}
|
||||
|
||||
|
||||
/*= T_TextFileLogWriterCfg ======================================================*/
|
||||
|
||||
T_TextFileLogWriterCfg::T_TextFileLogWriterCfg( )
|
||||
: T_LogWriterConfiguration( Name_( ) )
|
||||
{ }
|
||||
|
||||
T_TextFileLogWriterCfg::T_TextFileLogWriterCfg( T_TextFileLogWriterCfg const& source )
|
||||
: T_LogWriterConfiguration( source ) , path_( source.path_ ) ,
|
||||
append_( source.append_ )
|
||||
{ }
|
||||
|
||||
OP_LogWriterConfiguration T_TextFileLogWriterCfg::clone( )
|
||||
{
|
||||
T_TextFileLogWriterCfg* ptr( new T_TextFileLogWriterCfg( *this ) );
|
||||
return OwnRawPointer( ptr );
|
||||
}
|
||||
|
||||
void T_TextFileLogWriterCfg::check( T_SRDErrors& errors , T_SRDList const& input )
|
||||
{
|
||||
T_LogWriterConfiguration::check( errors , input );
|
||||
if ( path_.type( ) == E_VFSPathType::UNKNOWN || path_.elements( ) == 0 ) {
|
||||
errors.add( "no file selected" , input[ 0 ] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*= T_TextFileLogWriter =========================================================*/
|
||||
|
||||
T_TextFileLogWriter::T_TextFileLogWriter( OP_LogWriterConfiguration&& configuration , T_VFS& vfs )
|
||||
: A_LogWriter( std::move( configuration ) ) , vfs_( vfs )
|
||||
{ }
|
||||
|
||||
void T_TextFileLogWriter::log( T_LogTimestamp const& timestamp ,
|
||||
E_LogLevel level , T_LogPath const& path ,
|
||||
T_LogStringData const& data , uint32_t size )
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
char timeBuffer[ 128 ];
|
||||
std::time_t tst( T_LogTimestamp::clock::to_time_t( timestamp ) );
|
||||
std::strftime( timeBuffer , 128 , "%Y-%m-%d %H:%M:%S" , std::gmtime( &tst ) );
|
||||
const auto ms( ( duration_cast< milliseconds >( timestamp - T_LogTimestamp( ) ) ).count( ) % 1000 );
|
||||
|
||||
T_StringBuilder sb;
|
||||
|
||||
sb << timeBuffer << '.';
|
||||
if ( ms < 100 ) {
|
||||
sb << '0';
|
||||
if ( ms < 10 ) {
|
||||
sb << '0';
|
||||
}
|
||||
}
|
||||
sb << ms << ' ' << path.toString( ) << " - " << level << ": ";
|
||||
sb.append( &( (*data) [ 0 ] ) , size );
|
||||
sb << '\n';
|
||||
|
||||
auto const& cfg( configuration< T_TextFileLogWriterCfg >( ) );
|
||||
auto const& p( cfg.path( ) );
|
||||
if ( !file_ ) {
|
||||
vfs_.mkdir( p.parent( ) );
|
||||
file_ = vfs_.file( p , cfg.append( ) ? E_FileMode::READ_WRITE : E_FileMode::OVERWRITE );
|
||||
if ( !file_ ) {
|
||||
disable( );
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
file_->open( );
|
||||
} catch ( X_StreamError const& ) {
|
||||
disable( );
|
||||
file_.clear( );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
file_->position( 0 , true );
|
||||
file_->write( sb.data( ) , sb.size( ) );
|
||||
file_->flush( );
|
||||
} catch ( X_StreamError ) {
|
||||
disable( );
|
||||
file_.clear( );
|
||||
return;
|
||||
}
|
||||
}
|
930
src/VFS.cc
930
src/VFS.cc
|
@ -1,930 +0,0 @@
|
|||
/******************************************************************************/
|
||||
/* VIRTUAL FILE SYSTEM ********************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/VFS.hh>
|
||||
#ifndef _WIN32
|
||||
# include <sys/stat.h>
|
||||
# include <dirent.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
using namespace lw;
|
||||
|
||||
|
||||
/*= VARIOUS INTERNALS ========================================================*/
|
||||
|
||||
namespace {
|
||||
|
||||
bool MkDir_( T_String const& path )
|
||||
{
|
||||
const auto chars( path.toOSString( ) );
|
||||
#ifdef _WIN32
|
||||
return CreateDirectoryW( ( wchar_t const* ) &chars[ 0 ] , nullptr );
|
||||
#else
|
||||
return mkdir( ( char const* ) &chars[ 0 ] , 0755 ) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
E_VFSEntryType TypeOfPath_( T_String const& path )
|
||||
{
|
||||
const auto chars( path.toOSString( ) );
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
uint32_t fa( GetFileAttributesW( (wchar_t const*) & chars[ 0 ] ) );
|
||||
if ( fa == INVALID_FILE_ATTRIBUTES ) {
|
||||
return E_VFSEntryType::NONE;
|
||||
} else if ( ( fa & FILE_ATTRIBUTE_DIRECTORY ) != 0 ) {
|
||||
return E_VFSEntryType::DIRECTORY;
|
||||
} else if ( ( fa & FILE_ATTRIBUTE_NORMAL ) != 0 ) {
|
||||
return E_VFSEntryType::FILE;
|
||||
} else {
|
||||
return E_VFSEntryType::OTHER;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
struct stat fa;
|
||||
if ( stat( ( char const* ) &chars[ 0 ] , &fa ) ) {
|
||||
return E_VFSEntryType::NONE;
|
||||
}
|
||||
|
||||
const auto masked( fa.st_mode & S_IFMT );
|
||||
switch ( masked ) {
|
||||
case S_IFREG:
|
||||
return E_VFSEntryType::FILE;
|
||||
|
||||
case S_IFDIR:
|
||||
return E_VFSEntryType::DIRECTORY;
|
||||
|
||||
default:
|
||||
return E_VFSEntryType::OTHER;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
/*= T_DirLister_ =============================================================*/
|
||||
|
||||
namespace {
|
||||
|
||||
class T_DirLister_
|
||||
{
|
||||
private:
|
||||
T_Buffer< char > path_;
|
||||
#ifdef _WIN32
|
||||
HANDLE handle_;
|
||||
WIN32_FIND_DATAW output_;
|
||||
#else
|
||||
DIR* dir_;
|
||||
struct dirent* output_;
|
||||
#endif
|
||||
|
||||
public:
|
||||
explicit T_DirLister_( T_Buffer< char > path );
|
||||
~T_DirLister_( );
|
||||
|
||||
bool start( );
|
||||
void next( );
|
||||
bool hasValue( ) const;
|
||||
T_String getName( ) const;
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
inline T_DirLister_::T_DirLister_( T_Buffer< char > path )
|
||||
: path_( std::move( path ) )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# error "Not implemented"
|
||||
#else
|
||||
dir_ = nullptr;
|
||||
output_ = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
T_DirLister_::~T_DirLister_( )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# error "Not implemented"
|
||||
#else
|
||||
if ( dir_ != nullptr ) {
|
||||
closedir( dir_ );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool T_DirLister_::start( )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# error "Not implemented"
|
||||
#else
|
||||
dir_ = opendir( &path_[ 0 ] );
|
||||
if ( dir_ == nullptr ) {
|
||||
return false;
|
||||
}
|
||||
output_ = readdir( dir_ );
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void T_DirLister_::next( )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# error "Not implemented"
|
||||
#else
|
||||
output_ = readdir( dir_ );
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool T_DirLister_::hasValue( ) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# error "Not implemented"
|
||||
#else
|
||||
return output_ != nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline T_String T_DirLister_::getName( ) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# error "Not implemented"
|
||||
#else
|
||||
return T_String( output_->d_name , strlen( output_->d_name ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*= T_VFSPath ================================================================*/
|
||||
|
||||
T_VFSPath::T_VFSPath( E_VFSPathType type )
|
||||
: type_( type ) , elements_( 16 )
|
||||
{ }
|
||||
|
||||
|
||||
T_VFSPath::T_VFSPath( T_String const& path )
|
||||
: T_VFSPath( )
|
||||
{
|
||||
extractPath( path );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_VFSPath::T_VFSPath( T_VFSPath const& parent , T_VFSPath const& child )
|
||||
: T_VFSPath( parent.type_ )
|
||||
{
|
||||
if ( child.type( ) == E_VFSPathType::UNKNOWN ) {
|
||||
type_ = E_VFSPathType::UNKNOWN;
|
||||
}
|
||||
if ( type_ == E_VFSPathType::UNKNOWN || child.type( ) == E_VFSPathType::UNKNOWN ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( child.type_ == E_VFSPathType::INVALID ) {
|
||||
type_ = E_VFSPathType::INVALID;
|
||||
}
|
||||
elements_.ensureCapacity( parent.elements( ) + child.elements( ) );
|
||||
elements_.addAll( parent.elements_ );
|
||||
elements_.addAll( child.elements_ );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_VFSPath::T_VFSPath( T_VFSPath const& other )
|
||||
: type_( other.type_ ) , elements_( other.elements_ )
|
||||
{ }
|
||||
|
||||
T_VFSPath::T_VFSPath( T_VFSPath&& other ) noexcept
|
||||
: T_VFSPath( )
|
||||
{
|
||||
swap( *this , other );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_VFSPath& T_VFSPath::operator= ( T_VFSPath const& other )
|
||||
{
|
||||
type_ = other.type_;
|
||||
elements_ = other.elements_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T_VFSPath& T_VFSPath::operator= ( T_VFSPath&& other ) noexcept
|
||||
{
|
||||
swap( *this , other );
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_VFSPath::operator T_String ( ) const
|
||||
{
|
||||
T_StringBuilder sb;
|
||||
if ( type_ == E_VFSPathType::ABSOLUTE ) {
|
||||
sb << '/';
|
||||
}
|
||||
|
||||
const auto n( elements_.size( ) );
|
||||
for ( uint32_t i = 0 ; i < n ; i ++ ) {
|
||||
if ( i > 0 ) {
|
||||
sb << '/';
|
||||
}
|
||||
sb << elements_[ i ];
|
||||
}
|
||||
|
||||
return T_String( std::move( sb ) );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
void lw::swap( T_VFSPath& lhs , T_VFSPath& rhs ) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
swap( lhs.type_ , rhs.type_ );
|
||||
swap( lhs.elements_ , rhs.elements_ );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_VFSPath T_VFSPath::normalize( ) const
|
||||
{
|
||||
T_VFSPath normed;
|
||||
if ( type_ == E_VFSPathType::UNKNOWN || type_ == E_VFSPathType::INVALID ) {
|
||||
return normed;
|
||||
}
|
||||
|
||||
normed.type_ = type_;
|
||||
const auto absolute( type_ == E_VFSPathType::ABSOLUTE );
|
||||
const auto n( elements_.size( ) );
|
||||
for ( uint32_t i = 0 ; i < n ; i ++ ) {
|
||||
auto const& e( elements_[ i ] );
|
||||
|
||||
// Simply remove .'s
|
||||
if ( e == "." ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( e == ".." ) {
|
||||
auto const te( normed.elements( ) );
|
||||
|
||||
// Remove ..'s at the start of an absolute path
|
||||
if ( te == 0 && absolute ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use ..'s to remove stuff like "dir/../wat/../wut/.."
|
||||
if ( te != 0 && normed.elements_[ te - 1 ] != ".." ) {
|
||||
normed.elements_.remove( te - 1 );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
normed.elements_.add( e );
|
||||
}
|
||||
|
||||
if ( normed.elements( ) == 0 && normed.type( ) == E_VFSPathType::RELATIVE ) {
|
||||
normed.elements_.add( T_String::Pooled( "." ) );
|
||||
}
|
||||
|
||||
return normed;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_VFSPath T_VFSPath::parent( ) const
|
||||
{
|
||||
if ( type_ == E_VFSPathType::UNKNOWN || type_ == E_VFSPathType::INVALID
|
||||
|| elements_.size( ) == 0 ) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
if ( type_ == E_VFSPathType::RELATIVE && elements_.size( ) == 1 ) {
|
||||
T_VFSPath p( E_VFSPathType::RELATIVE );
|
||||
p.elements_.add( T_String::Pooled( "." ) );
|
||||
return p;
|
||||
}
|
||||
|
||||
T_VFSPath p( *this );
|
||||
p.elements_.remove( elements_.size( ) - 1 );
|
||||
return p;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
bool T_VFSPath::operator== ( T_VFSPath const& other ) const
|
||||
{
|
||||
if ( this == &other ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto n( elements( ) );
|
||||
if ( type_ != other.type_ || n != other.elements( ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for ( uint32_t i = 0 ; i < n ; i ++ ) {
|
||||
if ( elements_[ i ] != other[ i ] ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
void T_VFSPath::extractPath( T_String const& path )
|
||||
{
|
||||
// Extract the path's elements
|
||||
T_StringIterator it( path );
|
||||
bool elementStarted( false ) , absolute( false ) , invalid( false );
|
||||
uint32_t startIndex , index( 0 );
|
||||
while ( !it.atEnd( ) ) {
|
||||
T_Character c( it );
|
||||
|
||||
if ( c == '/' ) {
|
||||
if ( elementStarted ) {
|
||||
elements_.add( path.range( startIndex , it.index( ) - 1 ) );
|
||||
elementStarted = false;
|
||||
}
|
||||
if ( it.index( ) == 0 ) {
|
||||
absolute = true;
|
||||
}
|
||||
} else {
|
||||
if ( !( c.isNumeric( ) || c.isLowercase( )
|
||||
|| c == '-' || c == '_' || c == '.' ) ) {
|
||||
invalid = true;
|
||||
}
|
||||
if ( !elementStarted ) {
|
||||
elementStarted = true;
|
||||
startIndex = it.index( );
|
||||
}
|
||||
}
|
||||
|
||||
it.next( );
|
||||
index ++;
|
||||
}
|
||||
|
||||
if ( elementStarted ) {
|
||||
elements_.add( path.substr( startIndex ) );
|
||||
}
|
||||
|
||||
// Only "." and ".." should start with a dot
|
||||
if ( !invalid ) {
|
||||
const auto n( elements_.size( ) );
|
||||
for ( uint32_t i = 0 ; i < n && !invalid ; i ++ ) {
|
||||
T_String const& s( elements_[ i ] );
|
||||
if ( s[ 0 ] == '.' ) {
|
||||
invalid = ( s != "." && s != ".." );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the type
|
||||
if ( invalid ) {
|
||||
type_ = E_VFSPathType::INVALID;
|
||||
} else if ( absolute ) {
|
||||
type_ = E_VFSPathType::ABSOLUTE;
|
||||
} else if ( elements_.size( ) == 0 ) {
|
||||
type_ = E_VFSPathType::UNKNOWN;
|
||||
} else {
|
||||
type_ = E_VFSPathType::RELATIVE;
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
M_DEFINE_HASH( T_VFSPath )
|
||||
{
|
||||
uint32_t hash( uint32_t( item.type( ) ) << 16 );
|
||||
const auto n( item.elements( ) );
|
||||
hash ^= n;
|
||||
for ( uint32_t i = 0 ; i < n ; i ++ ) {
|
||||
hash = ( hash << 11 ) | ( hash >> 21 );
|
||||
hash ^= ComputeHash( item[ i ] );
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*= A_VFSDriver ==============================================================*/
|
||||
|
||||
bool A_VFSDriver::init( )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void A_VFSDriver::shutdown( )
|
||||
{ }
|
||||
|
||||
OP_File A_VFSDriver::file( T_VFSPath const& )
|
||||
{
|
||||
return OP_File( );
|
||||
}
|
||||
|
||||
|
||||
/*= T_VFSFilesystemDriver ====================================================*/
|
||||
|
||||
#ifdef _WIN32
|
||||
# define C_SEPARATOR_ '\\'
|
||||
#else
|
||||
# define C_SEPARATOR_ '/'
|
||||
#endif
|
||||
|
||||
T_VFSFilesystemDriver::T_VFSFilesystemDriver( T_String root )
|
||||
: root_( root )
|
||||
{ }
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
bool T_VFSFilesystemDriver::init( )
|
||||
{
|
||||
const auto path( root_.toOSString( ) );
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
uint32_t fa( GetFileAttributesW( (wchar_t const*) & path[ 0 ] ) );
|
||||
return fa != INVALID_FILE_ATTRIBUTES && ( fa & FILE_ATTRIBUTE_DIRECTORY ) != 0;
|
||||
|
||||
#else
|
||||
|
||||
struct stat fa;
|
||||
if ( stat( ( char const* ) &path[ 0 ] , &fa ) ) {
|
||||
return false;
|
||||
}
|
||||
return ( fa.st_mode & S_IFMT ) == S_IFDIR;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
E_VFSEntryType T_VFSFilesystemDriver::typeOf( T_VFSPath const& path )
|
||||
{
|
||||
return TypeOfPath_( getFullPath( path ) );
|
||||
}
|
||||
|
||||
bool T_VFSFilesystemDriver::list( T_VFSPath const& path , T_Array< T_VFSPath >& values , T_HashIndex& index )
|
||||
{
|
||||
T_DirLister_ dl( getOSPath( path ) );
|
||||
if ( !dl.start( ) ) {
|
||||
return false;
|
||||
}
|
||||
while ( dl.hasValue( ) ) {
|
||||
const auto v( dl.getName( ) );
|
||||
dl.next( );
|
||||
if ( v == "." || v == ".." ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const T_VFSPath entry( v );
|
||||
if ( entry.type( ) == E_VFSPathType::INVALID ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t hash( ComputeHash( entry ) );
|
||||
uint32_t idx( index.first( hash ) );
|
||||
while ( idx != T_HashIndex::INVALID_INDEX ) {
|
||||
if ( values[ idx ] == entry ) {
|
||||
break;
|
||||
}
|
||||
idx = index.next( idx );
|
||||
}
|
||||
if ( idx == T_HashIndex::INVALID_INDEX ) {
|
||||
index.add( hash );
|
||||
values.add( entry );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
OP_InputStream T_VFSFilesystemDriver::read( T_VFSPath const& path )
|
||||
{
|
||||
if ( typeOf( path ) != E_VFSEntryType::FILE ) {
|
||||
return OP_InputStream( );
|
||||
} else {
|
||||
auto f( file( path ) );
|
||||
if ( f ) {
|
||||
return NewOwned< T_FileInputStream >( std::move( f ) );
|
||||
} else {
|
||||
return OP_InputStream( );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OP_File T_VFSFilesystemDriver::file( T_VFSPath const& path )
|
||||
{
|
||||
if ( typeOf( path ) != E_VFSEntryType::FILE ) {
|
||||
return OP_File( );
|
||||
}
|
||||
return NewOwned< T_File >( getFullPath( path ) , E_FileMode::READ_ONLY );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_String T_VFSFilesystemDriver::getFullPath( T_VFSPath const& path ) const
|
||||
{
|
||||
T_StringBuilder sb( root_ );
|
||||
const auto n( path.elements( ) );
|
||||
for ( uint32_t i = 0 ; i < n ; i ++ ) {
|
||||
sb << C_SEPARATOR_ << path[ i ];
|
||||
}
|
||||
return T_String( std::move( sb ) );
|
||||
}
|
||||
|
||||
inline T_Buffer< char > T_VFSFilesystemDriver::getOSPath( T_VFSPath const& path ) const
|
||||
{
|
||||
return getFullPath( path ).toOSString( );
|
||||
}
|
||||
|
||||
|
||||
/*= T_VFSUserDirectory_ ======================================================*/
|
||||
|
||||
namespace {
|
||||
|
||||
class T_VFSUserDirectory_ : public T_VFSFilesystemDriver
|
||||
{
|
||||
public:
|
||||
explicit T_VFSUserDirectory_( T_String const& base );
|
||||
|
||||
bool init( ) override;
|
||||
|
||||
OP_File file( T_VFSPath const& path ) override;
|
||||
OP_File file( T_VFSPath const& path , E_FileMode mode );
|
||||
OP_OutputStream write( T_VFSPath const& path );
|
||||
bool mkdir( T_VFSPath const& path ) const;
|
||||
bool rmdir( T_VFSPath const& path ) const;
|
||||
bool rm( T_VFSPath const& path ) const;
|
||||
bool move( T_VFSPath const& from , T_VFSPath const& to ) const;
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_VFSUserDirectory_::T_VFSUserDirectory_( T_String const& base )
|
||||
: T_VFSFilesystemDriver( base )
|
||||
{ }
|
||||
|
||||
bool T_VFSUserDirectory_::init( )
|
||||
{
|
||||
if ( T_VFSFilesystemDriver::init( ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try creating the user directory
|
||||
T_String const& r( root( ) );
|
||||
int32_t nextSep( r.find( C_SEPARATOR_ ) );
|
||||
while ( 1 ) {
|
||||
const T_String p( nextSep == -1 ? r : r.substr( 0 , nextSep ) );
|
||||
if ( p ) {
|
||||
const auto t( TypeOfPath_( p ) );
|
||||
if ( ( t != E_VFSEntryType::DIRECTORY && t != E_VFSEntryType::NONE )
|
||||
|| ( t == E_VFSEntryType::NONE && !MkDir_( p ) ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ( nextSep == -1 ) {
|
||||
break;
|
||||
} else {
|
||||
nextSep = r.find( C_SEPARATOR_ , nextSep + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
OP_File T_VFSUserDirectory_::file( T_VFSPath const& path )
|
||||
{
|
||||
return T_VFSFilesystemDriver::file( path );
|
||||
}
|
||||
|
||||
OP_File T_VFSUserDirectory_::file( T_VFSPath const& path , E_FileMode mode )
|
||||
{
|
||||
if ( mode == E_FileMode::READ_ONLY ) {
|
||||
return file( path );
|
||||
}
|
||||
|
||||
const auto fp( getFullPath( path ) );
|
||||
const auto ft( TypeOfPath_( fp ) );
|
||||
if ( ft != E_VFSEntryType::FILE && ft != E_VFSEntryType::NONE ) {
|
||||
return OP_File( );
|
||||
}
|
||||
return NewOwned< T_File >( fp , mode );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
OP_OutputStream T_VFSUserDirectory_::write( T_VFSPath const& path )
|
||||
{
|
||||
auto f( file( path , E_FileMode::OVERWRITE ) );
|
||||
if ( f ) {
|
||||
return NewOwned< T_FileOutputStream >( std::move( f ) );
|
||||
} else {
|
||||
return OP_OutputStream( );
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
bool T_VFSUserDirectory_::mkdir( T_VFSPath const& path ) const
|
||||
{
|
||||
T_StringBuilder sb( root( ) );
|
||||
const auto n( path.elements( ) );
|
||||
for ( uint32_t i = 0 ; i < n ; i ++ ) {
|
||||
sb << C_SEPARATOR_ << path[ i ];
|
||||
if ( !MkDir_( sb ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool T_VFSUserDirectory_::rmdir( T_VFSPath const& path ) const
|
||||
{
|
||||
const auto chars( getOSPath( path ) );
|
||||
#ifdef _WIN32
|
||||
return RemoveDirectoryW( ( wchar_t const* ) &chars[ 0 ] );
|
||||
#else
|
||||
return ::rmdir( ( char const* ) &chars[ 0 ] ) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool T_VFSUserDirectory_::rm( T_VFSPath const& path ) const
|
||||
{
|
||||
const auto chars( getOSPath( path ) );
|
||||
#ifdef _WIN32
|
||||
return DeleteFileW( ( wchar_t const* ) &chars[ 0 ] );
|
||||
#else
|
||||
return unlink( ( char const* ) &chars[ 0 ] ) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool T_VFSUserDirectory_::move( T_VFSPath const& from , T_VFSPath const& to ) const
|
||||
{
|
||||
const auto charsFrom( getOSPath( from ) );
|
||||
const auto charsTo( getOSPath( to ) );
|
||||
#ifdef _WIN32
|
||||
return MoveFileW( ( wchar_t const* ) &charsFrom[ 0 ] ,
|
||||
( wchar_t const* ) &charsTo[ 0 ] );
|
||||
#else
|
||||
return rename( ( char const* ) &charsFrom[ 0 ] ,
|
||||
( char const* ) &charsTo[ 0 ] ) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*= T_VFSPrivate_ ============================================================*/
|
||||
|
||||
namespace {
|
||||
struct T_VFSPrivate_
|
||||
{
|
||||
T_RegisteredItem::SP_Unregister unregisterFunction_{
|
||||
NewShared< T_RegisteredItem::F_Unregister >(
|
||||
[this]( void* data ) {
|
||||
auto const n( drivers_.size( ) );
|
||||
for ( uint32_t i = 0 ; i < n ; i ++ ) {
|
||||
auto& p( drivers_[ i ] );
|
||||
if ( p.get( ) == data ) {
|
||||
p->shutdown( );
|
||||
drivers_.removeSwap( i );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
) };
|
||||
OP_VFSFilesystemDriver userDir_;
|
||||
T_Array< OP_VFSDriver > drivers_{ 16 };
|
||||
|
||||
static T_String findUserDir( );
|
||||
void initUserDir( T_String const& dir );
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_String T_VFSPrivate_::findUserDir( )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# error "Not implemented!"
|
||||
#else
|
||||
T_String base;
|
||||
char const* const lwHome( getenv( "LWHOME" ) );
|
||||
if ( lwHome == nullptr ) {
|
||||
char const* const userHome( getenv( "HOME" ) );
|
||||
if ( userHome == nullptr ) {
|
||||
throw X_VFSInitialisationFailure( );
|
||||
} else {
|
||||
T_StringBuilder sb( userHome );
|
||||
sb << "/.local/share/LegacyWorlds";
|
||||
base = std::move( sb );
|
||||
}
|
||||
} else {
|
||||
base = T_String( lwHome , strlen( lwHome ) );
|
||||
}
|
||||
return base;
|
||||
#endif
|
||||
}
|
||||
|
||||
void T_VFSPrivate_::initUserDir( T_String const& dir )
|
||||
{
|
||||
assert( !userDir_ );
|
||||
userDir_ = NewOwned< T_VFSUserDirectory_ >( dir );
|
||||
if ( !userDir_->init( ) ) {
|
||||
throw X_VFSInitialisationFailure( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*= T_VFS ====================================================================*/
|
||||
|
||||
T_VFS::T_VFS( )
|
||||
: A_PrivateImplementation( new T_VFSPrivate_( ) )
|
||||
{
|
||||
p< T_VFSPrivate_ >( ).initUserDir( T_VFSPrivate_::findUserDir( ) );
|
||||
}
|
||||
|
||||
T_VFS::T_VFS( T_String const& userDir )
|
||||
: A_PrivateImplementation( new T_VFSPrivate_( ) )
|
||||
{
|
||||
p< T_VFSPrivate_ >( ).initUserDir( userDir );
|
||||
}
|
||||
|
||||
T_RegisteredItem T_VFS::addDriver( OP_VFSDriver&& driver )
|
||||
{
|
||||
assert( driver );
|
||||
const bool ok( driver->init( ) );
|
||||
if ( ok ) {
|
||||
auto& pi( p< T_VFSPrivate_ >( ) );
|
||||
void* const ptr( driver.get( ) );
|
||||
pi.drivers_.add( std::move( driver ) );
|
||||
return T_RegisteredItem( pi.unregisterFunction_ , ptr );
|
||||
}
|
||||
return T_RegisteredItem( );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
E_VFSEntryType T_VFS::typeOf( T_VFSPath const& path ) const
|
||||
{
|
||||
if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
|
||||
return E_VFSEntryType::NONE;
|
||||
}
|
||||
|
||||
auto& pi( p< T_VFSPrivate_ >( ) );
|
||||
const auto np( path.normalize( ) );
|
||||
E_VFSEntryType rv( pi.userDir_->typeOf( np ) );
|
||||
const auto nd( pi.drivers_.size( ) );
|
||||
for ( uint32_t i = nd ; i != 0 && rv == E_VFSEntryType::NONE ; i -- ) {
|
||||
rv = pi.drivers_[ i - 1 ]->typeOf( np );
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool T_VFS::list( T_VFSPath const& path , T_Array< T_VFSPath >& output ) const
|
||||
{
|
||||
output.clear( );
|
||||
if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto np( path.normalize( ) );
|
||||
auto& pi( p< T_VFSPrivate_ >( ) );
|
||||
T_HashIndex index( 1024 , 256 , 256 );
|
||||
bool rv( pi.userDir_->list( np , output , index ) );
|
||||
const auto nd( pi.drivers_.size( ) );
|
||||
for ( uint32_t i = nd ; i != 0 ; i -- ) {
|
||||
rv = pi.drivers_[ i - 1 ]->list( np , output , index ) || rv;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
OP_InputStream T_VFS::read( T_VFSPath const& path ) const
|
||||
{
|
||||
if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
|
||||
return OP_InputStream( );
|
||||
}
|
||||
|
||||
const auto np( path.normalize( ) );
|
||||
auto& pi( p< T_VFSPrivate_ >( ) );
|
||||
OP_InputStream rv( pi.userDir_->read( np ) );
|
||||
const auto nd( pi.drivers_.size( ) );
|
||||
for ( uint32_t i = nd ; i != 0 && !rv ; i -- ) {
|
||||
rv = pi.drivers_[ i - 1 ]->read( np );
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
OP_File T_VFS::file( T_VFSPath const& path ) const
|
||||
{
|
||||
if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
|
||||
return OP_File( );
|
||||
}
|
||||
|
||||
const auto np( path.normalize( ) );
|
||||
auto& pi( p< T_VFSPrivate_ >( ) );
|
||||
OP_File rv( pi.userDir_->file( np ) );
|
||||
const auto nd( pi.drivers_.size( ) );
|
||||
for ( uint32_t i = nd ; i != 0 && !rv ; i -- ) {
|
||||
rv = pi.drivers_[ i - 1 ]->file( np );
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
OP_File T_VFS::file( T_VFSPath const& path , E_FileMode mode ) const
|
||||
{
|
||||
if ( mode == E_FileMode::READ_ONLY ) {
|
||||
return file( path );
|
||||
} else if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
|
||||
return OP_File( );
|
||||
} else {
|
||||
return dynamic_cast< T_VFSUserDirectory_* >(
|
||||
p< T_VFSPrivate_ >( ).userDir_.get( ) )
|
||||
->file( path.normalize( ) , mode );
|
||||
}
|
||||
}
|
||||
|
||||
OP_OutputStream T_VFS::write( T_VFSPath const& path ) const
|
||||
{
|
||||
if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
|
||||
return OP_OutputStream( );
|
||||
} else {
|
||||
return dynamic_cast< T_VFSUserDirectory_* >(
|
||||
p< T_VFSPrivate_ >( ).userDir_.get( ) )
|
||||
->write( path.normalize( ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool T_VFS::mkdir( T_VFSPath const& path ) const
|
||||
{
|
||||
if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
|
||||
return false;
|
||||
} else {
|
||||
return dynamic_cast< T_VFSUserDirectory_* >(
|
||||
p< T_VFSPrivate_ >( ).userDir_.get( ) )
|
||||
->mkdir( path.normalize( ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool T_VFS::rmdir( T_VFSPath const& path ) const
|
||||
{
|
||||
if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
|
||||
return false;
|
||||
} else {
|
||||
return dynamic_cast< T_VFSUserDirectory_* >(
|
||||
p< T_VFSPrivate_ >( ).userDir_.get( ) )
|
||||
->rmdir( path.normalize( ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool T_VFS::rm( T_VFSPath const& path ) const
|
||||
{
|
||||
if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
|
||||
return false;
|
||||
} else {
|
||||
return dynamic_cast< T_VFSUserDirectory_* >(
|
||||
p< T_VFSPrivate_ >( ).userDir_.get( ) )
|
||||
->rm( path.normalize( ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool T_VFS::move( T_VFSPath const& from , T_VFSPath const& to ) const
|
||||
{
|
||||
if ( from.type( ) != E_VFSPathType::ABSOLUTE ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto nFrom( from.normalize( ) );
|
||||
if ( from.elements( ) == 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
T_VFSPath nTo;
|
||||
if ( to.type( ) == E_VFSPathType::ABSOLUTE ) {
|
||||
nTo = to.normalize( );
|
||||
} else if ( to.type( ) == E_VFSPathType::RELATIVE ) {
|
||||
nTo = T_VFSPath( nFrom , T_VFSPath( ".." , to ) ).normalize( );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if ( nTo.elements( ) == 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return dynamic_cast< T_VFSUserDirectory_* >(
|
||||
p< T_VFSPrivate_ >( ).userDir_.get( ) )
|
||||
->move( nFrom , nTo );
|
||||
}
|
|
@ -1,614 +0,0 @@
|
|||
/******************************************************************************/
|
||||
/* VIRTUAL FILE SYSTEM - NON-ESSENTIAL DRIVERS ********************************/
|
||||
/******************************************************************************/
|
||||
|
||||
#include <lw/lib/VFSDrivers.hh>
|
||||
#include <lw/lib/Threading.hh>
|
||||
#include <lw/lib/MemoryStreams.hh>
|
||||
#include <lw/lib/BinaryStreams.hh>
|
||||
using namespace lw;
|
||||
|
||||
|
||||
/*= T_VFSDataIndexPrivate_ ===================================================*/
|
||||
|
||||
namespace {
|
||||
struct T_VFSDataIndexPrivate_
|
||||
{
|
||||
// Information about an entry.
|
||||
struct T_EntryInfo {
|
||||
uint32_t index;
|
||||
bool isFile;
|
||||
};
|
||||
|
||||
const uint32_t magic_;
|
||||
T_KeyValueTable< T_VFSPath , T_EntryInfo > entries_;
|
||||
T_MultiArray< T_String > dirEntries_;
|
||||
T_Array< T_VFSDataIndex::T_FileInfo > files_;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
explicit T_VFSDataIndexPrivate_(
|
||||
uint32_t magic );
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Read the index from a stream
|
||||
bool readIndex(
|
||||
T_BinaryReader& reader );
|
||||
// Free the index
|
||||
void free( );
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Get the type of an entry
|
||||
E_VFSEntryType typeOf( T_VFSPath const& path ) const;
|
||||
|
||||
// List a directory
|
||||
bool list(
|
||||
T_VFSPath const& path ,
|
||||
T_Array< T_VFSPath >& values ,
|
||||
T_HashIndex& index ) const;
|
||||
|
||||
// Returns the file information record for a given path
|
||||
T_VFSDataIndex::T_FileInfo const* getFileInfo(
|
||||
T_VFSPath const& path ) const;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
inline T_VFSDataIndexPrivate_::T_VFSDataIndexPrivate_(
|
||||
uint32_t magic )
|
||||
: magic_( magic )
|
||||
{ }
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
inline bool T_VFSDataIndexPrivate_::readIndex(
|
||||
T_BinaryReader& reader )
|
||||
{
|
||||
/*
|
||||
* FIXME clean this up later
|
||||
*
|
||||
* Format:
|
||||
*
|
||||
* magic
|
||||
* version
|
||||
* root directory
|
||||
* other directories
|
||||
* file data
|
||||
*
|
||||
* a directory:
|
||||
* number of entries
|
||||
* list of entries
|
||||
*
|
||||
* a directory entry:
|
||||
* Entry's name (T_String)
|
||||
* is file? (0/1)
|
||||
* if it's a file:
|
||||
* size (uint32_t)
|
||||
* offset relative to start of data (uint32_t)
|
||||
*
|
||||
* size of a directory entry:
|
||||
* size( name ) + 4 + isFile ? 8 : 0
|
||||
*
|
||||
* size of a directory list:
|
||||
* 4 + sum( size( entry ) for all entries )
|
||||
*
|
||||
*/
|
||||
if ( reader.read< uint32_t >( ) != magic_ ) {
|
||||
return false;
|
||||
}
|
||||
if ( reader.read< uint32_t >( ) != 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto inSize( reader.stream( ).size( ) );
|
||||
T_RingBuffer< T_VFSPath > directories;
|
||||
directories.put( T_VFSPath( "/" ) );
|
||||
entries_.add( T_VFSPath( "/" ) , T_EntryInfo{ 0 , false } );
|
||||
uint32_t dirReserved( 1 );
|
||||
|
||||
T_VFSPath current;
|
||||
while ( directories.size( ) ) {
|
||||
directories.readNext( current );
|
||||
dirEntries_.next( );
|
||||
|
||||
const auto nEntries( reader.read< uint32_t >( ) );
|
||||
for ( uint32_t i = 0 ; i < nEntries ; i ++ ) {
|
||||
T_String name( reader.read< T_String >( ) );
|
||||
const T_VFSPath path( current , name );
|
||||
|
||||
const bool isFile( reader.read< uint8_t >( ) );
|
||||
const uint32_t index( isFile ? files_.size( ) : dirReserved );
|
||||
dirEntries_.add( name );
|
||||
if ( !entries_.add( path , T_EntryInfo{ index , isFile } ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( isFile ) {
|
||||
const auto size( reader.read< uint32_t >( ) );
|
||||
const auto offset( reader.read< uint32_t >( ) );
|
||||
if ( offset >= inSize || offset + size > inSize ) {
|
||||
return false;
|
||||
}
|
||||
files_.add( T_VFSDataIndex::T_FileInfo{ size , offset } );
|
||||
} else {
|
||||
directories.put( path );
|
||||
dirReserved ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dirReserved == dirEntries_.size( );
|
||||
}
|
||||
|
||||
inline void T_VFSDataIndexPrivate_::free( )
|
||||
{
|
||||
entries_.free( );
|
||||
dirEntries_.free( );
|
||||
files_.free( );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
inline E_VFSEntryType T_VFSDataIndexPrivate_::typeOf(
|
||||
T_VFSPath const& path ) const
|
||||
{
|
||||
T_EntryInfo const* eptr( entries_.get( path ) );
|
||||
if ( eptr == nullptr ) {
|
||||
return E_VFSEntryType::NONE;
|
||||
}
|
||||
return eptr->isFile ? E_VFSEntryType::FILE : E_VFSEntryType::DIRECTORY;
|
||||
}
|
||||
|
||||
inline bool T_VFSDataIndexPrivate_::list(
|
||||
T_VFSPath const& path ,
|
||||
T_Array< T_VFSPath >& values ,
|
||||
T_HashIndex& index ) const
|
||||
{
|
||||
T_EntryInfo const* eptr( entries_.get( path ) );
|
||||
if ( eptr == nullptr || eptr->isFile ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto first( dirEntries_.firstOf( eptr->index ) );
|
||||
const auto size( dirEntries_.sizeOf( eptr->index ) );
|
||||
for ( uint32_t i = 0 ; i < size ; i ++ ) {
|
||||
const T_VFSPath p( dirEntries_[ i + first ] );
|
||||
const uint32_t hash( ComputeHash( p ) );
|
||||
uint32_t idx( index.first( hash ) );
|
||||
while ( idx != T_HashIndex::INVALID_INDEX ) {
|
||||
if ( values[ idx ] == p ) {
|
||||
break;
|
||||
}
|
||||
idx = index.next( idx );
|
||||
}
|
||||
if ( idx == T_HashIndex::INVALID_INDEX ) {
|
||||
index.add( hash );
|
||||
values.add( p );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline T_VFSDataIndex::T_FileInfo const* T_VFSDataIndexPrivate_::getFileInfo(
|
||||
T_VFSPath const& path ) const
|
||||
{
|
||||
T_EntryInfo const* eptr( entries_.get( path ) );
|
||||
if ( eptr == nullptr || !eptr->isFile ) {
|
||||
return nullptr;
|
||||
}
|
||||
return &files_[ eptr->index ];
|
||||
}
|
||||
|
||||
|
||||
/*= T_VFSDataIndex ===========================================================*/
|
||||
|
||||
T_VFSDataIndex::T_VFSDataIndex(
|
||||
uint32_t magic )
|
||||
: A_PrivateImplementation( new T_VFSDataIndexPrivate_( magic ) )
|
||||
{ }
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
bool T_VFSDataIndex::readIndex(
|
||||
T_BinaryReader& reader )
|
||||
{
|
||||
return p< T_VFSDataIndexPrivate_ >( ).readIndex( reader );
|
||||
}
|
||||
|
||||
void T_VFSDataIndex::free( )
|
||||
{
|
||||
return p< T_VFSDataIndexPrivate_ >( ).free( );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
E_VFSEntryType T_VFSDataIndex::typeOf(
|
||||
T_VFSPath const& path ) const
|
||||
{
|
||||
return p< T_VFSDataIndexPrivate_ >( ).typeOf( path );
|
||||
}
|
||||
|
||||
bool T_VFSDataIndex::list(
|
||||
T_VFSPath const& path ,
|
||||
T_Array< T_VFSPath >& values ,
|
||||
T_HashIndex& index ) const
|
||||
{
|
||||
return p< T_VFSDataIndexPrivate_ >( ).list( path , values , index );
|
||||
}
|
||||
|
||||
T_VFSDataIndex::T_FileInfo const* T_VFSDataIndex::getFileInfo(
|
||||
T_VFSPath const& path ) const
|
||||
{
|
||||
return p< T_VFSDataIndexPrivate_ >( ).getFileInfo( path );
|
||||
}
|
||||
|
||||
|
||||
/*= T_VFSRomDriverStream_ ====================================================*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
M_CLASS_POINTERS( VFSRomDriverStream_ );
|
||||
class T_VFSRomDriverStream_ : public A_InputStream
|
||||
{
|
||||
private:
|
||||
RP_VFSRomDriverStream_& head_;
|
||||
T_ReadWriteMutex& mutex_;
|
||||
|
||||
RP_VFSRomDriverStream_ prev_;
|
||||
RP_VFSRomDriverStream_ next_;
|
||||
OP_MemoryInputStream actual_;
|
||||
|
||||
public:
|
||||
T_VFSRomDriverStream_( ) = delete;
|
||||
T_VFSRomDriverStream_( T_VFSRomDriverStream_ const& ) = delete;
|
||||
T_VFSRomDriverStream_( T_VFSRomDriverStream_&& ) = delete;
|
||||
|
||||
T_VFSRomDriverStream_( RP_VFSRomDriverStream_& head ,
|
||||
T_ReadWriteMutex& mutex ,
|
||||
OP_MemoryInputStream&& stream );
|
||||
~T_VFSRomDriverStream_( );
|
||||
|
||||
size_t read( void* data , size_t size ) override;
|
||||
|
||||
void disable( );
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_VFSRomDriverStream_::T_VFSRomDriverStream_( RP_VFSRomDriverStream_& head ,
|
||||
T_ReadWriteMutex& mutex ,
|
||||
OP_MemoryInputStream&& stream )
|
||||
: A_InputStream( stream->position( ) , stream->size( ) ) ,
|
||||
head_( head ) , mutex_( mutex ) ,
|
||||
prev_( nullptr ) , next_( head ) ,
|
||||
actual_( std::move( stream ) )
|
||||
{
|
||||
head = this;
|
||||
if ( next_ ) {
|
||||
next_->prev_ = this;
|
||||
}
|
||||
}
|
||||
|
||||
T_VFSRomDriverStream_::~T_VFSRomDriverStream_( )
|
||||
{
|
||||
T_WriteLock lock( mutex_ );
|
||||
disable( );
|
||||
}
|
||||
|
||||
size_t T_VFSRomDriverStream_::read( void* data , size_t size )
|
||||
{
|
||||
T_ReadLock lock( mutex_ );
|
||||
if ( actual_ ) {
|
||||
return actual_->read( data , size );
|
||||
} else {
|
||||
throw X_StreamError( E_StreamError::UNAVAILABLE );
|
||||
}
|
||||
}
|
||||
|
||||
void T_VFSRomDriverStream_::disable( )
|
||||
{
|
||||
actual_.clear( );
|
||||
if ( prev_ == nullptr ) {
|
||||
head_ = next_;
|
||||
} else {
|
||||
prev_->next_ = next_;
|
||||
}
|
||||
if ( next_ != nullptr ) {
|
||||
next_->prev_ = prev_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*= T_VFSRomDriverPrivate_ ===================================================*/
|
||||
|
||||
struct T_VFSRomDriverPrivate_
|
||||
{
|
||||
uint8_t const* data_;
|
||||
uint32_t size_;
|
||||
T_VFSDataIndex index_;
|
||||
T_ReadWriteMutex mutex_;
|
||||
T_VFSRomDriverStream_* streams_;
|
||||
|
||||
T_VFSRomDriverPrivate_(
|
||||
uint8_t const* const data ,
|
||||
uint32_t const size );
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
inline T_VFSRomDriverPrivate_::T_VFSRomDriverPrivate_(
|
||||
uint8_t const* const data ,
|
||||
uint32_t const size )
|
||||
: data_( data ) , size_( size ) ,
|
||||
index_( T_VFSRomDriver::C_MAGIC ) ,
|
||||
streams_( nullptr )
|
||||
{
|
||||
assert( data_ != nullptr );
|
||||
assert( size_ > 0 );
|
||||
}
|
||||
|
||||
|
||||
/*= T_VFSRomDriver ===========================================================*/
|
||||
|
||||
constexpr uint32_t T_VFSRomDriver::C_MAGIC;
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_VFSRomDriver::T_VFSRomDriver(
|
||||
uint8_t const* const data ,
|
||||
uint32_t const size )
|
||||
: A_PrivateImplementation( new T_VFSRomDriverPrivate_( data , size ) )
|
||||
{ }
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
uint8_t const* T_VFSRomDriver::data( ) const
|
||||
{
|
||||
return p< T_VFSRomDriverPrivate_ >( ).data_;
|
||||
}
|
||||
|
||||
uint32_t T_VFSRomDriver::size( ) const
|
||||
{
|
||||
return p< T_VFSRomDriverPrivate_ >( ).size_;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
bool T_VFSRomDriver::init( )
|
||||
{
|
||||
auto& pi( p< T_VFSRomDriverPrivate_ >( ) );
|
||||
T_MemoryInputStream input( pi.data_ , pi.size_ );
|
||||
T_BinaryReader reader( input , E_Endian::NATIVE );
|
||||
bool rv;
|
||||
try {
|
||||
rv = pi.index_.readIndex( reader );
|
||||
} catch ( X_StreamError const& ) {
|
||||
rv = false;
|
||||
}
|
||||
if ( !rv ) {
|
||||
pi.index_.free( );
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void T_VFSRomDriver::shutdown( )
|
||||
{
|
||||
auto& pi( p< T_VFSRomDriverPrivate_ >( ) );
|
||||
T_WriteLock lock( pi.mutex_ );
|
||||
while ( pi.streams_ != nullptr ) {
|
||||
pi.streams_->disable( );
|
||||
}
|
||||
pi.index_.free( );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
E_VFSEntryType T_VFSRomDriver::typeOf( T_VFSPath const& path )
|
||||
{
|
||||
auto& pi( p< T_VFSRomDriverPrivate_ >( ) );
|
||||
T_ReadLock lock( pi.mutex_ );
|
||||
return pi.index_.typeOf( path );
|
||||
}
|
||||
|
||||
bool T_VFSRomDriver::list( T_VFSPath const& path , T_Array< T_VFSPath >& values , T_HashIndex& index )
|
||||
{
|
||||
auto& pi( p< T_VFSRomDriverPrivate_ >( ) );
|
||||
T_ReadLock lock( pi.mutex_ );
|
||||
return pi.index_.list( path , values , index );
|
||||
}
|
||||
|
||||
OP_InputStream T_VFSRomDriver::read( T_VFSPath const& path )
|
||||
{
|
||||
auto& pi( p< T_VFSRomDriverPrivate_ >( ) );
|
||||
T_ReadLock lock( pi.mutex_ );
|
||||
T_VFSDataIndex::T_FileInfo const* p( pi.index_.getFileInfo( path ) );
|
||||
if ( p == nullptr ) {
|
||||
return OP_InputStream( );
|
||||
}
|
||||
|
||||
T_WriteLock wLock( lock.upgrade( ) );
|
||||
return NewOwned< T_VFSRomDriverStream_ >( pi.streams_ , pi.mutex_ ,
|
||||
NewOwned< T_MemoryInputStream >( pi.data_ + p->offset , p->size ) );
|
||||
}
|
||||
|
||||
|
||||
/*= T_VFSDataFileDriverStream_ ===============================================*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
M_CLASS_POINTERS( VFSDataFileDriverStream_ );
|
||||
class T_VFSDataFileDriverStream_ : public A_InputStream
|
||||
{
|
||||
private:
|
||||
RP_VFSDataFileDriverStream_& head_;
|
||||
T_Mutex& mutex_;
|
||||
|
||||
RP_VFSDataFileDriverStream_ prev_;
|
||||
RP_VFSDataFileDriverStream_ next_;
|
||||
OP_FileInputStream actual_;
|
||||
|
||||
public:
|
||||
T_VFSDataFileDriverStream_( ) = delete;
|
||||
T_VFSDataFileDriverStream_( T_VFSDataFileDriverStream_ const& ) = delete;
|
||||
T_VFSDataFileDriverStream_( T_VFSDataFileDriverStream_&& ) = delete;
|
||||
|
||||
T_VFSDataFileDriverStream_( RP_VFSDataFileDriverStream_& head , T_Mutex& mutex ,
|
||||
OP_FileInputStream&& stream );
|
||||
~T_VFSDataFileDriverStream_( );
|
||||
|
||||
size_t read( void* data , size_t size ) override;
|
||||
|
||||
void disable( );
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_VFSDataFileDriverStream_::T_VFSDataFileDriverStream_(
|
||||
RP_VFSDataFileDriverStream_& head , T_Mutex& mutex ,
|
||||
OP_FileInputStream&& stream )
|
||||
: A_InputStream( stream->position( ) , stream->size( ) ) ,
|
||||
head_( head ) , mutex_( mutex ) ,
|
||||
prev_( nullptr ) , next_( head ) ,
|
||||
actual_( std::move( stream ) )
|
||||
{
|
||||
head = this;
|
||||
if ( next_ ) {
|
||||
next_->prev_ = this;
|
||||
}
|
||||
}
|
||||
|
||||
T_VFSDataFileDriverStream_::~T_VFSDataFileDriverStream_( )
|
||||
{
|
||||
T_ScopeLock lock( mutex_ );
|
||||
disable( );
|
||||
}
|
||||
|
||||
size_t T_VFSDataFileDriverStream_::read( void* data , size_t size )
|
||||
{
|
||||
T_ScopeLock lock( mutex_ );
|
||||
if ( actual_ ) {
|
||||
return actual_->read( data , size );
|
||||
} else {
|
||||
throw X_StreamError( E_StreamError::UNAVAILABLE );
|
||||
}
|
||||
}
|
||||
|
||||
void T_VFSDataFileDriverStream_::disable( )
|
||||
{
|
||||
actual_.clear( );
|
||||
if ( prev_ == nullptr ) {
|
||||
head_ = next_;
|
||||
} else {
|
||||
prev_->next_ = next_;
|
||||
}
|
||||
if ( next_ != nullptr ) {
|
||||
next_->prev_ = prev_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*= T_VFSDataFileDriverPrivate_ ==============================================*/
|
||||
|
||||
namespace {
|
||||
struct T_VFSDataFileDriverPrivate_
|
||||
{
|
||||
T_Mutex mutex_;
|
||||
OP_File file_;
|
||||
T_VFSDataIndex index_;
|
||||
T_VFSDataFileDriverStream_* streams_;
|
||||
|
||||
explicit T_VFSDataFileDriverPrivate_(
|
||||
OP_File&& file );
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_VFSDataFileDriverPrivate_::T_VFSDataFileDriverPrivate_(
|
||||
OP_File&& file )
|
||||
: file_( std::move( file ) ) ,
|
||||
index_( T_VFSDataFileDriver::C_MAGIC ) ,
|
||||
streams_( nullptr )
|
||||
{
|
||||
assert( file_ );
|
||||
}
|
||||
|
||||
|
||||
/*= T_VFSDataFileDriver ======================================================*/
|
||||
|
||||
T_VFSDataFileDriver::T_VFSDataFileDriver( OP_File&& file )
|
||||
: A_PrivateImplementation( new T_VFSDataFileDriverPrivate_( std::move( file ) ) )
|
||||
{ }
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_File const& T_VFSDataFileDriver::dataFile( ) const
|
||||
{
|
||||
return *( p< T_VFSDataFileDriverPrivate_ >( ).file_ );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
bool T_VFSDataFileDriver::init( )
|
||||
{
|
||||
auto& pi( p< T_VFSDataFileDriverPrivate_ >( ) );
|
||||
bool rv;
|
||||
try {
|
||||
T_FileInputStream input( *pi.file_ , 0 );
|
||||
T_BinaryReader reader( input , E_Endian::NATIVE );
|
||||
rv = pi.index_.readIndex( reader );
|
||||
} catch ( X_StreamError const& ) {
|
||||
rv = false;
|
||||
}
|
||||
if ( !rv ) {
|
||||
pi.index_.free( );
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void T_VFSDataFileDriver::shutdown( )
|
||||
{
|
||||
auto& pi( p< T_VFSDataFileDriverPrivate_ >( ) );
|
||||
T_ScopeLock lock( pi.mutex_ );
|
||||
while ( pi.streams_ != nullptr ) {
|
||||
pi.streams_->disable( );
|
||||
}
|
||||
pi.index_.free( );
|
||||
pi.file_->close( );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
E_VFSEntryType T_VFSDataFileDriver::typeOf( T_VFSPath const& path )
|
||||
{
|
||||
auto& pi( p< T_VFSDataFileDriverPrivate_ >( ) );
|
||||
T_ScopeLock lock( pi.mutex_ );
|
||||
return pi.index_.typeOf( path );
|
||||
}
|
||||
|
||||
bool T_VFSDataFileDriver::list( T_VFSPath const& path , T_Array< T_VFSPath >& values , T_HashIndex& index )
|
||||
{
|
||||
auto& pi( p< T_VFSDataFileDriverPrivate_ >( ) );
|
||||
T_ScopeLock lock( pi.mutex_ );
|
||||
return pi.index_.list( path , values , index );
|
||||
}
|
||||
|
||||
OP_InputStream T_VFSDataFileDriver::read( T_VFSPath const& path )
|
||||
{
|
||||
auto& pi( p< T_VFSDataFileDriverPrivate_ >( ) );
|
||||
T_ScopeLock lock( pi.mutex_ );
|
||||
T_VFSDataIndex::T_FileInfo const* p( pi.index_.getFileInfo( path ) );
|
||||
if ( p == nullptr ) {
|
||||
return OP_InputStream( );
|
||||
}
|
||||
pi.file_->position( p->offset );
|
||||
return NewOwned< T_VFSDataFileDriverStream_ >( pi.streams_ , pi.mutex_ ,
|
||||
NewOwned< T_FileInputStream >( *pi.file_ , 0 , p->size ) );
|
||||
}
|
32
src/files.mk
32
src/files.mk
|
@ -1,34 +1,18 @@
|
|||
LIB_SOURCES = \
|
||||
src/DynLib.cc \
|
||||
src/Files.cc \
|
||||
src/HashIndex.cc \
|
||||
src/MemoryStreams.cc \
|
||||
src/Pointers.cc \
|
||||
src/SRDBinary.cc \
|
||||
src/SRDData.cc \
|
||||
src/SRDDefinitions.cc \
|
||||
src/SRDIO.cc \
|
||||
src/SRDParser.cc \
|
||||
src/SRDParserConfig.cc \
|
||||
src/SRDText.cc \
|
||||
src/Streams.cc \
|
||||
src/Strings.cc \
|
||||
src/TemplateInstantiation.cc \
|
||||
src/Utilities.cc \
|
||||
# END
|
||||
|
||||
#
|
||||
# src/SRDBinary.cc \
|
||||
# src/SRDData.cc \
|
||||
# src/SRDDefinitions.cc \
|
||||
# src/SRDIO.cc \
|
||||
# src/SRDParser.cc \
|
||||
# src/SRDParserConfig.cc \
|
||||
# src/SRDPreproc.cc \
|
||||
# src/SRDPPCommands.cc \
|
||||
# src/SRDText.cc \
|
||||
# src/VFS.cc \
|
||||
# src/VFSDrivers.cc \
|
||||
# src/Console.cc \
|
||||
# src/ConsoleLogWriter.cc \
|
||||
# src/CwdFileLogger.cc \
|
||||
# src/DynLib.cc \
|
||||
# src/GameLoop.cc \
|
||||
# src/LW.cc \
|
||||
# src/Log.cc \
|
||||
# src/Messages.cc \
|
||||
# src/ModInterface.cc \
|
||||
# src/Mods.cc \
|
||||
# src/TextFileLogger.cc
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue