2001-11-03 10:52:42 +00:00
# include "stdafx.h"
2001-11-04 19:34:28 +00:00
/*
-----------------------------------------------------------------------------
2002-06-24 22:04:31 +00:00
Class: Song
2001-11-04 19:34:28 +00:00
Desc: Holds metadata for a song and the song's step data.
2002-05-19 01:59:48 +00:00
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
2002-06-24 22:04:31 +00:00
Chris Danford
2003-01-16 20:21:31 +00:00
Glenn Maynard
2001-11-04 19:34:28 +00:00
-----------------------------------------------------------------------------
*/
2001-11-03 10:52:42 +00:00
2002-05-19 01:59:48 +00:00
# include "Notes.h"
2001-11-03 10:52:42 +00:00
# include "RageUtil.h"
2002-02-28 19:40:40 +00:00
# include <math.h> // for fmod
2002-05-01 19:14:55 +00:00
# include "RageLog.h"
2002-06-30 23:19:33 +00:00
# include "IniFile.h"
# include "Song.h"
2002-07-03 03:13:13 +00:00
# include "NoteData.h"
2002-07-23 01:41:40 +00:00
# include "MsdFile.h"
2002-12-13 23:41:46 +00:00
# include "RageSound.h"
2002-07-27 19:29:51 +00:00
# include "RageException.h"
2002-08-21 02:13:08 +00:00
# include "SongCacheIndex.h"
2002-08-25 19:00:12 +00:00
# include "GameManager.h"
2002-08-30 04:28:12 +00:00
# include "PrefsManager.h"
2002-09-07 11:45:15 +00:00
# include "StyleDef.h"
# include "Notes.h"
2002-09-29 23:29:01 +00:00
# include "GameState.h"
2003-01-16 20:21:31 +00:00
# include "FontCharAliases.h"
2002-06-14 22:25:22 +00:00
2002-09-06 23:36:04 +00:00
# include "NotesLoaderSM.h"
# include "NotesLoaderDWI.h"
# include "NotesLoaderBMS.h"
# include "NotesLoaderKSF.h"
2002-09-09 00:22:02 +00:00
# include "NotesWriterDWI.h"
2002-12-14 00:21:22 +00:00
# include "NotesWriterSM.h"
2001-11-03 10:52:42 +00:00
2002-11-13 05:17:15 +00:00
# include "SDL.h"
# include "SDL_image.h"
2002-10-28 05:30:45 +00:00
2003-01-16 20:21:31 +00:00
const int FILE_CACHE_VERSION = 104 ; // increment this when Song or Notes changes to invalidate cache
2001-12-28 10:15:59 +00:00
2001-12-19 01:50:57 +00:00
2002-10-24 07:56:20 +00:00
static int CompareBPMSegments ( const BPMSegment & seg1 , const BPMSegment & seg2 )
2001-12-19 01:50:57 +00:00
{
2002-10-24 07:56:20 +00:00
return seg1 . m_fStartBeat < seg2 . m_fStartBeat ;
2001-12-19 01:50:57 +00:00
}
2003-01-03 05:56:28 +00:00
void SortBPMSegmentsArray ( vector < BPMSegment > & arrayBPMSegments )
2001-12-19 01:50:57 +00:00
{
2002-10-24 08:40:27 +00:00
sort ( arrayBPMSegments . begin ( ) , arrayBPMSegments . end ( ) , CompareBPMSegments ) ;
2001-12-19 01:50:57 +00:00
}
2002-10-24 07:56:20 +00:00
static int CompareStopSegments ( const StopSegment & seg1 , const StopSegment & seg2 )
2001-12-28 10:15:59 +00:00
{
2002-10-24 07:56:20 +00:00
return seg1 . m_fStartBeat < seg2 . m_fStartBeat ;
2001-12-28 10:15:59 +00:00
}
2003-01-03 05:56:28 +00:00
void SortStopSegmentsArray ( vector < StopSegment > & arrayStopSegments )
2001-12-28 10:15:59 +00:00
{
2002-10-24 08:40:27 +00:00
sort ( arrayStopSegments . begin ( ) , arrayStopSegments . end ( ) , CompareStopSegments ) ;
2002-07-11 19:02:26 +00:00
}
2002-10-24 07:56:20 +00:00
int CompareBackgroundChanges ( const BackgroundChange & seg1 , const BackgroundChange & seg2 )
2002-07-11 19:02:26 +00:00
{
2002-10-24 07:56:20 +00:00
return seg1 . m_fStartBeat < seg2 . m_fStartBeat ;
2002-07-11 19:02:26 +00:00
}
2003-01-03 05:56:28 +00:00
void SortBackgroundChangesArray ( vector < BackgroundChange > & arrayBackgroundChanges )
2002-07-11 19:02:26 +00:00
{
2002-10-24 08:40:27 +00:00
sort ( arrayBackgroundChanges . begin ( ) , arrayBackgroundChanges . end ( ) , CompareBackgroundChanges ) ;
2001-12-28 10:15:59 +00:00
}
2001-12-19 01:50:57 +00:00
2001-11-03 10:52:42 +00:00
//////////////////////////////
// Song
//////////////////////////////
2001-11-04 19:34:28 +00:00
Song : : Song ( )
{
2001-11-27 22:47:30 +00:00
m_bChangedSinceSave = false ;
2002-07-03 21:27:26 +00:00
m_fBeat0OffsetInSeconds = 0 ;
2002-06-29 11:59:09 +00:00
m_fMusicSampleStartSeconds = 0 ;
2002-09-11 05:32:15 +00:00
m_fMusicSampleLengthSeconds = 12.0f ; // start fading out at m_fMusicSampleLengthSeconds-1 seconds
2002-05-19 01:59:48 +00:00
m_iMusicBytes = 0 ;
2002-06-29 11:59:09 +00:00
m_fMusicLengthSeconds = 0 ;
2002-07-11 19:02:26 +00:00
m_fFirstBeat = - 1 ;
m_fLastBeat = - 1 ;
2002-08-30 04:28:12 +00:00
m_SelectionDisplay = SHOW_ALWAYS ;
2001-11-04 19:34:28 +00:00
}
2001-11-03 10:52:42 +00:00
2002-06-14 22:25:22 +00:00
Song : : ~ Song ( )
{
2002-10-31 02:31:15 +00:00
for ( unsigned i = 0 ; i < m_apNotes . size ( ) ; i + + )
2002-07-11 19:02:26 +00:00
SAFE_DELETE ( m_apNotes [ i ] ) ;
2002-06-14 22:25:22 +00:00
2002-10-24 20:15:24 +00:00
m_apNotes . clear ( ) ;
2002-06-14 22:25:22 +00:00
}
2001-12-19 01:50:57 +00:00
2002-06-24 22:04:31 +00:00
void Song : : AddBPMSegment ( BPMSegment seg )
{
2002-10-31 04:23:39 +00:00
m_BPMSegments . push_back ( seg ) ;
2002-06-24 22:04:31 +00:00
SortBPMSegmentsArray ( m_BPMSegments ) ;
}
2002-07-11 19:02:26 +00:00
void Song : : AddStopSegment ( StopSegment seg )
{
2002-10-31 04:23:39 +00:00
m_StopSegments . push_back ( seg ) ;
2002-07-11 19:02:26 +00:00
SortStopSegmentsArray ( m_StopSegments ) ;
}
2002-08-23 20:18:29 +00:00
void Song : : AddBackgroundChange ( BackgroundChange seg )
2002-06-24 22:04:31 +00:00
{
2002-10-31 04:23:39 +00:00
m_BackgroundChanges . push_back ( seg ) ;
2002-08-23 20:18:29 +00:00
SortBackgroundChangesArray ( m_BackgroundChanges ) ;
2002-06-24 22:04:31 +00:00
}
2002-08-21 00:47:29 +00:00
float Song : : GetMusicStartBeat ( ) const
2002-08-18 23:20:18 +00:00
{
float fBPS = m_BPMSegments [ 0 ] . m_fBPM / 60.0f ;
return - m_fBeat0OffsetInSeconds * fBPS ;
} ;
2002-06-24 22:04:31 +00:00
2002-08-21 00:47:29 +00:00
void Song : : GetBeatAndBPSFromElapsedTime ( float fElapsedTime , float & fBeatOut , float & fBPSOut , bool & bFreezeOut ) const
2001-12-19 01:50:57 +00:00
{
2002-07-31 19:40:40 +00:00
// LOG->Trace( "GetBeatAndBPSFromElapsedTime( fElapsedTime = %f )", fElapsedTime );
2002-02-28 19:40:40 +00:00
// This function is a nightmare. Don't even try to understand it. :-)
2002-07-03 21:27:26 +00:00
fElapsedTime + = m_fBeat0OffsetInSeconds ;
2001-12-19 01:50:57 +00:00
2001-12-28 10:15:59 +00:00
2002-10-31 02:31:15 +00:00
for ( unsigned i = 0 ; i < m_BPMSegments . size ( ) ; i + + ) // foreach BPMSegment
2001-12-28 10:15:59 +00:00
{
2001-12-19 01:50:57 +00:00
float fStartBeatThisSegment = m_BPMSegments [ i ] . m_fStartBeat ;
2002-10-31 02:31:15 +00:00
bool bIsLastBPMSegment = i = = m_BPMSegments . size ( ) - 1 ;
2001-12-19 01:50:57 +00:00
float fStartBeatNextSegment = bIsLastBPMSegment ? 40000 /*inf*/ : m_BPMSegments [ i + 1 ] . m_fStartBeat ;
float fBeatsInThisSegment = fStartBeatNextSegment - fStartBeatThisSegment ;
float fBPM = m_BPMSegments [ i ] . m_fBPM ;
float fBPS = fBPM / 60.0f ;
2001-12-28 10:15:59 +00:00
// calculate the number of seconds in this segment
2001-12-19 01:50:57 +00:00
float fSecondsInThisSegment = fBeatsInThisSegment / fBPS ;
2002-10-31 02:31:15 +00:00
unsigned j ;
for ( j = 0 ; j < m_StopSegments . size ( ) ; j + + ) // foreach freeze
2001-12-28 10:15:59 +00:00
{
2002-07-11 19:02:26 +00:00
if ( fStartBeatThisSegment < = m_StopSegments [ j ] . m_fStartBeat & & m_StopSegments [ j ] . m_fStartBeat < fStartBeatNextSegment )
2001-12-28 10:15:59 +00:00
{
// this freeze lies within this BPMSegment
2002-07-11 19:02:26 +00:00
fSecondsInThisSegment + = m_StopSegments [ j ] . m_fStopSeconds ;
2001-12-28 10:15:59 +00:00
}
}
2001-12-19 01:50:57 +00:00
if ( fElapsedTime > fSecondsInThisSegment )
2001-12-28 10:15:59 +00:00
{
2002-06-30 23:19:33 +00:00
// this BPMSegement is NOT the current segment
2001-12-19 01:50:57 +00:00
fElapsedTime - = fSecondsInThisSegment ;
2002-08-20 07:20:59 +00:00
continue ;
2001-12-28 10:15:59 +00:00
}
2002-08-20 07:20:59 +00:00
// this BPMSegment IS the current segment
float fBeatEstimate = fStartBeatThisSegment + fElapsedTime * fBPS ;
2002-10-31 02:31:15 +00:00
for ( j = 0 ; j < m_StopSegments . size ( ) ; j + + ) // foreach freeze
2001-12-28 10:15:59 +00:00
{
2002-08-20 07:20:59 +00:00
if ( fStartBeatThisSegment > m_StopSegments [ j ] . m_fStartBeat | |
m_StopSegments [ j ] . m_fStartBeat > fStartBeatNextSegment )
continue ;
2001-12-28 10:15:59 +00:00
2002-08-20 07:20:59 +00:00
// this freeze lies within this BPMSegment
2001-12-28 10:15:59 +00:00
2002-08-20 07:20:59 +00:00
if ( m_StopSegments [ j ] . m_fStartBeat > fBeatEstimate )
break ;
fElapsedTime - = m_StopSegments [ j ] . m_fStopSeconds ;
// re-estimate
fBeatEstimate = fStartBeatThisSegment + fElapsedTime * fBPS ;
if ( fBeatEstimate < m_StopSegments [ j ] . m_fStartBeat )
2001-12-28 10:15:59 +00:00
{
2002-08-20 07:20:59 +00:00
fBeatOut = m_StopSegments [ j ] . m_fStartBeat ;
fBPSOut = fBPS ;
bFreezeOut = true ;
return ;
2001-12-28 10:15:59 +00:00
}
2001-12-19 01:50:57 +00:00
}
2001-12-28 10:15:59 +00:00
2002-08-20 07:20:59 +00:00
fBeatOut = fBeatEstimate ;
fBPSOut = fBPS ;
bFreezeOut = false ;
return ;
2001-12-19 01:50:57 +00:00
}
}
2002-08-25 19:00:12 +00:00
// This is a super hack, but it's only called from ScreenEdit, so it's OK.
// Writing an inverse function of GetBeatAndBPSFromElapsedTime() uber difficult,
2002-04-16 17:31:00 +00:00
// so do a binary search to get close to the correct elapsed time.
2002-08-21 00:47:29 +00:00
float Song : : GetElapsedTimeFromBeat ( float fBeat ) const
2002-04-16 17:31:00 +00:00
{
2002-10-09 18:14:29 +00:00
float fElapsedTimeBestGuess = this - > m_fMusicLengthSeconds / 2 ; // seconds
2002-07-11 19:02:26 +00:00
float fSecondsToMove = fElapsedTimeBestGuess ; // seconds
2002-04-16 17:31:00 +00:00
float fBeatOut , fBPSOut ;
2002-06-30 23:19:33 +00:00
bool bFreezeOut ;
2002-04-16 17:31:00 +00:00
while ( fSecondsToMove > 0.1f )
{
2002-06-30 23:19:33 +00:00
GetBeatAndBPSFromElapsedTime ( fElapsedTimeBestGuess , fBeatOut , fBPSOut , bFreezeOut ) ;
2002-04-16 17:31:00 +00:00
if ( fBeatOut > fBeat )
fElapsedTimeBestGuess - = fSecondsToMove ;
else
fElapsedTimeBestGuess + = fSecondsToMove ;
fSecondsToMove / = 2 ;
}
return fElapsedTimeBestGuess ;
}
2002-08-21 00:47:29 +00:00
CString Song : : GetCacheFilePath ( ) const
2002-05-19 01:59:48 +00:00
{
2002-08-25 19:00:12 +00:00
return ssprintf ( " Cache \\ %u " , GetHashForString ( m_sSongDir ) ) ;
2002-05-19 01:59:48 +00:00
}
2002-08-22 21:59:47 +00:00
/* Get a path to the SM containing data for this song. It might
* be a cache file. */
const CString & Song : : GetSongFilePath ( ) const
2002-07-02 00:27:58 +00:00
{
2002-08-24 07:57:28 +00:00
ASSERT ( m_sSongFileName . GetLength ( ) ! = 0 ) ;
2002-08-22 21:59:47 +00:00
return m_sSongFileName ;
2002-07-02 00:27:58 +00:00
}
2002-09-11 05:15:46 +00:00
NotesLoader * Song : : MakeLoader ( CString sDir ) const
2001-11-03 10:52:42 +00:00
{
2002-09-11 05:15:46 +00:00
NotesLoader * ret ;
/* Actually, none of these have any persistant data, so we
* could optimize this, but since they don't have any data,
* there's no real point ... */
ret = new SMLoader ;
if ( ret - > Loadable ( sDir ) ) return ret ;
delete ret ;
2001-11-03 10:52:42 +00:00
2002-09-11 05:15:46 +00:00
ret = new DWILoader ;
if ( ret - > Loadable ( sDir ) ) return ret ;
delete ret ;
2002-07-31 19:40:40 +00:00
2002-09-11 05:15:46 +00:00
ret = new BMSLoader ;
if ( ret - > Loadable ( sDir ) ) return ret ;
delete ret ;
2001-11-03 10:52:42 +00:00
2002-09-11 05:15:46 +00:00
ret = new KSFLoader ;
if ( ret - > Loadable ( sDir ) ) return ret ;
delete ret ;
2001-11-03 10:52:42 +00:00
2002-09-11 05:15:46 +00:00
return NULL ;
}
2002-04-16 17:31:00 +00:00
2002-09-11 05:15:46 +00:00
bool Song : : LoadWithoutCache ( CString sDir )
{
//
// There was no entry in the cache for this song.
// Let's load it from a file, then write a cache entry.
//
2002-09-06 23:36:04 +00:00
2002-09-11 05:15:46 +00:00
NotesLoader * ld = MakeLoader ( sDir ) ;
if ( ! ld )
2002-07-31 19:40:40 +00:00
{
2002-10-29 07:58:44 +00:00
LOG - > Warn ( " Couldn't find any SM, DWI, BMS, or KSF files in '%s'. This is not a valid song directory. " , sDir . GetString ( ) ) ;
2002-07-31 19:40:40 +00:00
return false ;
}
2001-11-03 10:52:42 +00:00
2002-09-11 05:15:46 +00:00
bool success = ld - > LoadFromDir ( sDir , * this ) ;
delete ld ;
if ( ! success )
return false ;
2002-05-19 01:59:48 +00:00
TidyUpData ( ) ;
2003-01-02 22:10:51 +00:00
2002-08-22 21:59:47 +00:00
// save a cache file so we don't have to parse it all over again next time
SaveToCacheFile ( ) ;
2002-08-24 07:57:28 +00:00
return true ;
}
bool Song : : LoadFromSongDir ( CString sDir )
{
2002-10-29 07:58:44 +00:00
LOG - > Trace ( " Song::LoadFromSongDir(%s) " , sDir . GetString ( ) ) ;
2002-08-24 07:57:28 +00:00
// make sure there is a trailing '\\' at the end of sDir
if ( sDir . Right ( 1 ) ! = " \\ " )
sDir + = " \\ " ;
// save song dir
m_sSongDir = sDir ;
// save group name
CStringArray sDirectoryParts ;
split ( m_sSongDir , " \\ " , sDirectoryParts , false ) ;
2002-10-31 02:31:15 +00:00
m_sGroupName = sDirectoryParts [ sDirectoryParts . size ( ) - 3 ] ; // second from last item
2002-08-24 07:57:28 +00:00
//
// First look in the cache for this song (without loading NoteData)
//
2002-09-07 10:22:49 +00:00
unsigned uDirHash = SONGINDEX - > GetCacheHash ( m_sSongDir ) ;
2002-09-07 10:22:06 +00:00
if ( GetHashForDirectory ( m_sSongDir ) = = uDirHash & & // this cache is up to date
2002-08-24 07:57:28 +00:00
DoesFileExist ( GetCacheFilePath ( ) ) )
{
2002-10-29 07:58:44 +00:00
LOG - > Trace ( " Loading '%s' from cache file '%s'. " , m_sSongDir . GetString ( ) , GetCacheFilePath ( ) . GetString ( ) ) ;
2002-09-06 23:36:04 +00:00
SMLoader ld ;
ld . LoadFromSMFile ( GetCacheFilePath ( ) , * this ) ;
2002-08-24 07:57:28 +00:00
}
else
{
2002-09-10 08:39:58 +00:00
if ( ! LoadWithoutCache ( m_sSongDir ) )
2002-08-24 07:57:28 +00:00
return false ;
}
{
/* Generated filename; this doesn't always point to a loadable file,
2002-10-17 20:43:47 +00:00
* but instead points to the file we should write changed files to,
* and will always be an .SM.
*
* This is a little tricky. We can't always use the song title directly,
* since it might contain characters we can't store in filenames. Two
* easy options: we could manually filter out invalid characters, or we
* could use the name of the directory, which is always a valid filename
* and should always be the same as the song. The former might not catch
* everything--filename restrictions are platform-specific; we might even
* be on an 8.3 filesystem, so let's do the latter.
*
* We can't rely on searching for other data filenames; it works for DWIs,
* but not KSFs and BMSs.
*
* So, let's do this (by priority):
* 1. If there's an .SM file, use that filename. No reason to use anything
* else; it's the filename in use.
* 2. If there's a .DWI, use it with a changed extension.
* 3. Otherwise, use the name of the directory, since it's definitely a valid
* filename, and should always be the title of the song (unlike KSFs).
*/
m_sSongFileName = m_sSongDir ;
2002-08-24 07:57:28 +00:00
CStringArray asFileNames ;
GetDirListing ( m_sSongDir + " *.sm " , asFileNames ) ;
2002-10-31 02:31:15 +00:00
if ( ! asFileNames . empty ( ) )
2002-10-17 20:43:47 +00:00
m_sSongFileName + = asFileNames [ 0 ] ;
else {
GetDirListing ( m_sSongDir + " *.dwi " , asFileNames ) ;
2002-10-31 02:31:15 +00:00
if ( ! asFileNames . empty ( ) ) {
2002-10-17 20:43:47 +00:00
m_sSongFileName + = asFileNames [ 0 ] ;
/* XXX: This would mess up "vote.for.dwight.d.eisenhower.dwi". */
m_sSongFileName . Replace ( " .dwi " , " .sm " ) ;
} else {
2002-10-31 02:31:15 +00:00
m_sSongFileName + = sDirectoryParts [ sDirectoryParts . size ( ) - 2 ] ; // last item
2002-10-17 20:43:47 +00:00
m_sSongFileName + = " .sm " ;
}
}
2002-08-24 07:57:28 +00:00
}
2002-05-19 01:59:48 +00:00
2003-01-02 22:10:51 +00:00
/* Add AutoGen pointers. (These aren't cached.) */
AddAutoGenNotes ( ) ;
2002-05-19 01:59:48 +00:00
return true ;
2001-11-03 10:52:42 +00:00
}
2001-12-28 10:15:59 +00:00
2001-11-25 04:31:44 +00:00
void Song : : TidyUpData ( )
2001-11-03 10:52:42 +00:00
{
2002-12-14 21:29:44 +00:00
if ( ! HasMusic ( ) )
{
CStringArray arrayPossibleMusic ;
GetDirListing ( m_sSongDir + CString ( " *.mp3 " ) , arrayPossibleMusic ) ;
GetDirListing ( m_sSongDir + CString ( " *.ogg " ) , arrayPossibleMusic ) ;
GetDirListing ( m_sSongDir + CString ( " *.wav " ) , arrayPossibleMusic ) ;
if ( ! arrayPossibleMusic . empty ( ) ) // we found a match
m_sMusicFile = arrayPossibleMusic [ 0 ] ;
// Don't throw on missing music. -Chris
// else
2002-12-21 18:38:29 +00:00
// RageException::Throw( "The song in '%s' is missing a music file. You must place a music file in the song folder or remove the song", m_sSongDir.GetString() );
2002-12-14 21:29:44 +00:00
}
2002-12-14 06:22:33 +00:00
/* This must be done before radar calculation. */
if ( HasMusic ( ) )
{
RageSound sound ;
sound . Load ( GetMusicPath ( ) , false ) ; /* don't pre-cache */
2003-01-02 07:58:34 +00:00
2002-12-14 06:22:33 +00:00
m_fMusicLengthSeconds = sound . GetLengthSeconds ( ) ;
/* XXX: if(m_fMusicLengthSeconds == -1), warn and throw out the song */
2002-12-14 21:29:44 +00:00
if ( m_fMusicLengthSeconds = = 0 )
{
LOG - > Warn ( " File %s is empty? " , GetMusicPath ( ) . GetString ( ) ) ;
} else if ( m_fMusicLengthSeconds = = - 1 ) {
LOG - > Warn ( " File %s: error getting length " , GetMusicPath ( ) . GetString ( ) ) ;
}
2002-12-14 06:22:33 +00:00
}
else // ! HasMusic()
{
m_fMusicLengthSeconds = 100 ; // guess
2002-12-14 21:29:44 +00:00
LOG - > Warn ( " File %s has no music; guessing at %i seconds " , m_fMusicLengthSeconds ) ;
2002-12-14 06:22:33 +00:00
}
/* Generate these before we autogen notes, so the new notes can inherit
* their source's values. */
ReCalulateRadarValuesAndLastBeat ( ) ;
2002-10-31 08:05:13 +00:00
TrimRight ( m_sMainTitle ) ;
2002-04-28 20:42:32 +00:00
if ( m_sMainTitle = = " " ) m_sMainTitle = " Untitled song " ;
2002-10-31 08:05:13 +00:00
TrimRight ( m_sSubTitle ) ;
2002-04-28 20:42:32 +00:00
if ( m_sArtist = = " " ) m_sArtist = " Unknown artist " ;
2003-01-12 04:59:40 +00:00
/* XXX don't throw due to broken songs */
2002-10-31 02:31:15 +00:00
if ( m_BPMSegments . empty ( ) )
2002-12-21 18:38:29 +00:00
RageException : : Throw ( " No #BPM specified in '%s%s.' " , m_sSongDir . GetString ( ) , m_sSongFileName . GetString ( ) ) ;
2001-11-03 10:52:42 +00:00
2002-09-11 06:01:07 +00:00
// We're going to try and do something intelligent here...
// The MusicSampleStart always seems to be about 100-120 beats into
// the song regardless of BPM. Let's take a shot-in-the dark guess.
if ( m_fMusicSampleStartSeconds = = 0 )
m_fMusicSampleStartSeconds = this - > GetElapsedTimeFromBeat ( 100 ) ;
2003-01-12 04:24:38 +00:00
/* Some DWIs have lengths in ms when they meant seconds, eg. #SAMPLELENGTH:10;.
* If the sample length is way too short, change it. */
if ( m_fMusicSampleLengthSeconds < 3 )
m_fMusicSampleLengthSeconds = 10 ;
2002-07-11 19:02:26 +00:00
//
// Here's the problem: We have a directory full of images. We want to determine which
// image is the banner, which is the background, and which is the CDTitle.
//
//
// First, check the file name for hints.
//
2002-07-03 21:27:26 +00:00
if ( ! HasBanner ( ) )
2001-11-03 10:52:42 +00:00
{
2002-06-29 11:59:09 +00:00
m_sBannerFile = " " ;
2001-12-19 14:56:22 +00:00
2002-07-11 19:02:26 +00:00
// find an image with "banner" in the file name
2001-11-25 04:31:44 +00:00
CStringArray arrayPossibleBanners ;
2002-07-11 19:02:26 +00:00
GetDirListing ( m_sSongDir + CString ( " *banner*.png " ) , arrayPossibleBanners ) ;
GetDirListing ( m_sSongDir + CString ( " *banner*.jpg " ) , arrayPossibleBanners ) ;
GetDirListing ( m_sSongDir + CString ( " *banner*.bmp " ) , arrayPossibleBanners ) ;
2002-07-27 19:29:51 +00:00
GetDirListing ( m_sSongDir + CString ( " *banner*.gif " ) , arrayPossibleBanners ) ;
2002-10-31 02:31:15 +00:00
if ( ! arrayPossibleBanners . empty ( ) )
2002-07-11 19:02:26 +00:00
m_sBannerFile = arrayPossibleBanners [ 0 ] ;
}
2001-11-25 04:31:44 +00:00
2002-07-11 19:02:26 +00:00
if ( ! HasBackground ( ) )
{
m_sBackgroundFile = " " ;
2001-12-19 14:56:22 +00:00
2002-07-11 19:02:26 +00:00
// find an image with "bg" or "background" in the file name
CStringArray arrayPossibleBGs ;
GetDirListing ( m_sSongDir + CString ( " *bg*.png " ) , arrayPossibleBGs ) ;
GetDirListing ( m_sSongDir + CString ( " *bg*.jpg " ) , arrayPossibleBGs ) ;
GetDirListing ( m_sSongDir + CString ( " *bg*.bmp " ) , arrayPossibleBGs ) ;
2002-07-27 19:29:51 +00:00
GetDirListing ( m_sSongDir + CString ( " *bg*.gif " ) , arrayPossibleBGs ) ;
2002-07-11 19:02:26 +00:00
GetDirListing ( m_sSongDir + CString ( " *background*.png " ) , arrayPossibleBGs ) ;
GetDirListing ( m_sSongDir + CString ( " *background*.jpg " ) , arrayPossibleBGs ) ;
GetDirListing ( m_sSongDir + CString ( " *background*.bmp " ) , arrayPossibleBGs ) ;
2002-07-27 19:29:51 +00:00
GetDirListing ( m_sSongDir + CString ( " *background*.gif " ) , arrayPossibleBGs ) ;
2002-10-31 02:31:15 +00:00
if ( ! arrayPossibleBGs . empty ( ) )
2002-07-11 19:02:26 +00:00
m_sBackgroundFile = arrayPossibleBGs [ 0 ] ;
}
2001-12-19 14:56:22 +00:00
2002-07-11 19:02:26 +00:00
if ( ! HasCDTitle ( ) )
{
m_sCDTitleFile = " " ;
2001-12-28 10:15:59 +00:00
2002-07-11 19:02:26 +00:00
// find an image with "cdtitle" in the file name
CStringArray arrayPossibleCDTitles ;
GetDirListing ( m_sSongDir + CString ( " *cdtitle*.png " ) , arrayPossibleCDTitles ) ;
GetDirListing ( m_sSongDir + CString ( " *cdtitle*.jpg " ) , arrayPossibleCDTitles ) ;
GetDirListing ( m_sSongDir + CString ( " *cdtitle*.bmp " ) , arrayPossibleCDTitles ) ;
2002-07-27 19:29:51 +00:00
GetDirListing ( m_sSongDir + CString ( " *cdtitle*.gif " ) , arrayPossibleCDTitles ) ;
2002-10-31 02:31:15 +00:00
if ( ! arrayPossibleCDTitles . empty ( ) )
2002-07-11 19:02:26 +00:00
m_sCDTitleFile = arrayPossibleCDTitles [ 0 ] ;
2001-11-03 10:52:42 +00:00
}
2002-01-16 10:01:32 +00:00
2002-07-11 19:02:26 +00:00
//
// Now, For the images we still haven't found, look at the image dimensions of the remaining unclassified images.
//
CStringArray arrayImages ;
GetDirListing ( m_sSongDir + CString ( " *.png " ) , arrayImages ) ;
GetDirListing ( m_sSongDir + CString ( " *.jpg " ) , arrayImages ) ;
GetDirListing ( m_sSongDir + CString ( " *.bmp " ) , arrayImages ) ;
2002-07-27 19:29:51 +00:00
GetDirListing ( m_sSongDir + CString ( " *.gif " ) , arrayImages ) ;
2002-10-31 02:31:15 +00:00
unsigned i ;
for ( i = 0 ; i < arrayImages . size ( ) ; i + + ) // foreach image
2001-11-03 10:52:42 +00:00
{
2002-07-11 19:02:26 +00:00
// Skip any image that we've already classified
2002-07-23 01:41:40 +00:00
if ( HasBanner ( ) & & stricmp ( m_sBannerFile , arrayImages [ i ] ) = = 0 )
2002-07-11 19:02:26 +00:00
continue ; // skip
2001-12-19 14:56:22 +00:00
2002-07-23 01:41:40 +00:00
if ( HasBackground ( ) & & stricmp ( m_sBackgroundFile , arrayImages [ i ] ) = = 0 )
2002-07-11 19:02:26 +00:00
continue ; // skip
2001-12-19 14:56:22 +00:00
2002-07-23 01:41:40 +00:00
if ( HasCDTitle ( ) & & stricmp ( m_sCDTitleFile , arrayImages [ i ] ) = = 0 )
2002-07-11 19:02:26 +00:00
continue ; // skip
2001-11-25 04:31:44 +00:00
2002-11-13 05:17:15 +00:00
SDL_Surface * img = IMG_Load ( m_sSongDir + arrayImages [ i ] ) ;
if ( img )
2001-12-19 14:56:22 +00:00
{
2002-11-13 05:17:15 +00:00
int width = img - > w ;
int height = img - > h ;
SDL_FreeSurface ( img ) ;
if ( ! HasBackground ( ) & & width > = 320 & & height > = 240 )
2001-12-19 14:56:22 +00:00
{
2002-07-11 19:02:26 +00:00
m_sBackgroundFile = arrayImages [ i ] ;
continue ;
}
2002-11-13 05:17:15 +00:00
if ( ! HasBanner ( ) & & 100 < width & & width < 320 & & 50 < height & & height < 240 )
2002-07-11 19:02:26 +00:00
{
m_sBannerFile = arrayImages [ i ] ;
continue ;
}
2002-11-13 05:17:15 +00:00
if ( ! HasCDTitle ( ) & & width < = 100 & & height < = 50 )
2002-07-11 19:02:26 +00:00
{
m_sCDTitleFile = arrayImages [ i ] ;
continue ;
2001-12-19 14:56:22 +00:00
}
}
2001-11-03 10:52:42 +00:00
}
2002-07-03 21:27:26 +00:00
2002-10-06 20:17:38 +00:00
// If no BGChanges are specified and there are movies in the song directory, then assume
// they are DWI style where the movie begins at beat 0.
if ( ! HasBGChanges ( ) )
2001-11-30 09:38:35 +00:00
{
2002-07-11 19:02:26 +00:00
CStringArray arrayPossibleMovies ;
GetDirListing ( m_sSongDir + CString ( " *movie*.avi " ) , arrayPossibleMovies ) ;
GetDirListing ( m_sSongDir + CString ( " *movie*.mpg " ) , arrayPossibleMovies ) ;
GetDirListing ( m_sSongDir + CString ( " *movie*.mpeg " ) , arrayPossibleMovies ) ;
GetDirListing ( m_sSongDir + CString ( " *.avi " ) , arrayPossibleMovies ) ;
GetDirListing ( m_sSongDir + CString ( " *.mpg " ) , arrayPossibleMovies ) ;
GetDirListing ( m_sSongDir + CString ( " *.mpeg " ) , arrayPossibleMovies ) ;
2002-10-31 02:31:15 +00:00
if ( arrayPossibleMovies . size ( ) = = 1 )
2002-10-06 20:17:38 +00:00
{
CString sBGMovieFile = arrayPossibleMovies [ 0 ] ;
// calculate start beat of music
float fMusicStartBeat , fBPS ;
bool bFreeze ;
this - > GetBeatAndBPSFromElapsedTime ( - this - > m_fBeat0OffsetInSeconds , fMusicStartBeat , fBPS , bFreeze ) ;
2002-10-25 07:35:47 +00:00
this - > AddBackgroundChange ( BackgroundChange ( fMusicStartBeat , sBGMovieFile ) ) ;
2002-10-06 20:17:38 +00:00
}
2001-11-30 09:38:35 +00:00
}
2002-05-19 01:59:48 +00:00
2002-11-13 05:17:15 +00:00
// challenge notes are encoded as smaniac. If there is only one Notes for
// a NotesType and it's "smaniac", then convert it to "Challenge"
for ( NotesType nt = ( NotesType ) 0 ; nt < NUM_NOTES_TYPES ; nt = ( NotesType ) ( nt + 1 ) )
{
2003-01-03 05:56:28 +00:00
vector < Notes * > apNotes ;
2002-11-13 05:17:15 +00:00
GetNotesThatMatch ( nt , apNotes ) ;
if ( apNotes . size ( ) = = 1 )
{
2003-01-02 22:10:51 +00:00
if ( 0 = = apNotes [ 0 ] - > GetDescription ( ) . CompareNoCase ( " smaniac " ) )
2002-11-13 05:17:15 +00:00
{
2003-01-02 22:10:51 +00:00
apNotes [ 0 ] - > SetDescription ( " Challenge " ) ;
apNotes [ 0 ] - > SetDifficulty ( DIFFICULTY_HARD ) ;
2002-11-13 05:17:15 +00:00
}
}
}
2002-12-18 23:07:58 +00:00
for ( i = 0 ; i < m_apNotes . size ( ) ; i + + )
m_apNotes [ i ] - > Compress ( ) ;
2002-11-13 05:17:15 +00:00
}
2003-01-13 06:04:44 +00:00
struct TitleTrans
{
CString TitleFrom , SubFrom , ArtistFrom , /* regex */
TitleTo , SubTo , ArtistTo ; /* plain text */
TitleTrans ( CString tf , CString sf , CString af , CString tt , CString st , CString at ) :
TitleFrom ( tf ) , SubFrom ( sf ) , ArtistFrom ( af ) ,
TitleTo ( tt ) , SubTo ( st ) , ArtistTo ( at ) { }
bool Matches ( CString title , CString sub , CString artist )
{
if ( ! TitleFrom . empty ( ) & & ! regex ( TitleFrom , title ) ) return false ; /* no match */
if ( ! SubFrom . empty ( ) & & ! regex ( SubFrom , sub ) ) return false ; /* no match */
if ( ! ArtistFrom . empty ( ) & & ! regex ( ArtistFrom , artist ) ) return false ; /* no match */
return true ;
}
} ;
2002-11-13 05:17:15 +00:00
void Song : : ReCalulateRadarValuesAndLastBeat ( )
{
2002-07-11 19:02:26 +00:00
//
// calculate radar values and first/last beat
//
2003-01-13 06:04:44 +00:00
unsigned int i ;
for ( i = 0 ; i < m_apNotes . size ( ) ; i + + )
2002-05-19 01:59:48 +00:00
{
2002-07-11 19:02:26 +00:00
Notes * pNotes = m_apNotes [ i ] ;
2002-07-03 03:13:13 +00:00
NoteData tempNoteData ;
pNotes - > GetNoteData ( & tempNoteData ) ;
2002-05-19 01:59:48 +00:00
2002-07-02 00:27:58 +00:00
for ( int r = 0 ; r < NUM_RADAR_VALUES ; r + + )
2002-12-14 06:22:33 +00:00
{
2003-01-02 22:10:51 +00:00
/* If it's autogen, radar vals come from the parent. */
if ( pNotes - > IsAutogen ( ) )
2002-12-14 06:22:33 +00:00
continue ;
2003-01-02 22:10:51 +00:00
pNotes - > SetRadarValue ( r , NoteDataUtil : : GetRadarValue ( tempNoteData , ( RadarCategory ) r , m_fMusicLengthSeconds ) ) ;
2002-12-14 06:22:33 +00:00
}
2002-07-11 19:02:26 +00:00
float fFirstBeat = tempNoteData . GetFirstBeat ( ) ;
float fLastBeat = tempNoteData . GetLastBeat ( ) ;
if ( m_fFirstBeat = = - 1 )
m_fFirstBeat = fFirstBeat ;
else
m_fFirstBeat = min ( m_fFirstBeat , fFirstBeat ) ;
if ( m_fLastBeat = = - 1 )
m_fLastBeat = fLastBeat ;
else
m_fLastBeat = max ( m_fLastBeat , fLastBeat ) ;
2002-05-19 01:59:48 +00:00
}
2002-10-06 16:56:58 +00:00
2003-01-08 06:02:02 +00:00
/* XXX make theme metrics for these or something. Candy makes it
* a little annoying ...*/
2003-01-13 06:04:44 +00:00
static vector < TitleTrans > ttab ;
/* These REs aren't completely tested. XXX */
if ( ttab . empty ( ) )
{
2003-01-16 20:21:31 +00:00
/* Ambiguous, so check artist. Do this early; Riyu will be replaced later: */
2003-01-13 06:04:44 +00:00
ttab . push_back ( TitleTrans ( " ^Candy$ " , " " , " ^Luv.* " , " Candy &whitestar; " , " " , " " ) ) ;
2003-01-18 03:14:40 +00:00
ttab . push_back ( TitleTrans ( " ^Candy$ " , " " , " .*Riyu.* " , " Candy &whiteheart; " , " " , " " ) ) ;
2003-01-13 06:04:44 +00:00
2003-01-16 20:21:31 +00:00
/* Make sure this appears after the above "Riyu" match, so it doesn't
* break it. I've seen both "Kosaku" and "Kosaka"; I think Kosaka is
* correct, but handle both. */
ttab . push_back ( TitleTrans ( " " , " " , " Riyu Kosak[au] " , " " , " " , " &kosaka1;&kosaka2;&hri;&hyu; " ) ) ;
2003-01-18 03:14:40 +00:00
ttab . push_back ( TitleTrans ( " " , " " , " Kosak[au] Riyu " , " " , " " , " &kosaka1;&kosaka2;&hri;&hyu; " ) ) ;
2003-01-16 20:21:31 +00:00
/* This is fuzzy, so check the artist, too. */
2003-01-13 06:04:44 +00:00
ttab . push_back ( TitleTrans ( " .*Japan.* " , " " , " (Re-Venge)|(RevenG) " , " &matsuri; Japan " , " " , " " ) ) ;
2003-01-16 20:21:31 +00:00
/* Fix up hacked titles: */
ttab . push_back ( TitleTrans ( " ^Max 300$ " , " " , " % " , " " , " " , " ω " ) ) ;
ttab . push_back ( TitleTrans ( " ^Bre=kdown$ " , " " , " " , " Bre&flipped-a;kdown " , " " , " " ) ) ;
2003-01-13 06:04:44 +00:00
ttab . push_back ( TitleTrans ( " ^Candy #$ " , " " , " " , " Candy &whitestar; " , " " , " " ) ) ;
ttab . push_back ( TitleTrans ( " ^Candy \\ $$ " , " " , " " , " Candy &whiteheart; " , " " , " " ) ) ;
2003-01-16 20:21:31 +00:00
ttab . push_back ( TitleTrans ( " ^} JAPAN$ " , " " , " " , " &matsuri; Japan " , " " , " " ) ) ;
ttab . push_back ( TitleTrans ( " ^ \\ + \\ {$ " , " " , " " , " &kakumei1;&kakumei2; " , " " , " " ) ) ;
ttab . push_back ( TitleTrans ( " ^Sweet Sweet \\ $ Magic$ " , " " , " " , " Sweet Sweet &whiteheart; Magic " , " " , " " ) ) ;
2003-01-13 06:04:44 +00:00
2003-01-16 20:21:31 +00:00
/* Special stuff is done. Titles: */
ttab . push_back ( TitleTrans ( " ^Matsuri Japan$ " , " " , " " , " &matsuri; Japan " , " " , " " ) ) ;
ttab . push_back ( TitleTrans ( " ^Kakumei$ " , " " , " " , " &kakumei1;&kakumei2; " , " " , " " ) ) ;
2003-01-19 22:50:40 +00:00
ttab . push_back ( TitleTrans ( " ^Sweet Sweet (Love )?Magic$ " , " " , " " , " Sweet Sweet &whiteheart; Magic " , " " , " " ) ) ;
2003-01-16 20:21:31 +00:00
ttab . push_back ( TitleTrans ( " ^Breakdown$ " , " " , " " , " Bre&flipped-a;kdown " , " " , " " ) ) ;
2003-01-19 22:43:16 +00:00
/* サナ・モレッテ・ネ・エンテ
* People can't decide how they want to spell this, so cope with
* both l or r, and one or two l/r and t. */
ttab . push_back ( TitleTrans ( " ^Sana Mo((ll?)|(rr?))et(t?)e Ne Ente " , " " , " " ,
2003-01-16 20:21:31 +00:00
" &ksa;&kna;&kdot;&kmo;&kre;&kq;&kte;&kdot;&kne;&kdot;&ke;&kn;&kte; " , " " , " " ) ) ;
ttab . push_back ( TitleTrans ( " ^Sobakasu$ " , " " , " " , " &hso;&hba;&hka;&hsu; " , " " , " " ) ) ;
2003-01-13 06:04:44 +00:00
2003-01-16 20:21:31 +00:00
/* 夜空ノムコウ */
ttab . push_back ( TitleTrans ( " ^Yozora no Muko$ " , " " , " " , " &yozora1;&yozora2;&hno;&kmu;&kko; " , " " , " " ) ) ;
2003-01-13 06:04:44 +00:00
2003-01-19 22:43:16 +00:00
/* 17才 */
ttab . push_back ( TitleTrans ( " ^17 ?(Sai)?$ " , " " , " " , " 17&sai; " , " " , " " ) ) ;
2003-01-16 20:21:31 +00:00
/* XXX blackstar or whitestar? Is this supposed to be capped? */
ttab . push_back ( TitleTrans ( " ^Mobo Moga$ " , " " , " " , " Mobo&whitestar;Moga " , " " , " " ) ) ;
/* XXX whiteheart or blackheart? Is this supposed to be capped? */
ttab . push_back ( TitleTrans ( " ^Love Shine$ " , " " , " " , " Love &whiteheart; Shine " , " " , " " ) ) ;
/* ロマンスの神様 */
ttab . push_back ( TitleTrans ( " ^God of Romance$ " , " " , " " , " &kro;&kma;&kn;&ksu;&hno;&kami;&sama; " , " " , " " ) ) ;
/* ダンシン・オール・アローン */
ttab . push_back ( TitleTrans ( " ^Dancing Pompokolin$ " , " " , " " , " &kda;&kn;&ksi;&kn;&kdot;&ko;&kdash;&kru;&kdot;&ka;&kro;&kdash;&kn; " , " " , " " ) ) ;
2003-01-18 03:14:40 +00:00
/* メキシコ民謡 */
ttab . push_back ( TitleTrans ( " " , " " , " ^Spanish Folk Music$ " , " " , " " , " &kme;&kki;&ksi;&kko;&minyou1;&minyou2; " ) ) ;
2003-01-16 20:21:31 +00:00
/* 青い振動 XXX verify this title */
ttab . push_back ( TitleTrans ( " ^Blue Impulse$ " , " " , " " , " &aoi;&hi;&shoudou1;&shoudou2; " , " " , " " ) ) ;
/* XXX 大見解 verify this title (Night Line or Nightline or am I completely wrong?) */
ttab . push_back ( TitleTrans ( " ^Night ?Line$ " , " " , " " , " &ookii;&kenkai1;&kenkai2; " , " " , " " ) ) ;
2003-01-18 03:14:40 +00:00
/* ♡LOVE²シュガ→♡ */
ttab . push_back ( TitleTrans ( " ^Love Love Sugar$ " , " " , " " , " &whiteheart;LOVE&squared; &ksi;&kyus;&kga;→&whiteheart; " , " " , " " ) ) ;
/* 三毛猫ロック */
ttab . push_back ( TitleTrans ( " ^Mikeneko Rock$ " , " " , " " , " &num-san;&hair;&neko;&kro;&kq;&kku; " , " " , " " ) ) ;
/* XXX: "door of magic" (tobira no mahou) -> 魔法の扉 */
/* XXX スペース★マコのテーマ (space? special? * "mako"?'s team) (title or subtitle, not sure) */
2003-01-16 20:21:31 +00:00
/* Subtitles: */
/* XXX それぞれの明日 (every tomorrow?) (subtitle) (title is Graduation) */
2003-01-18 03:14:40 +00:00
2003-01-16 20:21:31 +00:00
/* Artists: */
ttab . push_back ( TitleTrans ( " " , " " , " Omega " , " " , " " , " ω " ) ) ;
2003-01-18 03:14:40 +00:00
/* 亜熱帯マジ-SKA爆弾 (serious tropical ska bomb? ruh roh) */
ttab . push_back ( TitleTrans ( " " , " " , " ^Anettai Maji.*Ska (Bakudan|Bukuden) " , " " , " " , " &anettai1;&anettai2;&anettai3;&kma;&kji;-SKA&bakudan1;&bakudan2; " ) ) ;
/* 新谷さなえ (Sanae or incorrect Sana) */
ttab . push_back ( TitleTrans ( " " , " " , " Sanae? Shintani " , " " , " " , " &shintani1;&shintani2;&hsa;&hna;&he; " ) ) ;
2003-01-16 20:21:31 +00:00
ttab . push_back ( TitleTrans ( " " , " " , " dj TAKA feat. ?Noria " , " " , " " , " dj TAKA feat. &hno;&hri;&ha; " ) ) ;
2003-01-18 03:14:40 +00:00
/* くにたけみゆき */
ttab . push_back ( TitleTrans ( " " , " " , " Miyuki Kunitake " , " " , " " , " &hku;&hni;&hta;&hke;&hmi;&hyu;&hki; " ) ) ;
ttab . push_back ( TitleTrans ( " " , " " , " Kunitake Miyuki " , " " , " " , " &hku;&hni;&hta;&hke;&hmi;&hyu;&hki; " ) ) ;
// ttab.push_back(TitleTrans(".*Legend.*", "", "ZZ", "", "", "&zz;") );
2003-01-16 20:21:31 +00:00
}
2003-01-13 06:04:44 +00:00
for ( i = 0 ; i < ttab . size ( ) ; + + i )
{
if ( ! ttab [ i ] . Matches ( m_sMainTitle , m_sSubTitle , m_sArtist ) )
continue ;
/* The song matches. Replace whichever strings aren't empty: */
if ( ! ttab [ i ] . TitleTo . empty ( ) )
2003-01-08 06:02:02 +00:00
{
m_sMainTitleTranslit = m_sMainTitle ;
2003-01-13 06:04:44 +00:00
m_sMainTitle = ttab [ i ] . TitleTo ;
2003-01-16 20:21:31 +00:00
FontCharAliases : : ReplaceMarkers ( m_sMainTitle ) ;
2003-01-13 06:04:44 +00:00
}
if ( ! ttab [ i ] . SubTo . empty ( ) )
{
m_sSubTitleTranslit = m_sSubTitle ;
m_sSubTitle = ttab [ i ] . SubTo ;
2003-01-16 20:21:31 +00:00
FontCharAliases : : ReplaceMarkers ( m_sSubTitle ) ;
2003-01-13 06:04:44 +00:00
}
if ( ! ttab [ i ] . ArtistTo . empty ( ) )
{
m_sArtistTranslit = m_sArtist ;
m_sArtist = ttab [ i ] . ArtistTo ;
2003-01-16 20:21:31 +00:00
FontCharAliases : : ReplaceMarkers ( m_sArtist ) ;
2003-01-08 06:02:02 +00:00
}
}
2001-11-30 09:38:35 +00:00
}
2003-01-03 05:56:28 +00:00
void Song : : GetNotesThatMatch ( NotesType nt , vector < Notes * > & arrayAddTo ) const
2002-04-16 17:31:00 +00:00
{
2002-10-31 02:31:15 +00:00
for ( unsigned i = 0 ; i < m_apNotes . size ( ) ; i + + ) // for each of the Song's Notes
2002-09-06 10:15:00 +00:00
{
2002-10-05 20:03:14 +00:00
if ( m_apNotes [ i ] - > m_NotesType = = nt )
2002-10-31 04:23:39 +00:00
arrayAddTo . push_back ( m_apNotes [ i ] ) ;
2002-09-06 10:15:00 +00:00
}
2001-12-28 10:15:59 +00:00
}
2002-09-06 08:56:28 +00:00
/* Return whether the song is playable in the given style. */
bool Song : : SongCompleteForStyle ( const StyleDef * st ) const
{
2002-10-14 00:28:27 +00:00
if ( ! SongHasNotesType ( st - > m_NotesType ) )
return false ;
2002-09-06 08:56:28 +00:00
return true ;
}
2002-08-25 19:00:12 +00:00
bool Song : : SongHasNotesType ( NotesType nt ) const
2002-08-20 06:47:30 +00:00
{
2002-10-31 02:31:15 +00:00
for ( unsigned i = 0 ; i < m_apNotes . size ( ) ; i + + ) // foreach Notes
2002-08-20 06:47:30 +00:00
if ( m_apNotes [ i ] - > m_NotesType = = nt )
return true ;
2002-08-25 19:00:12 +00:00
return false ;
}
2002-08-20 06:47:30 +00:00
2002-09-29 05:06:18 +00:00
bool Song : : SongHasNotesTypeAndDifficulty ( NotesType nt , Difficulty dc ) const
2002-08-25 19:00:12 +00:00
{
2002-10-31 02:31:15 +00:00
for ( unsigned i = 0 ; i < m_apNotes . size ( ) ; i + + ) // foreach Notes
2003-01-02 22:10:51 +00:00
if ( m_apNotes [ i ] - > m_NotesType = = nt & & m_apNotes [ i ] - > GetDifficulty ( ) = = dc )
2002-08-25 19:00:12 +00:00
return true ;
2002-08-20 06:47:30 +00:00
return false ;
}
2002-05-19 01:59:48 +00:00
void Song : : SaveToCacheFile ( )
2002-02-11 04:46:31 +00:00
{
2002-07-31 19:40:40 +00:00
LOG - > Trace ( " Song::SaveToCacheFile() " ) ;
2002-02-11 04:46:31 +00:00
2002-08-21 02:13:08 +00:00
SONGINDEX - > AddCacheIndex ( m_sSongDir , GetHashForDirectory ( m_sSongDir ) ) ;
2002-08-25 19:00:12 +00:00
SaveToSMFile ( GetCacheFilePath ( ) , true ) ;
2002-05-19 01:59:48 +00:00
}
2002-02-11 04:46:31 +00:00
2002-09-11 04:49:07 +00:00
void Song : : Save ( )
2002-04-16 17:31:00 +00:00
{
2002-08-25 19:00:12 +00:00
LOG - > Trace ( " Song::SaveToSongFile() " ) ;
2002-10-17 20:52:32 +00:00
/* rename all old files to avoid confusion.
*
* This also serves as a backup, so rename .sm's, too. If we crash when
* saving the .sm, we don't want to lose what we had. But, what we really
* should be doing is saving to another file (eg. foo.sm.new), then once we
* know we havn't crashed, move the old .sm to .sm.old and the new one to
* the real filename. That way, if we crash, we don't leave the song in an
* unplayable state where the user has to manually un-rename stuff. XXX -glenn
*/
2002-08-25 19:00:12 +00:00
CStringArray arrayOldFileNames ;
GetDirListing ( m_sSongDir + " *.bms " , arrayOldFileNames ) ;
GetDirListing ( m_sSongDir + " *.dwi " , arrayOldFileNames ) ;
GetDirListing ( m_sSongDir + " *.ksf " , arrayOldFileNames ) ;
2002-10-17 20:52:32 +00:00
GetDirListing ( m_sSongDir + " *.sm " , arrayOldFileNames ) ;
2002-08-25 19:00:12 +00:00
2002-10-31 02:31:15 +00:00
for ( unsigned i = 0 ; i < arrayOldFileNames . size ( ) ; i + + )
2002-07-02 00:27:58 +00:00
{
2002-08-25 19:00:12 +00:00
CString sOldPath = m_sSongDir + arrayOldFileNames [ i ] ;
CString sNewPath = sOldPath + " .old " ;
MoveFile ( sOldPath , sNewPath ) ;
2002-07-02 00:27:58 +00:00
}
2002-11-13 05:17:15 +00:00
ReCalulateRadarValuesAndLastBeat ( ) ;
2002-08-25 19:00:12 +00:00
SaveToSMFile ( GetSongFilePath ( ) , false ) ;
2002-09-11 04:49:07 +00:00
SaveToDWIFile ( ) ;
2002-08-25 19:00:12 +00:00
}
2002-07-02 00:27:58 +00:00
2002-08-25 19:00:12 +00:00
void Song : : SaveToSMFile ( CString sPath , bool bSavingCache )
{
2002-12-14 00:21:22 +00:00
LOG - > Trace ( " Song::SaveToSMFile('%s') " , sPath . GetString ( ) ) ;
2002-04-16 17:31:00 +00:00
2002-12-14 00:21:22 +00:00
NotesWriterSM wr ;
wr . Write ( sPath , * this , bSavingCache ) ;
2002-02-11 04:46:31 +00:00
}
2002-09-11 04:49:07 +00:00
void Song : : SaveToDWIFile ( )
2002-08-17 06:44:04 +00:00
{
2002-08-25 19:00:12 +00:00
LOG - > Trace ( " Song::SaveToSongFileAndDWI() " ) ;
2002-08-17 06:44:04 +00:00
CString sPath = GetSongFilePath ( ) ;
sPath . Replace ( " .sm " , " .dwi " ) ;
2002-09-09 00:22:02 +00:00
NotesWriterDWI wr ;
wr . Write ( sPath , * this ) ;
2002-08-17 06:44:04 +00:00
}
2002-08-25 19:00:12 +00:00
void Song : : AddAutoGenNotes ( )
{
2002-10-24 01:51:01 +00:00
for ( NotesType ntMissing = ( NotesType ) 0 ; ntMissing < NUM_NOTES_TYPES ; ntMissing = ( NotesType ) ( ntMissing + 1 ) )
2002-08-25 19:00:12 +00:00
{
2002-10-24 01:51:01 +00:00
if ( SongHasNotesType ( ntMissing ) )
continue ;
2002-10-11 18:23:05 +00:00
// missing Notes of this type
int iNumTracksOfMissing = GAMEMAN - > NotesTypeToNumTracks ( ntMissing ) ;
2002-10-05 14:47:03 +00:00
2002-10-11 18:23:05 +00:00
// look for closest match
NotesType ntBestMatch = ( NotesType ) - 1 ;
int iBestTrackDifference = 10000 ; // inf
2002-10-05 14:47:03 +00:00
2002-10-11 18:23:05 +00:00
for ( NotesType nt = ( NotesType ) 0 ; nt < NUM_NOTES_TYPES ; nt = ( NotesType ) ( nt + 1 ) )
{
2003-01-03 05:56:28 +00:00
vector < Notes * > apNotes ;
2002-10-11 18:23:05 +00:00
this - > GetNotesThatMatch ( nt , apNotes ) ;
2003-01-02 22:10:51 +00:00
if ( apNotes . empty ( ) | | apNotes [ 0 ] - > IsAutogen ( ) )
continue ; /* can't autogen from other autogen */
int iNumTracks = GAMEMAN - > NotesTypeToNumTracks ( nt ) ;
int iTrackDifference = abs ( iNumTracks - iNumTracksOfMissing ) ;
if ( iTrackDifference < iBestTrackDifference )
2002-10-05 14:47:03 +00:00
{
2002-10-11 18:23:05 +00:00
ntBestMatch = nt ;
iBestTrackDifference = iTrackDifference ;
2002-10-05 14:47:03 +00:00
}
2002-10-11 18:23:05 +00:00
}
2002-10-05 14:47:03 +00:00
2002-11-13 05:17:15 +00:00
if ( ntBestMatch ! = - 1 )
AutoGen ( ntMissing , ntBestMatch ) ;
}
}
2002-10-11 18:25:15 +00:00
2002-11-13 05:17:15 +00:00
void Song : : AutoGen ( NotesType ntTo , NotesType ntFrom )
{
2003-01-02 22:10:51 +00:00
// int iNumTracksOfTo = GAMEMAN->NotesTypeToNumTracks(ntTo);
2002-10-11 18:25:15 +00:00
2002-11-13 05:17:15 +00:00
for ( unsigned int j = 0 ; j < m_apNotes . size ( ) ; j + + )
{
Notes * pOriginalNotes = m_apNotes [ j ] ;
if ( pOriginalNotes - > m_NotesType = = ntFrom )
{
2002-10-11 18:25:15 +00:00
Notes * pNewNotes = new Notes ;
2003-01-02 22:10:51 +00:00
pNewNotes - > AutogenFrom ( pOriginalNotes , ntTo ) ;
2002-10-31 04:23:39 +00:00
this - > m_apNotes . push_back ( pNewNotes ) ;
2002-08-25 19:00:12 +00:00
}
}
}
2002-09-29 05:06:18 +00:00
Grade Song : : GetGradeForDifficulty ( const StyleDef * st , int p , Difficulty dc ) const
2002-04-16 17:31:00 +00:00
{
2002-10-07 05:45:59 +00:00
// return max grade of notes in difficulty class
2003-01-03 05:56:28 +00:00
vector < Notes * > aNotes ;
2002-10-14 00:28:27 +00:00
this - > GetNotesThatMatch ( st - > m_NotesType , aNotes ) ;
2002-08-01 13:42:56 +00:00
SortNotesArrayByDifficulty ( aNotes ) ;
2002-02-11 04:46:31 +00:00
2002-10-07 05:45:59 +00:00
Grade grade = GRADE_NO_DATA ;
2002-10-31 02:31:15 +00:00
for ( unsigned i = 0 ; i < aNotes . size ( ) ; i + + )
2002-04-16 17:31:00 +00:00
{
2002-09-06 10:15:00 +00:00
const Notes * pNotes = aNotes [ i ] ;
2003-01-02 22:10:51 +00:00
if ( pNotes - > GetDifficulty ( ) = = dc )
2002-10-07 05:45:59 +00:00
grade = max ( grade , pNotes - > m_TopGrade ) ;
2002-04-16 17:31:00 +00:00
}
2002-10-07 05:45:59 +00:00
return grade ;
2002-04-16 17:31:00 +00:00
}
2002-01-16 10:01:32 +00:00
2002-05-19 01:59:48 +00:00
2002-08-21 00:47:29 +00:00
bool Song : : IsNew ( ) const
2002-08-20 21:00:56 +00:00
{
return GetNumTimesPlayed ( ) = = 0 ;
}
2002-08-21 00:47:29 +00:00
bool Song : : IsEasy ( NotesType nt ) const
2002-08-20 21:00:56 +00:00
{
2002-10-31 02:31:15 +00:00
for ( unsigned i = 0 ; i < m_apNotes . size ( ) ; i + + )
2002-08-20 21:00:56 +00:00
{
Notes * pNotes = m_apNotes [ i ] ;
if ( pNotes - > m_NotesType ! = nt )
continue ;
2003-01-02 22:10:51 +00:00
if ( pNotes - > GetMeter ( ) < = 2 )
2002-08-20 21:00:56 +00:00
return true ;
}
return false ;
}
2002-05-19 01:59:48 +00:00
2002-02-11 04:46:31 +00:00
/////////////////////////////////////
// Sorting
/////////////////////////////////////
2002-01-16 10:01:32 +00:00
2002-10-24 07:56:20 +00:00
int CompareSongPointersByTitle ( const Song * pSong1 , const Song * pSong2 )
2002-10-02 01:32:43 +00:00
{
//Prefer transliterations to full titles
CString sTitle1 = pSong1 - > GetSortTitle ( ) ;
CString sTitle2 = pSong2 - > GetSortTitle ( ) ;
int ret = sTitle1 . CompareNoCase ( sTitle2 ) ;
2002-10-24 07:56:20 +00:00
if ( ret < 0 ) return true ;
if ( ret > 0 ) return false ;
2002-10-02 01:32:43 +00:00
/* The titles are the same. Ensure we get a consistent ordering
* by comparing the unique SongFilePaths. */
2003-01-03 05:29:45 +00:00
return pSong1 - > GetSongFilePath ( ) . CompareNoCase ( pSong2 - > GetSongFilePath ( ) ) < 0 ;
2002-10-02 01:32:43 +00:00
}
2003-01-03 05:56:28 +00:00
void SortSongPointerArrayByTitle ( vector < Song * > & arraySongPointers )
2002-10-02 01:32:43 +00:00
{
2002-10-24 08:40:27 +00:00
sort ( arraySongPointers . begin ( ) , arraySongPointers . end ( ) , CompareSongPointersByTitle ) ;
2002-10-02 01:32:43 +00:00
}
2002-10-24 07:56:20 +00:00
int CompareSongPointersByDifficulty ( const Song * pSong1 , const Song * pSong2 )
2002-09-29 23:29:01 +00:00
{
2003-01-03 05:56:28 +00:00
vector < Notes * > aNotes1 ;
vector < Notes * > aNotes2 ;
2002-09-29 23:29:01 +00:00
2002-10-14 00:28:27 +00:00
pSong1 - > GetNotesThatMatch ( GAMESTATE - > GetCurrentStyleDef ( ) - > m_NotesType , aNotes1 ) ;
pSong2 - > GetNotesThatMatch ( GAMESTATE - > GetCurrentStyleDef ( ) - > m_NotesType , aNotes2 ) ;
2002-09-29 23:29:01 +00:00
int iEasiestMeter1 = 1000 ; // infinity
int iEasiestMeter2 = 1000 ; // infinity
2002-10-31 02:31:15 +00:00
unsigned i ;
for ( i = 0 ; i < aNotes1 . size ( ) ; i + + )
2003-01-02 22:10:51 +00:00
iEasiestMeter1 = min ( iEasiestMeter1 , aNotes1 [ i ] - > GetMeter ( ) ) ;
2002-10-31 02:31:15 +00:00
for ( i = 0 ; i < aNotes2 . size ( ) ; i + + )
2003-01-02 22:10:51 +00:00
iEasiestMeter2 = min ( iEasiestMeter2 , aNotes2 [ i ] - > GetMeter ( ) ) ;
2002-09-29 23:29:01 +00:00
if ( iEasiestMeter1 < iEasiestMeter2 )
2002-10-24 07:56:20 +00:00
return true ;
if ( iEasiestMeter1 > iEasiestMeter2 )
return false ;
return CompareSongPointersByTitle ( pSong1 , pSong2 ) ;
2002-09-29 23:29:01 +00:00
}
2003-01-03 05:56:28 +00:00
void SortSongPointerArrayByDifficulty ( vector < Song * > & arraySongPointers )
2002-09-29 23:29:01 +00:00
{
2002-10-24 08:40:27 +00:00
sort ( arraySongPointers . begin ( ) , arraySongPointers . end ( ) , CompareSongPointersByDifficulty ) ;
2002-09-29 23:29:01 +00:00
}
2002-10-24 07:56:20 +00:00
bool CompareSongPointersByBPM ( const Song * pSong1 , const Song * pSong2 )
2002-01-16 10:01:32 +00:00
{
float fMinBPM1 , fMaxBPM1 , fMinBPM2 , fMaxBPM2 ;
pSong1 - > GetMinMaxBPM ( fMinBPM1 , fMaxBPM1 ) ;
pSong2 - > GetMinMaxBPM ( fMinBPM2 , fMaxBPM2 ) ;
if ( fMaxBPM1 < fMaxBPM2 )
2002-10-24 07:56:20 +00:00
return true ;
if ( fMaxBPM1 > fMaxBPM2 )
return false ;
return CompareCStringsAsc ( pSong1 - > GetSongFilePath ( ) , pSong2 - > GetSongFilePath ( ) ) ;
2002-01-16 10:01:32 +00:00
}
2003-01-03 05:56:28 +00:00
void SortSongPointerArrayByBPM ( vector < Song * > & arraySongPointers )
2002-01-16 10:01:32 +00:00
{
2002-10-24 08:40:27 +00:00
sort ( arraySongPointers . begin ( ) , arraySongPointers . end ( ) , CompareSongPointersByBPM ) ;
2002-01-16 10:01:32 +00:00
}
2002-10-24 07:56:20 +00:00
int CompareSongPointersByArtist ( const Song * pSong1 , const Song * pSong2 )
2002-01-16 10:01:32 +00:00
{
2002-06-29 11:59:09 +00:00
CString sArtist1 = pSong1 - > m_sArtist ;
CString sArtist2 = pSong2 - > m_sArtist ;
2002-01-16 10:01:32 +00:00
2002-02-24 01:43:11 +00:00
if ( sArtist1 < sArtist2 )
2002-10-24 07:56:20 +00:00
return true ;
if ( sArtist1 > sArtist2 )
return false ;
return CompareSongPointersByTitle ( pSong1 , pSong2 ) ;
2002-01-16 10:01:32 +00:00
}
2003-01-03 05:56:28 +00:00
void SortSongPointerArrayByArtist ( vector < Song * > & arraySongPointers )
2002-01-16 10:01:32 +00:00
{
2002-10-24 08:40:27 +00:00
sort ( arraySongPointers . begin ( ) , arraySongPointers . end ( ) , CompareSongPointersByArtist ) ;
2002-01-16 10:01:32 +00:00
}
2002-10-24 07:56:20 +00:00
int CompareSongPointersByGroup ( const Song * pSong1 , const Song * pSong2 )
2002-01-16 10:01:32 +00:00
{
2002-08-22 21:59:47 +00:00
const CString & sGroup1 = pSong1 - > m_sGroupName ;
const CString & sGroup2 = pSong2 - > m_sGroupName ;
2002-01-16 10:01:32 +00:00
2002-02-24 01:43:11 +00:00
if ( sGroup1 < sGroup2 )
2002-10-24 07:56:20 +00:00
return true ;
if ( sGroup1 > sGroup2 )
return false ;
2002-08-22 21:59:47 +00:00
2002-09-29 23:29:01 +00:00
/* Same group; compare by difficulty. */
2002-10-24 07:56:20 +00:00
return CompareSongPointersByDifficulty ( pSong1 , pSong2 ) ;
2002-01-16 10:01:32 +00:00
}
2003-01-03 05:56:28 +00:00
void SortSongPointerArrayByGroup ( vector < Song * > & arraySongPointers )
2002-01-16 10:01:32 +00:00
{
2002-10-24 08:40:27 +00:00
sort ( arraySongPointers . begin ( ) , arraySongPointers . end ( ) , CompareSongPointersByGroup ) ;
2002-01-16 10:01:32 +00:00
}
2002-10-24 07:56:20 +00:00
int CompareSongPointersByMostPlayed ( const Song * pSong1 , const Song * pSong2 )
2002-01-16 10:01:32 +00:00
{
2002-02-02 05:11:12 +00:00
int iNumTimesPlayed1 = pSong1 - > GetNumTimesPlayed ( ) ;
int iNumTimesPlayed2 = pSong2 - > GetNumTimesPlayed ( ) ;
2002-01-16 10:01:32 +00:00
2002-08-20 21:00:56 +00:00
if ( iNumTimesPlayed1 > iNumTimesPlayed2 )
2002-10-24 07:56:20 +00:00
return true ;
if ( iNumTimesPlayed1 < iNumTimesPlayed2 )
return false ;
return CompareSongPointersByTitle ( pSong1 , pSong2 ) ;
2002-01-16 10:01:32 +00:00
}
2003-01-03 05:56:28 +00:00
void SortSongPointerArrayByMostPlayed ( vector < Song * > & arraySongPointers )
2002-01-16 10:01:32 +00:00
{
2002-10-24 08:40:27 +00:00
sort ( arraySongPointers . begin ( ) , arraySongPointers . end ( ) , CompareSongPointersByMostPlayed ) ;
2002-01-16 10:01:32 +00:00
}
2002-08-30 04:28:12 +00:00
bool Song : : NormallyDisplayed ( ) const
{
if ( ! PREFSMAN - > m_bHiddenSongs ) return true ;
return m_SelectionDisplay = = SHOW_ALWAYS ;
}
bool Song : : RouletteDisplayed ( ) const
{
if ( ! PREFSMAN - > m_bHiddenSongs ) return true ;
return m_SelectionDisplay ! = SHOW_NEVER ;
}
2002-09-07 11:45:15 +00:00
2002-10-31 02:31:15 +00:00
bool Song : : HasMusic ( ) const { return m_sMusicFile ! = " " & & IsAFile ( GetMusicPath ( ) ) ; }
bool Song : : HasBanner ( ) const { return m_sBannerFile ! = " " & & IsAFile ( GetBannerPath ( ) ) ; }
bool Song : : HasBackground ( ) const { return m_sBackgroundFile ! = " " & & IsAFile ( GetBackgroundPath ( ) ) ; }
bool Song : : HasCDTitle ( ) const { return m_sCDTitleFile ! = " " & & IsAFile ( GetCDTitlePath ( ) ) ; }
bool Song : : HasBGChanges ( ) const { return ! m_BackgroundChanges . empty ( ) ; }
2002-09-07 11:45:15 +00:00
int Song : : GetNumTimesPlayed ( ) const
{
int iTotalNumTimesPlayed = 0 ;
2002-10-31 02:31:15 +00:00
for ( unsigned i = 0 ; i < m_apNotes . size ( ) ; i + + )
2002-09-07 11:45:15 +00:00
{
iTotalNumTimesPlayed + = m_apNotes [ i ] - > m_iNumTimesPlayed ;
}
return iTotalNumTimesPlayed ;
}
2002-09-10 08:39:58 +00:00
2002-09-15 02:58:33 +00:00
/* Search semantics for all song files:
2002-09-10 09:03:08 +00:00
*
2002-09-15 02:58:33 +00:00
* If the path doesn't have any directory separators, it's a filename in the
* song directory.
2002-09-10 09:03:08 +00:00
*
2002-09-15 02:58:33 +00:00
* If it does, it's relative to the top directory of the tree it was loaded
* from, eg ".\music\stuff\song.ogg". Most of the time, that's the SM tree,
* and that's the PWD, so just return it directly. If the file was originally
* loaded from the DWIPath, it's relative to that, so prepend it.
2002-09-10 09:03:08 +00:00
*
2002-09-15 02:58:33 +00:00
* Some DWI's do have relative paths that don't start with ".\".
2002-09-10 09:03:08 +00:00
*/
2002-09-15 02:58:33 +00:00
/* We only follow this for song files and cdtitles; it's for compatibility
* with DWI. We prefer paths relative to the song directory; only support
* that for all other paths. */
/* Note: Prepending the dwipath is ugly. The first impression might be to
* add a Song::TopFilePath, but don't do that--we have too much stuff in there
* already. We don't really need it; this is the only place it'd be used. What
* we *should* be doing is prepending this path when we first load the song.
* However, dwipaths are usually like "c:\games\dwi", and we can't store colons
* in SM's; they'll get interpreted as delimiters. So:
* XXX: Add some kind of escape character to SM's.
*
* -glenn */
2002-09-10 09:03:08 +00:00
2002-09-10 08:39:58 +00:00
CString Song : : GetMusicPath ( ) const
{
2002-09-15 02:58:33 +00:00
/* If there's no path in the music file, the file is in the same directory
* as the song. (This is the preferred configuration.) */
if ( m_sMusicFile . Find ( ' \\ ' ) = = - 1 )
return m_sSongDir + m_sMusicFile ;
/* The file has a path. If it was loaded from the m_DWIPath, it's relative
* to that. */
2002-09-10 16:31:35 +00:00
if ( PREFSMAN - > m_DWIPath ! = " " & & m_sSongDir . Left ( PREFSMAN - > m_DWIPath . GetLength ( ) ) = = PREFSMAN - > m_DWIPath )
2002-09-10 09:03:08 +00:00
return PREFSMAN - > m_DWIPath + " \\ " + m_sMusicFile ;
2002-09-15 02:58:33 +00:00
/* Otherwise, it's relative to the top of the SM directory (the CWD), so
* return it directly. */
return m_sMusicFile ;
2002-09-10 08:39:58 +00:00
}
CString Song : : GetBannerPath ( ) const
{
return m_sSongDir + m_sBannerFile ;
}
CString Song : : GetCDTitlePath ( ) const
{
2002-09-15 02:58:33 +00:00
if ( m_sCDTitleFile . Find ( ' \\ ' ) = = - 1 )
return m_sSongDir + m_sCDTitleFile ;
2002-09-10 20:55:06 +00:00
if ( PREFSMAN - > m_DWIPath ! = " " & & m_sSongDir . Left ( PREFSMAN - > m_DWIPath . GetLength ( ) ) = = PREFSMAN - > m_DWIPath )
2002-09-10 09:03:08 +00:00
return PREFSMAN - > m_DWIPath + " \\ " + m_sCDTitleFile ;
2002-09-15 02:58:33 +00:00
return m_sCDTitleFile ;
2002-09-10 08:39:58 +00:00
}
2002-09-15 02:58:33 +00:00
2002-10-06 20:17:38 +00:00
CString Song : : GetBackgroundPath ( ) const
2002-09-10 08:39:58 +00:00
{
2002-10-06 20:17:38 +00:00
return m_sSongDir + m_sBackgroundFile ;
2002-09-10 08:39:58 +00:00
}
2002-10-24 07:56:20 +00:00
2003-01-09 09:01:20 +00:00
CString Song : : GetSortTitle ( ) const
{
CString Title = m_sMainTitleTranslit . empty ( ) ?
m_sMainTitle : m_sMainTitleTranslit ;
CString SubTitle = m_sSubTitleTranslit . empty ( ) ?
m_sSubTitle : m_sSubTitleTranslit ;
if ( ! SubTitle . empty ( ) )
{
Title + = " " + SubTitle ;
}
return Title ;
}
2002-10-24 07:56:20 +00:00
/* Get the first/last beat of any currently active note pattern. If two
* players are active, they often have the same start beat, but they don't
* have to.
*
* This is currently slow (notedata can't cache the return, and getnotedata
* is slow). */
#if 0 /* XXX not finished/tested/used yet -glenn */
float Song::GetFirstBeat() const
{
float first = MAX_BEATS;
for( int pn = 0; pn < NUM_PLAYERS; ++pn) {
if(!GAMESTATE->IsPlayerEnabled(pn)) continue;
NoteData tempNoteData;
GAMESTATE->m_pCurNotes[pn]->GetNoteData( &tempNoteData );
first = min(first, tempNoteData.GetFirstBeat());
}
return first;
}
float Song::GetLastBeat() const
{
float last = MAX_BEATS;
for( int pn = 0; pn < NUM_PLAYERS; ++pn) {
if(!GAMESTATE->IsPlayerEnabled(pn)) continue;
NoteData tempNoteData;
GAMESTATE->m_pCurNotes[pn]->GetNoteData( &tempNoteData );
last = max(last, tempNoteData.GetLastBeat());
}
return last;
}
#endif