2003-02-16 04:01:45 +00:00
|
|
|
#include "global.h"
|
2002-02-28 19:40:40 +00:00
|
|
|
#include "SongManager.h"
|
2002-05-01 19:14:55 +00:00
|
|
|
#include "RageLog.h"
|
2003-09-05 09:14:11 +00:00
|
|
|
#include "MsdFile.h"
|
2002-09-10 07:05:14 +00:00
|
|
|
#include "NotesLoaderDWI.h"
|
2003-06-15 01:53:51 +00:00
|
|
|
#include "BannerCache.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"
|
2002-11-18 21:37:58 +00:00
|
|
|
#include "arch/LoadingWindow/LoadingWindow.h"
|
2003-07-17 20:11:24 +00:00
|
|
|
#include "Course.h"
|
2002-02-28 19:40:40 +00:00
|
|
|
|
2002-11-11 04:53:31 +00:00
|
|
|
#include "AnnouncerManager.h"
|
|
|
|
|
#include "ThemeManager.h"
|
|
|
|
|
#include "GameManager.h"
|
2003-07-22 07:47:27 +00:00
|
|
|
#include "RageFile.h"
|
2004-03-26 07:56:18 +00:00
|
|
|
#include "Sprite.h"
|
2003-12-07 07:09:13 +00:00
|
|
|
#include "ProfileManager.h"
|
2004-02-08 01:05:53 +00:00
|
|
|
#include "MemoryCardManager.h"
|
|
|
|
|
#include "NotesLoaderSM.h"
|
2005-07-03 07:50:59 +00:00
|
|
|
#include "song.h"
|
2004-04-18 07:34:37 +00:00
|
|
|
#include "SongUtil.h"
|
2005-07-03 02:48:38 +00:00
|
|
|
#include "Steps.h"
|
2004-04-18 07:34:37 +00:00
|
|
|
#include "StepsUtil.h"
|
2004-04-18 08:36:04 +00:00
|
|
|
#include "CourseUtil.h"
|
2005-07-01 05:07:22 +00:00
|
|
|
#include "TrailUtil.h"
|
2004-04-23 02:08:11 +00:00
|
|
|
#include "RageFileManager.h"
|
2005-02-21 06:22:46 +00:00
|
|
|
#include "UnlockManager.h"
|
2004-07-24 06:40:51 +00:00
|
|
|
#include "Foreach.h"
|
2005-02-16 03:25:45 +00:00
|
|
|
#include "StatsManager.h"
|
2004-09-12 05:56:24 +00:00
|
|
|
#include "Style.h"
|
2005-06-04 21:22:50 +00:00
|
|
|
#include "BackgroundUtil.h"
|
2005-07-31 05:41:32 +00:00
|
|
|
#include "Profile.h"
|
|
|
|
|
#include "CourseLoaderCRS.h"
|
2005-10-06 07:01:58 +00:00
|
|
|
#include "TitleSubstitution.h"
|
2003-03-27 21:23:56 +00:00
|
|
|
|
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
|
|
|
|
2005-06-03 01:57:10 +00:00
|
|
|
const CString SONGS_DIR = "Songs/";
|
|
|
|
|
const CString COURSES_DIR = "Courses/";
|
2003-01-22 05:29:27 +00:00
|
|
|
|
2004-11-08 06:16:22 +00:00
|
|
|
static const ThemeMetric<RageColor> EXTRA_COLOR ("SongManager","ExtraColor");
|
|
|
|
|
static const ThemeMetric<int> EXTRA_COLOR_METER ("SongManager","ExtraColorMeter");
|
2002-08-27 23:31:41 +00:00
|
|
|
|
2005-06-03 01:57:10 +00:00
|
|
|
CString SONG_GROUP_COLOR_NAME( size_t i ) { return ssprintf("SongGroupColor%i",(int) i+1); }
|
|
|
|
|
CString COURSE_GROUP_COLOR_NAME( size_t i ) { return ssprintf("CourseGroupColor%i",(int) i+1); }
|
2002-04-16 17:31:00 +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
|
|
|
{
|
2005-06-03 01:57:10 +00:00
|
|
|
NUM_SONG_GROUP_COLORS .Load("SongManager","NumSongGroupColors");
|
|
|
|
|
SONG_GROUP_COLOR .Load("SongManager",SONG_GROUP_COLOR_NAME,NUM_SONG_GROUP_COLORS);
|
|
|
|
|
NUM_COURSE_GROUP_COLORS .Load("SongManager","NumCourseGroupColors");
|
|
|
|
|
COURSE_GROUP_COLOR .Load("SongManager",COURSE_GROUP_COLOR_NAME,NUM_COURSE_GROUP_COLORS);
|
2002-02-28 19:40:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SongManager::~SongManager()
|
|
|
|
|
{
|
2004-07-24 06:52:39 +00:00
|
|
|
// Courses depend on Songs and Songs don't depend on Courses.
|
|
|
|
|
// So, delete the Courses first.
|
2004-01-21 03:09:59 +00:00
|
|
|
FreeCourses();
|
2004-07-24 06:40:51 +00:00
|
|
|
FreeSongs();
|
2003-08-06 05:28:58 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-25 05:52:57 +00:00
|
|
|
void SongManager::InitAll( LoadingWindow *ld )
|
|
|
|
|
{
|
|
|
|
|
InitSongsFromDisk( ld );
|
|
|
|
|
InitCoursesFromDisk( ld );
|
|
|
|
|
InitAutogenCourses();
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-21 01:52:00 +00:00
|
|
|
void SongManager::Reload( LoadingWindow *ld )
|
2003-08-06 05:28:58 +00:00
|
|
|
{
|
|
|
|
|
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();
|
2003-08-06 05:28:58 +00:00
|
|
|
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;
|
2005-05-06 20:41:05 +00:00
|
|
|
PREFSMAN->m_bFastLoad.Set( 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
|
|
|
|
2005-05-06 20:41:05 +00:00
|
|
|
PREFSMAN->m_bFastLoad.Set( OldVal );
|
2003-07-22 07:47:27 +00:00
|
|
|
}
|
2002-02-28 19:40:40 +00:00
|
|
|
|
2003-08-06 05:28:58 +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
|
|
|
}
|
|
|
|
|
|
2002-09-10 07:05:14 +00:00
|
|
|
void SongManager::SanityCheckGroupDir( CString sDir ) const
|
|
|
|
|
{
|
|
|
|
|
// Check to see if they put a song directly inside the group folder.
|
|
|
|
|
CStringArray arrayFiles;
|
2003-02-14 08:15:15 +00:00
|
|
|
GetDirListing( sDir + "/*.mp3", arrayFiles );
|
|
|
|
|
GetDirListing( sDir + "/*.ogg", arrayFiles );
|
|
|
|
|
GetDirListing( sDir + "/*.wav", arrayFiles );
|
2002-10-31 02:46:42 +00:00
|
|
|
if( !arrayFiles.empty() )
|
2002-12-21 19:34:02 +00:00
|
|
|
RageException::Throw(
|
2002-10-29 07:58:44 +00:00
|
|
|
"The folder '%s' contains music files.\n\n"
|
|
|
|
|
"This means that you have a music outside of a song folder.\n"
|
2005-10-07 05:00:52 +00:00
|
|
|
"All song folders must reside in a group folder. For example, 'Songs/Originals/MySong'.\n"
|
2002-10-29 07:58:44 +00:00
|
|
|
"See the StepMania readme for more info.",
|
2003-04-25 00:01:35 +00:00
|
|
|
sDir.c_str()
|
2002-09-10 07:05:14 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-10 07:11:29 +00:00
|
|
|
void SongManager::AddGroup( CString sDir, CString sGroupDirName )
|
|
|
|
|
{
|
2002-10-31 02:46:42 +00:00
|
|
|
unsigned j;
|
2005-06-03 01:57:10 +00:00
|
|
|
for(j = 0; j < m_sSongGroupNames.size(); ++j)
|
|
|
|
|
if( sGroupDirName == m_sSongGroupNames[j] ) break;
|
2002-09-10 07:11:29 +00:00
|
|
|
|
2005-06-03 01:57:10 +00:00
|
|
|
if( j != m_sSongGroupNames.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;
|
2002-10-31 02:46:42 +00:00
|
|
|
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(),
|
2004-09-20 17:02:37 +00:00
|
|
|
sBannerPath != ""? sBannerPath.c_str():"(none)" );
|
2005-06-03 01:57:10 +00:00
|
|
|
m_sSongGroupNames.push_back( sGroupDirName );
|
|
|
|
|
m_sSongGroupBannerPaths.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 );
|
2005-06-23 08:05:09 +00:00
|
|
|
StripCvs( arrayGroupDirs );
|
2003-09-05 09:14:11 +00:00
|
|
|
|
2005-06-23 08:05:09 +00:00
|
|
|
FOREACH_CONST( CString, arrayGroupDirs, s ) // foreach dir in /Songs/
|
2002-03-06 08:25:09 +00:00
|
|
|
{
|
2005-06-23 08:05:09 +00:00
|
|
|
CString sGroupDirName = *s;
|
2002-03-06 08:25:09 +00:00
|
|
|
|
2003-01-23 04:43:22 +00:00
|
|
|
SanityCheckGroupDir(sDir+sGroupDirName);
|
2002-09-10 07:05:14 +00:00
|
|
|
|
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 );
|
2005-06-23 08:05:09 +00:00
|
|
|
StripCvs( arraySongDirs );
|
2002-03-06 08:25:09 +00:00
|
|
|
SortCStringArray( arraySongDirs );
|
|
|
|
|
|
2004-09-20 17:02:37 +00:00
|
|
|
LOG->Trace("Attempting to load %i songs from \"%s\"", int(arraySongDirs.size()),
|
|
|
|
|
(sDir+sGroupDirName).c_str() );
|
2002-09-09 02:23:47 +00:00
|
|
|
int loaded = 0;
|
|
|
|
|
|
2004-09-20 17:02:37 +00:00
|
|
|
for( unsigned j=0; j< arraySongDirs.size(); ++j ) // for each song dir
|
2002-03-06 08:25:09 +00:00
|
|
|
{
|
|
|
|
|
CString sSongDirName = arraySongDirs[j];
|
|
|
|
|
|
|
|
|
|
// this is a song directory. Load a new song!
|
2004-09-20 17:02:37 +00:00
|
|
|
if( ld )
|
|
|
|
|
{
|
2003-01-23 04:43:22 +00:00
|
|
|
ld->SetText( ssprintf("Loading songs...\n%s\n%s",
|
2004-09-20 17:02:37 +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;
|
2004-09-20 17:02:37 +00:00
|
|
|
if( !pNewSong->LoadFromSongDir( sSongDirName ) )
|
|
|
|
|
{
|
2002-09-10 22:55:12 +00:00
|
|
|
/* 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++;
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-17 04:07:33 +00:00
|
|
|
LOG->Trace("Loaded %i songs from \"%s\"", loaded, (sDir+sGroupDirName).c_str() );
|
|
|
|
|
|
2002-09-09 02:23:47 +00:00
|
|
|
/* 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. */
|
2005-06-23 08:05:09 +00:00
|
|
|
BANNERCACHE->CacheBanner( GetSongGroupBannerPath(sGroupDirName) );
|
2003-09-05 09:14:11 +00:00
|
|
|
|
|
|
|
|
/* Load the group sym links (if any)*/
|
|
|
|
|
LoadGroupSymLinks(sDir, sGroupDirName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-04 21:22:50 +00:00
|
|
|
// Instead of "symlinks", songs should have membership in multiple groups.
|
2005-05-26 09:35:57 +00:00
|
|
|
// -Chris
|
2003-09-05 09:14:11 +00:00
|
|
|
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 );
|
2003-09-05 09:14:11 +00:00
|
|
|
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() );
|
2003-09-05 09:14:11 +00:00
|
|
|
CString sSymDestination = msdF.GetParam(0,1); // Should only be 1 vale¶m...period.
|
|
|
|
|
|
|
|
|
|
Song* pNewSong = new Song;
|
|
|
|
|
if( !pNewSong->LoadFromSongDir( sSymDestination ) )
|
2005-05-26 09:35:57 +00:00
|
|
|
{
|
2003-09-05 09:14:11 +00:00
|
|
|
delete pNewSong; // The song failed to load.
|
2005-05-26 09:35:57 +00:00
|
|
|
}
|
2003-09-05 09:14:11 +00:00
|
|
|
else
|
|
|
|
|
{
|
2004-06-05 05:13:23 +00:00
|
|
|
const vector<Steps*>& vpSteps = pNewSong->GetAllSteps();
|
|
|
|
|
while( vpSteps.size() )
|
2005-07-29 02:23:02 +00:00
|
|
|
pNewSong->DeleteSteps( vpSteps[0] );
|
2004-06-05 05:13:23 +00:00
|
|
|
|
2005-06-04 21:22:50 +00:00
|
|
|
FOREACH_BackgroundLayer( i )
|
|
|
|
|
pNewSong->GetBackgroundChanges(i).clear();
|
2003-09-05 09:14:11 +00:00
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
{
|
2005-04-28 08:27:40 +00:00
|
|
|
if( PREFSMAN->m_BannerCache != PrefsManager::BNCACHE_FULL )
|
2003-11-25 22:56:48 +00:00
|
|
|
return;
|
|
|
|
|
|
2005-06-30 22:04:10 +00:00
|
|
|
/* Load textures before unloading old ones, so we don't reload textures
|
|
|
|
|
* that we don't need to. */
|
|
|
|
|
RageTexturePreloader preload;
|
|
|
|
|
|
2003-11-25 22:56:48 +00:00
|
|
|
const vector<Song*> &songs = SONGMAN->GetAllSongs();
|
2004-09-21 07:53:39 +00:00
|
|
|
for( unsigned i = 0; i < songs.size(); ++i )
|
2003-11-25 22:56:48 +00:00
|
|
|
{
|
|
|
|
|
if( !songs[i]->HasBanner() )
|
|
|
|
|
continue;
|
|
|
|
|
|
2004-03-26 07:56:18 +00:00
|
|
|
const RageTextureID ID = Sprite::SongBannerTexture( songs[i]->GetBannerPath() );
|
2005-06-30 22:04:10 +00:00
|
|
|
preload.Load( ID );
|
2003-11-25 22:56:48 +00:00
|
|
|
}
|
|
|
|
|
|
2003-11-25 23:01:53 +00:00
|
|
|
vector<Course*> courses;
|
|
|
|
|
SONGMAN->GetAllCourses( courses, false );
|
2004-09-21 07:53:39 +00:00
|
|
|
for( unsigned i = 0; i < courses.size(); ++i )
|
2003-11-25 23:01:53 +00:00
|
|
|
{
|
|
|
|
|
if( !courses[i]->HasBanner() )
|
|
|
|
|
continue;
|
|
|
|
|
|
2004-03-26 07:56:18 +00:00
|
|
|
const RageTextureID ID = Sprite::SongBannerTexture( courses[i]->m_sBannerPath );
|
2005-06-30 22:04:10 +00:00
|
|
|
preload.Load( ID );
|
2003-11-25 23:01:53 +00:00
|
|
|
}
|
2005-06-30 22:04:10 +00:00
|
|
|
|
|
|
|
|
preload.Swap( m_TexturePreload );
|
2003-11-25 22:56:48 +00:00
|
|
|
}
|
|
|
|
|
|
2003-08-06 05:28:58 +00:00
|
|
|
void SongManager::FreeSongs()
|
2002-02-28 19:40:40 +00:00
|
|
|
{
|
2005-06-03 01:57:10 +00:00
|
|
|
m_sSongGroupNames.clear();
|
|
|
|
|
m_sSongGroupBannerPaths.clear();
|
2004-05-29 20:15:07 +00:00
|
|
|
|
2002-10-31 02:46:42 +00:00
|
|
|
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
|
|
|
|
2005-06-03 01:57:10 +00:00
|
|
|
m_sSongGroupBannerPaths.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
|
|
|
}
|
|
|
|
|
|
2005-06-23 08:05:09 +00:00
|
|
|
CString SongManager::GetSongGroupBannerPath( CString sSongGroup )
|
2002-03-06 08:25:09 +00:00
|
|
|
{
|
2005-06-23 08:05:09 +00:00
|
|
|
for( unsigned i = 0; i < m_sSongGroupNames.size(); ++i )
|
|
|
|
|
{
|
|
|
|
|
if( sSongGroup == m_sSongGroupNames[i] )
|
|
|
|
|
return m_sSongGroupBannerPaths[i];
|
|
|
|
|
}
|
2002-09-07 07:24:44 +00:00
|
|
|
|
2005-09-04 16:55:21 +00:00
|
|
|
return CString();
|
2002-03-06 08:25:09 +00:00
|
|
|
}
|
2002-04-01 02:04:43 +00:00
|
|
|
|
2005-06-23 08:05:09 +00:00
|
|
|
void SongManager::GetSongGroupNames( CStringArray &AddTo )
|
2002-04-01 02:04:43 +00:00
|
|
|
{
|
2005-06-03 01:57:10 +00:00
|
|
|
AddTo.insert(AddTo.end(), m_sSongGroupNames.begin(), m_sSongGroupNames.end() );
|
2002-04-16 17:31:00 +00:00
|
|
|
}
|
2002-04-01 02:04:43 +00:00
|
|
|
|
2005-06-23 08:05:09 +00:00
|
|
|
bool SongManager::DoesSongGroupExist( CString sSongGroup )
|
2003-02-16 23:54:30 +00:00
|
|
|
{
|
2005-06-23 08:05:09 +00:00
|
|
|
return find( m_sSongGroupNames.begin(), m_sSongGroupNames.end(), sSongGroup ) != m_sSongGroupNames.end();
|
2003-02-16 23:54:30 +00:00
|
|
|
}
|
|
|
|
|
|
2005-06-23 08:05:09 +00:00
|
|
|
RageColor SongManager::GetSongGroupColor( const CString &sSongGroup )
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
2005-06-23 08:05:09 +00:00
|
|
|
for( unsigned i=0; i<m_sSongGroupNames.size(); i++ )
|
2002-04-01 02:04:43 +00:00
|
|
|
{
|
2005-06-23 08:05:09 +00:00
|
|
|
if( m_sSongGroupNames[i] == sSongGroup )
|
|
|
|
|
return SONG_GROUP_COLOR.GetValue( i%NUM_SONG_GROUP_COLORS ); // TODO: Add course group colors?
|
2002-04-01 02:04:43 +00:00
|
|
|
}
|
2005-06-23 08:05:09 +00:00
|
|
|
|
|
|
|
|
ASSERT_M( 0, ssprintf("requested color for song group '%s' that doesn't exist",sSongGroup.c_str()) );
|
|
|
|
|
return RageColor(1,1,1,1);
|
2002-04-01 02:04:43 +00:00
|
|
|
}
|
|
|
|
|
|
2002-12-17 05:22:32 +00:00
|
|
|
RageColor SongManager::GetSongColor( const Song* pSong )
|
2002-08-27 23:31:41 +00:00
|
|
|
{
|
|
|
|
|
ASSERT( pSong );
|
2003-04-20 01:40:40 +00:00
|
|
|
|
|
|
|
|
/* 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
|
|
|
*/
|
2004-06-28 07:26:00 +00:00
|
|
|
// const StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType;
|
2004-06-04 23:29:13 +00:00
|
|
|
const vector<Steps*>& vpSteps = pSong->GetAllSteps();
|
|
|
|
|
for( unsigned i=0; i<vpSteps.size(); i++ )
|
2002-08-27 23:31:41 +00:00
|
|
|
{
|
2004-06-04 23:29:13 +00:00
|
|
|
const Steps* pSteps = vpSteps[i];
|
2004-05-24 03:41:39 +00:00
|
|
|
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
|
|
|
}
|
2003-04-20 01:40:40 +00:00
|
|
|
|
2005-06-03 01:57:10 +00:00
|
|
|
// if(pSteps->m_StepsType != st)
|
2003-04-20 22:57:35 +00:00
|
|
|
// continue;
|
2003-04-20 01:40:40 +00:00
|
|
|
|
2004-05-24 03:41:39 +00:00
|
|
|
if( pSteps->GetMeter() >= EXTRA_COLOR_METER )
|
2004-11-08 06:16:22 +00:00
|
|
|
return (RageColor)EXTRA_COLOR;
|
2002-08-27 23:31:41 +00:00
|
|
|
}
|
|
|
|
|
|
2005-06-03 01:57:10 +00:00
|
|
|
return GetSongGroupColor( pSong->m_sGroupName );
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-31 05:41:32 +00:00
|
|
|
CString SongManager::GetCourseGroupBannerPath( const CString &sCourseGroup )
|
2005-06-23 08:05:09 +00:00
|
|
|
{
|
2005-07-31 05:41:32 +00:00
|
|
|
map<CString, CourseGroupInfo>::const_iterator iter = m_mapCourseGroupToInfo.find( sCourseGroup );
|
|
|
|
|
if( iter == m_mapCourseGroupToInfo.end() )
|
2005-06-23 08:05:09 +00:00
|
|
|
{
|
2005-07-31 05:41:32 +00:00
|
|
|
ASSERT_M( 0, ssprintf("requested banner for course group '%s' that doesn't exist",sCourseGroup.c_str()) );
|
2005-09-04 16:55:21 +00:00
|
|
|
return CString();
|
2005-07-31 05:41:32 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return iter->second.m_sBannerPath;
|
2005-06-23 08:05:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SongManager::GetCourseGroupNames( CStringArray &AddTo )
|
|
|
|
|
{
|
2005-07-31 05:41:32 +00:00
|
|
|
FOREACHM_CONST( CString, CourseGroupInfo, m_mapCourseGroupToInfo, iter )
|
|
|
|
|
AddTo.push_back( iter->first );
|
2005-06-23 08:05:09 +00:00
|
|
|
}
|
|
|
|
|
|
2005-07-31 05:41:32 +00:00
|
|
|
bool SongManager::DoesCourseGroupExist( const CString &sCourseGroup )
|
2005-06-03 01:57:10 +00:00
|
|
|
{
|
2005-07-31 05:41:32 +00:00
|
|
|
return m_mapCourseGroupToInfo.find( sCourseGroup ) != m_mapCourseGroupToInfo.end();
|
2005-06-23 08:05:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RageColor SongManager::GetCourseGroupColor( const CString &sCourseGroup )
|
|
|
|
|
{
|
2005-07-31 05:41:32 +00:00
|
|
|
int iIndex = 0;
|
|
|
|
|
FOREACHM_CONST( CString, CourseGroupInfo, m_mapCourseGroupToInfo, iter )
|
2005-06-03 01:57:10 +00:00
|
|
|
{
|
2005-07-31 05:41:32 +00:00
|
|
|
if( iter->first == sCourseGroup )
|
|
|
|
|
return SONG_GROUP_COLOR.GetValue( iIndex%NUM_SONG_GROUP_COLORS );
|
|
|
|
|
iIndex++;
|
2005-06-03 01:57:10 +00:00
|
|
|
}
|
2005-06-23 08:05:09 +00:00
|
|
|
|
|
|
|
|
ASSERT_M( 0, ssprintf("requested color for course group '%s' that doesn't exist",sCourseGroup.c_str()) );
|
|
|
|
|
return RageColor(1,1,1,1);
|
2005-06-03 01:57:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RageColor SongManager::GetCourseColor( const Course* pCourse )
|
|
|
|
|
{
|
|
|
|
|
return GetCourseGroupColor( pCourse->m_sGroupName );
|
2002-08-27 23:31:41 +00:00
|
|
|
}
|
|
|
|
|
|
2004-03-07 00:17:50 +00:00
|
|
|
static void GetSongsFromVector( const vector<Song*> &Songs, vector<Song*> &AddTo, CString sGroupName, int iMaxStages )
|
2002-04-01 02:04:43 +00:00
|
|
|
{
|
2003-02-03 05:53:59 +00:00
|
|
|
AddTo.clear();
|
|
|
|
|
|
2004-03-07 00:17:50 +00:00
|
|
|
for( unsigned i=0; i<Songs.size(); i++ )
|
2005-06-24 06:06:16 +00:00
|
|
|
if( sGroupName==GROUP_ALL || sGroupName==Songs[i]->m_sGroupName )
|
2004-03-07 00:17:50 +00:00
|
|
|
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
|
2003-02-03 05:53:59 +00:00
|
|
|
{
|
|
|
|
|
return m_pSongs.size();
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-23 08:05:09 +00:00
|
|
|
int SongManager::GetNumSongGroups() const
|
2003-02-03 05:53:59 +00:00
|
|
|
{
|
2005-06-03 01:57:10 +00:00
|
|
|
return m_sSongGroupNames.size();
|
2003-02-03 05:53:59 +00:00
|
|
|
}
|
|
|
|
|
|
2003-02-14 21:42:44 +00:00
|
|
|
int SongManager::GetNumCourses() const
|
|
|
|
|
{
|
|
|
|
|
return m_pCourses.size();
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-23 08:05:09 +00:00
|
|
|
int SongManager::GetNumCourseGroups() const
|
|
|
|
|
{
|
2005-07-31 05:41:32 +00:00
|
|
|
return m_mapCourseGroupToInfo.size();
|
2005-06-23 08:05:09 +00:00
|
|
|
}
|
|
|
|
|
|
2005-08-05 10:07:49 +00:00
|
|
|
int SongManager::GetNumEditCourses( ProfileSlot slot ) const
|
|
|
|
|
{
|
|
|
|
|
int iNum = 0;
|
|
|
|
|
FOREACH_CONST( Course*, m_pCourses, p )
|
|
|
|
|
{
|
|
|
|
|
if( (*p)->GetLoadedFromProfileSlot() == slot )
|
|
|
|
|
iNum++;
|
|
|
|
|
}
|
|
|
|
|
return iNum;
|
|
|
|
|
}
|
|
|
|
|
|
2003-02-10 05:30:12 +00:00
|
|
|
CString SongManager::ShortenGroupName( CString sLongGroupName )
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
2005-10-06 07:01:58 +00:00
|
|
|
static TitleSubst tsub("Groups");
|
|
|
|
|
|
|
|
|
|
TitleFields title;
|
|
|
|
|
title.Title = sLongGroupName;
|
|
|
|
|
tsub.Subst( title );
|
|
|
|
|
return title.Title;
|
2003-02-10 05:30:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
2003-08-06 05:28:58 +00:00
|
|
|
void SongManager::InitCoursesFromDisk( LoadingWindow *ld )
|
2002-06-14 22:25:22 +00:00
|
|
|
{
|
2003-08-16 23:34:47 +00:00
|
|
|
LOG->Trace( "Loading courses." );
|
|
|
|
|
|
2005-06-03 01:57:10 +00:00
|
|
|
|
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
|
|
|
//
|
|
|
|
|
{
|
2005-06-03 01:57:10 +00:00
|
|
|
CStringArray saCourseFiles;
|
|
|
|
|
GetDirListing( COURSES_DIR+"*.crs", saCourseFiles, false, true );
|
|
|
|
|
for( unsigned i=0; i<saCourseFiles.size(); i++ )
|
2003-08-06 05:28:58 +00:00
|
|
|
{
|
2005-06-03 01:57:10 +00:00
|
|
|
Course* pCourse = new Course;
|
2005-07-31 05:41:32 +00:00
|
|
|
CourseLoaderCRS::LoadFromCRSFile( saCourseFiles[i], *pCourse );
|
2005-06-03 01:57:10 +00:00
|
|
|
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
|
|
|
|
|
{
|
2005-07-31 05:41:32 +00:00
|
|
|
vector<CString> vsCourseGroupNames;
|
|
|
|
|
GetDirListing( COURSES_DIR+"*", vsCourseGroupNames, true );
|
|
|
|
|
StripCvs( vsCourseGroupNames );
|
|
|
|
|
SortCStringArray( vsCourseGroupNames );
|
2005-06-03 01:57:10 +00:00
|
|
|
|
2005-07-31 05:41:32 +00:00
|
|
|
FOREACH( CString, vsCourseGroupNames, sCourseGroup ) // for each dir in /Courses/
|
2003-04-11 00:29:44 +00:00
|
|
|
{
|
2005-06-03 01:57:10 +00:00
|
|
|
// Find all CRS files in this group directory
|
|
|
|
|
CStringArray vsCoursePaths;
|
|
|
|
|
GetDirListing( COURSES_DIR + *sCourseGroup + "/*.crs", vsCoursePaths, false, true );
|
|
|
|
|
SortCStringArray( vsCoursePaths );
|
|
|
|
|
|
|
|
|
|
FOREACH_CONST( CString, vsCoursePaths, sCoursePath )
|
|
|
|
|
{
|
|
|
|
|
if( ld )
|
|
|
|
|
{
|
|
|
|
|
ld->SetText( ssprintf("Loading courses...\n%s\n%s",
|
|
|
|
|
Basename(*sCourseGroup).c_str(),
|
|
|
|
|
Basename(*sCoursePath).c_str()));
|
|
|
|
|
ld->Paint();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Course* pCourse = new Course;
|
2005-07-31 05:41:32 +00:00
|
|
|
CourseLoaderCRS::LoadFromCRSFile( *sCoursePath, *pCourse );
|
2005-06-03 01:57:10 +00:00
|
|
|
m_pCourses.push_back( pCourse );
|
|
|
|
|
}
|
2003-04-11 00:29:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
2005-07-31 05:41:32 +00:00
|
|
|
|
|
|
|
|
RefreshCourseGroupInfo();
|
2003-03-27 01:56:21 +00:00
|
|
|
}
|
2002-07-31 04:06:34 +00:00
|
|
|
|
2003-03-27 01:56:21 +00:00
|
|
|
void SongManager::InitAutogenCourses()
|
|
|
|
|
{
|
2002-07-02 17:34:20 +00:00
|
|
|
//
|
2003-01-21 05:14:59 +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;
|
2005-06-23 08:05:09 +00:00
|
|
|
this->GetSongGroupNames( 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;
|
2003-02-10 05:30:12 +00:00
|
|
|
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.
|
2004-05-20 03:26:36 +00:00
|
|
|
pCourse = new Course;
|
2004-05-20 23:14:36 +00:00
|
|
|
pCourse->AutogenEndlessFromGroup( sGroupName, DIFFICULTY_MEDIUM );
|
2004-05-20 03:26:36 +00:00
|
|
|
m_pCourses.push_back( pCourse );
|
|
|
|
|
|
|
|
|
|
pCourse = new Course;
|
2004-05-20 23:14:36 +00:00
|
|
|
pCourse->AutogenNonstopFromGroup( sGroupName, DIFFICULTY_MEDIUM );
|
2003-03-27 01:56:21 +00:00
|
|
|
m_pCourses.push_back( pCourse );
|
2002-06-14 22:25:22 +00:00
|
|
|
}
|
2004-05-20 03:26:36 +00:00
|
|
|
|
|
|
|
|
vector<Song*> apCourseSongs = GetAllSongs();
|
|
|
|
|
|
2004-05-20 23:14:36 +00:00
|
|
|
// Generate "All Songs" endless course.
|
2004-05-20 03:26:36 +00:00
|
|
|
pCourse = new Course;
|
2004-05-20 23:14:36 +00:00
|
|
|
pCourse->AutogenEndlessFromGroup( "", DIFFICULTY_MEDIUM );
|
2004-05-20 03:26:36 +00:00
|
|
|
m_pCourses.push_back( pCourse );
|
2004-05-25 02:17:17 +00:00
|
|
|
|
|
|
|
|
/* 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. */
|
2004-05-25 02:17:17 +00:00
|
|
|
vector<Song*> apSongs = this->GetAllSongs();
|
2004-05-25 04:08:21 +00:00
|
|
|
SongUtil::SortSongPointerArrayByDisplayArtist( apSongs );
|
2004-05-25 02:17:17 +00:00
|
|
|
|
|
|
|
|
CString sCurArtist = "";
|
2004-05-25 04:08:21 +00:00
|
|
|
CString sCurArtistTranslit = "";
|
2004-05-25 02:17:17 +00:00
|
|
|
int iCurArtistCount = 0;
|
|
|
|
|
|
2004-05-25 04:08:21 +00:00
|
|
|
vector<Song *> aSongs;
|
2004-05-25 02:17:17 +00:00
|
|
|
unsigned i = 0;
|
|
|
|
|
do {
|
2004-08-06 21:01:28 +00:00
|
|
|
CString sArtist = i >= apSongs.size()? CString(""): apSongs[i]->GetDisplayArtist();
|
|
|
|
|
CString sTranslitArtist = i >= apSongs.size()? CString(""): apSongs[i]->GetTranslitArtist();
|
2004-05-25 02:17:17 +00:00
|
|
|
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 != "" &&
|
2004-08-25 08:25:43 +00:00
|
|
|
sCurArtistTranslit.CompareNoCase("Unknown artist") &&
|
|
|
|
|
sCurArtist.CompareNoCase("Unknown artist") )
|
2004-05-25 02:17:17 +00:00
|
|
|
{
|
|
|
|
|
pCourse = new Course;
|
2004-05-25 04:08:21 +00:00
|
|
|
pCourse->AutogenOniFromArtist( sCurArtist, sCurArtistTranslit, aSongs, DIFFICULTY_HARD );
|
2004-05-25 02:17:17 +00:00
|
|
|
m_pCourses.push_back( pCourse );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
aSongs.clear();
|
|
|
|
|
|
|
|
|
|
if( i < apSongs.size() )
|
|
|
|
|
{
|
|
|
|
|
sCurArtist = sArtist;
|
2004-05-25 04:08:21 +00:00
|
|
|
sCurArtistTranslit = sTranslitArtist;
|
2004-05-25 02:17:17 +00:00
|
|
|
iCurArtistCount = 1;
|
|
|
|
|
aSongs.push_back( apSongs[i] );
|
|
|
|
|
}
|
|
|
|
|
} while( i++ < apSongs.size() );
|
|
|
|
|
}
|
2002-06-14 22:25:22 +00:00
|
|
|
}
|
|
|
|
|
|
2003-03-27 01:56:21 +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 )
|
2005-04-07 08:46:40 +00:00
|
|
|
FOREACH_CourseType( ct )
|
|
|
|
|
m_pBestCourses[i][ct].clear();
|
2004-05-29 20:15:07 +00:00
|
|
|
m_pShuffledCourses.clear();
|
2005-06-03 01:57:10 +00:00
|
|
|
|
2005-07-31 05:41:32 +00:00
|
|
|
m_mapCourseGroupToInfo.clear();
|
2002-06-14 22:25:22 +00:00
|
|
|
}
|
2002-08-01 13:42:56 +00:00
|
|
|
|
2005-07-29 02:23:02 +00:00
|
|
|
void SongManager::AddCourse( Course *pCourse )
|
|
|
|
|
{
|
|
|
|
|
m_pCourses.push_back( pCourse );
|
|
|
|
|
UpdateBest();
|
|
|
|
|
UpdateShuffled();
|
2005-07-31 05:41:32 +00:00
|
|
|
m_mapCourseGroupToInfo[ pCourse->m_sGroupName ]; // insert
|
2005-07-29 02:23:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SongManager::DeleteCourse( Course *pCourse )
|
|
|
|
|
{
|
|
|
|
|
vector<Course*>::iterator iter = find( m_pCourses.begin(), m_pCourses.end(), pCourse );
|
|
|
|
|
ASSERT( iter != m_pCourses.end() );
|
|
|
|
|
m_pCourses.erase( iter );
|
|
|
|
|
UpdateBest();
|
|
|
|
|
UpdateShuffled();
|
2005-07-31 05:41:32 +00:00
|
|
|
RefreshCourseGroupInfo();
|
2005-07-29 02:23:02 +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
|
|
|
{
|
2004-06-03 08:22:02 +00:00
|
|
|
for( unsigned 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-06-04 23:29:13 +00:00
|
|
|
const vector<Steps*>& vpSteps = pSong->GetAllSteps();
|
|
|
|
|
for( unsigned n=0; n<vpSteps.size(); n++ )
|
2003-05-05 04:13:39 +00:00
|
|
|
{
|
2004-06-04 23:29:13 +00:00
|
|
|
Steps* pSteps = vpSteps[n];
|
2004-05-24 03:41:39 +00:00
|
|
|
pSteps->Compress();
|
2002-12-21 18:23:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
2004-06-03 08:22:02 +00:00
|
|
|
}
|
|
|
|
|
|
2004-06-11 21:31:34 +00:00
|
|
|
/* Flush all Song*, Steps* and Course* caches. This is called on reload, and when
|
|
|
|
|
* any of those are removed or changed. This doesn't touch GAMESTATE and StageStats
|
|
|
|
|
* pointers, which are updated explicitly in Song::RevertFromDisk. */
|
2004-07-24 06:40:51 +00:00
|
|
|
void SongManager::Invalidate( Song *pStaleSong )
|
2004-06-03 08:22:02 +00:00
|
|
|
{
|
2004-08-30 23:15:03 +00:00
|
|
|
//
|
|
|
|
|
// Save list of all old Course and Trail pointers
|
|
|
|
|
//
|
|
|
|
|
map<Course*,CourseID> mapOldCourseToCourseID;
|
|
|
|
|
typedef pair<TrailID,Course*> TrailIDAndCourse;
|
|
|
|
|
map<Trail*,TrailIDAndCourse> mapOldTrailToTrailIDAndCourse;
|
|
|
|
|
FOREACH_CONST( Course*, this->m_pCourses, pCourse )
|
|
|
|
|
{
|
|
|
|
|
CourseID id;
|
|
|
|
|
id.FromCourse( *pCourse );
|
|
|
|
|
mapOldCourseToCourseID[*pCourse] = id;
|
|
|
|
|
vector<Trail *> Trails;
|
|
|
|
|
(*pCourse)->GetAllCachedTrails( Trails );
|
|
|
|
|
FOREACH_CONST( Trail*, Trails, pTrail )
|
|
|
|
|
{
|
|
|
|
|
TrailID id;
|
|
|
|
|
id.FromTrail( *pTrail );
|
|
|
|
|
mapOldTrailToTrailIDAndCourse[*pTrail] = TrailIDAndCourse(id, *pCourse);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-08-12 04:49:15 +00:00
|
|
|
// It's a real pain to selectively invalidate only those Courses with
|
|
|
|
|
// dependencies on the stale Song. So, instead, just reload all Courses.
|
|
|
|
|
// It doesn't take very long.
|
|
|
|
|
FreeCourses();
|
|
|
|
|
InitCoursesFromDisk( NULL );
|
|
|
|
|
InitAutogenCourses();
|
2004-06-11 21:31:34 +00:00
|
|
|
|
2004-08-12 04:49:15 +00:00
|
|
|
// invalidate cache
|
2004-11-26 14:26:20 +00:00
|
|
|
StepsID::ClearCache();
|
2004-08-30 23:15:03 +00:00
|
|
|
|
2004-09-20 17:02:37 +00:00
|
|
|
#define CONVERT_COURSE_POINTER( pCourse ) do { \
|
2004-08-30 23:15:03 +00:00
|
|
|
CourseID id = mapOldCourseToCourseID[pCourse]; /* this will always succeed */ \
|
2004-09-20 17:02:37 +00:00
|
|
|
pCourse = id.ToCourse(); \
|
|
|
|
|
} while(false)
|
2004-08-30 23:15:03 +00:00
|
|
|
|
|
|
|
|
/* Ugly: We need the course pointer to restore a trail pointer, and both have
|
|
|
|
|
* been invalidated. We need to go through our mapping, and update the course
|
|
|
|
|
* pointers, so we can use that to update trail pointers. */
|
|
|
|
|
{
|
|
|
|
|
map<Trail*,TrailIDAndCourse>::iterator it;
|
|
|
|
|
for( it = mapOldTrailToTrailIDAndCourse.begin(); it != mapOldTrailToTrailIDAndCourse.end(); ++it )
|
|
|
|
|
{
|
|
|
|
|
TrailIDAndCourse &tidc = it->second;
|
|
|
|
|
CONVERT_COURSE_POINTER( tidc.second );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-18 07:14:19 +00:00
|
|
|
{
|
|
|
|
|
CourseID id = mapOldCourseToCourseID[GAMESTATE->m_pCurCourse]; /* this will always succeed */
|
|
|
|
|
GAMESTATE->m_pCurCourse.Set( id.ToCourse() );
|
|
|
|
|
}
|
2004-08-30 23:15:03 +00:00
|
|
|
CONVERT_COURSE_POINTER( GAMESTATE->m_pPreferredCourse );
|
|
|
|
|
|
2004-09-20 17:02:37 +00:00
|
|
|
#define CONVERT_TRAIL_POINTER( pTrail ) do { \
|
2004-08-30 23:15:03 +00:00
|
|
|
if( pTrail != NULL ) { \
|
|
|
|
|
map<Trail*,TrailIDAndCourse>::iterator it; \
|
|
|
|
|
it = mapOldTrailToTrailIDAndCourse.find(pTrail); \
|
2005-05-20 03:52:48 +00:00
|
|
|
ASSERT_M( it != mapOldTrailToTrailIDAndCourse.end(), ssprintf("%p", pTrail.Get()) ); \
|
2004-08-30 23:15:03 +00:00
|
|
|
const TrailIDAndCourse &tidc = it->second; \
|
|
|
|
|
const TrailID &id = tidc.first; \
|
|
|
|
|
const Course *pCourse = tidc.second; \
|
2005-05-18 07:14:19 +00:00
|
|
|
pTrail.Set( id.ToTrail( pCourse, true ) ); \
|
2004-08-30 23:15:03 +00:00
|
|
|
} \
|
2004-09-20 17:02:37 +00:00
|
|
|
} while(false)
|
2004-08-30 23:15:03 +00:00
|
|
|
|
|
|
|
|
FOREACH_PlayerNumber( pn )
|
|
|
|
|
{
|
|
|
|
|
CONVERT_TRAIL_POINTER( GAMESTATE->m_pCurTrail[pn] );
|
|
|
|
|
}
|
2004-07-24 06:40:51 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-11 08:23:14 +00:00
|
|
|
/* If bAllowNotesLoss is true, any global notes pointers which no longer exist
|
|
|
|
|
* (or exist but couldn't be matched) will be set to NULL. This is used when
|
|
|
|
|
* reverting out of the editor. If false, this is unexpected and will assert.
|
|
|
|
|
* This is used when reverting out of gameplay, in which case we may have StageStats,
|
|
|
|
|
* etc. which may cause hard-to-trace crashes down the line if we set them to NULL. */
|
2005-02-28 20:12:07 +00:00
|
|
|
void CONVERT_STEPS_POINTER( Steps *&pSteps, const map<Steps*,StepsID> &mapOldStepsToStepsID, const Song *pSong, bool bAllowNotesLoss )
|
|
|
|
|
{
|
|
|
|
|
if( pSteps == NULL )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
map<Steps*,StepsID>::const_iterator it = mapOldStepsToStepsID.find(pSteps);
|
|
|
|
|
if( it != mapOldStepsToStepsID.end() )
|
|
|
|
|
pSteps = it->second.ToSteps(pSong, bAllowNotesLoss);
|
|
|
|
|
}
|
|
|
|
|
void CONVERT_STEPS_POINTER( BroadcastOnChangePtr<Steps> &pSteps, const map<Steps*,StepsID> &mapOldStepsToStepsID, const Song *pSong, bool bAllowNotesLoss )
|
|
|
|
|
{
|
|
|
|
|
if( pSteps == NULL )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
map<Steps*,StepsID>::const_iterator it = mapOldStepsToStepsID.find(pSteps);
|
|
|
|
|
if( it != mapOldStepsToStepsID.end() )
|
|
|
|
|
pSteps.Set( it->second.ToSteps(pSong, bAllowNotesLoss) );
|
|
|
|
|
}
|
2004-08-11 08:23:14 +00:00
|
|
|
void SongManager::RevertFromDisk( Song *pSong, bool bAllowNotesLoss )
|
|
|
|
|
{
|
2004-09-07 20:38:01 +00:00
|
|
|
/* Reverting from disk is brittle, and touches a lot of tricky and rarely-
|
|
|
|
|
* used code paths. If it's ever used during a game, log it. */
|
|
|
|
|
LOG->MapLog( "RevertFromDisk", "Reverted \"%s\" from disk", pSong->GetTranslitMainTitle().c_str() );
|
|
|
|
|
|
2004-08-11 08:23:14 +00:00
|
|
|
// Ugly: When we re-load the song, the Steps* will change.
|
2005-02-16 03:25:45 +00:00
|
|
|
// Fix GAMESTATE->m_CurSteps, STATSMAN->m_CurStageStats, STATSMAN->m_vPlayedStageStats[] after reloading.
|
2004-08-11 08:23:14 +00:00
|
|
|
/* XXX: This is very brittle. However, we must know about all globals uses of Steps*,
|
|
|
|
|
* so we can check to make sure we didn't lose any steps which are referenced ... */
|
|
|
|
|
|
2004-08-30 04:09:23 +00:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Save list of all old Steps pointers for the song
|
|
|
|
|
//
|
|
|
|
|
map<Steps*,StepsID> mapOldStepsToStepsID;
|
|
|
|
|
FOREACH_CONST( Steps*, pSong->GetAllSteps(), pSteps )
|
|
|
|
|
{
|
|
|
|
|
StepsID id;
|
|
|
|
|
id.FromSteps( *pSteps );
|
|
|
|
|
mapOldStepsToStepsID[*pSteps] = id;
|
2004-08-11 08:23:14 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-30 04:09:23 +00:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Reload the song
|
|
|
|
|
//
|
2004-08-11 08:23:14 +00:00
|
|
|
const CString dir = pSong->GetSongDir();
|
|
|
|
|
FILEMAN->FlushDirCache( dir );
|
|
|
|
|
|
|
|
|
|
/* Erase existing data and reload. */
|
|
|
|
|
pSong->Reset();
|
|
|
|
|
const bool OldVal = PREFSMAN->m_bFastLoad;
|
2005-05-06 20:41:05 +00:00
|
|
|
PREFSMAN->m_bFastLoad.Set( false );
|
2004-08-11 08:23:14 +00:00
|
|
|
pSong->LoadFromSongDir( dir );
|
|
|
|
|
/* XXX: reload edits? */
|
2005-05-06 20:41:05 +00:00
|
|
|
PREFSMAN->m_bFastLoad.Set( OldVal );
|
2004-08-11 08:23:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Courses cache Steps pointers. On the off chance that this isn't the last
|
|
|
|
|
* thing this screen does, clear that cache. */
|
|
|
|
|
/* TODO: Don't make Song depend on SongManager. This is breaking
|
|
|
|
|
* encapsulation and placing confusing limitation on what can be done in
|
|
|
|
|
* SONGMAN->Invalidate(). -Chris */
|
|
|
|
|
this->Invalidate( pSong );
|
2004-11-26 14:26:20 +00:00
|
|
|
StepsID::ClearCache();
|
2004-08-11 08:23:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
FOREACH_PlayerNumber( p )
|
|
|
|
|
{
|
2005-02-28 20:12:07 +00:00
|
|
|
CONVERT_STEPS_POINTER( GAMESTATE->m_pCurSteps[p], mapOldStepsToStepsID, pSong, bAllowNotesLoss );
|
2004-08-30 04:09:23 +00:00
|
|
|
|
2005-04-20 06:13:06 +00:00
|
|
|
FOREACH( Steps*, STATSMAN->m_CurStageStats.m_player[p].vpPlayedSteps, pSteps )
|
2005-02-28 20:12:07 +00:00
|
|
|
CONVERT_STEPS_POINTER( *pSteps, mapOldStepsToStepsID, pSong, bAllowNotesLoss );
|
2004-08-30 04:09:23 +00:00
|
|
|
|
2005-02-16 03:25:45 +00:00
|
|
|
FOREACH( StageStats, STATSMAN->m_vPlayedStageStats, ss )
|
2005-04-20 06:13:06 +00:00
|
|
|
FOREACH( Steps*, ss->m_player[p].vpPlayedSteps, pSteps )
|
2005-02-28 20:12:07 +00:00
|
|
|
CONVERT_STEPS_POINTER( *pSteps, mapOldStepsToStepsID, pSong, bAllowNotesLoss );
|
2004-08-11 08:23:14 +00:00
|
|
|
}
|
2005-02-28 20:12:07 +00:00
|
|
|
|
|
|
|
|
CONVERT_STEPS_POINTER( GAMESTATE->m_pEditSourceSteps, mapOldStepsToStepsID, pSong, bAllowNotesLoss );
|
2004-08-11 08:23:14 +00:00
|
|
|
}
|
|
|
|
|
|
2004-07-24 06:40:51 +00:00
|
|
|
void SongManager::RegenerateNonFixedCourses()
|
|
|
|
|
{
|
|
|
|
|
for( unsigned i=0; i < m_pCourses.size(); i++ )
|
|
|
|
|
m_pCourses[i]->RegenerateNonFixedTrails();
|
2003-01-21 05:14:59 +00:00
|
|
|
}
|
2002-12-21 18:23:37 +00:00
|
|
|
|
2004-06-05 08:08:42 +00:00
|
|
|
void SongManager::SetPreferences()
|
|
|
|
|
{
|
|
|
|
|
for( unsigned int i=0; i<m_pSongs.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
/* PREFSMAN->m_bAutogenSteps may have changed. */
|
|
|
|
|
m_pSongs[i]->RemoveAutoGenNotes();
|
|
|
|
|
m_pSongs[i]->AddAutoGenNotes();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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] );
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-07 08:46:40 +00:00
|
|
|
void SongManager::GetCourses( CourseType ct, vector<Course*> &AddTo, bool bIncludeAutogen )
|
2003-01-21 05:14:59 +00:00
|
|
|
{
|
2003-01-22 05:29:27 +00:00
|
|
|
for( unsigned i=0; i<m_pCourses.size(); i++ )
|
2005-04-07 08:46:40 +00:00
|
|
|
if( m_pCourses[i]->GetCourseType() == ct )
|
2003-03-27 01:56:21 +00:00
|
|
|
if( bIncludeAutogen || !m_pCourses[i]->m_bIsAutogen )
|
|
|
|
|
AddTo.push_back( m_pCourses[i] );
|
2002-12-21 18:23:37 +00:00
|
|
|
}
|
|
|
|
|
|
2005-06-23 22:43:48 +00:00
|
|
|
void SongManager::GetCoursesInGroup( vector<Course*> &AddTo, const CString &sCourseGroup, bool bIncludeAutogen )
|
|
|
|
|
{
|
|
|
|
|
for( unsigned i=0; i<m_pCourses.size(); i++ )
|
|
|
|
|
if( m_pCourses[i]->m_sGroupName == sCourseGroup )
|
|
|
|
|
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,
|
2005-09-03 03:52:22 +00:00
|
|
|
Song*& pSongOut, Steps*& pStepsOut, PlayerOptions *pPlayerOptionsOut, SongOptions *pSongOptionsOut )
|
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-05 03:24:47 +00:00
|
|
|
|
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;
|
2005-07-31 05:41:32 +00:00
|
|
|
CourseLoaderCRS::LoadFromCRSFile( sCoursePath, course );
|
2003-02-14 21:42:44 +00:00
|
|
|
if( course.GetEstimatedNumStages() <= 0 ) return false;
|
2002-08-30 20:49:56 +00:00
|
|
|
|
2004-06-28 07:26:00 +00:00
|
|
|
Trail *pTrail = course.GetTrail( GAMESTATE->GetCurrentStyle()->m_StepsType );
|
2004-05-23 00:53:20 +00:00
|
|
|
if( pTrail->m_vEntries.empty() )
|
2003-02-14 21:42:44 +00:00
|
|
|
return false;
|
2003-04-23 07:28:24 +00:00
|
|
|
|
2005-09-03 03:52:22 +00:00
|
|
|
if( pPlayerOptionsOut != NULL )
|
|
|
|
|
{
|
|
|
|
|
pPlayerOptionsOut->Init();
|
|
|
|
|
pPlayerOptionsOut->FromString( pTrail->m_vEntries[0].Modifiers );
|
|
|
|
|
}
|
|
|
|
|
if( pSongOptionsOut != NULL )
|
|
|
|
|
{
|
|
|
|
|
pSongOptionsOut->Init();
|
|
|
|
|
pSongOptionsOut->FromString( pTrail->m_vEntries[0].Modifiers );
|
|
|
|
|
}
|
2003-08-12 08:03:16 +00:00
|
|
|
|
2004-05-23 00:53:20 +00:00
|
|
|
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
|
|
|
|
2003-02-05 00:33:14 +00:00
|
|
|
/* Return true if n1 < n2. */
|
2003-08-03 00:13:55 +00:00
|
|
|
bool CompareNotesPointersForExtra(const Steps *n1, const Steps *n2)
|
2003-02-05 00:33:14 +00:00
|
|
|
{
|
|
|
|
|
/* 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 */
|
|
|
|
|
|
2004-04-18 07:34:37 +00:00
|
|
|
if(StepsUtil::CompareNotesPointersByMeter(n1,n2)) return true;
|
|
|
|
|
if(StepsUtil::CompareNotesPointersByMeter(n2,n1)) return false;
|
2003-02-05 00:33:14 +00:00
|
|
|
/* n1 meter == n2 meter */
|
|
|
|
|
|
2004-04-18 07:34:37 +00:00
|
|
|
return StepsUtil::CompareNotesPointersByRadarValues(n1,n2);
|
2003-02-05 00:33:14 +00:00
|
|
|
}
|
|
|
|
|
|
2004-06-28 07:26:00 +00:00
|
|
|
void SongManager::GetExtraStageInfo( bool bExtra2, const Style *sd,
|
2005-09-03 03:52:22 +00:00
|
|
|
Song*& pSongOut, Steps*& pStepsOut, PlayerOptions *pPlayerOptionsOut, SongOptions *pSongOptionsOut )
|
2002-08-01 13:42:56 +00:00
|
|
|
{
|
2004-12-01 07:24:55 +00:00
|
|
|
CString sGroup = GAMESTATE->m_sPreferredSongGroup;
|
2005-06-24 06:06:16 +00:00
|
|
|
if( sGroup == GROUP_ALL )
|
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. */
|
2005-06-24 06:06:16 +00:00
|
|
|
LOG->Warn( "GetExtraStageInfo() called in GROUP_ALL, but GAMESTATE->m_pCurSong == NULL" );
|
2005-02-24 12:27:30 +00:00
|
|
|
GAMESTATE->m_pCurSong.Set( SONGMAN->GetRandomSong() );
|
2004-03-24 03:10:12 +00:00
|
|
|
}
|
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
|
|
|
|
2004-06-16 00:38:31 +00:00
|
|
|
ASSERT_M( sGroup != "", ssprintf("%p '%s' '%s'",
|
2005-02-27 21:55:30 +00:00
|
|
|
GAMESTATE->m_pCurSong.Get(),
|
2003-07-31 01:27:54 +00:00
|
|
|
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
|
|
|
|
2005-09-03 03:52:22 +00:00
|
|
|
if( GetExtraStageInfoFromCourse(bExtra2, sGroup, pSongOut, pStepsOut, pPlayerOptionsOut, pSongOptionsOut) )
|
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;
|
2003-02-10 05:30:12 +00:00
|
|
|
SONGMAN->GetSongs( apSongs, sGroup );
|
2002-10-31 02:46:42 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2005-09-03 03:52:22 +00:00
|
|
|
if( pPlayerOptionsOut != NULL )
|
|
|
|
|
{
|
|
|
|
|
pPlayerOptionsOut->Init();
|
|
|
|
|
pPlayerOptionsOut->m_fScrolls[PlayerOptions::SCROLL_REVERSE] = 1;
|
|
|
|
|
pPlayerOptionsOut->m_fScrollSpeed = 1.5f;
|
|
|
|
|
pPlayerOptionsOut->m_fDark = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( pSongOptionsOut != NULL )
|
|
|
|
|
{
|
|
|
|
|
pSongOptionsOut->Init();
|
|
|
|
|
pSongOptionsOut->m_DrainType = (bExtra2 ? SongOptions::DRAIN_SUDDEN_DEATH : SongOptions::DRAIN_NO_RECOVER);
|
|
|
|
|
}
|
2002-08-01 13:42:56 +00:00
|
|
|
}
|
2002-08-20 20:38:40 +00:00
|
|
|
|
2003-01-10 02:22:07 +00:00
|
|
|
Song* SongManager::GetRandomSong()
|
2002-08-20 20:38:40 +00:00
|
|
|
{
|
2004-02-01 23:06:07 +00:00
|
|
|
if( m_pShuffledSongs.empty() )
|
2003-01-10 02:22:07 +00:00
|
|
|
return NULL;
|
2002-08-20 20:38:40 +00:00
|
|
|
|
2004-02-02 05:49:18 +00:00
|
|
|
static int i = 0;
|
2004-02-01 23:06:07 +00:00
|
|
|
|
2005-05-22 15:05:15 +00:00
|
|
|
for( int iThrowAway=0; iThrowAway<100; iThrowAway++ )
|
|
|
|
|
{
|
|
|
|
|
i++;
|
|
|
|
|
wrap( i, m_pShuffledSongs.size() );
|
|
|
|
|
Song *pSong = m_pShuffledSongs[ i ];
|
|
|
|
|
if( pSong->IsTutorial() )
|
|
|
|
|
continue;
|
|
|
|
|
if( UNLOCKMAN->SongIsLocked(pSong) )
|
|
|
|
|
continue;
|
|
|
|
|
return pSong;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
2004-02-01 23:06:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
2005-05-22 15:05:15 +00:00
|
|
|
for( int iThrowAway=0; iThrowAway<100; iThrowAway++ )
|
|
|
|
|
{
|
|
|
|
|
i++;
|
|
|
|
|
wrap( i, m_pShuffledCourses.size() );
|
|
|
|
|
Course *pCourse = m_pShuffledCourses[ i ];
|
|
|
|
|
if( pCourse->m_bIsAutogen && !PREFSMAN->m_bAutogenGroupCourses )
|
|
|
|
|
continue;
|
|
|
|
|
if( pCourse->GetCourseType() == COURSE_TYPE_ENDLESS )
|
|
|
|
|
continue;
|
|
|
|
|
if( UNLOCKMAN->CourseIsLocked(pCourse) )
|
|
|
|
|
continue;
|
|
|
|
|
return pCourse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
2002-08-20 20:38:40 +00:00
|
|
|
}
|
2002-11-08 08:17:59 +00:00
|
|
|
|
2002-12-02 05:25:44 +00:00
|
|
|
Song* SongManager::GetSongFromDir( CString sDir )
|
2002-11-08 08:17:59 +00:00
|
|
|
{
|
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( '\\', '/' );
|
|
|
|
|
|
2002-11-08 08:17:59 +00:00
|
|
|
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 )
|
2002-11-08 08:17:59 +00:00
|
|
|
return m_pSongs[i];
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2003-01-21 05:14:59 +00:00
|
|
|
|
|
|
|
|
Course* SongManager::GetCourseFromPath( CString sPath )
|
|
|
|
|
{
|
2004-08-30 21:55:50 +00:00
|
|
|
if( sPath == "" )
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2005-06-18 15:00:11 +00:00
|
|
|
FOREACH_CONST( Course*, m_pCourses, c )
|
|
|
|
|
{
|
|
|
|
|
if( sPath.CompareNoCase((*c)->m_sPath) == 0 )
|
|
|
|
|
return *c;
|
|
|
|
|
}
|
2003-01-21 05:14:59 +00:00
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2003-01-22 05:29:27 +00:00
|
|
|
|
2003-02-14 21:42:44 +00:00
|
|
|
Course* SongManager::GetCourseFromName( CString sName )
|
|
|
|
|
{
|
2004-08-30 21:55:50 +00:00
|
|
|
if( sName == "" )
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2003-02-14 21:42:44 +00:00
|
|
|
for( unsigned int i=0; i<m_pCourses.size(); i++ )
|
2005-05-23 00:38:09 +00:00
|
|
|
if( sName.CompareNoCase(m_pCourses[i]->GetDisplayFullTitle()) == 0 )
|
2003-02-14 21:42:44 +00:00
|
|
|
return m_pCourses[i];
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-07-22 05:45:30 +00:00
|
|
|
/*
|
|
|
|
|
* 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( '\\', '/' );
|
2003-07-22 05:45:30 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2003-02-03 05:53:59 +00:00
|
|
|
|
2003-07-17 12:14:19 +00:00
|
|
|
Course *SongManager::FindCourse( CString sName )
|
|
|
|
|
{
|
|
|
|
|
for( unsigned i = 0; i < m_pCourses.size(); i++ )
|
|
|
|
|
{
|
2005-05-23 00:38:09 +00:00
|
|
|
if( !sName.CompareNoCase(m_pCourses[i]->GetDisplayFullTitle()) )
|
2003-07-17 12:14:19 +00:00
|
|
|
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
|
2005-09-04 23:29:31 +00:00
|
|
|
vector<Song*> apBestSongs = m_pSongs;
|
|
|
|
|
for ( unsigned j=0; j < apBestSongs.size() ; ++j )
|
2003-11-14 21:52:05 +00:00
|
|
|
{
|
2005-09-04 23:29:31 +00:00
|
|
|
bool bFiltered = false;
|
|
|
|
|
/* Filter out hidden songs. */
|
|
|
|
|
if( apBestSongs[j]->GetDisplayed() != Song::SHOW_ALWAYS )
|
|
|
|
|
bFiltered = true;
|
|
|
|
|
/* Filter out locked songs. */
|
|
|
|
|
if( UNLOCKMAN->SongIsLocked(apBestSongs[j]) )
|
|
|
|
|
bFiltered = true;
|
|
|
|
|
if( !bFiltered )
|
|
|
|
|
continue;
|
2004-03-07 00:17:50 +00:00
|
|
|
|
2005-09-04 23:29:31 +00:00
|
|
|
/* Remove it. */
|
|
|
|
|
swap( apBestSongs[j], apBestSongs.back() );
|
|
|
|
|
apBestSongs.erase( apBestSongs.end()-1 );
|
|
|
|
|
--j;
|
|
|
|
|
}
|
2004-03-07 00:17:50 +00:00
|
|
|
|
2005-09-04 23:29:31 +00:00
|
|
|
SongUtil::SortSongPointerArrayByTitle( apBestSongs );
|
|
|
|
|
|
|
|
|
|
vector<Course*> apBestCourses[NUM_CourseType];
|
|
|
|
|
FOREACH_CourseType( ct )
|
|
|
|
|
{
|
|
|
|
|
GetCourses( ct, apBestCourses[ct], PREFSMAN->m_bAutogenGroupCourses );
|
|
|
|
|
CourseUtil::SortCoursePointerArrayByTitle( apBestCourses[ct] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FOREACH_ProfileSlot( i )
|
|
|
|
|
{
|
|
|
|
|
m_pBestSongs[i] = apBestSongs;
|
2004-03-07 00:17:50 +00:00
|
|
|
|
2005-04-07 08:46:40 +00:00
|
|
|
SongUtil::SortSongPointerArrayByNumPlays( m_pBestSongs[i], i, true );
|
2003-11-17 03:20:39 +00:00
|
|
|
|
2005-04-07 08:46:40 +00:00
|
|
|
FOREACH_CourseType( ct )
|
|
|
|
|
{
|
|
|
|
|
vector<Course*> &vpCourses = m_pBestCourses[i][ct];
|
2005-09-04 23:29:31 +00:00
|
|
|
vpCourses = apBestCourses[ct];
|
2005-04-07 08:46:40 +00:00
|
|
|
CourseUtil::SortCoursePointerArrayByNumPlays( vpCourses, 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()
|
|
|
|
|
{
|
2004-04-18 07:34:37 +00:00
|
|
|
SongUtil::SortSongPointerArrayByTitle( m_pSongs );
|
2004-02-27 21:28:15 +00:00
|
|
|
}
|
|
|
|
|
|
2003-08-04 09:18:26 +00:00
|
|
|
void SongManager::UpdateRankingCourses()
|
|
|
|
|
{
|
2005-05-19 20:32:10 +00:00
|
|
|
/* Updating the ranking courses data is fairly expensive
|
|
|
|
|
* since it involves comparing strings. Do so sparingly. */
|
2003-08-04 09:18:26 +00:00
|
|
|
CStringArray RankingCourses;
|
|
|
|
|
split( THEME->GetMetric("ScreenRanking","CoursesToShow"),",", RankingCourses);
|
|
|
|
|
|
2005-05-19 20:32:10 +00:00
|
|
|
for( unsigned i=0; i < m_pCourses.size(); i++ )
|
2003-08-04 09:18:26 +00:00
|
|
|
{
|
2005-05-19 20:32:10 +00:00
|
|
|
if( m_pCourses[i]->GetEstimatedNumStages() > 7 )
|
2004-05-23 00:53:20 +00:00
|
|
|
m_pCourses[i]->m_SortOrder_Ranking = 3;
|
2003-08-04 09:18:26 +00:00
|
|
|
else
|
2004-05-23 00:53:20 +00:00
|
|
|
m_pCourses[i]->m_SortOrder_Ranking = 2;
|
2003-08-04 09:18:26 +00:00
|
|
|
|
2005-05-19 20:32:10 +00:00
|
|
|
for( unsigned j = 0; j < RankingCourses.size(); j++ )
|
|
|
|
|
if( !RankingCourses[j].CompareNoCase(m_pCourses[i]->m_sPath) )
|
2004-05-23 00:53:20 +00:00
|
|
|
m_pCourses[i]->m_SortOrder_Ranking = 1;
|
2003-08-04 09:18:26 +00:00
|
|
|
}
|
2003-08-10 03:23:17 +00:00
|
|
|
}
|
2003-08-17 18:03:02 +00:00
|
|
|
|
2005-07-31 05:41:32 +00:00
|
|
|
void SongManager::RefreshCourseGroupInfo()
|
2005-04-24 23:39:35 +00:00
|
|
|
{
|
2005-07-31 05:41:32 +00:00
|
|
|
m_mapCourseGroupToInfo.clear();
|
2004-04-23 02:08:11 +00:00
|
|
|
|
2005-07-31 05:41:32 +00:00
|
|
|
FOREACH_CONST( Course*, m_pCourses, c )
|
|
|
|
|
{
|
|
|
|
|
m_mapCourseGroupToInfo[(*c)->m_sGroupName]; // insert
|
|
|
|
|
}
|
2004-02-08 01:05:53 +00:00
|
|
|
|
2005-07-31 05:41:32 +00:00
|
|
|
// TODO: Search for course group banners
|
|
|
|
|
FOREACHM( CString, CourseGroupInfo, m_mapCourseGroupToInfo, iter )
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-04-23 00:26:51 +00:00
|
|
|
|
2005-07-31 05:41:32 +00:00
|
|
|
void SongManager::LoadAllFromProfileDir( const CString &sProfileDir, ProfileSlot slot )
|
|
|
|
|
{
|
2005-04-24 23:39:35 +00:00
|
|
|
{
|
2005-07-31 05:41:32 +00:00
|
|
|
//
|
|
|
|
|
// Load all edit steps
|
|
|
|
|
//
|
|
|
|
|
CString sDir = sProfileDir + EDIT_STEPS_SUBDIR;
|
|
|
|
|
|
|
|
|
|
CStringArray vsFiles;
|
|
|
|
|
GetDirListing( sDir+"*.edit", vsFiles, false, true );
|
2004-02-08 01:05:53 +00:00
|
|
|
|
2005-07-31 05:41:32 +00:00
|
|
|
int iNumEditsLoaded = GetNumEditsLoadedFromProfile( slot );
|
2005-08-03 03:22:14 +00:00
|
|
|
int size = min( (int) vsFiles.size(), MAX_EDIT_STEPS_PER_PROFILE - iNumEditsLoaded );
|
2005-07-31 05:41:32 +00:00
|
|
|
|
|
|
|
|
for( int i=0; i<size; i++ )
|
2005-04-24 23:39:35 +00:00
|
|
|
{
|
2005-07-31 05:41:32 +00:00
|
|
|
CString fn = vsFiles[i];
|
|
|
|
|
|
|
|
|
|
SMLoader::LoadEdit( fn, slot );
|
2004-02-08 01:05:53 +00:00
|
|
|
}
|
2005-07-31 05:41:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// Load all edit courses
|
|
|
|
|
//
|
|
|
|
|
CString sDir = sProfileDir + EDIT_COURSES_SUBDIR;
|
2005-04-24 23:39:35 +00:00
|
|
|
|
2005-07-31 05:41:32 +00:00
|
|
|
CStringArray vsFiles;
|
|
|
|
|
GetDirListing( sDir+"*.crs", vsFiles, false, true );
|
|
|
|
|
|
|
|
|
|
int iNumEditsLoaded = GetNumEditsLoadedFromProfile( slot );
|
2005-08-03 03:22:14 +00:00
|
|
|
int size = min( (int) vsFiles.size(), MAX_EDIT_COURSES_PER_PROFILE - iNumEditsLoaded );
|
2005-07-31 05:41:32 +00:00
|
|
|
|
|
|
|
|
for( int i=0; i<size; i++ )
|
|
|
|
|
{
|
|
|
|
|
CString fn = vsFiles[i];
|
|
|
|
|
|
|
|
|
|
CourseLoaderCRS::LoadEdit( fn, slot );
|
|
|
|
|
}
|
2004-02-08 01:05:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-25 02:27:17 +00:00
|
|
|
int SongManager::GetNumEditsLoadedFromProfile( ProfileSlot slot ) const
|
|
|
|
|
{
|
|
|
|
|
int iCount = 0;
|
|
|
|
|
for( unsigned s=0; s<m_pSongs.size(); s++ )
|
|
|
|
|
{
|
|
|
|
|
const Song *pSong = m_pSongs[s];
|
|
|
|
|
vector<Steps*> apSteps;
|
|
|
|
|
pSong->GetSteps( apSteps );
|
|
|
|
|
|
|
|
|
|
for( unsigned i = 0; i < apSteps.size(); ++i )
|
|
|
|
|
{
|
|
|
|
|
const Steps *pSteps = apSteps[i];
|
|
|
|
|
if( pSteps->WasLoadedFromProfile() && pSteps->GetLoadedFromProfileSlot() == slot )
|
|
|
|
|
++iCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return iCount;
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-25 00:09:59 +00:00
|
|
|
void SongManager::FreeAllLoadedFromProfile( ProfileSlot slot )
|
2004-02-08 01:05:53 +00:00
|
|
|
{
|
2005-07-31 05:41:32 +00:00
|
|
|
FOREACH( Song*, m_pSongs, s )
|
|
|
|
|
(*s)->FreeAllLoadedFromProfile( slot );
|
|
|
|
|
|
2005-08-11 20:51:15 +00:00
|
|
|
vector<Course*> apToDelete;
|
2005-07-31 05:41:32 +00:00
|
|
|
FOREACH( Course*, m_pCourses, c )
|
2004-02-08 01:05:53 +00:00
|
|
|
{
|
2005-08-11 20:51:15 +00:00
|
|
|
Course *pCourse = *c;
|
|
|
|
|
if( pCourse->GetLoadedFromProfileSlot() == PROFILE_SLOT_INVALID )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if( slot == PROFILE_SLOT_INVALID || pCourse->GetLoadedFromProfileSlot() == slot )
|
|
|
|
|
apToDelete.push_back( *c );
|
2004-02-08 01:05:53 +00:00
|
|
|
}
|
2004-11-24 16:59:57 +00:00
|
|
|
|
2005-08-11 20:51:15 +00:00
|
|
|
// XXX: this will update best, etc. every time; too slow
|
|
|
|
|
for( unsigned i = 0; i < apToDelete.size(); ++i )
|
|
|
|
|
this->DeleteCourse( apToDelete[i] );
|
|
|
|
|
|
2004-11-24 16:59:57 +00:00
|
|
|
// After freeing some Steps pointers, the cache will be invalid.
|
2004-11-26 14:26:20 +00:00
|
|
|
StepsID::ClearCache();
|
2004-02-08 01:05:53 +00:00
|
|
|
}
|
|
|
|
|
|
2005-03-08 01:46:57 +00:00
|
|
|
int SongManager::GetNumStepsLoadedFromProfile()
|
2005-03-07 05:20:32 +00:00
|
|
|
{
|
2005-03-08 01:46:57 +00:00
|
|
|
int iCount = 0;
|
|
|
|
|
FOREACH( Song*, m_pSongs, s )
|
2005-03-07 05:20:32 +00:00
|
|
|
{
|
2005-03-08 01:46:57 +00:00
|
|
|
vector<Steps*> vpAllSteps = (*s)->GetAllSteps();
|
|
|
|
|
|
|
|
|
|
FOREACH( Steps*, vpAllSteps, ss )
|
|
|
|
|
{
|
2005-07-29 00:03:12 +00:00
|
|
|
if( (*ss)->GetLoadedFromProfileSlot() != PROFILE_SLOT_INVALID )
|
2005-03-08 01:46:57 +00:00
|
|
|
iCount++;
|
|
|
|
|
}
|
2005-03-07 05:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
2005-03-08 01:46:57 +00:00
|
|
|
return iCount;
|
2005-03-07 05:20:32 +00:00
|
|
|
}
|
2005-02-21 17:26:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
// lua start
|
|
|
|
|
#include "LuaBinding.h"
|
|
|
|
|
|
2005-06-20 05:02:03 +00:00
|
|
|
class LunaSongManager: public Luna<SongManager>
|
2005-02-21 17:26:43 +00:00
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
LunaSongManager() { LUA->Register( Register ); }
|
|
|
|
|
|
2005-02-22 23:08:46 +00:00
|
|
|
static int GetAllSongs( T* p, lua_State *L )
|
|
|
|
|
{
|
|
|
|
|
const vector<Song*> &v = p->GetAllSongs();
|
2005-03-13 00:01:05 +00:00
|
|
|
LuaHelpers::CreateTableFromArray<Song*>( v, L );
|
2005-02-22 23:08:46 +00:00
|
|
|
return 1;
|
|
|
|
|
}
|
2005-02-21 17:26:43 +00:00
|
|
|
static int GetAllCourses( T* p, lua_State *L )
|
|
|
|
|
{
|
2005-02-22 23:08:46 +00:00
|
|
|
vector<Course*> v;
|
|
|
|
|
p->GetAllCourses( v, BArg(1) );
|
2005-03-13 00:01:05 +00:00
|
|
|
LuaHelpers::CreateTableFromArray<Course*>( v, L );
|
2005-02-21 17:26:43 +00:00
|
|
|
return 1;
|
|
|
|
|
}
|
2005-05-22 15:05:15 +00:00
|
|
|
static int FindSong( T* p, lua_State *L ) { Song *pS = p->FindSong(SArg(1)); if(pS) pS->PushSelf(L); else lua_pushnil(L); return 1; }
|
|
|
|
|
static int FindCourse( T* p, lua_State *L ) { Course *pC = p->FindCourse(SArg(1)); if(pC) pC->PushSelf(L); else lua_pushnil(L); return 1; }
|
|
|
|
|
static int GetRandomSong( T* p, lua_State *L ) { Song *pS = p->GetRandomSong(); if(pS) pS->PushSelf(L); else lua_pushnil(L); return 1; }
|
|
|
|
|
static int GetRandomCourse( T* p, lua_State *L ){ Course *pC = p->GetRandomCourse(); if(pC) pC->PushSelf(L); else lua_pushnil(L); return 1; }
|
2005-06-25 03:11:16 +00:00
|
|
|
static int GetNumSongs( T* p, lua_State *L ) { lua_pushnumber( L, p->GetNumSongs() ); return 1; }
|
|
|
|
|
static int GetNumSongGroups( T* p, lua_State *L ) { lua_pushnumber( L, p->GetNumSongGroups() ); return 1; }
|
|
|
|
|
static int GetNumCourses( T* p, lua_State *L ) { lua_pushnumber( L, p->GetNumCourses() ); return 1; }
|
|
|
|
|
static int GetNumCourseGroups( T* p, lua_State *L ) { lua_pushnumber( L, p->GetNumCourseGroups() ); return 1; }
|
2005-02-21 17:26:43 +00:00
|
|
|
|
|
|
|
|
static void Register(lua_State *L)
|
|
|
|
|
{
|
2005-09-10 02:47:04 +00:00
|
|
|
ADD_METHOD( GetAllSongs );
|
|
|
|
|
ADD_METHOD( GetAllCourses );
|
|
|
|
|
ADD_METHOD( FindSong );
|
|
|
|
|
ADD_METHOD( FindCourse );
|
|
|
|
|
ADD_METHOD( GetRandomSong );
|
|
|
|
|
ADD_METHOD( GetRandomCourse );
|
|
|
|
|
ADD_METHOD( GetNumSongs );
|
|
|
|
|
ADD_METHOD( GetNumSongGroups );
|
|
|
|
|
ADD_METHOD( GetNumCourses );
|
|
|
|
|
ADD_METHOD( GetNumCourseGroups );
|
|
|
|
|
|
2005-02-21 17:26:43 +00:00
|
|
|
Luna<T>::Register( L );
|
|
|
|
|
|
|
|
|
|
// Add global singleton if constructed already. If it's not constructed yet,
|
|
|
|
|
// then we'll register it later when we reinit Lua just before
|
|
|
|
|
// initializing the display.
|
|
|
|
|
if( SONGMAN )
|
|
|
|
|
{
|
|
|
|
|
lua_pushstring(L, "SONGMAN");
|
2005-06-15 02:21:24 +00:00
|
|
|
SONGMAN->PushSelf( L );
|
2005-02-21 17:26:43 +00:00
|
|
|
lua_settable(L, LUA_GLOBALSINDEX);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
LUA_REGISTER_CLASS( SongManager )
|
|
|
|
|
// lua end
|
|
|
|
|
|
2004-06-08 01:24:17 +00:00
|
|
|
/*
|
|
|
|
|
* (c) 2001-2004 Chris Danford, Glenn Maynard
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|