Files
itgmania212121/src/NoteSkinManager.cpp
T

548 lines
15 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
#include "NoteSkinManager.h"
#include "RageFileManager.h"
#include "RageLog.h"
#include "GameState.h"
2004-07-25 17:07:32 +00:00
#include "Game.h"
2004-06-28 07:26:00 +00:00
#include "Style.h"
#include "RageUtil.h"
2003-09-03 04:30:29 +00:00
#include "RageDisplay.h"
2004-06-10 22:47:51 +00:00
#include "arch/Dialog/Dialog.h"
2004-03-01 02:45:29 +00:00
#include "PrefsManager.h"
2004-07-30 06:32:24 +00:00
#include "Foreach.h"
2006-09-21 02:48:15 +00:00
#include "ActorUtil.h"
2007-02-06 08:03:34 +00:00
#include "XmlFileUtil.h"
#include "Sprite.h"
2007-02-04 02:01:16 +00:00
#include <map>
#include "SpecialFiles.h"
NoteSkinManager* NOTESKIN = NULL; // global object accessable from anywhere in the program
2007-02-06 08:03:34 +00:00
const RString GAME_COMMON_NOTESKIN_NAME = "common";
2007-02-04 03:56:17 +00:00
const RString GAME_BASE_NOTESKIN_NAME = "default";
// this isn't a global because of nondeterministic global ctor ordering might init this before SpecialFiles::NOTESKINS_DIR
#define GLOBAL_BASE_DIR (SpecialFiles::NOTESKINS_DIR + GAME_COMMON_NOTESKIN_NAME + "/")
2006-01-22 01:00:06 +00:00
static map<RString,RString> g_PathCache;
2007-02-04 02:01:16 +00:00
struct NoteSkinData
{
RString sName;
IniFile metrics;
// When looking for an element, search these dirs from head to tail.
vector<RString> vsDirSearchOrder;
2007-02-06 08:03:34 +00:00
LuaReference m_Loader;
2007-02-04 02:01:16 +00:00
};
namespace
{
static map<RString,NoteSkinData> g_mapNameToData;
};
NoteSkinManager::NoteSkinManager()
{
m_pCurGame = NULL;
2006-09-21 04:42:45 +00:00
// Register with Lua.
{
Lua *L = LUA->Get();
lua_pushstring( L, "NOTESKIN" );
this->PushSelf( L );
lua_settable( L, LUA_GLOBALSINDEX );
LUA->Release( L );
}
}
NoteSkinManager::~NoteSkinManager()
{
2006-09-21 04:42:45 +00:00
// Unregister with Lua.
LUA->UnsetGlobal( "NOTESKIN" );
2007-02-04 02:01:16 +00:00
g_mapNameToData.clear();
}
void NoteSkinManager::RefreshNoteSkinData( const Game* pGame )
{
if( m_pCurGame == pGame )
return;
m_pCurGame = pGame;
2004-02-17 22:16:22 +00:00
// clear path cache
g_PathCache.clear();
RString sBaseSkinFolder = SpecialFiles::NOTESKINS_DIR + pGame->m_szName + "/";
2006-01-22 01:00:06 +00:00
vector<RString> asNoteSkinNames;
2003-04-22 04:54:04 +00:00
GetDirListing( sBaseSkinFolder + "*", asNoteSkinNames, true );
StripCvsAndSvn( asNoteSkinNames );
2007-02-04 02:01:16 +00:00
g_mapNameToData.clear();
2003-04-23 02:45:51 +00:00
for( unsigned j=0; j<asNoteSkinNames.size(); j++ )
{
2006-01-22 01:00:06 +00:00
RString sName = asNoteSkinNames[j];
2003-04-22 04:54:04 +00:00
sName.MakeLower();
2007-02-04 02:01:16 +00:00
LoadNoteSkinData( sName, g_mapNameToData[sName] );
}
2003-04-22 04:54:04 +00:00
}
2006-01-22 01:00:06 +00:00
void NoteSkinManager::LoadNoteSkinData( const RString &sNoteSkinName, NoteSkinData& data_out )
2003-04-22 04:54:04 +00:00
{
data_out.sName = sNoteSkinName;
2005-01-07 14:28:00 +00:00
data_out.metrics.Clear();
2004-07-30 06:32:24 +00:00
data_out.vsDirSearchOrder.clear();
2004-07-30 06:32:24 +00:00
/* Read the current NoteSkin and all of its fallbacks */
LoadNoteSkinDataRecursive( sNoteSkinName, data_out );
}
2007-02-06 08:03:34 +00:00
void NoteSkinManager::LoadNoteSkinDataRecursive( const RString &sNoteSkinName_, NoteSkinData& data_out )
2004-07-30 06:32:24 +00:00
{
2007-02-06 08:03:34 +00:00
RString sNoteSkinName(sNoteSkinName_);
int iDepth = 0;
bool bLoadedCommon = false;
bool bLoadedBase = false;
while(1)
{
++iDepth;
ASSERT_M( iDepth < 20, "Circular NoteSkin fallback references detected." );
RString sDir = SpecialFiles::NOTESKINS_DIR + m_pCurGame->m_szName + "/" + sNoteSkinName + "/";
2007-02-06 08:03:34 +00:00
if( !FILEMAN->IsADirectory(sDir) )
{
sDir = GLOBAL_BASE_DIR + sNoteSkinName + "/";
if( !FILEMAN->IsADirectory(sDir) )
{
LOG->Trace( "NoteSkin \"%s\" references skin \"%s\" that is not present",
data_out.sName.c_str(), sNoteSkinName.c_str() );
return;
}
}
LOG->Trace( "LoadNoteSkinDataRecursive: %s (%s)", sNoteSkinName.c_str(), sDir.c_str() );
// read global fallback the current NoteSkin (if any)
IniFile ini;
ini.ReadFile( sDir+"metrics.ini" );
2004-07-30 06:32:24 +00:00
2007-02-06 08:03:34 +00:00
if( !sNoteSkinName.CompareNoCase(GAME_BASE_NOTESKIN_NAME) )
bLoadedBase = true;
if( !sNoteSkinName.CompareNoCase(GAME_COMMON_NOTESKIN_NAME) )
bLoadedCommon = true;
2004-07-30 06:32:24 +00:00
2007-02-06 08:03:34 +00:00
RString sFallback;
if( !ini.GetValue("Global","FallbackNoteSkin", sFallback) )
{
if( !bLoadedBase )
sFallback = GAME_BASE_NOTESKIN_NAME;
else if( !bLoadedCommon )
sFallback = GAME_COMMON_NOTESKIN_NAME;
}
2004-07-30 06:32:24 +00:00
2007-02-06 08:03:34 +00:00
XmlFileUtil::MergeIniUnder( &ini, &data_out.metrics );
2004-07-30 06:32:24 +00:00
2007-04-07 02:33:13 +00:00
data_out.vsDirSearchOrder.push_back( sDir );
2007-02-06 08:03:34 +00:00
if( sFallback.empty() )
break;
sNoteSkinName = sFallback;
}
LuaReference refScript;
for( vector<RString>::reverse_iterator dir = data_out.vsDirSearchOrder.rbegin(); dir != data_out.vsDirSearchOrder.rend(); ++dir )
{
RString sFile = *dir + "NoteSkin.lua";
RString sScript;
if( !FILEMAN->IsAFile(sFile) )
continue;
2004-07-30 06:32:24 +00:00
2007-02-06 08:03:34 +00:00
if( !GetFileContents(sFile, sScript) )
continue;
LOG->Trace( "Load script \"%s\"", sFile.c_str() );
Lua *L = LUA->Get();
RString sError;
refScript.PushSelf( L );
if( !LuaHelpers::RunScript(L, sScript, "@" + sFile, sError, 1, 1) )
{
LOG->Trace( "Error running %s: %s", sFile.c_str(), sError.c_str() );
lua_pop( L, 1 );
}
else
{
refScript.SetFromStack( L );
}
LUA->Release( L );
}
data_out.m_Loader = refScript;
2003-04-22 04:54:04 +00:00
}
2006-01-22 01:00:06 +00:00
void NoteSkinManager::GetNoteSkinNames( vector<RString> &AddTo )
2003-04-22 04:54:04 +00:00
{
GetNoteSkinNames( GAMESTATE->m_pCurGame, AddTo );
}
2006-01-22 01:00:06 +00:00
void NoteSkinManager::GetNoteSkinNames( const Game* pGame, vector<RString> &AddTo, bool bFilterDefault )
{
GetAllNoteSkinNamesForGame( pGame, AddTo );
2004-02-17 22:23:07 +00:00
/* Move "default" to the front if it exists. */
2007-02-04 03:56:17 +00:00
vector<RString>::iterator iter = find( AddTo.begin(), AddTo.end(), GAME_BASE_NOTESKIN_NAME );
if( iter != AddTo.end() )
{
AddTo.erase( iter );
if( !bFilterDefault || !PREFSMAN->m_bHideDefaultNoteSkin )
AddTo.insert( AddTo.begin(), GAME_BASE_NOTESKIN_NAME );
}
2003-04-22 04:54:04 +00:00
}
2006-01-22 01:00:06 +00:00
bool NoteSkinManager::DoesNoteSkinExist( const RString &sSkinName )
2003-04-22 04:54:04 +00:00
{
2006-01-22 01:00:06 +00:00
vector<RString> asSkinNames;
GetAllNoteSkinNamesForGame( GAMESTATE->m_pCurGame, asSkinNames );
2003-04-22 04:54:04 +00:00
for( unsigned i=0; i<asSkinNames.size(); i++ )
if( 0==stricmp(sSkinName, asSkinNames[i]) )
return true;
return false;
}
bool NoteSkinManager::DoNoteSkinsExistForGame( const Game *pGame )
{
vector<RString> asSkinNames;
GetAllNoteSkinNamesForGame( pGame, asSkinNames );
return !asSkinNames.empty();
}
void NoteSkinManager::GetAllNoteSkinNamesForGame( const Game *pGame, vector<RString> &AddTo )
{
if( pGame == m_pCurGame )
{
/* Faster: */
2007-02-04 02:01:16 +00:00
for( map<RString,NoteSkinData>::const_iterator iter = g_mapNameToData.begin();
iter != g_mapNameToData.end(); ++iter )
{
AddTo.push_back( iter->second.sName );
}
}
else
{
RString sBaseSkinFolder = SpecialFiles::NOTESKINS_DIR + pGame->m_szName + "/";
GetDirListing( sBaseSkinFolder + "*", AddTo, true );
StripCvsAndSvn( AddTo );
}
}
2006-01-22 01:00:06 +00:00
RString NoteSkinManager::GetMetric( const RString &sButtonName, const RString &sValue )
{
// if no noteskin has loaded by now, something seriously went wrong!
if( m_sCurrentNoteSkin.empty() )
{
// try selecting the default noteskin
if( DoesNoteSkinExist( "default" ) )
{
m_sCurrentNoteSkin = "default";
LOG->Warn("A noteskin was not loaded before NoteSkinManager::GetMetric() so the default noteskin was forced.");
}
else
{
LOG->Warn("A noteskin was not loaded before NoteSkinManager::GetMetric() and none could be loaded.");
}
}
ASSERT( !m_sCurrentNoteSkin.empty() );
2006-01-22 01:00:06 +00:00
RString sNoteSkinName = m_sCurrentNoteSkin;
2003-09-12 04:44:53 +00:00
sNoteSkinName.MakeLower();
2007-02-04 02:01:16 +00:00
map<RString,NoteSkinData>::const_iterator it = g_mapNameToData.find(sNoteSkinName);
ASSERT_M( it != g_mapNameToData.end(), sNoteSkinName ); // this NoteSkin doesn't exist!
2004-05-23 22:17:26 +00:00
const NoteSkinData& data = it->second;
2006-01-22 01:00:06 +00:00
RString sReturn;
2003-04-22 04:54:04 +00:00
if( data.metrics.GetValue( sButtonName, sValue, sReturn ) )
return sReturn;
2003-04-22 04:54:04 +00:00
if( !data.metrics.GetValue( "NoteDisplay", sValue, sReturn ) )
RageException::Throw( "Could not read metric \"%s::%s\" or \"NoteDisplay::%s\" in \"%s\".",
sButtonName.c_str(), sValue.c_str(), sValue.c_str(), sNoteSkinName.c_str() );
return sReturn;
}
2006-01-22 01:00:06 +00:00
int NoteSkinManager::GetMetricI( const RString &sButtonName, const RString &sValueName )
{
return atoi( GetMetric(sButtonName,sValueName) );
}
2006-01-22 01:00:06 +00:00
float NoteSkinManager::GetMetricF( const RString &sButtonName, const RString &sValueName )
{
2006-06-12 06:42:25 +00:00
return StringToFloat( GetMetric(sButtonName,sValueName) );
}
2006-01-22 01:00:06 +00:00
bool NoteSkinManager::GetMetricB( const RString &sButtonName, const RString &sValueName )
{
return atoi( GetMetric(sButtonName,sValueName) ) != 0;
}
2006-01-22 01:00:06 +00:00
apActorCommands NoteSkinManager::GetMetricA( const RString &sButtonName, const RString &sValueName )
{
return ActorUtil::ParseActorCommands( GetMetric(sButtonName,sValueName) );
}
2006-01-22 01:00:06 +00:00
RString NoteSkinManager::GetPath( const RString &sButtonName, const RString &sElement )
{
2004-07-30 06:32:24 +00:00
try_again:
2006-01-22 01:00:06 +00:00
const RString CacheString = m_sCurrentNoteSkin + "/" + sButtonName + "/" + sElement;
map<RString,RString>::iterator it = g_PathCache.find( CacheString );
2004-02-17 22:16:22 +00:00
if( it != g_PathCache.end() )
return it->second;
2007-02-04 02:01:16 +00:00
map<RString,NoteSkinData>::const_iterator iter = g_mapNameToData.find( m_sCurrentNoteSkin );
ASSERT( iter != g_mapNameToData.end() );
const NoteSkinData &data = iter->second;
2004-07-30 06:32:24 +00:00
2006-01-22 01:00:06 +00:00
RString sPath; // fill this in below
FOREACH_CONST( RString, data.vsDirSearchOrder, iter )
2004-07-30 06:32:24 +00:00
{
if( sButtonName.empty() )
sPath = GetPathFromDirAndFile( *iter, sElement );
2005-10-05 05:49:50 +00:00
else
sPath = GetPathFromDirAndFile( *iter, sButtonName+" "+sElement );
if( !sPath.empty() )
break; // done searching
2004-07-30 06:32:24 +00:00
}
2004-02-17 22:23:07 +00:00
2004-07-30 06:32:24 +00:00
if( sPath.empty() )
{
2007-02-06 08:03:34 +00:00
FOREACH_CONST( RString, data.vsDirSearchOrder, iter )
{
if( !sButtonName.empty() )
sPath = GetPathFromDirAndFile( *iter, "Fallback "+sElement );
if( !sPath.empty() )
break; // done searching
}
}
if( sPath.empty() )
{
RString sPaths;
FOREACH_CONST( RString, data.vsDirSearchOrder, dir )
{
if( !sPaths.empty() )
sPaths += ", ";
sPaths += *dir;
}
2006-01-22 01:00:06 +00:00
RString message = ssprintf(
2007-02-06 08:03:34 +00:00
"The NoteSkin element \"%s %s\" could not be found in any of the following directories:\n%s",
sButtonName.c_str(), sElement.c_str(),
2007-02-06 08:03:34 +00:00
sPaths.c_str() );
2004-07-30 06:32:24 +00:00
if( Dialog::AbortRetryIgnore(message) == Dialog::retry )
{
2007-02-06 08:03:34 +00:00
FOREACH_CONST( RString, data.vsDirSearchOrder, dir )
FILEMAN->FlushDirCache( *dir );
2004-07-30 06:32:24 +00:00
g_PathCache.clear();
goto try_again;
}
RageException::Throw( "%s", message.c_str() );
}
2005-12-08 08:24:30 +00:00
int iLevel = 0;
while( GetExtension(sPath) == "redir" )
{
2005-12-08 08:24:30 +00:00
iLevel++;
ASSERT_M( iLevel < 100, ssprintf("Infinite recursion while looking up %s - %s", sButtonName.c_str(), sElement.c_str()) );
2006-01-22 01:00:06 +00:00
RString sNewFileName;
GetFileContents( sPath, sNewFileName, true );
2006-01-22 01:00:06 +00:00
RString sRealPath;
2004-07-30 06:32:24 +00:00
FOREACH_CONST( RString, data.vsDirSearchOrder, iter )
2004-07-30 06:32:24 +00:00
{
sRealPath = GetPathFromDirAndFile( *iter, sNewFileName );
2004-07-30 06:32:24 +00:00
if( !sRealPath.empty() )
break; // done searching
}
if( sRealPath == "" )
{
2006-01-22 01:00:06 +00:00
RString message = ssprintf(
"NoteSkinManager: The redirect \"%s\" points to the file \"%s\", which does not exist. "
"Verify that this redirect is correct.",
sPath.c_str(), sNewFileName.c_str());
2004-06-10 22:47:51 +00:00
if( Dialog::AbortRetryIgnore(message) == Dialog::retry )
{
2007-02-06 08:03:34 +00:00
FOREACH_CONST( RString, data.vsDirSearchOrder, dir )
FILEMAN->FlushDirCache( *dir );
2004-02-17 22:16:22 +00:00
g_PathCache.clear();
2004-07-30 06:32:24 +00:00
goto try_again;
}
RageException::Throw( "%s", message.c_str() );
}
sPath = sRealPath;
}
2004-02-17 22:16:22 +00:00
g_PathCache[CacheString] = sPath;
return sPath;
}
2004-02-17 08:25:32 +00:00
2007-02-06 08:03:34 +00:00
bool NoteSkinManager::PushActorTemplate( Lua *L, const RString &sButton, const RString &sElement, bool bSpriteOnly )
{
map<RString,NoteSkinData>::const_iterator iter = g_mapNameToData.find( m_sCurrentNoteSkin );
ASSERT( iter != g_mapNameToData.end() );
const NoteSkinData &data = iter->second;
LuaThreadVariable varButton( "Button", sButton );
LuaThreadVariable varElement( "Element", sElement );
LuaThreadVariable varSpriteOnly( "SpriteOnly", LuaReference::Create(bSpriteOnly) );
ASSERT( !data.m_Loader.IsNil() );
data.m_Loader.PushSelf( L );
lua_remove( L, -2 );
lua_getfield( L, -1, "Load" );
return ActorUtil::LoadTableFromStackShowErrors(L);
}
Actor *NoteSkinManager::LoadActor( const RString &sButton, const RString &sElement, Actor *pParent, bool bSpriteOnly )
{
Lua *L = LUA->Get();
if( !PushActorTemplate(L, sButton, sElement, bSpriteOnly) )
{
// ActorUtil will warn about the error
return new Actor;
}
auto_ptr<XNode> pNode( XmlFileUtil::XNodeFromTable(L) );
if( pNode.get() == NULL )
{
// XNode will warn about the error
return new Actor;
}
LUA->Release( L );
Actor *pRet = ActorUtil::LoadFromNode( pNode.get(), pParent );
if( bSpriteOnly )
{
/* Make sure pActor is a Sprite (or something derived from Sprite). */
Sprite *pSprite = dynamic_cast<Sprite *>( pRet );
if( pSprite == NULL )
LOG->Warn( "%s: %s %s must be a Sprite", m_sCurrentNoteSkin.c_str(), sButton.c_str(), sElement.c_str() );
}
return pRet;
}
2006-01-22 01:00:06 +00:00
RString NoteSkinManager::GetPathFromDirAndFile( const RString &sDir, const RString &sFileName )
{
2006-01-22 01:00:06 +00:00
vector<RString> matches; // fill this with the possible files
GetDirListing( sDir+sFileName+"*", matches, false, true );
2003-09-03 04:30:29 +00:00
if( matches.empty() )
2006-01-22 01:00:06 +00:00
return RString();
2003-09-03 04:30:29 +00:00
if( matches.size() > 1 )
{
2006-01-22 01:00:06 +00:00
RString sError = "Multiple files match '"+sDir+sFileName+"'. Please remove all but one of these files.";
2004-06-10 22:47:51 +00:00
Dialog::OK( sError );
2003-09-03 04:30:29 +00:00
}
return matches[0];
}
2004-06-08 01:24:17 +00:00
// lua start
#include "LuaBinding.h"
2005-06-20 05:02:03 +00:00
class LunaNoteSkinManager: public Luna<NoteSkinManager>
{
public:
2007-08-12 03:25:56 +00:00
DEFINE_METHOD( GetPath, GetPath(SArg(1), SArg(2)) );
DEFINE_METHOD( GetMetricI, GetMetricI(SArg(1), SArg(2)) );
DEFINE_METHOD( GetMetricF, GetMetricF(SArg(1), SArg(2)) );
DEFINE_METHOD( GetMetricB, GetMetricB(SArg(1), SArg(2)) );
2005-10-05 07:37:26 +00:00
static int GetMetricA( T* p, lua_State *L ) { p->GetMetricA(SArg(1),SArg(2))->PushSelf(L); return 1; }
2007-02-06 08:03:34 +00:00
static int LoadActor( T* p, lua_State *L )
{
RString sButton = SArg(1);
RString sElement = SArg(2);
if( !p->PushActorTemplate(L, sButton, sElement, false) )
lua_pushnil( L );
2007-02-06 08:03:34 +00:00
return 1;
}
2007-08-12 03:25:56 +00:00
#define FOR_NOTESKIN(x,n) \
static int x ## ForNoteSkin( T* p, lua_State *L ) \
{ \
const RString sOldNoteSkin = p->GetCurrentNoteSkin(); \
p->SetCurrentNoteSkin( SArg(n+1) ); \
x( p, L ); \
p->SetCurrentNoteSkin( sOldNoteSkin ); \
return 1; \
}
2007-08-12 03:25:56 +00:00
FOR_NOTESKIN( GetPath, 2 );
FOR_NOTESKIN( GetMetricI, 2 );
FOR_NOTESKIN( GetMetricF, 2 );
FOR_NOTESKIN( GetMetricB, 2 );
FOR_NOTESKIN( GetMetricA, 2 );
FOR_NOTESKIN( LoadActor, 2 );
#undef FOR_NOTESKIN
2006-09-27 20:30:29 +00:00
LunaNoteSkinManager()
{
ADD_METHOD( GetPath );
2007-08-12 03:25:56 +00:00
ADD_METHOD( GetMetricI );
2007-03-05 04:41:56 +00:00
ADD_METHOD( GetMetricF );
2007-08-12 03:25:56 +00:00
ADD_METHOD( GetMetricB );
ADD_METHOD( GetMetricA );
2007-02-06 08:03:34 +00:00
ADD_METHOD( LoadActor );
2007-08-12 03:25:56 +00:00
ADD_METHOD( GetPathForNoteSkin );
ADD_METHOD( GetMetricIForNoteSkin );
ADD_METHOD( GetMetricFForNoteSkin );
ADD_METHOD( GetMetricBForNoteSkin );
ADD_METHOD( GetMetricAForNoteSkin );
ADD_METHOD( LoadActorForNoteSkin );
}
};
LUA_REGISTER_CLASS( NoteSkinManager )
// lua end
2004-06-08 01:24:17 +00:00
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/