implement course radar value caching
This commit is contained in:
+114
-11
@@ -10,6 +10,7 @@
|
||||
#include "MsdFile.h"
|
||||
#include "PlayerOptions.h"
|
||||
#include "SongOptions.h"
|
||||
#include "SongCacheIndex.h"
|
||||
#include "RageUtil.h"
|
||||
#include "TitleSubstitution.h"
|
||||
#include "Steps.h"
|
||||
@@ -47,12 +48,33 @@ PlayMode Course::GetPlayMode() const
|
||||
|
||||
void Course::LoadFromCRSFile( CString sPath )
|
||||
{
|
||||
LOG->Trace( "Course::LoadFromCRSFile( '%s' )", sPath.c_str() );
|
||||
|
||||
Init();
|
||||
|
||||
m_sPath = sPath; // save path
|
||||
|
||||
bool bUseCache = true;
|
||||
{
|
||||
/* First look in the cache for this course. Don't bother
|
||||
* honoring FastLoad for checking the course hash, since
|
||||
* courses are normally grouped into a few directories, not
|
||||
* one directory per course. XXX: if !FastLoad, regen
|
||||
* cache if the used songs have changed */
|
||||
unsigned uHash = SONGINDEX->GetCacheHash( m_sPath );
|
||||
if( !DoesFileExist(GetCacheFilePath()) )
|
||||
bUseCache = false;
|
||||
if( !PREFSMAN->m_bFastLoad && GetHashForDirectory(m_sPath) != uHash )
|
||||
bUseCache = false; // this cache is out of date
|
||||
}
|
||||
|
||||
if( bUseCache )
|
||||
{
|
||||
CString sCacheFile = GetCacheFilePath();
|
||||
LOG->Trace( "Course::LoadFromCRSFile(\"%s\") (\"%s\")", sPath.c_str(), sCacheFile.c_str() );
|
||||
sPath = sCacheFile.c_str();
|
||||
}
|
||||
else
|
||||
LOG->Trace( "Course::LoadFromCRSFile(\"%s\")", sPath.c_str() );
|
||||
|
||||
MsdFile msd;
|
||||
if( !msd.ReadFile(sPath) )
|
||||
RageException::Throw( "Error opening CRS file '%s'.", sPath.c_str() );
|
||||
@@ -243,7 +265,15 @@ void Course::LoadFromCRSFile( CString sPath )
|
||||
|
||||
m_entries.push_back( new_entry );
|
||||
}
|
||||
else if( bUseCache && !stricmp(sValueName, "RADAR") )
|
||||
{
|
||||
StepsType st = (StepsType) atoi(sParams[1]);
|
||||
CourseDifficulty cd = (CourseDifficulty) atoi(sParams[2]);
|
||||
|
||||
RadarValues rv;
|
||||
rv.FromString( sParams[3] );
|
||||
m_RadarCache[CacheEntry(st, cd)] = rv;
|
||||
}
|
||||
else
|
||||
LOG->Trace( "Unexpected value named '%s'", sValueName.c_str() );
|
||||
}
|
||||
@@ -260,6 +290,33 @@ void Course::LoadFromCRSFile( CString sPath )
|
||||
* song was found in the course. */
|
||||
if( m_sBannerPath != "" && !m_entries.empty() )
|
||||
BANNERCACHE->CacheBanner( m_sBannerPath );
|
||||
|
||||
/* Cache each trail RadarValues that's slow to load, so we
|
||||
* don't have to do it at runtime. */
|
||||
if( !bUseCache )
|
||||
{
|
||||
FOREACH_StepsType( st )
|
||||
{
|
||||
FOREACH_CourseDifficulty( cd )
|
||||
{
|
||||
Trail *pTrail = GetTrail( st, cd );
|
||||
if( pTrail == NULL || !pTrail->SlowGetRadarValues() )
|
||||
continue;
|
||||
|
||||
RadarValues rv = pTrail->GetRadarValues();
|
||||
m_RadarCache[CacheEntry(st, cd)] = rv;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have any cache data, write the cache file. */
|
||||
if( m_RadarCache.size() )
|
||||
{
|
||||
CString sCachePath = GetCacheFilePath();
|
||||
Save( sCachePath, true );
|
||||
|
||||
SONGINDEX->AddCacheIndex( m_sPath, GetHashForFile(m_sPath) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Course::RevertFromDisk()
|
||||
@@ -270,6 +327,11 @@ void Course::RevertFromDisk()
|
||||
LoadFromCRSFile( m_sPath );
|
||||
}
|
||||
|
||||
CString Course::GetCacheFilePath() const
|
||||
{
|
||||
return SongCacheIndex::GetCacheFilePath( "Courses", m_sPath );
|
||||
}
|
||||
|
||||
void Course::Init()
|
||||
{
|
||||
m_bIsAutogen = false;
|
||||
@@ -291,14 +353,18 @@ void Course::Init()
|
||||
m_iTrailCacheSeed = 0;
|
||||
}
|
||||
|
||||
void Course::Save()
|
||||
void Course::Save( CString sPath, bool bSavingCache )
|
||||
{
|
||||
ASSERT( !m_bIsAutogen );
|
||||
|
||||
/* By default, save to the file we loaded from. */
|
||||
if( sPath == "" )
|
||||
sPath = m_sPath;
|
||||
|
||||
RageFile f;
|
||||
if( !f.Open( m_sPath, RageFile::WRITE ) )
|
||||
if( !f.Open( sPath, RageFile::WRITE ) )
|
||||
{
|
||||
LOG->Warn( "Could not write course file '%s': %s", m_sPath.c_str(), f.GetError().c_str() );
|
||||
LOG->Warn( "Could not write course file '%s': %s", sPath.c_str(), f.GetError().c_str() );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -316,6 +382,29 @@ void Course::Save()
|
||||
f.PutLine( ssprintf("#METER:%s:%i;", CourseDifficultyToString(cd).c_str(), m_iCustomMeter[cd]) );
|
||||
}
|
||||
|
||||
if( bSavingCache )
|
||||
{
|
||||
f.PutLine( "// cache tags:" );
|
||||
|
||||
RadarCache_t::const_iterator it;
|
||||
for( it = m_RadarCache.begin(); it != m_RadarCache.end(); ++it )
|
||||
{
|
||||
// #RADAR:type:difficulty:value,value,value...;
|
||||
const CacheEntry &entry = it->first;
|
||||
StepsType st = entry.first;
|
||||
CourseDifficulty cd = entry.second;
|
||||
|
||||
CStringArray asRadarValues;
|
||||
const RadarValues &rv = it->second;
|
||||
for( int r=0; r < NUM_RADAR_CATEGORIES; r++ )
|
||||
asRadarValues.push_back( ssprintf("%.3f", rv[r]) );
|
||||
CString sLine = ssprintf( "#RADAR:%i:%i:", st, cd );
|
||||
sLine += join( ",", asRadarValues ) + ";";
|
||||
f.PutLine( sLine );
|
||||
}
|
||||
f.PutLine( "// end cache tags" );
|
||||
}
|
||||
|
||||
for( unsigned i=0; i<m_entries.size(); i++ )
|
||||
{
|
||||
const CourseEntry& entry = m_entries[i];
|
||||
@@ -608,13 +697,15 @@ Trail* Course::GetTrail( StepsType st, CourseDifficulty cd ) const
|
||||
//
|
||||
// Look in the Trail cache
|
||||
//
|
||||
TrailCache_t::iterator it = m_TrailCache.find( CacheEntry(st, cd) );
|
||||
if( it != m_TrailCache.end() )
|
||||
{
|
||||
CacheData &cache = it->second;
|
||||
if( cache.null )
|
||||
return NULL;
|
||||
return &cache.trail;
|
||||
TrailCache_t::iterator it = m_TrailCache.find( CacheEntry(st, cd) );
|
||||
if( it != m_TrailCache.end() )
|
||||
{
|
||||
CacheData &cache = it->second;
|
||||
if( cache.null )
|
||||
return NULL;
|
||||
return &cache.trail;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -630,6 +721,18 @@ Trail* Course::GetTrail( StepsType st, CourseDifficulty cd ) const
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// If we have cached RadarValues for this trail, insert them.
|
||||
//
|
||||
{
|
||||
RadarCache_t::const_iterator it = m_RadarCache.find( CacheEntry( st, cd ) );
|
||||
if( it != m_RadarCache.end() )
|
||||
{
|
||||
const RadarValues &rv = it->second;
|
||||
trail.SetRadarValues(rv);
|
||||
}
|
||||
}
|
||||
|
||||
cache.null = false;
|
||||
return &cache.trail;
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
void LoadFromCRSFile( CString sPath );
|
||||
void RevertFromDisk();
|
||||
void Init();
|
||||
void Save();
|
||||
void Save( CString sPath = "", bool bSavingCache=false ); /* default is source file */
|
||||
void AutogenEndlessFromGroup( CString sGroupName, Difficulty dc );
|
||||
void AutogenNonstopFromGroup( CString sGroupName, Difficulty dc );
|
||||
void AutogenOniFromArtist( CString sArtistName, CString sArtistNameTranslit, vector<Song*> aSongs, Difficulty dc );
|
||||
@@ -151,6 +151,7 @@ public:
|
||||
void Invalidate( Song *pStaleSong );
|
||||
|
||||
void GetAllCachedTrails( vector<Trail *> &out );
|
||||
CString GetCacheFilePath() const;
|
||||
|
||||
const CourseEntry *FindFixedSong( const Song *pSong ) const;
|
||||
|
||||
@@ -170,6 +171,9 @@ private:
|
||||
typedef map<CacheEntry, CacheData> TrailCache_t;
|
||||
mutable TrailCache_t m_TrailCache;
|
||||
mutable int m_iTrailCacheSeed;
|
||||
|
||||
typedef map<CacheEntry, RadarValues> RadarCache_t;
|
||||
RadarCache_t m_RadarCache;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -58,6 +58,37 @@ void RadarValues::LoadFromNode( const XNode* pNode )
|
||||
pNode->GetChildValue( RadarCategoryToString(rc), m_fValues[rc] );
|
||||
}
|
||||
|
||||
/* iMaxValues is only used for writing compatibility fields in non-cache
|
||||
* SM files; they're never actually read. */
|
||||
CString RadarValues::ToString( int iMaxValues ) const
|
||||
{
|
||||
if( iMaxValues == -1 )
|
||||
iMaxValues = NUM_RADAR_CATEGORIES;
|
||||
iMaxValues = min( iMaxValues, (int)NUM_RADAR_CATEGORIES );
|
||||
|
||||
CStringArray asRadarValues;
|
||||
for( int r=0; r < iMaxValues; r++ )
|
||||
asRadarValues.push_back( ssprintf("%.3f", m_fValues[r]) );
|
||||
|
||||
return join( ",",asRadarValues );
|
||||
}
|
||||
|
||||
void RadarValues::FromString( CString sRadarValues )
|
||||
{
|
||||
CStringArray saValues;
|
||||
split( sRadarValues, ",", saValues, true );
|
||||
|
||||
if( saValues.size() != NUM_RADAR_CATEGORIES )
|
||||
{
|
||||
MakeUnknown();
|
||||
return;
|
||||
}
|
||||
|
||||
FOREACH_RadarCategory(rc)
|
||||
m_fValues[rc] = strtof( saValues[rc], NULL );
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2001-2004 Chris Danford
|
||||
* All rights reserved.
|
||||
|
||||
@@ -43,6 +43,9 @@ struct RadarValues
|
||||
|
||||
XNode* CreateNode() const;
|
||||
void LoadFromNode( const XNode* pNode );
|
||||
|
||||
CString ToString( int iMaxValues = -1 ) const; // default = all
|
||||
void FromString( CString sValues );
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ void SongCacheIndex::ReadCacheIndex()
|
||||
EmptyDir( CACHE_DIR );
|
||||
EmptyDir( CACHE_DIR "Banners/" );
|
||||
EmptyDir( CACHE_DIR "Songs/" );
|
||||
EmptyDir( CACHE_DIR "Courses/" );
|
||||
|
||||
CacheIndex.Clear();
|
||||
}
|
||||
|
||||
+31
-6
@@ -40,6 +40,26 @@ bool TrailEntry::ContainsTransformOrTurn() const
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return true if GetRadarValues may be very slow (reads, decodes and
|
||||
* transforms the Steps), in which case it's worth caching it in Course. */
|
||||
bool Trail::SlowGetRadarValues() const
|
||||
{
|
||||
FOREACH_CONST( TrailEntry, m_vEntries, e )
|
||||
{
|
||||
const Steps *pSteps = e->pSteps;
|
||||
if( !pSteps->IsAutogen() && e->ContainsTransformOrTurn() )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Trail::SetRadarValues( const RadarValues &rv )
|
||||
{
|
||||
m_CachedRadarValues = rv;
|
||||
m_bRadarValuesCached = true;
|
||||
}
|
||||
|
||||
RadarValues Trail::GetRadarValues() const
|
||||
{
|
||||
if( IsMystery() )
|
||||
@@ -55,8 +75,8 @@ RadarValues Trail::GetRadarValues() const
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bRadarValuesCached = true;
|
||||
m_CachedRadarValues.Zero();
|
||||
RadarValues rv;
|
||||
rv.Zero();
|
||||
|
||||
FOREACH_CONST( TrailEntry, m_vEntries, e )
|
||||
{
|
||||
@@ -75,16 +95,21 @@ RadarValues Trail::GetRadarValues() const
|
||||
if( po.ContainsTransformOrTurn() )
|
||||
NoteDataUtil::TransformNoteData( nd, po, pSteps->m_StepsType );
|
||||
NoteDataUtil::TransformNoteData( nd, e->Attacks, pSteps->m_StepsType, e->pSong );
|
||||
RadarValues rv;
|
||||
NoteDataUtil::GetRadarValues( nd, e->pSong->m_fMusicLengthSeconds, rv );
|
||||
m_CachedRadarValues += rv;
|
||||
RadarValues transformed_rv;
|
||||
NoteDataUtil::GetRadarValues( nd, e->pSong->m_fMusicLengthSeconds, transformed_rv );
|
||||
rv += transformed_rv;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_CachedRadarValues += pSteps->GetRadarValues();
|
||||
rv += pSteps->GetRadarValues();
|
||||
}
|
||||
}
|
||||
|
||||
/* Hack: SetRadarValues is non-const (a const setter doesn't
|
||||
* make sense), but it only modifies a mutable value. Just
|
||||
* cast away const. */
|
||||
const_cast<Trail*>(this)->SetRadarValues( rv );
|
||||
|
||||
return m_CachedRadarValues;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,9 @@ public:
|
||||
m_bRadarValuesCached = false;
|
||||
}
|
||||
|
||||
bool SlowGetRadarValues() const;
|
||||
RadarValues GetRadarValues() const;
|
||||
void SetRadarValues( const RadarValues &rv ); // for pre-populating cache
|
||||
int GetMeter() const;
|
||||
int GetTotalMeter() const;
|
||||
float GetLengthSeconds() const;
|
||||
|
||||
Reference in New Issue
Block a user