Files
itgmania212121/stepmania/src/SongManager.cpp
T

1107 lines
30 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
2002-02-28 19:40:40 +00:00
/*
-----------------------------------------------------------------------------
Class: SongManager
Desc: See header.
2002-05-19 01:59:48 +00:00
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
2002-02-28 19:40:40 +00:00
Chris Danford
-----------------------------------------------------------------------------
*/
#include "SongManager.h"
#include "IniFile.h"
2002-05-01 19:14:55 +00:00
#include "RageLog.h"
#include "MsdFile.h"
#include "NotesLoaderDWI.h"
2003-06-15 01:53:51 +00:00
#include "BannerCache.h"
2003-07-22 07:47:27 +00:00
#include "arch/arch.h"
2002-06-14 22:25:22 +00:00
2002-07-23 01:41:40 +00:00
#include "GameState.h"
2002-05-29 09:47:24 +00:00
#include "PrefsManager.h"
2002-07-27 19:29:51 +00:00
#include "RageException.h"
#include "arch/LoadingWindow/LoadingWindow.h"
2003-07-17 20:11:24 +00:00
#include "Course.h"
2002-02-28 19:40:40 +00:00
#include "AnnouncerManager.h"
#include "ThemeManager.h"
#include "GameManager.h"
2003-07-22 07:47:27 +00:00
#include "RageFile.h"
2003-11-25 22:56:48 +00:00
#include "RageTextureManager.h"
2004-03-26 07:56:18 +00:00
#include "Sprite.h"
#include "ProfileManager.h"
2004-02-08 01:05:53 +00:00
#include "MemoryCardManager.h"
#include "NotesLoaderSM.h"
#include "SongUtil.h"
#include "StepsUtil.h"
#include "CourseUtil.h"
2004-04-23 02:08:11 +00:00
#include "RageFileManager.h"
2004-05-02 00:21:47 +00:00
#include "UnlockSystem.h"
2004-05-25 05:52:57 +00:00
#include "CatalogXml.h"
2002-05-19 01:59:48 +00:00
SongManager* SONGMAN = NULL; // global and accessable from anywhere in our program
2002-02-28 19:40:40 +00:00
2003-12-10 09:26:05 +00:00
#define SONGS_DIR "Songs/"
#define COURSES_DIR "Courses/"
2004-05-25 05:52:57 +00:00
#define DATA_DIR "Data/"
2003-01-22 05:29:27 +00:00
2004-04-23 00:26:51 +00:00
#define MAX_EDITS_PER_PROFILE 200
2002-02-28 19:40:40 +00:00
2002-09-03 06:33:08 +00:00
#define NUM_GROUP_COLORS THEME->GetMetricI("SongManager","NumGroupColors")
#define GROUP_COLOR( i ) THEME->GetMetricC("SongManager",ssprintf("GroupColor%d",i+1))
CachedThemeMetricC BEGINNER_COLOR ("SongManager","BeginnerColor");
CachedThemeMetricC EASY_COLOR ("SongManager","EasyColor");
CachedThemeMetricC MEDIUM_COLOR ("SongManager","MediumColor");
CachedThemeMetricC HARD_COLOR ("SongManager","HardColor");
CachedThemeMetricC CHALLENGE_COLOR ("SongManager","ChallengeColor");
2004-02-08 01:05:53 +00:00
CachedThemeMetricC EDIT_COLOR ("SongManager","EditColor");
CachedThemeMetricC EXTRA_COLOR ("SongManager","ExtraColor");
CachedThemeMetricI EXTRA_COLOR_METER ("SongManager","ExtraColorMeter");
vector<RageColor> g_vGroupColors;
2003-07-31 22:48:49 +00:00
RageTimer g_LastMetricUpdate; /* can't use RageTimer globally */
2002-04-16 17:31:00 +00:00
2003-07-31 22:48:49 +00:00
static void UpdateMetrics()
{
2004-01-12 00:31:43 +00:00
if( !g_LastMetricUpdate.IsZero() && g_LastMetricUpdate.PeekDeltaTime() < 1 )
2003-07-31 22:48:49 +00:00
return;
g_LastMetricUpdate.Touch();
g_vGroupColors.clear();
for( int i=0; i<NUM_GROUP_COLORS; i++ )
g_vGroupColors.push_back( GROUP_COLOR(i) );
BEGINNER_COLOR.Refresh();
EASY_COLOR.Refresh();
MEDIUM_COLOR.Refresh();
HARD_COLOR.Refresh();
CHALLENGE_COLOR.Refresh();
2004-02-08 01:05:53 +00:00
EDIT_COLOR.Refresh();
EXTRA_COLOR.Refresh();
EXTRA_COLOR_METER.Refresh();
2003-07-31 22:48:49 +00:00
}
2002-02-28 19:40:40 +00:00
2004-05-25 05:52:57 +00:00
SongManager::SongManager()
2002-02-28 19:40:40 +00:00
{
2004-01-12 00:31:43 +00:00
g_LastMetricUpdate.SetZero();
UpdateMetrics();
2002-02-28 19:40:40 +00:00
}
SongManager::~SongManager()
{
FreeSongs();
2004-01-21 03:09:59 +00:00
FreeCourses();
}
2004-05-25 05:52:57 +00:00
void SongManager::InitAll( LoadingWindow *ld )
{
InitSongsFromDisk( ld );
InitCoursesFromDisk( ld );
InitAutogenCourses();
if( ld )
ld->SetText( "Saving Catalog.xml ..." );
2004-05-25 05:52:57 +00:00
SaveCatalogXml( DATA_DIR );
}
2004-02-21 01:52:00 +00:00
void SongManager::Reload( LoadingWindow *ld )
{
FlushDirCache();
2004-02-21 01:52:00 +00:00
if( ld )
ld->SetText( "Reloading ..." );
2004-02-16 05:35:06 +00:00
// save scores before unloading songs, of the scores will be lost
PROFILEMAN->SaveMachineProfile();
2003-08-06 08:06:27 +00:00
FreeSongs();
FreeCourses();
2003-08-06 08:06:27 +00:00
2003-12-31 09:32:21 +00:00
/* Always check songs for changes. */
const bool OldVal = PREFSMAN->m_bFastLoad;
PREFSMAN->m_bFastLoad = false;
2003-12-30 04:26:39 +00:00
2004-05-25 05:52:57 +00:00
InitAll( ld );
2004-02-16 05:35:06 +00:00
// reload scores afterward
PROFILEMAN->LoadMachineProfile();
2003-12-30 04:26:39 +00:00
2003-12-31 09:32:21 +00:00
PREFSMAN->m_bFastLoad = OldVal;
2003-07-22 07:47:27 +00:00
}
2002-02-28 19:40:40 +00:00
void SongManager::InitSongsFromDisk( LoadingWindow *ld )
2002-02-28 19:40:40 +00:00
{
2003-09-25 02:27:17 +00:00
RageTimer tm;
2003-07-22 07:47:27 +00:00
LoadStepManiaSongDir( SONGS_DIR, ld );
2003-10-21 20:57:48 +00:00
LOG->Trace( "Found %d songs in %f seconds.", (int)m_pSongs.size(), tm.GetDeltaTime() );
2002-03-06 08:25:09 +00:00
}
void SongManager::SanityCheckGroupDir( CString sDir ) const
{
// Check to see if they put a song directly inside the group folder.
CStringArray arrayFiles;
GetDirListing( sDir + "/*.mp3", arrayFiles );
GetDirListing( sDir + "/*.ogg", arrayFiles );
GetDirListing( sDir + "/*.wav", arrayFiles );
if( !arrayFiles.empty() )
2002-12-21 19:34:02 +00:00
RageException::Throw(
"The folder '%s' contains music files.\n\n"
"This means that you have a music outside of a song folder.\n"
"All song folders must reside in a group folder. For example, 'Songs/DDR 4th Mix/B4U'.\n"
"See the StepMania readme for more info.",
2003-04-25 00:01:35 +00:00
sDir.c_str()
);
}
2002-09-10 07:11:29 +00:00
void SongManager::AddGroup( CString sDir, CString sGroupDirName )
{
unsigned j;
2003-08-06 08:06:27 +00:00
for(j = 0; j < m_sGroupNames.size(); ++j)
if( sGroupDirName == m_sGroupNames[j] ) break;
2002-09-10 07:11:29 +00:00
2003-08-06 08:06:27 +00:00
if( j != m_sGroupNames.size() )
2002-09-10 07:11:29 +00:00
return; /* the group is already added */
// Look for a group banner in this group folder
CStringArray arrayGroupBanners;
2003-01-23 04:43:22 +00:00
GetDirListing( sDir+sGroupDirName+"/*.png", arrayGroupBanners );
GetDirListing( sDir+sGroupDirName+"/*.jpg", arrayGroupBanners );
GetDirListing( sDir+sGroupDirName+"/*.gif", arrayGroupBanners );
GetDirListing( sDir+sGroupDirName+"/*.bmp", arrayGroupBanners );
2002-09-10 07:11:29 +00:00
2003-06-17 21:34:43 +00:00
CString sBannerPath;
if( !arrayGroupBanners.empty() )
2003-12-10 09:44:16 +00:00
sBannerPath = sDir+sGroupDirName+"/"+arrayGroupBanners[0] ;
2003-06-17 21:34:43 +00:00
else
{
// Look for a group banner in the parent folder
GetDirListing( sDir+sGroupDirName+".png", arrayGroupBanners );
GetDirListing( sDir+sGroupDirName+".jpg", arrayGroupBanners );
GetDirListing( sDir+sGroupDirName+".gif", arrayGroupBanners );
GetDirListing( sDir+sGroupDirName+".bmp", arrayGroupBanners );
if( !arrayGroupBanners.empty() )
sBannerPath = sDir+arrayGroupBanners[0];
2002-09-10 07:11:29 +00:00
}
2003-12-15 07:53:30 +00:00
LOG->Trace( "Group banner for '%s' is '%s'.", sGroupDirName.c_str(),
sBannerPath != ""? sBannerPath.c_str():"(none)" );
2003-08-06 08:06:27 +00:00
m_sGroupNames.push_back( sGroupDirName );
m_sGroupBannerPaths.push_back(sBannerPath);
2002-09-10 07:11:29 +00:00
}
2002-11-17 09:13:35 +00:00
void SongManager::LoadStepManiaSongDir( CString sDir, LoadingWindow *ld )
2002-03-06 08:25:09 +00:00
{
2003-01-23 04:43:22 +00:00
/* Make sure sDir has a trailing slash. */
2003-12-10 09:44:16 +00:00
if( sDir.Right(1) != "/" )
sDir += "/";
2002-03-06 08:25:09 +00:00
// Find all group directories in "Songs" folder
CStringArray arrayGroupDirs;
2003-01-23 04:43:22 +00:00
GetDirListing( sDir+"*", arrayGroupDirs, true );
2002-03-06 08:25:09 +00:00
SortCStringArray( arrayGroupDirs );
for( unsigned i=0; i< arrayGroupDirs.size(); i++ ) // for each dir in /Songs/
2002-03-06 08:25:09 +00:00
{
CString sGroupDirName = arrayGroupDirs[i];
if( 0 == stricmp( sGroupDirName, "cvs" ) ) // the directory called "CVS"
continue; // ignore it
2003-01-23 04:43:22 +00:00
SanityCheckGroupDir(sDir+sGroupDirName);
2002-03-06 08:25:09 +00:00
// Find all Song folders in this group directory
CStringArray arraySongDirs;
2003-12-10 09:44:16 +00:00
GetDirListing( sDir+sGroupDirName + "/*", arraySongDirs, true, true );
2002-03-06 08:25:09 +00:00
SortCStringArray( arraySongDirs );
unsigned j;
2002-09-09 02:23:47 +00:00
int loaded = 0;
for( j=0; j< arraySongDirs.size(); j++ ) // for each song dir
2002-03-06 08:25:09 +00:00
{
CString sSongDirName = arraySongDirs[j];
2003-01-23 04:43:22 +00:00
if( 0 == stricmp( Basename(sSongDirName), "cvs" ) ) // the directory called "CVS"
2002-03-06 08:25:09 +00:00
continue; // ignore it
// this is a song directory. Load a new song!
2002-11-17 09:13:35 +00:00
if( ld ) {
2003-01-23 04:43:22 +00:00
ld->SetText( ssprintf("Loading songs...\n%s\n%s",
2003-04-25 00:01:35 +00:00
Basename(sGroupDirName).c_str(),
Basename(sSongDirName).c_str()));
2002-11-17 09:13:35 +00:00
ld->Paint();
}
2002-03-06 08:25:09 +00:00
Song* pNewSong = new Song;
2003-01-23 04:43:22 +00:00
if( !pNewSong->LoadFromSongDir( sSongDirName ) ) {
/* The song failed to load. */
delete pNewSong;
continue;
}
2002-10-31 04:23:39 +00:00
m_pSongs.push_back( pNewSong );
2002-09-09 02:23:47 +00:00
loaded++;
}
/* Don't add the group name if we didn't load any songs in this group. */
if(!loaded) continue;
/* Add this group to the group array. */
2002-09-10 07:11:29 +00:00
AddGroup(sDir, sGroupDirName);
2003-06-15 01:53:51 +00:00
/* Cache and load the group banner. */
BANNERCACHE->CacheBanner( GetGroupBannerPath(sGroupDirName) );
/* Load the group sym links (if any)*/
LoadGroupSymLinks(sDir, sGroupDirName);
}
}
void SongManager::LoadGroupSymLinks(CString sDir, CString sGroupFolder)
{
// Find all symlink files in this folder
CStringArray arraySymLinks;
2003-12-10 09:44:16 +00:00
GetDirListing( sDir+sGroupFolder+"/*.include", arraySymLinks, false );
SortCStringArray( arraySymLinks );
for( unsigned s=0; s< arraySymLinks.size(); s++ ) // for each symlink in this dir, add it in as a song.
{
MsdFile msdF;
2003-12-10 09:44:16 +00:00
msdF.ReadFile( sDir+sGroupFolder+"/"+arraySymLinks[s].c_str() );
CString sSymDestination = msdF.GetParam(0,1); // Should only be 1 vale&param...period.
Song* pNewSong = new Song;
if( !pNewSong->LoadFromSongDir( sSymDestination ) )
delete pNewSong; // The song failed to load.
else
{
2004-05-24 03:41:39 +00:00
pNewSong->m_vpSteps.clear(); // No memory hogs..
pNewSong->m_BackgroundChanges.clear();
pNewSong->m_bIsSymLink = true; // Very important so we don't double-parse later
pNewSong->m_sGroupName = sGroupFolder;
m_pSongs.push_back( pNewSong );
}
2002-03-06 08:25:09 +00:00
}
}
2003-11-25 22:56:48 +00:00
void SongManager::PreloadSongImages()
{
ASSERT( TEXTUREMAN );
if( PREFSMAN->m_BannerCache != PrefsManager::BNCACHE_FULL )
return;
const vector<Song*> &songs = SONGMAN->GetAllSongs();
unsigned i;
for( i = 0; i < songs.size(); ++i )
{
if( !songs[i]->HasBanner() )
continue;
2004-03-26 07:56:18 +00:00
const RageTextureID ID = Sprite::SongBannerTexture( songs[i]->GetBannerPath() );
2003-11-25 22:56:48 +00:00
TEXTUREMAN->CacheTexture( ID );
}
2003-11-25 23:01:53 +00:00
vector<Course*> courses;
SONGMAN->GetAllCourses( courses, false );
for( i = 0; i < courses.size(); ++i )
{
if( !courses[i]->HasBanner() )
continue;
2004-03-26 07:56:18 +00:00
const RageTextureID ID = Sprite::SongBannerTexture( courses[i]->m_sBannerPath );
2003-11-25 23:01:53 +00:00
TEXTUREMAN->CacheTexture( ID );
}
2003-11-25 22:56:48 +00:00
}
void SongManager::FreeSongs()
2002-02-28 19:40:40 +00:00
{
2004-05-29 20:15:07 +00:00
m_sGroupNames.clear();
m_sGroupBannerPaths.clear();
for( unsigned i=0; i<m_pSongs.size(); i++ )
2002-02-28 19:40:40 +00:00
SAFE_DELETE( m_pSongs[i] );
2002-10-24 20:15:24 +00:00
m_pSongs.clear();
2002-06-14 22:25:22 +00:00
2003-08-06 08:06:27 +00:00
m_sGroupBannerPaths.clear();
2004-05-29 20:15:07 +00:00
for( int i = 0; i < NUM_PROFILE_SLOTS; ++i )
m_pBestSongs[i].clear();
m_pShuffledSongs.clear();
2002-02-28 19:40:40 +00:00
}
CString SongManager::GetGroupBannerPath( CString sGroupName )
2002-03-06 08:25:09 +00:00
{
unsigned i;
2003-08-06 08:06:27 +00:00
for(i = 0; i < m_sGroupNames.size(); ++i)
if( sGroupName == m_sGroupNames[i] ) break;
2002-03-06 08:25:09 +00:00
2003-08-06 08:06:27 +00:00
if( i == m_sGroupNames.size() )
2002-03-06 08:25:09 +00:00
return "";
2002-09-07 07:24:44 +00:00
2003-08-06 08:06:27 +00:00
return m_sGroupBannerPaths[i];
2002-03-06 08:25:09 +00:00
}
2002-04-01 02:04:43 +00:00
void SongManager::GetGroupNames( CStringArray &AddTo )
{
2003-08-06 08:06:27 +00:00
AddTo.insert(AddTo.end(), m_sGroupNames.begin(), m_sGroupNames.end() );
2002-04-16 17:31:00 +00:00
}
2002-04-01 02:04:43 +00:00
bool SongManager::DoesGroupExist( CString sGroupName )
{
2003-09-21 21:35:59 +00:00
for( unsigned i = 0; i < m_sGroupNames.size(); ++i )
if( !m_sGroupNames[i].CompareNoCase(sGroupName) )
return true;
return false;
}
RageColor SongManager::GetGroupColor( const CString &sGroupName )
2002-04-16 17:31:00 +00:00
{
2003-07-31 22:48:49 +00:00
UpdateMetrics();
2002-04-16 17:31:00 +00:00
// search for the group index
unsigned i;
2003-08-06 08:06:27 +00:00
for( i=0; i<m_sGroupNames.size(); i++ )
2002-04-01 02:04:43 +00:00
{
2003-08-06 08:06:27 +00:00
if( m_sGroupNames[i] == sGroupName )
2002-04-16 17:31:00 +00:00
break;
2002-04-01 02:04:43 +00:00
}
2003-12-15 07:53:30 +00:00
RAGE_ASSERT_M( i != m_sGroupNames.size(), sGroupName ); // this is not a valid group
2002-04-16 17:31:00 +00:00
return g_vGroupColors[i%g_vGroupColors.size()];
2002-04-01 02:04:43 +00:00
}
2002-12-17 05:22:32 +00:00
RageColor SongManager::GetSongColor( const Song* pSong )
{
ASSERT( pSong );
/* XXX:
* Previously, this matched all notes, which set a song to "extra" if it
* had any 10-foot steps at all, even edits or doubles.
*
* For now, only look at notes for the current note type. This means that
* if a song has 10-foot steps on Doubles, it'll only show up red in Doubles.
* That's not too bad, I think. This will also change it in the song scroll,
* which is a little odd but harmless.
*
2003-04-20 22:57:35 +00:00
* XXX: Ack. This means this function can only be called when we have a style
* set up, which is too restrictive. How to handle this?
2004-02-29 07:36:03 +00:00
*/
// const StepsType st = GAMESTATE->GetCurrentStyleDef()->m_StepsType;
2004-05-24 03:41:39 +00:00
for( unsigned i=0; i<pSong->m_vpSteps.size(); i++ )
{
2004-05-24 03:41:39 +00:00
const Steps* pSteps = pSong->m_vpSteps[i];
switch( pSteps->GetDifficulty() )
2004-02-29 07:36:03 +00:00
{
case DIFFICULTY_CHALLENGE:
case DIFFICULTY_EDIT:
2003-07-17 20:11:24 +00:00
continue;
2004-02-29 07:36:03 +00:00
}
2004-05-24 03:41:39 +00:00
// if(pSteps->m_StepsType != nt)
2003-04-20 22:57:35 +00:00
// continue;
2004-05-24 03:41:39 +00:00
if( pSteps->GetMeter() >= EXTRA_COLOR_METER )
return EXTRA_COLOR;
}
return GetGroupColor( pSong->m_sGroupName );
}
RageColor SongManager::GetDifficultyColor( Difficulty dc ) const
{
switch( dc )
{
case DIFFICULTY_BEGINNER: return BEGINNER_COLOR;
case DIFFICULTY_EASY: return EASY_COLOR;
case DIFFICULTY_MEDIUM: return MEDIUM_COLOR;
case DIFFICULTY_HARD: return HARD_COLOR;
case DIFFICULTY_CHALLENGE: return CHALLENGE_COLOR;
2004-02-08 01:05:53 +00:00
case DIFFICULTY_EDIT: return EDIT_COLOR;
default: ASSERT(0); return EDIT_COLOR;
}
}
static void GetSongsFromVector( const vector<Song*> &Songs, vector<Song*> &AddTo, CString sGroupName, int iMaxStages )
2002-04-01 02:04:43 +00:00
{
AddTo.clear();
for( unsigned i=0; i<Songs.size(); i++ )
if( sGroupName==GROUP_ALL_MUSIC || sGroupName==Songs[i]->m_sGroupName )
if( SongManager::GetNumStagesForSong(Songs[i]) <= iMaxStages )
AddTo.push_back( Songs[i] );
}
void SongManager::GetSongs( vector<Song*> &AddTo, CString sGroupName, int iMaxStages ) const
{
GetSongsFromVector( m_pSongs, AddTo, sGroupName, iMaxStages );
}
void SongManager::GetBestSongs( vector<Song*> &AddTo, CString sGroupName, int iMaxStages, ProfileSlot slot ) const
{
GetSongsFromVector( m_pBestSongs[slot], AddTo, sGroupName, iMaxStages );
2002-04-01 02:04:43 +00:00
}
2002-04-16 17:31:00 +00:00
2003-02-05 18:34:27 +00:00
int SongManager::GetNumSongs() const
{
return m_pSongs.size();
}
2003-02-05 18:34:27 +00:00
int SongManager::GetNumGroups() const
{
2003-08-06 08:06:27 +00:00
return m_sGroupNames.size();
}
int SongManager::GetNumCourses() const
{
return m_pCourses.size();
}
CString SongManager::ShortenGroupName( CString sLongGroupName )
2002-04-16 17:31:00 +00:00
{
sLongGroupName.Replace( "Dance Dance Revolution", "DDR" );
sLongGroupName.Replace( "dance dance revolution", "DDR" );
sLongGroupName.Replace( "DANCE DANCE REVOLUTION", "DDR" );
sLongGroupName.Replace( "Pump It Up", "PIU" );
sLongGroupName.Replace( "pump it up", "PIU" );
sLongGroupName.Replace( "PUMP IT UP", "PIU" );
sLongGroupName.Replace( "ParaParaParadise", "PPP" );
sLongGroupName.Replace( "paraparaparadise", "PPP" );
sLongGroupName.Replace( "PARAPARAPARADISE", "PPP" );
sLongGroupName.Replace( "Para Para Paradise", "PPP" );
sLongGroupName.Replace( "para para paradise", "PPP" );
sLongGroupName.Replace( "PARA PARA PARADISE", "PPP" );
sLongGroupName.Replace( "Dancing Stage", "DS" );
sLongGroupName.Replace( "Dancing Stage", "DS" );
sLongGroupName.Replace( "Dancing Stage", "DS" );
2003-05-22 01:34:03 +00:00
sLongGroupName.Replace( "Ez2dancer", "EZ2" );
sLongGroupName.Replace( "Ez 2 Dancer", "EZ2");
sLongGroupName.Replace( "Technomotion", "TM");
sLongGroupName.Replace( "Dance Station 3DDX", "3DDX");
sLongGroupName.Replace( "DS3DDX", "3DDX");
sLongGroupName.Replace( "BeatMania", "BM");
sLongGroupName.Replace( "Beatmania", "BM");
sLongGroupName.Replace( "BEATMANIA", "BM");
sLongGroupName.Replace( "beatmania", "BM");
return sLongGroupName;
}
int SongManager::GetNumStagesForSong( const Song* pSong )
{
ASSERT( pSong );
if( pSong->m_fMusicLengthSeconds > PREFSMAN->m_fMarathonVerSongSeconds )
return 3;
if( pSong->m_fMusicLengthSeconds > PREFSMAN->m_fLongVerSongSeconds )
return 2;
else
return 1;
2002-04-16 17:31:00 +00:00
}
2002-06-14 22:25:22 +00:00
void SongManager::InitCoursesFromDisk( LoadingWindow *ld )
2002-06-14 22:25:22 +00:00
{
2003-04-11 00:29:44 +00:00
unsigned i;
LOG->Trace( "Loading courses." );
2002-07-02 17:34:20 +00:00
//
2003-04-11 00:29:44 +00:00
// Load courses from in Courses dir
2002-07-02 17:34:20 +00:00
//
CStringArray saCourseFiles;
2003-07-22 07:47:27 +00:00
GetDirListing( COURSES_DIR "*.crs", saCourseFiles, false, true );
2003-04-11 00:29:44 +00:00
for( i=0; i<saCourseFiles.size(); i++ )
2002-07-02 17:34:20 +00:00
{
2003-01-22 05:29:27 +00:00
Course* pCourse = new Course;
2003-02-05 19:16:00 +00:00
pCourse->LoadFromCRSFile( saCourseFiles[i] );
m_pCourses.push_back( pCourse );
if( ld )
{
ld->SetText( ssprintf("Loading courses...\n%s\n%s",
"Courses",
Basename(saCourseFiles[i]).c_str()));
ld->Paint();
}
2002-07-02 17:34:20 +00:00
}
2003-04-11 00:29:44 +00:00
// Find all group directories in Courses dir
CStringArray arrayGroupDirs;
2003-07-22 07:47:27 +00:00
GetDirListing( COURSES_DIR "*", arrayGroupDirs, true );
2003-04-11 00:29:44 +00:00
SortCStringArray( arrayGroupDirs );
for( i=0; i< arrayGroupDirs.size(); i++ ) // for each dir in /Courses/
{
CString sGroupDirName = arrayGroupDirs[i];
if( 0 == stricmp( sGroupDirName, "cvs" ) ) // the directory called "CVS"
continue; // ignore it
// Find all CRS files in this group directory
CStringArray arrayCoursePaths;
2003-12-10 09:44:16 +00:00
GetDirListing( COURSES_DIR + sGroupDirName + "/*.crs", arrayCoursePaths, false, true );
2003-04-11 00:29:44 +00:00
SortCStringArray( arrayCoursePaths );
for( unsigned j=0; j<arrayCoursePaths.size(); j++ )
{
if( ld )
{
ld->SetText( ssprintf("Loading courses...\n%s\n%s",
Basename(arrayGroupDirs[i]).c_str(),
Basename(arrayCoursePaths[j]).c_str()));
ld->Paint();
}
2003-04-11 00:29:44 +00:00
Course* pCourse = new Course;
pCourse->LoadFromCRSFile( arrayCoursePaths[j] );
m_pCourses.push_back( pCourse );
}
}
}
void SongManager::InitAutogenCourses()
{
2002-07-02 17:34:20 +00:00
//
// Create group courses for Endless and Nonstop
2002-07-02 17:34:20 +00:00
//
2002-06-14 22:25:22 +00:00
CStringArray saGroupNames;
this->GetGroupNames( saGroupNames );
2004-05-20 23:14:36 +00:00
Course* pCourse;
for( unsigned g=0; g<saGroupNames.size(); g++ ) // foreach Group
2002-06-14 22:25:22 +00:00
{
CString sGroupName = saGroupNames[g];
2003-01-03 05:56:28 +00:00
vector<Song*> apGroupSongs;
GetSongs( apGroupSongs, sGroupName );
2002-06-14 22:25:22 +00:00
2004-05-20 23:14:36 +00:00
// Generate random courses from each group.
pCourse = new Course;
2004-05-20 23:14:36 +00:00
pCourse->AutogenEndlessFromGroup( sGroupName, DIFFICULTY_MEDIUM );
m_pCourses.push_back( pCourse );
pCourse = new Course;
2004-05-20 23:14:36 +00:00
pCourse->AutogenNonstopFromGroup( sGroupName, DIFFICULTY_MEDIUM );
m_pCourses.push_back( pCourse );
2002-06-14 22:25:22 +00:00
}
vector<Song*> apCourseSongs = GetAllSongs();
2004-05-20 23:14:36 +00:00
// Generate "All Songs" endless course.
pCourse = new Course;
2004-05-20 23:14:36 +00:00
pCourse->AutogenEndlessFromGroup( "", DIFFICULTY_MEDIUM );
m_pCourses.push_back( pCourse );
/* Generate Oni courses from artists. Only create courses if we have at least
* four songs from an artist; create 3- and 4-song courses. */
{
2004-05-25 04:08:21 +00:00
/* We normally sort by translit artist. However, display artist is more
* consistent. For example, transliterated Japanese names are alternately
* spelled given- and family-name first, but display titles are more consistent. */
vector<Song*> apSongs = this->GetAllSongs();
2004-05-25 04:08:21 +00:00
SongUtil::SortSongPointerArrayByDisplayArtist( apSongs );
CString sCurArtist = "";
2004-05-25 04:08:21 +00:00
CString sCurArtistTranslit = "";
int iCurArtistCount = 0;
2004-05-25 04:08:21 +00:00
vector<Song *> aSongs;
unsigned i = 0;
do {
2004-05-25 04:08:21 +00:00
CString sArtist = i >= apSongs.size()? "": apSongs[i]->GetDisplayArtist();
CString sTranslitArtist = i >= apSongs.size()? "": apSongs[i]->GetTranslitArtist();
if( i < apSongs.size() && !sCurArtist.CompareNoCase(sArtist) )
{
aSongs.push_back( apSongs[i] );
++iCurArtistCount;
continue;
}
/* Different artist, or we're at the end. If we have enough entries for
* the last artist, add it. Skip blanks and "Unknown artist". */
2004-05-25 04:08:21 +00:00
if( iCurArtistCount >= 3 && sCurArtistTranslit != "" &&
sCurArtistTranslit.CompareNoCase("Unknown artist") )
{
pCourse = new Course;
2004-05-25 04:08:21 +00:00
pCourse->AutogenOniFromArtist( sCurArtist, sCurArtistTranslit, aSongs, DIFFICULTY_HARD );
m_pCourses.push_back( pCourse );
}
aSongs.clear();
if( i < apSongs.size() )
{
sCurArtist = sArtist;
2004-05-25 04:08:21 +00:00
sCurArtistTranslit = sTranslitArtist;
iCurArtistCount = 1;
aSongs.push_back( apSongs[i] );
}
} while( i++ < apSongs.size() );
}
2002-06-14 22:25:22 +00:00
}
2003-01-22 05:29:27 +00:00
void SongManager::FreeCourses()
2002-06-14 22:25:22 +00:00
{
2003-01-22 05:29:27 +00:00
for( unsigned i=0; i<m_pCourses.size(); i++ )
delete m_pCourses[i];
m_pCourses.clear();
2004-05-29 20:15:07 +00:00
for( int i = 0; i < NUM_PROFILE_SLOTS; ++i )
m_pBestCourses[i].clear();
m_pShuffledCourses.clear();
2002-06-14 22:25:22 +00:00
}
2002-08-01 13:42:56 +00:00
2002-12-21 18:23:37 +00:00
/* Called periodically to wipe out cached NoteData. This is called when we change
* screens. */
2003-12-21 02:54:23 +00:00
void SongManager::Cleanup()
2002-12-21 18:23:37 +00:00
{
2003-12-21 02:54:23 +00:00
unsigned i;
for( i=0; i<m_pSongs.size(); i++ )
2002-12-21 18:23:37 +00:00
{
2003-05-05 04:13:39 +00:00
Song* pSong = m_pSongs[i];
2004-05-24 03:41:39 +00:00
for( unsigned n=0; n<pSong->m_vpSteps.size(); n++ )
2003-05-05 04:13:39 +00:00
{
2004-05-24 03:41:39 +00:00
Steps* pSteps = pSong->m_vpSteps[n];
pSteps->Compress();
2002-12-21 18:23:37 +00:00
}
}
2003-12-21 02:54:23 +00:00
/* Erase cached course info. */
for( i=0; i < m_pCourses.size(); i++ )
m_pCourses[i]->ClearCache();
}
2002-12-21 18:23:37 +00:00
2003-07-20 00:30:24 +00:00
void SongManager::GetAllCourses( vector<Course*> &AddTo, bool bIncludeAutogen )
{
for( unsigned i=0; i<m_pCourses.size(); i++ )
if( bIncludeAutogen || !m_pCourses[i]->m_bIsAutogen )
AddTo.push_back( m_pCourses[i] );
}
void SongManager::GetNonstopCourses( vector<Course*> &AddTo, bool bIncludeAutogen )
{
2003-01-22 05:29:27 +00:00
for( unsigned i=0; i<m_pCourses.size(); i++ )
2003-06-30 08:06:47 +00:00
if( m_pCourses[i]->IsNonstop() )
if( bIncludeAutogen || !m_pCourses[i]->m_bIsAutogen )
AddTo.push_back( m_pCourses[i] );
2002-12-21 18:23:37 +00:00
}
void SongManager::GetOniCourses( vector<Course*> &AddTo, bool bIncludeAutogen )
{
2003-01-22 05:29:27 +00:00
for( unsigned i=0; i<m_pCourses.size(); i++ )
2003-06-30 08:06:47 +00:00
if( m_pCourses[i]->IsOni() )
if( bIncludeAutogen || !m_pCourses[i]->m_bIsAutogen )
AddTo.push_back( m_pCourses[i] );
}
void SongManager::GetEndlessCourses( vector<Course*> &AddTo, bool bIncludeAutogen )
{
2003-01-22 05:29:27 +00:00
for( unsigned i=0; i<m_pCourses.size(); i++ )
2003-06-30 08:06:47 +00:00
if( m_pCourses[i]->IsEndless() )
if( bIncludeAutogen || !m_pCourses[i]->m_bIsAutogen )
AddTo.push_back( m_pCourses[i] );
}
2002-08-30 20:49:56 +00:00
bool SongManager::GetExtraStageInfoFromCourse( bool bExtra2, CString sPreferredGroup,
2004-05-24 03:41:39 +00:00
Song*& pSongOut, Steps*& pStepsOut, PlayerOptions& po_out, SongOptions& so_out )
2002-08-30 20:49:56 +00:00
{
2003-12-10 09:44:16 +00:00
const CString sCourseSuffix = sPreferredGroup + "/" + (bExtra2 ? "extra2" : "extra1") + ".crs";
2003-07-22 07:47:27 +00:00
CString sCoursePath = SONGS_DIR + sCourseSuffix;
2003-02-28 08:42:02 +00:00
/* Couldn't find course in DWI path or alternative song folders */
if( !DoesFileExist(sCoursePath) )
return false;
2002-08-30 20:49:56 +00:00
Course course;
2003-02-05 19:16:00 +00:00
course.LoadFromCRSFile( sCoursePath );
if( course.GetEstimatedNumStages() <= 0 ) return false;
2002-08-30 20:49:56 +00:00
Trail *pTrail = course.GetTrail( GAMESTATE->GetCurrentStyleDef()->m_StepsType, COURSE_DIFFICULTY_REGULAR );
if( pTrail->m_vEntries.empty() )
return false;
2003-04-23 07:28:24 +00:00
po_out.Init();
po_out.FromString( pTrail->m_vEntries[0].Modifiers );
2003-04-23 07:28:24 +00:00
so_out.Init();
so_out.FromString( pTrail->m_vEntries[0].Modifiers );
pSongOut = pTrail->m_vEntries[0].pSong;
2004-05-24 03:41:39 +00:00
pStepsOut = pTrail->m_vEntries[0].pSteps;
2003-04-23 07:28:24 +00:00
return true;
2002-08-30 20:49:56 +00:00
}
2002-08-01 13:42:56 +00:00
/* Return true if n1 < n2. */
2003-08-03 00:13:55 +00:00
bool CompareNotesPointersForExtra(const Steps *n1, const Steps *n2)
{
/* Equate CHALLENGE to HARD. */
Difficulty d1 = min(n1->GetDifficulty(), DIFFICULTY_HARD);
Difficulty d2 = min(n2->GetDifficulty(), DIFFICULTY_HARD);
if(d1 < d2) return true;
if(d1 > d2) return false;
/* n1 difficulty == n2 difficulty */
if(StepsUtil::CompareNotesPointersByMeter(n1,n2)) return true;
if(StepsUtil::CompareNotesPointersByMeter(n2,n1)) return false;
/* n1 meter == n2 meter */
return StepsUtil::CompareNotesPointersByRadarValues(n1,n2);
}
2003-07-30 02:01:19 +00:00
void SongManager::GetExtraStageInfo( bool bExtra2, const StyleDef *sd,
2004-05-24 03:41:39 +00:00
Song*& pSongOut, Steps*& pStepsOut, PlayerOptions& po_out, SongOptions& so_out )
2002-08-01 13:42:56 +00:00
{
2003-07-30 05:34:47 +00:00
CString sGroup = GAMESTATE->m_sPreferredGroup;
2004-03-24 03:10:12 +00:00
if( sGroup == GROUP_ALL_MUSIC )
2003-04-19 22:24:01 +00:00
{
2004-03-24 03:10:12 +00:00
if( GAMESTATE->m_pCurSong == NULL )
{
/* This normally shouldn't happen, but it's helpful to permit it for testing. */
LOG->Warn( "GetExtraStageInfo() called in GROUP_ALL_MUSIC, but GAMESTATE->m_pCurSong == NULL" );
GAMESTATE->m_pCurSong = SONGMAN->GetRandomSong();
}
2003-07-30 05:34:47 +00:00
sGroup = GAMESTATE->m_pCurSong->m_sGroupName;
2003-04-19 22:24:01 +00:00
}
2004-03-24 03:10:12 +00:00
RAGE_ASSERT_M( sGroup != "", ssprintf("%p '%s' '%s'",
2003-07-31 01:27:54 +00:00
GAMESTATE->m_pCurSong,
GAMESTATE->m_pCurSong? GAMESTATE->m_pCurSong->GetSongDir().c_str():"",
2004-03-24 03:10:12 +00:00
GAMESTATE->m_pCurSong? GAMESTATE->m_pCurSong->m_sGroupName.c_str():"") );
2003-04-19 22:24:01 +00:00
2004-05-24 03:41:39 +00:00
if(GetExtraStageInfoFromCourse(bExtra2, sGroup, pSongOut, pStepsOut, po_out, so_out))
2002-08-01 13:42:56 +00:00
return;
// Choose a hard song for the extra stage
2003-08-03 00:13:55 +00:00
Song* pExtra1Song = NULL; // the absolute hardest Song and Steps. Use this for extra stage 1.
Steps* pExtra1Notes = NULL;
Song* pExtra2Song = NULL; // a medium-hard Song and Steps. Use this for extra stage 2.
Steps* pExtra2Notes = NULL;
2002-08-01 13:42:56 +00:00
2003-01-03 05:56:28 +00:00
vector<Song*> apSongs;
SONGMAN->GetSongs( apSongs, sGroup );
for( unsigned s=0; s<apSongs.size(); s++ ) // foreach song
2002-08-01 13:42:56 +00:00
{
Song* pSong = apSongs[s];
2004-05-24 03:41:39 +00:00
vector<Steps*> apSteps;
pSong->GetSteps( apSteps, sd->m_StepsType );
for( unsigned n=0; n<apSteps.size(); n++ ) // foreach Steps
2002-08-01 13:42:56 +00:00
{
2004-05-24 03:41:39 +00:00
Steps* pSteps = apSteps[n];
2002-08-01 13:42:56 +00:00
2004-05-24 03:41:39 +00:00
if( pExtra1Notes == NULL || CompareNotesPointersForExtra(pExtra1Notes,pSteps) ) // pSteps is harder than pHardestNotes
2002-08-01 13:42:56 +00:00
{
pExtra1Song = pSong;
2004-05-24 03:41:39 +00:00
pExtra1Notes = pSteps;
2002-08-01 13:42:56 +00:00
}
2003-08-03 00:13:55 +00:00
// for extra 2, we don't want to choose the hardest notes possible. So, we'll disgard Steps with meter > 8
2004-05-24 03:41:39 +00:00
if( bExtra2 && pSteps->GetMeter() > 8 )
2002-08-01 13:42:56 +00:00
continue; // skip
2004-05-24 03:41:39 +00:00
if( pExtra2Notes == NULL || CompareNotesPointersForExtra(pExtra2Notes,pSteps) ) // pSteps is harder than pHardestNotes
2002-08-01 13:42:56 +00:00
{
pExtra2Song = pSong;
2004-05-24 03:41:39 +00:00
pExtra2Notes = pSteps;
2002-08-01 13:42:56 +00:00
}
}
}
if( pExtra2Song == NULL && pExtra1Song != NULL )
{
pExtra2Song = pExtra1Song;
pExtra2Notes = pExtra1Notes;
}
2003-08-07 06:16:17 +00:00
// If there are any notes at all that match this StepsType, everything should be filled out.
// Also, it's guaranteed that there is at least one Steps that matches the StepsType because the player
2002-08-01 13:42:56 +00:00
// had to play something before reaching the extra stage!
ASSERT( pExtra2Song && pExtra1Song && pExtra2Notes && pExtra1Notes );
pSongOut = (bExtra2 ? pExtra2Song : pExtra1Song);
2004-05-24 03:41:39 +00:00
pStepsOut = (bExtra2 ? pExtra2Notes : pExtra1Notes);
2002-08-01 13:42:56 +00:00
po_out.Init();
so_out.Init();
2003-08-17 00:15:54 +00:00
po_out.m_fScrolls[PlayerOptions::SCROLL_REVERSE] = 1;
2003-01-25 11:05:12 +00:00
po_out.m_fScrollSpeed = 1.5f;
2002-08-01 13:42:56 +00:00
so_out.m_DrainType = (bExtra2 ? SongOptions::DRAIN_SUDDEN_DEATH : SongOptions::DRAIN_NO_RECOVER);
2003-04-01 19:31:27 +00:00
po_out.m_fDark = 1;
2002-08-01 13:42:56 +00:00
}
Song* SongManager::GetRandomSong()
{
2004-02-01 23:06:07 +00:00
if( m_pShuffledSongs.empty() )
return NULL;
2004-02-02 05:49:18 +00:00
static int i = 0;
2004-02-01 23:06:07 +00:00
i++;
wrap( i, m_pShuffledSongs.size() );
return m_pShuffledSongs[ i ];
}
Course* SongManager::GetRandomCourse()
{
if( m_pShuffledCourses.empty() )
return NULL;
2004-02-02 05:49:18 +00:00
static int i = 0;
2004-02-01 23:06:07 +00:00
i++;
wrap( i, m_pShuffledCourses.size() );
return m_pShuffledCourses[ i ];
}
2002-12-02 05:25:44 +00:00
Song* SongManager::GetSongFromDir( CString sDir )
{
2003-12-10 09:44:16 +00:00
if( sDir.Right(1) != "/" )
sDir += "/";
2002-12-02 05:25:44 +00:00
2004-02-08 01:05:53 +00:00
sDir.Replace( '\\', '/' );
for( unsigned int i=0; i<m_pSongs.size(); i++ )
2002-12-02 05:25:44 +00:00
if( sDir.CompareNoCase(m_pSongs[i]->GetSongDir()) == 0 )
return m_pSongs[i];
return NULL;
}
Course* SongManager::GetCourseFromPath( CString sPath )
{
2003-01-22 05:29:27 +00:00
for( unsigned int i=0; i<m_pCourses.size(); i++ )
if( sPath.CompareNoCase(m_pCourses[i]->m_sPath) == 0 )
return m_pCourses[i];
return NULL;
}
2003-01-22 05:29:27 +00:00
Course* SongManager::GetCourseFromName( CString sName )
{
for( unsigned int i=0; i<m_pCourses.size(); i++ )
if( sName.CompareNoCase(m_pCourses[i]->m_sName) == 0 )
return m_pCourses[i];
return NULL;
}
/*
* GetSongDir() contains a path to the song, possibly a full path, eg:
* Songs\Group\SongName or
* My Other Song Folder\Group\SongName or
* c:\Corny J-pop\Group\SongName
*
* Most course group names are "Group\SongName", so we want to
* match against the last two elements. Let's also support
* "SongName" alone, since the group is only important when it's
* potentially ambiguous.
*
* Let's *not* support "Songs\Group\SongName" in course files.
* That's probably a common error, but that would result in
* course files floating around that only work for people who put
* songs in "Songs"; we don't want that.
*/
Song *SongManager::FindSong( CString sPath )
{
2004-02-08 01:05:53 +00:00
sPath.Replace( '\\', '/' );
CStringArray bits;
split( sPath, "/", bits );
if( bits.size() == 1 )
return FindSong( "", bits[0] );
else if( bits.size() == 2 )
return FindSong( bits[0], bits[1] );
return NULL;
}
2003-07-09 04:09:35 +00:00
Song *SongManager::FindSong( CString sGroup, CString sSong )
{
// foreach song
for( unsigned i = 0; i < m_pSongs.size(); i++ )
{
if( m_pSongs[i]->Matches(sGroup, sSong) )
return m_pSongs[i];
}
return NULL;
}
Course *SongManager::FindCourse( CString sName )
{
for( unsigned i = 0; i < m_pCourses.size(); i++ )
{
if( !sName.CompareNoCase(m_pCourses[i]->m_sName) )
return m_pCourses[i];
}
return NULL;
2003-07-21 21:45:59 +00:00
}
2004-02-27 21:28:15 +00:00
void SongManager::UpdateBest()
2003-07-21 21:45:59 +00:00
{
2004-02-01 23:06:07 +00:00
// update players best
2004-02-08 01:05:53 +00:00
for( int i = 0; i < NUM_PROFILE_SLOTS; ++i )
2003-11-14 21:52:05 +00:00
{
vector<Song*> &Best = m_pBestSongs[i];
Best = m_pSongs;
for ( unsigned j=0; j < Best.size() ; ++j )
{
bool bFiltered = false;
/* Filter out hidden songs. */
if( Best[j]->GetDisplayed() != Song::SHOW_ALWAYS )
bFiltered = true;
/* Filter out locked songs. */
if( UNLOCKMAN->SongIsLocked(Best[j]) )
bFiltered = true;
if( !bFiltered )
continue;
/* Remove it. */
swap( Best[j], Best.back() );
Best.erase( Best.end()-1 );
}
SongUtil::SortSongPointerArrayByNumPlays( m_pBestSongs[i], (ProfileSlot) i, true );
2003-11-17 03:20:39 +00:00
m_pBestCourses[i] = m_pCourses;
CourseUtil::SortCoursePointerArrayByNumPlays( m_pBestCourses[i], (ProfileSlot) i, true );
2003-11-14 21:52:05 +00:00
}
2004-02-27 21:28:15 +00:00
}
2004-02-01 23:06:07 +00:00
2004-02-27 21:28:15 +00:00
void SongManager::UpdateShuffled()
{
2004-02-01 23:06:07 +00:00
// update shuffled
m_pShuffledSongs = m_pSongs;
random_shuffle( m_pShuffledSongs.begin(), m_pShuffledSongs.end() );
m_pShuffledCourses = m_pCourses;
random_shuffle( m_pShuffledCourses.begin(), m_pShuffledCourses.end() );
2003-07-21 21:45:59 +00:00
}
2004-02-27 21:28:15 +00:00
void SongManager::SortSongs()
{
SongUtil::SortSongPointerArrayByTitle( m_pSongs );
2004-02-27 21:28:15 +00:00
}
void SongManager::UpdateRankingCourses()
{
/* Updating the ranking courses data is fairly expensive
* since it involves comparing strings. Do so sparingly.
*/
CStringArray RankingCourses;
split( THEME->GetMetric("ScreenRanking","CoursesToShow"),",", RankingCourses);
for(unsigned i=0; i < m_pCourses.size(); i++)
{
if (m_pCourses[i]->GetEstimatedNumStages() > 7)
m_pCourses[i]->m_SortOrder_Ranking = 3;
else
m_pCourses[i]->m_SortOrder_Ranking = 2;
for(unsigned j = 0; j < RankingCourses.size(); j++)
if (!RankingCourses[j].CompareNoCase(m_pCourses[i]->m_sPath))
m_pCourses[i]->m_SortOrder_Ranking = 1;
}
2003-08-10 03:23:17 +00:00
}
2004-02-08 01:05:53 +00:00
void SongManager::LoadAllFromProfiles()
{
2004-04-23 02:08:11 +00:00
FOREACH_ProfileSlot( s )
2004-02-08 01:05:53 +00:00
{
2004-04-23 02:08:11 +00:00
if( !PROFILEMAN->IsUsingProfile(s) )
continue;
CString sProfileDir = PROFILEMAN->GetProfileDir( s );
2004-02-08 01:05:53 +00:00
if( sProfileDir.empty() )
continue; // skip
//
// Load all edits. Edits are dangling .sm files in the Edits folder
//
{
CString sEditsDir = sProfileDir+"Edits/";
2004-04-23 02:08:11 +00:00
FILEMAN->FlushDirCache( sEditsDir );
2004-02-08 01:05:53 +00:00
CStringArray asEditsFilesWithPath;
GetDirListing( sEditsDir+"*.sm", asEditsFilesWithPath, false, true );
2004-04-23 00:26:51 +00:00
unsigned size = min( asEditsFilesWithPath.size(), (unsigned)MAX_EDITS_PER_PROFILE );
for( unsigned i=0; i<size; i++ )
2004-02-08 01:05:53 +00:00
{
CString sEditFileWithPath = asEditsFilesWithPath[i];
SMLoader::LoadEdit( sEditFileWithPath, (ProfileSlot) s );
2004-02-08 01:05:53 +00:00
}
}
//
// Load all songs
//
{
}
}
}
void SongManager::FreeAllLoadedFromProfiles()
{
for( unsigned s=0; s<m_pSongs.size(); s++ )
{
Song* pSong = m_pSongs[s];
pSong->FreeAllLoadedFromProfiles();
}
}
2004-02-14 23:26:32 +00:00
static bool CheckPointer( const Song *p )
{
const vector<Song*> &songs = SONGMAN->GetAllSongs();
for( unsigned i = 0; i < songs.size(); ++i )
if( songs[i] == p )
return true;
return false;
}
#include "LuaFunctions.h"
#define LuaFunction_Song( func, call ) \
int LuaFunc_##func( lua_State *L ) { \
REQ_ARGS( #func, 1 ); \
REQ_ARG( #func, 1, lightuserdata ); \
const Song *p = (const Song *) (lua_touserdata( L, -1 )); \
LUA_ASSERT( CheckPointer(p), ssprintf("%p is not a valid song", p) ); \
LUA_RETURN( call ); \
} \
LuaFunction( func ); /* register it */
LuaFunction_Str( Song, SONGMAN->FindSong( str ) );
LuaFunction_Song( SongFullDisplayTitle, p->GetFullDisplayTitle() );
static bool CheckPointer( const Steps *p )
{
const vector<Song*> &songs = SONGMAN->GetAllSongs();
for( unsigned i = 0; i < songs.size(); ++i )
{
for( unsigned j = 0; j < songs.size(); ++j )
2004-05-24 03:41:39 +00:00
if( songs[i]->m_vpSteps[j] == p )
2004-02-14 23:26:32 +00:00
return true;
}
return false;
}
#define LuaFunction_Steps( func, call ) \
int LuaFunc_##func( lua_State *L ) { \
REQ_ARGS( #func, 1 ); \
REQ_ARG( #func, 1, lightuserdata ); \
const Steps *p = (const Steps *) (lua_touserdata( L, -1 )); \
LUA_ASSERT( CheckPointer(p), ssprintf("%p is not a valid steps", p) ); \
LUA_RETURN( call ); \
} \
LuaFunction( func ); /* register it */
LuaFunction_Steps( StepsMeter, p->GetMeter() );