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 "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"
2005-12-22 03:10:04 +00:00
# include "LocalizedString.h"
2006-05-16 06:01:12 +00:00
# include "CatalogXml.h"
2006-05-20 09:00:35 +00:00
# include "NoteSkinManager.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
2006-04-04 21:52:54 +00:00
const RString SONGS_DIR = " /Songs/ " ;
const RString ADDITIONAL_SONGS_DIR = " /AdditionalSongs/ " ;
const RString COURSES_DIR = " /Courses/ " ;
2006-08-14 20:05:35 +00:00
const RString ADDITIONAL_COURSES_DIR = " /AdditionalCourses/ " ;
2006-05-03 21:33:19 +00:00
const RString EDIT_SUBDIR = " Edits/ " ;
2003-01-22 05:29:27 +00:00
2006-08-07 01:23:59 +00:00
static const ThemeMetric < RageColor > EXTRA_COLOR ( " SongManager " , " ExtraColor " ) ;
static const ThemeMetric < int > EXTRA_COLOR_METER ( " SongManager " , " ExtraColorMeter " ) ;
static const ThemeMetric < bool > USE_PREFERRED_SORT_COLOR ( " SongManager " , " UsePreferredSortColor " ) ;
static const ThemeMetric < bool > USE_UNLOCK_COLOR ( " SongManager " , " UseUnlockColor " ) ;
static const ThemeMetric < RageColor > UNLOCK_COLOR ( " SongManager " , " UnlockColor " ) ;
static const ThemeMetric < bool > MOVE_UNLOCKS_TO_BOTTOM_OF_PREFERRED_SORT ( " SongManager " , " MoveUnlocksToBottomOfPreferredSort " ) ;
2006-08-07 01:53:58 +00:00
static const ThemeMetric < RString > EXTRA_STAGE_PLAYER_OPTIONS ( " SongManager " , " ExtraStagePlayerOptions " ) ;
static const ThemeMetric < RString > EXTRA_STAGE_SONG_OPTIONS ( " SongManager " , " ExtraStageSongOptions " ) ;
static const ThemeMetric < RString > EXTRA_STAGE2_PLAYER_OPTIONS ( " SongManager " , " ExtraStage2PlayerOptions " ) ;
static const ThemeMetric < RString > EXTRA_STAGE2_SONG_OPTIONS ( " SongManager " , " ExtraStage2SongOptions " ) ;
2006-08-20 05:55:09 +00:00
static const ThemeMetric < int > EXTRA_STAGE2_DIFFICULTY_MAX ( " SongManager " , " ExtraStage2DifficultyMax " ) ;
2006-08-07 01:23:59 +00:00
RString SONG_GROUP_COLOR_NAME ( size_t i ) { return ssprintf ( " SongGroupColor%i " , ( int ) i + 1 ) ; }
RString 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
{
2006-09-21 04:42:45 +00:00
// Register with Lua.
{
Lua * L = LUA - > Get ( ) ;
lua_pushstring ( L , " SONGMAN " ) ;
this - > PushSelf ( L ) ;
lua_settable ( L , LUA_GLOBALSINDEX ) ;
LUA - > Release ( L ) ;
}
2006-08-07 01:23:59 +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 ( )
{
2006-09-21 04:42:45 +00:00
// Unregister with Lua.
LUA - > UnsetGlobal ( " SONGMAN " ) ;
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 ( ) ;
}
2006-01-07 05:43:25 +00:00
static LocalizedString RELOADING ( " SongManager " , " Reloading... " ) ;
2006-05-05 00:41:17 +00:00
void SongManager : : Reload ( bool bAllowFastLoad , LoadingWindow * ld )
2003-08-06 05:28:58 +00:00
{
2006-09-16 03:31:27 +00:00
FILEMAN - > FlushDirCache ( SONGS_DIR ) ;
FILEMAN - > FlushDirCache ( ADDITIONAL_SONGS_DIR ) ;
FILEMAN - > FlushDirCache ( COURSES_DIR ) ;
FILEMAN - > FlushDirCache ( ADDITIONAL_COURSES_DIR ) ;
FILEMAN - > FlushDirCache ( EDIT_SUBDIR ) ;
2003-08-06 05:28:58 +00:00
2004-02-21 01:52:00 +00:00
if ( ld )
2006-01-07 05:43:25 +00:00
ld - > SetText ( RELOADING ) ;
2004-02-21 01:52:00 +00:00
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
const bool OldVal = PREFSMAN - > m_bFastLoad ;
2006-05-05 00:41:17 +00:00
if ( ! bAllowFastLoad )
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
2006-05-02 22:02:18 +00:00
// reload scores and unlocks afterward
2004-02-16 05:35:06 +00:00
PROFILEMAN - > LoadMachineProfile ( ) ;
2006-05-02 22:02:18 +00:00
UNLOCKMAN - > Reload ( ) ;
2006-05-16 06:01:12 +00:00
CatalogXml : : Save ( NULL ) ;
2003-12-30 04:26:39 +00:00
2006-05-05 00:41:17 +00:00
if ( ! bAllowFastLoad )
PREFSMAN - > m_bFastLoad . Set ( OldVal ) ;
2006-05-02 08:18:54 +00:00
UpdatePreferredSort ( ) ;
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 ) ;
2006-06-15 10:54:55 +00:00
const bool bOldVal = PREFSMAN - > m_bFastLoad ;
PREFSMAN - > m_bFastLoad . Set ( PREFSMAN - > m_bFastLoadAdditionalSongs ) ;
2006-04-04 21:52:54 +00:00
LoadStepManiaSongDir ( ADDITIONAL_SONGS_DIR , ld ) ;
2006-06-15 10:54:55 +00:00
PREFSMAN - > m_bFastLoad . Set ( bOldVal ) ;
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
}
2005-12-20 02:45:25 +00:00
2006-09-17 01:19:19 +00:00
static LocalizedString FOLDER_CONTAINS_MUSIC_FILES ( " SongManager " , " The folder \" %s \" appears to be a song folder. All song folders must reside in a group folder. For example, \" Songs/Originals/My Song \" . " ) ;
2006-01-22 01:00:06 +00:00
void SongManager : : SanityCheckGroupDir ( RString sDir ) const
2002-09-10 07:05:14 +00:00
{
// Check to see if they put a song directly inside the group folder.
2006-01-22 01:00:06 +00:00
vector < RString > 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 ( ) )
2005-12-20 02:45:25 +00:00
RageException : : Throw ( FOLDER_CONTAINS_MUSIC_FILES . GetValue ( ) , sDir . c_str ( ) ) ;
2002-09-10 07:05:14 +00:00
}
2006-01-22 01:00:06 +00:00
void SongManager : : AddGroup ( RString sDir , RString sGroupDirName )
2002-09-10 07:11:29 +00:00
{
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 )
2006-03-20 01:04:46 +00:00
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
2006-01-22 01:00:06 +00:00
vector < RString > 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
2006-01-22 01:00:06 +00:00
RString 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
}
2006-01-07 05:43:25 +00:00
static LocalizedString LOADING_SONGS ( " SongManager " , " Loading songs... " ) ;
2006-01-22 01:00:06 +00:00
void SongManager : : LoadStepManiaSongDir ( RString 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
2006-01-22 01:00:06 +00:00
vector < RString > arrayGroupDirs ;
2003-01-23 04:43:22 +00:00
GetDirListing ( sDir + " * " , arrayGroupDirs , true ) ;
2005-12-20 08:35:47 +00:00
SortRStringArray ( arrayGroupDirs ) ;
2005-06-23 08:05:09 +00:00
StripCvs ( arrayGroupDirs ) ;
2003-09-05 09:14:11 +00:00
2006-01-22 01:00:06 +00:00
FOREACH_CONST ( RString , arrayGroupDirs , s ) // foreach dir in /Songs/
2002-03-06 08:25:09 +00:00
{
2006-01-22 01:00:06 +00:00
RString 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
2006-01-22 01:00:06 +00:00
vector < RString > 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 ) ;
2005-12-20 08:35:47 +00:00
SortRStringArray ( arraySongDirs ) ;
2002-03-06 08:25:09 +00:00
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
{
2006-01-22 01:00:06 +00:00
RString sSongDirName = arraySongDirs [ j ] ;
2002-03-06 08:25:09 +00:00
// this is a song directory. Load a new song!
2004-09-20 17:02:37 +00:00
if ( ld )
{
2006-01-07 05:43:25 +00:00
ld - > SetText ( LOADING_SONGS . GetValue ( ) + ssprintf ( " \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 ;
}
2006-01-27 03:34:15 +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
2006-01-22 01:00:06 +00:00
void SongManager : : LoadGroupSymLinks ( RString sDir , RString sGroupFolder )
2003-09-05 09:14:11 +00:00
{
// Find all symlink files in this folder
2006-01-22 01:00:06 +00:00
vector < RString > arraySymLinks ;
2003-12-10 09:44:16 +00:00
GetDirListing ( sDir + sGroupFolder + " /*.include " , arraySymLinks , false ) ;
2005-12-20 08:35:47 +00:00
SortRStringArray ( arraySymLinks ) ;
2003-09-05 09:14:11 +00:00
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 ( ) ) ;
2006-01-22 01:00:06 +00:00
RString sSymDestination = msdF . GetParam ( 0 , 1 ) ; // Should only be 1 vale¶m...period.
2003-09-05 09:14:11 +00:00
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
2005-12-01 03:20:25 +00:00
for ( int i = 0 ; i < NUM_ProfileSlot ; + + i )
2006-03-20 01:04:46 +00:00
m_pPopularSongs [ i ] . clear ( ) ;
2004-05-29 20:15:07 +00:00
m_pShuffledSongs . clear ( ) ;
2002-02-28 19:40:40 +00:00
}
2006-01-22 01:00:06 +00:00
RString SongManager : : GetSongGroupBannerPath ( RString 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
2006-01-22 01:00:06 +00:00
return RString ( ) ;
2002-03-06 08:25:09 +00:00
}
2002-04-01 02:04:43 +00:00
2006-01-22 01:00:06 +00:00
void SongManager : : GetSongGroupNames ( vector < RString > & 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
2006-01-22 01:00:06 +00:00
bool SongManager : : DoesSongGroupExist ( RString 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
}
2006-01-22 01:00:06 +00:00
RageColor SongManager : : GetSongGroupColor ( const RString & 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 )
2006-03-19 05:59:24 +00:00
return SONG_GROUP_COLOR . GetValue ( i % NUM_SONG_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
2006-05-01 12:45:07 +00:00
// Use unlock color if applicable
const UnlockEntry * pUE = UNLOCKMAN - > FindSong ( pSong ) ;
2006-05-03 21:33:19 +00:00
if ( pUE & & USE_UNLOCK_COLOR . GetValue ( ) )
2006-05-02 01:18:21 +00:00
return UNLOCK_COLOR . GetValue ( ) ;
2006-05-01 12:45:07 +00:00
2006-03-20 01:04:46 +00:00
if ( USE_PREFERRED_SORT_COLOR )
2002-08-27 23:31:41 +00:00
{
2006-05-01 21:49:59 +00:00
FOREACH_CONST ( SongPointerVector , m_vPreferredSongSort , v )
2004-02-29 07:36:03 +00:00
{
2006-03-20 01:04:46 +00:00
FOREACH_CONST ( Song * , * v , s )
{
if ( * s = = pSong )
{
2006-05-01 21:49:59 +00:00
int i = v - m_vPreferredSongSort . begin ( ) ;
2006-03-20 01:04:46 +00:00
return SONG_GROUP_COLOR . GetValue ( i % NUM_SONG_GROUP_COLORS ) ;
}
}
2004-02-29 07:36:03 +00:00
}
2003-04-20 01:40:40 +00:00
2006-05-01 21:49:59 +00:00
int i = m_vPreferredSongSort . size ( ) ;
2006-03-20 01:04:46 +00:00
return SONG_GROUP_COLOR . GetValue ( i % NUM_SONG_GROUP_COLORS ) ;
2002-08-27 23:31:41 +00:00
}
2006-03-20 01:04:46 +00:00
else
{
2002-08-27 23:31:41 +00:00
2006-03-20 01:04:46 +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.
*
* 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?
*/
// const StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType;
const vector < Steps * > & vpSteps = pSong - > GetAllSteps ( ) ;
for ( unsigned i = 0 ; i < vpSteps . size ( ) ; i + + )
{
const Steps * pSteps = vpSteps [ i ] ;
switch ( pSteps - > GetDifficulty ( ) )
{
case DIFFICULTY_CHALLENGE :
case DIFFICULTY_EDIT :
continue ;
}
// if(pSteps->m_StepsType != st)
// continue;
if ( pSteps - > GetMeter ( ) > = EXTRA_COLOR_METER )
return ( RageColor ) EXTRA_COLOR ;
}
return GetSongGroupColor ( pSong - > m_sGroupName ) ;
}
2005-06-03 01:57:10 +00:00
}
2006-01-22 01:00:06 +00:00
RString SongManager : : GetCourseGroupBannerPath ( const RString & sCourseGroup )
2005-06-23 08:05:09 +00:00
{
2006-01-22 01:00:06 +00:00
map < RString , CourseGroupInfo > : : const_iterator iter = m_mapCourseGroupToInfo . find ( sCourseGroup ) ;
2005-07-31 05:41:32 +00:00
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 ( ) ) ) ;
2006-01-22 01:00:06 +00:00
return RString ( ) ;
2005-07-31 05:41:32 +00:00
}
else
{
return iter - > second . m_sBannerPath ;
2005-06-23 08:05:09 +00:00
}
}
2006-01-22 01:00:06 +00:00
void SongManager : : GetCourseGroupNames ( vector < RString > & AddTo )
2005-06-23 08:05:09 +00:00
{
2006-01-22 01:00:06 +00:00
FOREACHM_CONST ( RString , CourseGroupInfo , m_mapCourseGroupToInfo , iter )
2005-07-31 05:41:32 +00:00
AddTo . push_back ( iter - > first ) ;
2005-06-23 08:05:09 +00:00
}
2006-01-22 01:00:06 +00:00
bool SongManager : : DoesCourseGroupExist ( const RString & 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
}
2006-01-22 01:00:06 +00:00
RageColor SongManager : : GetCourseGroupColor ( const RString & sCourseGroup )
2005-06-23 08:05:09 +00:00
{
2005-07-31 05:41:32 +00:00
int iIndex = 0 ;
2006-01-22 01:00:06 +00:00
FOREACHM_CONST ( RString , 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 )
{
2006-05-01 21:49:59 +00:00
// Use unlock color if applicable
const UnlockEntry * pUE = UNLOCKMAN - > FindCourse ( pCourse ) ;
2006-05-03 21:33:19 +00:00
if ( pUE & & USE_UNLOCK_COLOR . GetValue ( ) )
2006-05-02 01:18:21 +00:00
return UNLOCK_COLOR . GetValue ( ) ;
2006-05-01 21:49:59 +00:00
if ( USE_PREFERRED_SORT_COLOR )
{
FOREACH_CONST ( CoursePointerVector , m_vPreferredCourseSort , v )
{
FOREACH_CONST ( Course * , * v , s )
{
if ( * s = = pCourse )
{
int i = v - m_vPreferredCourseSort . begin ( ) ;
return COURSE_GROUP_COLOR . GetValue ( i % NUM_COURSE_GROUP_COLORS ) ;
}
}
}
int i = m_vPreferredCourseSort . size ( ) ;
return COURSE_GROUP_COLOR . GetValue ( i % NUM_COURSE_GROUP_COLORS ) ;
}
else
{
return GetCourseGroupColor ( pCourse - > m_sGroupName ) ;
}
2002-08-27 23:31:41 +00:00
}
2006-01-22 01:00:06 +00:00
static void GetSongsFromVector ( const vector < Song * > & Songs , vector < Song * > & AddTo , RString 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 ] ) ;
}
2006-01-22 01:00:06 +00:00
void SongManager : : GetSongs ( vector < Song * > & AddTo , RString sGroupName , int iMaxStages ) const
2004-03-07 00:17:50 +00:00
{
GetSongsFromVector ( m_pSongs , AddTo , sGroupName , iMaxStages ) ;
}
2006-03-20 01:04:46 +00:00
void SongManager : : GetPopularSongs ( vector < Song * > & AddTo , RString sGroupName , int iMaxStages , ProfileSlot slot ) const
2004-03-07 00:17:50 +00:00
{
2006-03-20 01:04:46 +00:00
GetSongsFromVector ( m_pPopularSongs [ slot ] , AddTo , sGroupName , iMaxStages ) ;
}
void SongManager : : GetPreferredSortSongs ( vector < Song * > & AddTo , int iMaxStages ) const
{
2006-05-01 21:49:59 +00:00
if ( m_vPreferredSongSort . empty ( ) )
2006-03-20 01:04:46 +00:00
{
GetSongs ( AddTo , iMaxStages ) ;
return ;
}
2006-05-01 21:49:59 +00:00
FOREACH_CONST ( SongPointerVector , m_vPreferredSongSort , v )
2006-05-02 08:18:54 +00:00
{
2006-03-20 01:04:46 +00:00
FOREACH_CONST ( Song * , * v , s )
2006-05-02 08:18:54 +00:00
{
Song * pSong = * s ;
ASSERT ( pSong ) ;
AddTo . push_back ( pSong ) ;
}
}
2002-04-01 02:04:43 +00:00
}
2002-04-16 17:31:00 +00:00
2006-05-01 21:49:59 +00:00
void SongManager : : GetPreferredSortCourses ( CourseType ct , vector < Course * > & AddTo , bool bIncludeAutogen ) const
{
if ( m_vPreferredCourseSort . empty ( ) )
{
GetCourses ( ct , AddTo , bIncludeAutogen ) ;
return ;
}
FOREACH_CONST ( CoursePointerVector , m_vPreferredCourseSort , v )
2006-05-02 08:18:54 +00:00
{
2006-05-01 21:49:59 +00:00
FOREACH_CONST ( Course * , * v , c )
2006-05-02 08:18:54 +00:00
{
Course * pCourse = * c ;
if ( pCourse - > GetCourseType ( ) = = ct )
AddTo . push_back ( pCourse ) ;
}
}
2006-05-01 21:49:59 +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 ( ) ;
}
2006-05-03 21:33:19 +00:00
int SongManager : : GetNumUnlockedSongs ( ) const
{
int num = 0 ;
FOREACH_CONST ( Song * , m_pSongs , i )
{
2006-05-16 00:47:02 +00:00
if ( UNLOCKMAN - > SongIsLocked ( * i ) )
continue ;
num + + ;
}
return num ;
}
int SongManager : : GetNumSelectableAndUnlockedSongs ( ) const
{
int num = 0 ;
FOREACH_CONST ( Song * , m_pSongs , i )
{
if ( UNLOCKMAN - > SongIsLocked ( * i ) )
continue ;
if ( ( * i ) - > m_SelectionDisplay ! = Song : : SHOW_ALWAYS )
continue ;
num + + ;
2006-05-03 21:33:19 +00:00
}
return num ;
}
int SongManager : : GetNumAdditionalSongs ( ) const
{
int num = 0 ;
FOREACH_CONST ( Song * , m_pSongs , i )
{
if ( SONGMAN - > WasLoadedFromAdditionalSongs ( * i ) )
num + + ;
}
return num ;
}
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 ( ) ;
}
2006-08-14 20:05:35 +00:00
int SongManager : : GetNumAdditionalCourses ( ) const
{
int num = 0 ;
FOREACH_CONST ( Course * , m_pCourses , i )
{
if ( SONGMAN - > WasLoadedFromAdditionalCourses ( * i ) )
num + + ;
}
return num ;
}
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 ;
}
2006-01-22 01:00:06 +00:00
RString SongManager : : ShortenGroupName ( RString 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 ) ;
2006-08-17 00:37:40 +00:00
if ( pSong - > IsMarathon ( ) )
2003-02-10 05:30:12 +00:00
return 3 ;
2006-08-17 00:37:40 +00:00
if ( pSong - > IsLong ( ) )
2003-02-10 05:30:12 +00:00
return 2 ;
else
return 1 ;
2002-04-16 17:31:00 +00:00
}
2002-06-14 22:25:22 +00:00
2006-01-07 05:43:25 +00:00
static LocalizedString LOADING_COURSES ( " SongManager " , " Loading courses... " ) ;
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. " ) ;
2006-08-14 20:05:35 +00:00
vector < RString > vsCourseDirs ;
2006-08-15 09:23:10 +00:00
vsCourseDirs . push_back ( " " ) ;
2006-08-14 20:05:35 +00:00
vsCourseDirs . push_back ( COURSES_DIR ) ;
vsCourseDirs . push_back ( ADDITIONAL_COURSES_DIR ) ;
FOREACH_CONST ( RString , vsCourseDirs , sDir )
2003-04-11 00:29:44 +00:00
{
2006-08-14 20:05:35 +00:00
// Find all group directories in Courses dir
2006-01-22 01:00:06 +00:00
vector < RString > vsCourseGroupNames ;
2006-08-14 20:05:35 +00:00
GetDirListing ( * sDir + " * " , vsCourseGroupNames , true ) ;
2005-07-31 05:41:32 +00:00
StripCvs ( vsCourseGroupNames ) ;
2005-12-20 08:35:47 +00:00
SortRStringArray ( vsCourseGroupNames ) ;
2005-06-03 01:57:10 +00:00
2006-08-14 20:05:35 +00:00
FOREACH_CONST ( RString , vsCourseGroupNames , sCourseGroup ) // for each dir in /Courses/
2003-04-11 00:29:44 +00:00
{
2006-08-14 20:05:35 +00:00
# if defined(ITG)
if ( ! ALLOW_ITG_NAMES_IN_ADDITIONAL_FOLDERS )
{
// Don't load any folders containing "Marathon" so that they don't conflict with the official courses.
if ( * sDir = = ADDITIONAL_COURSES_DIR & & BeginsWith ( * sCourseGroup , " Marathon " ) )
continue ;
if ( * sDir = = ADDITIONAL_COURSES_DIR & & BeginsWith ( * sCourseGroup , " Survival " ) )
continue ;
if ( * sDir = = ADDITIONAL_COURSES_DIR & & BeginsWith ( * sCourseGroup , " Workout " ) )
continue ;
}
# endif
2005-06-03 01:57:10 +00:00
// Find all CRS files in this group directory
2006-01-22 01:00:06 +00:00
vector < RString > vsCoursePaths ;
2006-08-14 20:05:35 +00:00
GetDirListing ( * sDir + * sCourseGroup + " /*.crs " , vsCoursePaths , false , true ) ;
2005-12-20 08:35:47 +00:00
SortRStringArray ( vsCoursePaths ) ;
2005-06-03 01:57:10 +00:00
2006-01-22 01:00:06 +00:00
FOREACH_CONST ( RString , vsCoursePaths , sCoursePath )
2005-06-03 01:57:10 +00:00
{
if ( ld )
{
2006-01-07 05:43:25 +00:00
ld - > SetText ( LOADING_COURSES . GetValue ( ) + ssprintf ( " \n %s \n %s " ,
2005-06-03 01:57:10 +00:00
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
//
2006-01-22 01:00:06 +00:00
vector < RString > 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
{
2006-01-22 01:00:06 +00:00
RString 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 ;
2006-01-09 21:57:41 +00:00
CourseUtil : : AutogenEndlessFromGroup ( sGroupName , DIFFICULTY_MEDIUM , * pCourse ) ;
2004-05-20 03:26:36 +00:00
m_pCourses . push_back ( pCourse ) ;
pCourse = new Course ;
2006-01-09 21:57:41 +00:00
CourseUtil : : AutogenNonstopFromGroup ( sGroupName , DIFFICULTY_MEDIUM , * pCourse ) ;
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 ;
2006-01-09 21:57:41 +00:00
CourseUtil : : AutogenEndlessFromGroup ( " " , DIFFICULTY_MEDIUM , * pCourse ) ;
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
2006-01-22 01:00:06 +00:00
RString sCurArtist = " " ;
RString 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 {
2006-01-22 01:00:06 +00:00
RString sArtist = i > = apSongs . size ( ) ? RString ( " " ) : apSongs [ i ] - > GetDisplayArtist ( ) ;
RString sTranslitArtist = i > = apSongs . size ( ) ? RString ( " " ) : 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 ;
2006-01-09 21:57:41 +00:00
CourseUtil : : AutogenOniFromArtist ( sCurArtist , sCurArtistTranslit , aSongs , DIFFICULTY_HARD , * pCourse ) ;
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
2005-12-01 03:20:25 +00:00
for ( int i = 0 ; i < NUM_ProfileSlot ; + + i )
2005-04-07 08:46:40 +00:00
FOREACH_CourseType ( ct )
2006-03-20 01:04:46 +00:00
m_pPopularCourses [ 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 ) ;
2006-03-20 01:04:46 +00:00
UpdatePopular ( ) ;
2005-07-29 02:23:02 +00:00
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 ) ;
2006-03-20 01:04:46 +00:00
UpdatePopular ( ) ;
2005-07-29 02:23:02 +00:00
UpdateShuffled ( ) ;
2005-07-31 05:41:32 +00:00
RefreshCourseGroupInfo ( ) ;
2005-07-29 02:23:02 +00:00
}
2006-06-12 10:49:44 +00:00
void SongManager : : InvalidateCachedTrails ( )
{
FOREACH_CONST ( Course * , m_pCourses , pCourse )
{
const Course & c = * * pCourse ;
if ( c . IsAnEdit ( ) )
c . m_TrailCache . clear ( ) ;
}
}
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
//
2006-01-22 01:00:06 +00:00
const RString dir = pSong - > GetSongDir ( ) ;
2004-08-11 08:23:14 +00:00
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 ( ) ;
}
}
2005-11-30 22:42:28 +00:00
void SongManager : : GetStepsLoadedFromProfile ( vector < Steps * > & AddTo , ProfileSlot slot )
{
const vector < Song * > & vSongs = SONGMAN - > GetAllSongs ( ) ;
FOREACH_CONST ( Song * , vSongs , song )
{
( * song ) - > GetStepsLoadedFromProfile ( slot , AddTo ) ;
}
}
Song * SongManager : : GetSongFromSteps ( Steps * pSteps )
{
2005-12-06 04:20:20 +00:00
ASSERT ( pSteps ) ;
2005-11-30 22:42:28 +00:00
const vector < Song * > & vSongs = SONGMAN - > GetAllSongs ( ) ;
FOREACH_CONST ( Song * , vSongs , song )
{
vector < Steps * > vSteps ;
2006-06-13 01:10:37 +00:00
SongUtil : : GetSteps ( * song , vSteps ) ;
2005-11-30 22:42:28 +00:00
FOREACH_CONST ( Steps * , vSteps , steps )
{
if ( * steps = = pSteps )
{
return * song ;
}
}
}
ASSERT ( 0 ) ;
return NULL ;
}
void SongManager : : DeleteSteps ( Steps * pSteps )
{
Song * pSong = GetSongFromSteps ( pSteps ) ;
pSong - > DeleteSteps ( pSteps ) ;
}
2006-04-04 21:52:54 +00:00
bool SongManager : : WasLoadedFromAdditionalSongs ( const Song * pSong ) const
{
RString sDir = pSong - > GetSongDir ( ) ;
return BeginsWith ( sDir , ADDITIONAL_SONGS_DIR ) ;
}
2006-08-14 20:05:35 +00:00
bool SongManager : : WasLoadedFromAdditionalCourses ( const Course * pCourse ) const
{
RString sDir = pCourse - > m_sPath ;
return BeginsWith ( sDir , ADDITIONAL_COURSES_DIR ) ;
}
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 ] ) ;
}
2006-05-01 21:49:59 +00:00
void SongManager : : GetCourses ( CourseType ct , vector < Course * > & AddTo , bool bIncludeAutogen ) const
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
}
2006-01-22 01:00:06 +00:00
void SongManager : : GetCoursesInGroup ( vector < Course * > & AddTo , const RString & sCourseGroup , bool bIncludeAutogen )
2005-06-23 22:43:48 +00:00
{
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 ] ) ;
}
2006-05-20 09:00:35 +00:00
bool SongManager : : GetExtraStageInfoFromCourse ( bool bExtra2 , RString sPreferredGroup , Song * & pSongOut , Steps * & pStepsOut ,
PlayerOptions * pPlayerOptionsOut , SongOptions * pSongOptionsOut )
2002-08-30 20:49:56 +00:00
{
2006-01-22 01:00:06 +00:00
const RString sCourseSuffix = sPreferredGroup + " / " + ( bExtra2 ? " extra2 " : " extra1 " ) + " .crs " ;
RString 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 ) )
2006-08-07 03:11:32 +00:00
{
sCoursePath = ADDITIONAL_SONGS_DIR + sCourseSuffix ;
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 ( ) ;
2006-05-20 09:00:35 +00:00
pPlayerOptionsOut - > m_sNoteSkin = NOTESKIN - > GAME_BASE_NOTESKIN_NAME ;
2005-09-03 03:52:22 +00:00
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
}
2006-05-20 09:00:35 +00:00
void SongManager : : GetExtraStageInfo ( bool bExtra2 , const Style * sd , Song * & pSongOut , Steps * & pStepsOut ,
PlayerOptions * pPlayerOptionsOut , SongOptions * pSongOptionsOut )
2002-08-01 13:42:56 +00:00
{
2006-01-22 01:00:06 +00:00
RString 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 ;
2006-06-13 01:10:37 +00:00
SongUtil : : GetSteps ( pSong , apSteps , sd - > m_StepsType ) ;
2004-05-24 03:41:39 +00:00
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
}
2006-08-20 05:55:09 +00:00
// for extra 2, we don't want to choose the hardest notes possible. So, we'll disgard Steps with meter > 8 (assuming dance)
if ( bExtra2 & & pSteps - > GetMeter ( ) > EXTRA_STAGE2_DIFFICULTY_MAX )
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 ( ) ;
2006-05-20 09:00:35 +00:00
pPlayerOptionsOut - > m_sNoteSkin = NOTESKIN - > GAME_BASE_NOTESKIN_NAME ;
2006-08-07 01:53:58 +00:00
pPlayerOptionsOut - > FromString ( bExtra2 ? EXTRA_STAGE2_PLAYER_OPTIONS : EXTRA_STAGE_PLAYER_OPTIONS , true ) ;
2005-09-03 03:52:22 +00:00
}
if ( pSongOptionsOut ! = NULL )
{
pSongOptionsOut - > Init ( ) ;
2006-08-07 01:53:58 +00:00
pSongOptionsOut - > FromString ( bExtra2 ? EXTRA_STAGE2_SONG_OPTIONS : EXTRA_STAGE_SONG_OPTIONS ) ;
2005-09-03 03:52:22 +00:00
}
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
2006-01-22 01:00:06 +00:00
Song * SongManager : : GetSongFromDir ( RString 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
2006-01-22 01:00:06 +00:00
Course * SongManager : : GetCourseFromPath ( RString sPath )
2003-01-21 05:14:59 +00:00
{
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
2006-01-22 01:00:06 +00:00
Course * SongManager : : GetCourseFromName ( RString sName )
2003-02-14 21:42:44 +00:00
{
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.
*/
2006-01-22 01:00:06 +00:00
Song * SongManager : : FindSong ( RString sPath )
2003-07-22 05:45:30 +00:00
{
2004-02-08 01:05:53 +00:00
sPath . Replace ( ' \\ ' , ' / ' ) ;
2006-01-22 01:00:06 +00:00
vector < RString > bits ;
2003-07-22 05:45:30 +00:00
split ( sPath , " / " , bits ) ;
if ( bits . size ( ) = = 1 )
return FindSong ( " " , bits [ 0 ] ) ;
else if ( bits . size ( ) = = 2 )
return FindSong ( bits [ 0 ] , bits [ 1 ] ) ;
return NULL ;
}
2006-01-22 01:00:06 +00:00
Song * SongManager : : FindSong ( RString sGroup , RString sSong )
2003-07-09 04:09:35 +00:00
{
// foreach song
2006-05-01 21:49:59 +00:00
FOREACH_CONST ( Song * , m_pSongs , s )
2003-07-09 04:09:35 +00:00
{
2006-05-01 21:49:59 +00:00
if ( ( * s ) - > Matches ( sGroup , sSong ) )
return * s ;
2003-07-09 04:09:35 +00:00
}
return NULL ;
}
2003-02-03 05:53:59 +00:00
2006-05-01 21:49:59 +00:00
Course * SongManager : : FindCourse ( RString sPath )
2003-07-17 12:14:19 +00:00
{
2006-05-01 21:49:59 +00:00
sPath . Replace ( ' \\ ' , ' / ' ) ;
vector < RString > bits ;
split ( sPath , " / " , bits ) ;
if ( bits . size ( ) = = 1 )
return FindCourse ( " " , bits [ 0 ] ) ;
else if ( bits . size ( ) = = 2 )
return FindCourse ( bits [ 0 ] , bits [ 1 ] ) ;
return NULL ;
}
Course * SongManager : : FindCourse ( RString sGroup , RString sName )
{
FOREACH_CONST ( Course * , m_pCourses , c )
2003-07-17 12:14:19 +00:00
{
2006-05-01 21:49:59 +00:00
if ( ( * c ) - > Matches ( sGroup , sName ) )
return * c ;
2003-07-17 12:14:19 +00:00
}
return NULL ;
2003-07-21 21:45:59 +00:00
}
2006-03-20 01:04:46 +00:00
void SongManager : : UpdatePopular ( )
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. */
2006-06-14 05:43:50 +00:00
// XXX Hack, this depends on UNLOCKMAN being around.
if ( UNLOCKMAN & & UNLOCKMAN - > SongIsLocked ( apBestSongs [ j ] ) )
2005-09-04 23:29:31 +00:00
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 )
{
2006-03-20 01:04:46 +00:00
m_pPopularSongs [ i ] = apBestSongs ;
2004-03-07 00:17:50 +00:00
2006-03-20 01:04:46 +00:00
SongUtil : : SortSongPointerArrayByNumPlays ( m_pPopularSongs [ i ] , i , true ) ;
2003-11-17 03:20:39 +00:00
2005-04-07 08:46:40 +00:00
FOREACH_CourseType ( ct )
{
2006-03-20 01:04:46 +00:00
vector < Course * > & vpCourses = m_pPopularCourses [ 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
}
2006-03-20 01:04:46 +00:00
void SongManager : : UpdatePreferredSort ( )
{
2006-05-01 12:45:07 +00:00
ASSERT ( UNLOCKMAN ) ;
2006-05-01 21:49:59 +00:00
{
m_vPreferredSongSort . clear ( ) ;
2006-03-20 01:04:46 +00:00
2006-05-01 21:49:59 +00:00
RString sFile = THEME - > GetPathO ( " SongManager " , " PreferredSongs.txt " ) ;
RageFile file ;
if ( ! file . Open ( sFile ) )
return ;
2006-05-01 12:45:07 +00:00
2006-05-01 21:49:59 +00:00
vector < Song * > vpSongs ;
2006-03-20 01:04:46 +00:00
2006-05-01 21:49:59 +00:00
RString sLine ;
while ( file . GetLine ( sLine ) )
2006-03-20 01:04:46 +00:00
{
2006-05-01 21:49:59 +00:00
bool bSectionDivider = sLine . find ( " --- " ) ! = RString : : npos ;
if ( bSectionDivider )
{
if ( ! vpSongs . empty ( ) )
{
m_vPreferredSongSort . push_back ( vpSongs ) ;
vpSongs . clear ( ) ;
}
}
else
2006-03-20 01:04:46 +00:00
{
2006-05-01 21:49:59 +00:00
Song * pSong = NULL ;
if ( ! sLine . empty ( ) )
pSong = FindSong ( sLine ) ;
if ( pSong )
vpSongs . push_back ( pSong ) ;
2006-03-20 01:04:46 +00:00
}
}
2006-05-01 21:49:59 +00:00
if ( ! vpSongs . empty ( ) )
2006-05-01 12:45:07 +00:00
{
2006-05-01 21:49:59 +00:00
m_vPreferredSongSort . push_back ( vpSongs ) ;
vpSongs . clear ( ) ;
2006-05-01 12:45:07 +00:00
}
2006-03-20 01:04:46 +00:00
2006-05-03 21:33:19 +00:00
if ( MOVE_UNLOCKS_TO_BOTTOM_OF_PREFERRED_SORT . GetValue ( ) )
2006-05-01 21:49:59 +00:00
{
2006-05-03 21:33:19 +00:00
// move all unlock songs to a group at the bottom
vector < Song * > vpUnlockSongs ;
FOREACH ( UnlockEntry , UNLOCKMAN - > m_UnlockEntries , ue )
{
if ( ue - > m_Type = = UnlockRewardType_Song )
if ( ue - > m_pSong )
vpUnlockSongs . push_back ( ue - > m_pSong ) ;
}
2006-05-01 12:45:07 +00:00
2006-05-03 21:33:19 +00:00
FOREACH ( SongPointerVector , m_vPreferredSongSort , v )
2006-05-01 21:49:59 +00:00
{
2006-05-03 21:33:19 +00:00
for ( int i = v - > size ( ) - 1 ; i > = 0 ; i - - )
2006-05-01 21:49:59 +00:00
{
2006-05-03 21:33:19 +00:00
Song * pSong = ( * v ) [ i ] ;
if ( find ( vpUnlockSongs . begin ( ) , vpUnlockSongs . end ( ) , pSong ) ! = vpUnlockSongs . end ( ) )
{
v - > erase ( v - > begin ( ) + i ) ;
}
2006-05-01 21:49:59 +00:00
}
}
2006-05-03 21:33:19 +00:00
m_vPreferredSongSort . push_back ( vpUnlockSongs ) ;
2006-05-02 08:18:54 +00:00
2006-05-03 21:33:19 +00:00
// prune empty groups
for ( int i = m_vPreferredSongSort . size ( ) - 1 ; i > = 0 ; i - - )
if ( m_vPreferredSongSort [ i ] . empty ( ) )
m_vPreferredSongSort . erase ( m_vPreferredSongSort . begin ( ) + i ) ;
2006-05-02 08:18:54 +00:00
2006-05-03 21:33:19 +00:00
FOREACH ( SongPointerVector , m_vPreferredSongSort , i )
FOREACH ( Song * , * i , j )
ASSERT ( * j ) ;
}
2006-05-01 12:45:07 +00:00
}
{
2006-05-01 21:49:59 +00:00
m_vPreferredCourseSort . clear ( ) ;
RString sFile = THEME - > GetPathO ( " SongManager " , " PreferredCourses.txt " ) ;
RageFile file ;
if ( ! file . Open ( sFile ) )
return ;
vector < Course * > vpCourses ;
RString sLine ;
while ( file . GetLine ( sLine ) )
2006-03-20 01:04:46 +00:00
{
2006-05-01 21:49:59 +00:00
bool bSectionDivider = sLine . find ( " --- " ) ! = RString : : npos ;
if ( bSectionDivider )
{
if ( ! vpCourses . empty ( ) )
{
m_vPreferredCourseSort . push_back ( vpCourses ) ;
vpCourses . clear ( ) ;
}
}
else
2006-05-01 12:45:07 +00:00
{
2006-05-01 21:49:59 +00:00
Course * pCourse = NULL ;
if ( ! sLine . empty ( ) )
pCourse = FindCourse ( sLine ) ;
if ( pCourse )
vpCourses . push_back ( pCourse ) ;
2006-05-01 12:45:07 +00:00
}
2006-03-20 01:04:46 +00:00
}
2006-05-01 12:45:07 +00:00
2006-05-01 21:49:59 +00:00
if ( ! vpCourses . empty ( ) )
{
m_vPreferredCourseSort . push_back ( vpCourses ) ;
vpCourses . clear ( ) ;
}
2006-05-03 21:33:19 +00:00
if ( MOVE_UNLOCKS_TO_BOTTOM_OF_PREFERRED_SORT . GetValue ( ) )
2006-05-01 21:49:59 +00:00
{
2006-05-03 21:33:19 +00:00
// move all unlock Courses to a group at the bottom
vector < Course * > vpUnlockCourses ;
FOREACH ( UnlockEntry , UNLOCKMAN - > m_UnlockEntries , ue )
{
if ( ue - > m_Type = = UnlockRewardType_Course )
if ( ue - > m_pCourse )
vpUnlockCourses . push_back ( ue - > m_pCourse ) ;
}
2006-05-01 21:49:59 +00:00
2006-05-03 21:33:19 +00:00
FOREACH ( CoursePointerVector , m_vPreferredCourseSort , v )
2006-05-01 21:49:59 +00:00
{
2006-05-03 21:33:19 +00:00
for ( int i = v - > size ( ) - 1 ; i > = 0 ; i - - )
2006-05-01 21:49:59 +00:00
{
2006-05-03 21:33:19 +00:00
Course * pCourse = ( * v ) [ i ] ;
if ( find ( vpUnlockCourses . begin ( ) , vpUnlockCourses . end ( ) , pCourse ) ! = vpUnlockCourses . end ( ) )
{
v - > erase ( v - > begin ( ) + i ) ;
}
2006-05-01 21:49:59 +00:00
}
}
2006-05-03 21:33:19 +00:00
m_vPreferredCourseSort . push_back ( vpUnlockCourses ) ;
2006-05-02 08:18:54 +00:00
2006-05-03 21:33:19 +00:00
// prune empty groups
for ( int i = m_vPreferredCourseSort . size ( ) - 1 ; i > = 0 ; i - - )
if ( m_vPreferredCourseSort [ i ] . empty ( ) )
m_vPreferredCourseSort . erase ( m_vPreferredCourseSort . begin ( ) + i ) ;
2006-05-02 08:18:54 +00:00
2006-05-03 21:33:19 +00:00
FOREACH ( CoursePointerVector , m_vPreferredCourseSort , i )
FOREACH ( Course * , * i , j )
ASSERT ( * j ) ;
}
2006-05-01 21:49:59 +00:00
}
2006-03-20 01:04:46 +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. */
2006-01-22 01:00:06 +00:00
vector < RString > RankingCourses ;
2003-08-04 09:18:26 +00:00
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
2006-01-22 01:00:06 +00:00
FOREACHM ( RString , CourseGroupInfo , m_mapCourseGroupToInfo , iter )
2005-07-31 05:41:32 +00:00
{
}
}
2004-04-23 00:26:51 +00:00
2006-01-22 01:00:06 +00:00
void SongManager : : LoadAllFromProfileDir ( const RString & sProfileDir , ProfileSlot slot )
2005-07-31 05:41:32 +00:00
{
2005-04-24 23:39:35 +00:00
{
2005-07-31 05:41:32 +00:00
//
// Load all edit steps
//
2006-01-22 01:00:06 +00:00
RString sDir = sProfileDir + EDIT_STEPS_SUBDIR ;
2005-07-31 05:41:32 +00:00
2006-01-22 01:00:06 +00:00
vector < RString > vsFiles ;
2005-07-31 05:41:32 +00:00
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
{
2006-01-22 01:00:06 +00:00
RString fn = vsFiles [ i ] ;
2005-07-31 05:41:32 +00:00
2006-03-27 20:17:05 +00:00
SMLoader : : LoadEditFromFile ( fn , slot , true ) ;
2004-02-08 01:05:53 +00:00
}
2005-07-31 05:41:32 +00:00
}
{
//
// Load all edit courses
//
2006-01-22 01:00:06 +00:00
RString sDir = sProfileDir + EDIT_COURSES_SUBDIR ;
2005-04-24 23:39:35 +00:00
2006-01-22 01:00:06 +00:00
vector < RString > vsFiles ;
2005-07-31 05:41:32 +00:00
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 + + )
{
2006-01-22 01:00:06 +00:00
RString fn = vsFiles [ i ] ;
2005-07-31 05:41:32 +00:00
2006-03-27 20:17:05 +00:00
CourseLoaderCRS : : LoadEditFromFile ( fn , slot ) ;
2005-07-31 05:41:32 +00:00
}
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 ;
2006-06-13 01:10:37 +00:00
SongUtil : : GetSteps ( pSong , apSteps ) ;
2005-04-25 02:27:17 +00:00
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 ;
2005-12-01 03:20:25 +00:00
if ( pCourse - > GetLoadedFromProfileSlot ( ) = = ProfileSlot_INVALID )
2005-08-11 20:51:15 +00:00
continue ;
2005-12-01 03:20:25 +00:00
if ( slot = = ProfileSlot_INVALID | | pCourse - > GetLoadedFromProfileSlot ( ) = = slot )
2005-08-11 20:51:15 +00:00
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-12-01 03:20:25 +00:00
if ( ( * ss ) - > GetLoadedFromProfileSlot ( ) ! = ProfileSlot_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
2006-08-17 01:58:58 +00:00
template < class T >
int FindCourseIndexOfSameMode ( T begin , T end , const Course * p )
{
const PlayMode pm = p - > GetPlayMode ( ) ;
int n = 0 ;
for ( T it = begin ; it ! = end ; + + it )
{
if ( * it = = p )
return n ;
/* If it's not playable in this mode, don't increment. It might result in
* different output in different modes, but that's better than having holes. */
if ( ! ( * it ) - > IsPlayableIn ( GAMESTATE - > GetCurrentStyle ( ) - > m_StepsType ) )
continue ;
if ( ( * it ) - > GetPlayMode ( ) ! = pm )
continue ;
+ + n ;
}
return - 1 ;
}
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 ; }
2006-05-16 00:47:02 +00:00
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 ; }
static int GetNumSongs ( T * p , lua_State * L ) { lua_pushnumber ( L , p - > GetNumSongs ( ) ) ; return 1 ; }
2006-05-03 21:33:19 +00:00
static int GetNumUnlockedSongs ( T * p , lua_State * L ) { lua_pushnumber ( L , p - > GetNumUnlockedSongs ( ) ) ; return 1 ; }
2006-05-16 00:47:02 +00:00
static int GetNumSelectableAndUnlockedSongs ( T * p , lua_State * L ) { lua_pushnumber ( L , p - > GetNumSelectableAndUnlockedSongs ( ) ) ; return 1 ; }
static int GetNumAdditionalSongs ( T * p , lua_State * L ) { lua_pushnumber ( L , p - > GetNumAdditionalSongs ( ) ) ; 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 ; }
2006-08-14 20:05:35 +00:00
static int GetNumAdditionalCourses ( T * p , lua_State * L ) { lua_pushnumber ( L , p - > GetNumAdditionalCourses ( ) ) ; return 1 ; }
2006-05-16 00:47:02 +00:00
static int GetNumCourseGroups ( T * p , lua_State * L ) { lua_pushnumber ( L , p - > GetNumCourseGroups ( ) ) ; return 1 ; }
2005-12-05 19:08:42 +00:00
static int GetSongFromSteps ( T * p , lua_State * L )
{
Song * pSong = NULL ;
if ( lua_isnil ( L , 1 ) ) { pSong = p - > GetSongFromSteps ( NULL ) ; }
else { Steps * pSteps = Luna < Steps > : : check ( L , 1 ) ; pSong = p - > GetSongFromSteps ( pSteps ) ; }
if ( pSong ) pSong - > PushSelf ( L ) ;
else lua_pushnil ( L ) ;
return 1 ;
}
2006-08-17 01:56:29 +00:00
static int GetSongRank ( T * p , lua_State * L )
{
Song * pSong = Luna < Song > : : check ( L , 1 ) ;
const vector < Song * > apBest = p - > GetPopularSongs ( ProfileSlot_Machine ) ;
int iIndex = FindIndex ( apBest . begin ( ) , apBest . end ( ) , pSong ) ;
lua_pushnumber ( L , iIndex ) ;
return 1 ;
}
2006-08-17 01:58:58 +00:00
static int GetCourseRank ( T * p , lua_State * L )
{
Course * pCourse = Luna < Course > : : check ( L , 1 ) ;
CourseType ct = PlayModeToCourseType ( GAMESTATE - > m_PlayMode ) ;
const vector < Course * > best = SONGMAN - > GetPopularCourses ( ct , ProfileSlot_Machine ) ;
int iIndex = FindCourseIndexOfSameMode ( best . begin ( ) , best . end ( ) , pCourse ) ;
lua_pushnumber ( L , iIndex ) ;
return 1 ;
}
2006-08-18 00:11:30 +00:00
static int GetExtraStageInfo ( T * p , lua_State * L )
{
bool bExtra2 = BArg ( 1 ) ;
const Style * pStyle = Luna < Style > : : check ( L , 2 ) ;
Song * pSong ;
Steps * pSteps ;
PlayerOptions po ;
SongOptions so ;
p - > GetExtraStageInfo ( bExtra2 , pStyle , pSong , pSteps , & po , & so ) ;
pSong - > PushSelf ( L ) ;
pSteps - > PushSelf ( L ) ;
2006-09-26 08:54:54 +00:00
LuaHelpers : : Push ( L , po . GetString ( true ) ) ;
LuaHelpers : : Push ( L , so . GetString ( ) ) ;
2006-08-18 00:11:30 +00:00
return 4 ;
}
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 ) ;
2006-05-03 21:33:19 +00:00
ADD_METHOD ( GetNumUnlockedSongs ) ;
2006-05-16 00:47:02 +00:00
ADD_METHOD ( GetNumSelectableAndUnlockedSongs ) ;
2006-05-03 21:33:19 +00:00
ADD_METHOD ( GetNumAdditionalSongs ) ;
2005-09-10 02:47:04 +00:00
ADD_METHOD ( GetNumSongGroups ) ;
ADD_METHOD ( GetNumCourses ) ;
2006-08-14 20:05:35 +00:00
ADD_METHOD ( GetNumAdditionalCourses ) ;
2005-09-10 02:47:04 +00:00
ADD_METHOD ( GetNumCourseGroups ) ;
2005-12-05 19:08:42 +00:00
ADD_METHOD ( GetSongFromSteps ) ;
2006-08-17 01:56:29 +00:00
ADD_METHOD ( GetSongRank ) ;
2006-08-17 01:58:58 +00:00
ADD_METHOD ( GetCourseRank ) ;
2006-08-18 00:11:30 +00:00
ADD_METHOD ( GetExtraStageInfo ) ;
2005-09-10 02:47:04 +00:00
2005-02-21 17:26:43 +00:00
Luna < T > : : Register ( L ) ;
}
} ;
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.
*/