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
|
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-07-27 19:29:51 +00:00
|
|
|
#include "RageSoundStream.h"
|
|
|
|
|
#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"
|
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"
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-10-05 20:03:14 +00:00
|
|
|
const int FILE_CACHE_VERSION = 97; // 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
|
|
|
|
|
|
|
|
int CompareBPMSegments(const void *arg1, const void *arg2)
|
|
|
|
|
{
|
|
|
|
|
// arg1 and arg2 are of type Step**
|
2002-09-12 08:51:24 +00:00
|
|
|
const BPMSegment* seg1 = (const BPMSegment*)arg1;
|
|
|
|
|
const BPMSegment* seg2 = (const BPMSegment*)arg2;
|
2001-12-19 01:50:57 +00:00
|
|
|
|
|
|
|
|
float score1 = seg1->m_fStartBeat;
|
|
|
|
|
float score2 = seg2->m_fStartBeat;
|
|
|
|
|
|
|
|
|
|
if( score1 == score2 )
|
|
|
|
|
return 0;
|
|
|
|
|
else if( score1 < score2 )
|
|
|
|
|
return -1;
|
|
|
|
|
else
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SortBPMSegmentsArray( CArray<BPMSegment,BPMSegment&> &arrayBPMSegments )
|
|
|
|
|
{
|
|
|
|
|
qsort( arrayBPMSegments.GetData(), arrayBPMSegments.GetSize(), sizeof(BPMSegment), CompareBPMSegments );
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
int CompareStopSegments(const void *arg1, const void *arg2)
|
2001-12-28 10:15:59 +00:00
|
|
|
{
|
|
|
|
|
// arg1 and arg2 are of type Step**
|
2002-09-12 08:51:24 +00:00
|
|
|
const StopSegment* seg1 = (const StopSegment*)arg1;
|
|
|
|
|
const StopSegment* seg2 = (const StopSegment*)arg2;
|
2001-12-28 10:15:59 +00:00
|
|
|
|
|
|
|
|
float score1 = seg1->m_fStartBeat;
|
|
|
|
|
float score2 = seg2->m_fStartBeat;
|
|
|
|
|
|
|
|
|
|
if( score1 == score2 )
|
|
|
|
|
return 0;
|
|
|
|
|
else if( score1 < score2 )
|
|
|
|
|
return -1;
|
|
|
|
|
else
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
void SortStopSegmentsArray( CArray<StopSegment,StopSegment&> &arrayStopSegments )
|
2001-12-28 10:15:59 +00:00
|
|
|
{
|
2002-07-11 19:02:26 +00:00
|
|
|
qsort( arrayStopSegments.GetData(), arrayStopSegments.GetSize(), sizeof(StopSegment), CompareStopSegments );
|
|
|
|
|
}
|
|
|
|
|
|
2002-08-23 20:18:29 +00:00
|
|
|
int CompareBackgroundChanges(const void *arg1, const void *arg2)
|
2002-07-11 19:02:26 +00:00
|
|
|
{
|
|
|
|
|
// arg1 and arg2 are of type Step**
|
2002-08-23 20:18:29 +00:00
|
|
|
const BackgroundChange* seg1 = (const BackgroundChange*)arg1;
|
|
|
|
|
const BackgroundChange* seg2 = (const BackgroundChange*)arg2;
|
2002-07-11 19:02:26 +00:00
|
|
|
|
|
|
|
|
float score1 = seg1->m_fStartBeat;
|
|
|
|
|
float score2 = seg2->m_fStartBeat;
|
|
|
|
|
|
|
|
|
|
if( score1 == score2 )
|
|
|
|
|
return 0;
|
|
|
|
|
else if( score1 < score2 )
|
|
|
|
|
return -1;
|
|
|
|
|
else
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2002-08-23 20:18:29 +00:00
|
|
|
void SortBackgroundChangesArray( CArray<BackgroundChange,BackgroundChange&> &arrayBackgroundChanges )
|
2002-07-11 19:02:26 +00:00
|
|
|
{
|
2002-08-23 20:18:29 +00:00
|
|
|
qsort( arrayBackgroundChanges.GetData(), arrayBackgroundChanges.GetSize(), sizeof(BackgroundChange), 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-07-11 19:02:26 +00:00
|
|
|
for( int i=0; i<m_apNotes.GetSize(); i++ )
|
|
|
|
|
SAFE_DELETE( m_apNotes[i] );
|
2002-06-14 22:25:22 +00:00
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
m_apNotes.RemoveAll();
|
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 )
|
|
|
|
|
{
|
|
|
|
|
m_BPMSegments.Add( seg );
|
|
|
|
|
SortBPMSegmentsArray( m_BPMSegments );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
void Song::AddStopSegment( StopSegment seg )
|
|
|
|
|
{
|
|
|
|
|
m_StopSegments.Add( seg );
|
|
|
|
|
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-08-23 20:18:29 +00:00
|
|
|
m_BackgroundChanges.Add( seg );
|
|
|
|
|
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
|
|
|
|
|
|
|
|
for( int i=0; i<m_BPMSegments.GetSize(); i++ ) // foreach BPMSegment
|
|
|
|
|
{
|
2001-12-19 01:50:57 +00:00
|
|
|
float fStartBeatThisSegment = m_BPMSegments[i].m_fStartBeat;
|
|
|
|
|
bool bIsLastBPMSegment = i==m_BPMSegments.GetSize()-1;
|
|
|
|
|
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-08-20 07:20:59 +00:00
|
|
|
int j;
|
|
|
|
|
for( j=0; j<m_StopSegments.GetSize(); 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;
|
|
|
|
|
|
|
|
|
|
for( j=0; j<m_StopSegments.GetSize(); 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
|
|
|
{
|
|
|
|
|
float fElapsedTimeBestGuess = 100; // 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
|
|
|
{
|
|
|
|
|
LOG->Warn( "Couldn't find any SM, DWI, BMS, or KSF files in '%s'. This is not a valid song directory.", sDir );
|
|
|
|
|
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-08-25 19:00:12 +00:00
|
|
|
AddAutoGenNotes();
|
|
|
|
|
|
2002-05-19 01:59:48 +00:00
|
|
|
TidyUpData();
|
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 )
|
|
|
|
|
{
|
|
|
|
|
LOG->Trace( "Song::LoadFromSongDir(%s)", sDir );
|
|
|
|
|
|
|
|
|
|
// 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 );
|
|
|
|
|
m_sGroupName = sDirectoryParts[sDirectoryParts.GetSize()-3]; // second from last item
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// 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()))
|
|
|
|
|
{
|
|
|
|
|
LOG->Trace( "Loading '%s' from cache file '%s'.", m_sSongDir, GetCacheFilePath() );
|
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,
|
|
|
|
|
* but instead points to the place we should write changed files to.
|
|
|
|
|
* If we have an SM file in the directory, this should aways point to it. */
|
|
|
|
|
CStringArray asFileNames;
|
|
|
|
|
GetDirListing( m_sSongDir+"*.sm", asFileNames );
|
|
|
|
|
if( asFileNames.GetSize() > 0 )
|
|
|
|
|
m_sSongFileName = m_sSongDir + asFileNames[0];
|
|
|
|
|
else
|
|
|
|
|
m_sSongFileName = m_sSongDir + GetFullTitle() + ".sm";
|
|
|
|
|
}
|
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-04-28 20:42:32 +00:00
|
|
|
m_sMainTitle.TrimRight();
|
|
|
|
|
if( m_sMainTitle == "" ) m_sMainTitle = "Untitled song";
|
|
|
|
|
m_sSubTitle.TrimRight();
|
|
|
|
|
if( m_sArtist == "" ) m_sArtist = "Unknown artist";
|
2001-11-27 22:47:30 +00:00
|
|
|
if( m_BPMSegments.GetSize() == 0 )
|
2002-09-09 05:22:02 +00:00
|
|
|
throw RageException( "No #BPM specified in '%s.'", m_sSongDir+m_sSongFileName );
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
if( !HasMusic() )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2001-11-25 04:31:44 +00:00
|
|
|
CStringArray arrayPossibleMusic;
|
|
|
|
|
GetDirListing( m_sSongDir + CString("*.mp3"), arrayPossibleMusic );
|
2001-12-19 14:56:22 +00:00
|
|
|
GetDirListing( m_sSongDir + CString("*.ogg"), arrayPossibleMusic );
|
2001-11-25 04:31:44 +00:00
|
|
|
GetDirListing( m_sSongDir + CString("*.wav"), arrayPossibleMusic );
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
if( arrayPossibleMusic.GetSize() > 0 ) // we found a match
|
2002-06-29 11:59:09 +00:00
|
|
|
m_sMusicFile = arrayPossibleMusic[0];
|
2002-09-16 00:56:30 +00:00
|
|
|
// Chris: Don't throw on missing music
|
|
|
|
|
// else
|
|
|
|
|
// throw RageException( "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 );
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2002-09-16 00:56:30 +00:00
|
|
|
if( HasMusic() )
|
|
|
|
|
{
|
|
|
|
|
RageSoundStream sound;
|
|
|
|
|
sound.Load( GetMusicPath() );
|
|
|
|
|
m_fMusicLengthSeconds = sound.GetLengthSeconds();
|
|
|
|
|
}
|
|
|
|
|
else // ! HasMusic()
|
|
|
|
|
{
|
|
|
|
|
m_fMusicLengthSeconds = 100; // guess
|
|
|
|
|
}
|
2002-05-19 01:59:48 +00:00
|
|
|
|
2002-07-11 19:02:26 +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 );
|
|
|
|
|
|
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-07-11 19:02:26 +00:00
|
|
|
if( arrayPossibleBanners.GetSize() > 0 )
|
|
|
|
|
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-07-11 19:02:26 +00:00
|
|
|
if( arrayPossibleBGs.GetSize() > 0 )
|
|
|
|
|
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-07-11 19:02:26 +00:00
|
|
|
if( arrayPossibleCDTitles.GetSize() > 0 )
|
|
|
|
|
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-07-11 19:02:26 +00:00
|
|
|
|
|
|
|
|
for( int i=0; i<arrayImages.GetSize(); 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-07-11 19:02:26 +00:00
|
|
|
D3DXIMAGE_INFO ddii;
|
|
|
|
|
if( !FAILED( D3DXGetImageInfoFromFile( m_sSongDir + arrayImages[i], &ddii ) ) )
|
2001-12-19 14:56:22 +00:00
|
|
|
{
|
2002-07-11 19:02:26 +00:00
|
|
|
if( !HasBackground() && ddii.Width >= 320 && ddii.Height >= 240 )
|
2001-12-19 14:56:22 +00:00
|
|
|
{
|
2002-07-11 19:02:26 +00:00
|
|
|
m_sBackgroundFile = arrayImages[i];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2002-07-23 01:41:40 +00:00
|
|
|
if( !HasBanner() && 100 < ddii.Width && ddii.Width < 320 && 50 < ddii.Height && ddii.Height < 240 )
|
2002-07-11 19:02:26 +00:00
|
|
|
{
|
|
|
|
|
m_sBannerFile = arrayImages[i];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2002-07-23 01:41:40 +00:00
|
|
|
if( !HasCDTitle() && ddii.Width <= 100 && ddii.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-07-11 19:02:26 +00:00
|
|
|
|
|
|
|
|
if( !HasMovieBackground() )
|
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 );
|
|
|
|
|
if( arrayPossibleMovies.GetSize() > 0 )
|
|
|
|
|
m_sMovieBackgroundFile = arrayPossibleMovies[0];
|
2001-11-30 09:38:35 +00:00
|
|
|
}
|
2002-05-19 01:59:48 +00:00
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
//
|
|
|
|
|
// calculate radar values and first/last beat
|
|
|
|
|
//
|
|
|
|
|
for( i=0; i<m_apNotes.GetSize(); 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-07-03 03:13:13 +00:00
|
|
|
pNotes->m_fRadarValues[r] = tempNoteData.GetRadarValue( (RadarCategory)r, m_fMusicLengthSeconds );
|
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
|
|
|
}
|
2001-11-30 09:38:35 +00:00
|
|
|
}
|
|
|
|
|
|
2002-09-12 07:13:25 +00:00
|
|
|
/* Get Notes that match a given style and player. */
|
|
|
|
|
void Song::GetNotesThatMatch( const StyleDef *s, int p, CArray<Notes*, Notes*>& arrayAddTo ) const
|
2002-10-05 20:03:14 +00:00
|
|
|
{
|
|
|
|
|
GetNotesThatMatch( s->m_NotesTypes[p], arrayAddTo );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Song::GetNotesThatMatch( NotesType nt, CArray<Notes*, Notes*>& arrayAddTo ) const
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
2002-07-11 19:02:26 +00:00
|
|
|
for( int i=0; i<m_apNotes.GetSize(); 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-07-11 19:02:26 +00:00
|
|
|
arrayAddTo.Add( 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
|
|
|
|
|
{
|
|
|
|
|
for( int pn = 0; pn < NUM_PLAYERS; ++pn )
|
|
|
|
|
{
|
|
|
|
|
if(st->m_NotesTypes[pn] == NOTES_TYPE_INVALID) continue; /* unused */
|
2002-09-12 07:13:25 +00:00
|
|
|
if(!SongHasNotesType(st->m_NotesTypes[pn]))
|
2002-09-06 21:34:17 +00:00
|
|
|
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
|
|
|
{
|
|
|
|
|
for( int i=0; i < m_apNotes.GetSize(); i++ ) // foreach Notes
|
|
|
|
|
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
|
|
|
{
|
|
|
|
|
for( int i=0; i < m_apNotes.GetSize(); i++ ) // foreach Notes
|
2002-09-29 05:06:18 +00:00
|
|
|
if( m_apNotes[i]->m_NotesType == nt && m_apNotes[i]->m_Difficulty == 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()" );
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// rename all old files to avoid confusion
|
|
|
|
|
//
|
|
|
|
|
CStringArray arrayOldFileNames;
|
|
|
|
|
GetDirListing( m_sSongDir + "*.bms", arrayOldFileNames );
|
|
|
|
|
GetDirListing( m_sSongDir + "*.dwi", arrayOldFileNames );
|
|
|
|
|
GetDirListing( m_sSongDir + "*.ksf", arrayOldFileNames );
|
|
|
|
|
|
|
|
|
|
for( int i=0; i<arrayOldFileNames.GetSize(); 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-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-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Song::SaveToSMDir('%s')", sPath );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-05-19 01:59:48 +00:00
|
|
|
int i;
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-06-30 23:19:33 +00:00
|
|
|
FILE* fp = fopen( sPath, "w" );
|
|
|
|
|
if( fp == NULL )
|
|
|
|
|
throw RageException( "Error opening song file '%s' for writing.", sPath );
|
|
|
|
|
|
|
|
|
|
fprintf( fp, "#TITLE:%s;\n", m_sMainTitle );
|
|
|
|
|
fprintf( fp, "#SUBTITLE:%s;\n", m_sSubTitle );
|
|
|
|
|
fprintf( fp, "#ARTIST:%s;\n", m_sArtist );
|
2002-09-26 06:05:32 +00:00
|
|
|
fprintf( fp, "#TITLETRANSLIT:%s;\n", m_sMainTitleTranslit );
|
|
|
|
|
fprintf( fp, "#SUBTITLETRANSLIT:%s;\n", m_sSubTitleTranslit );
|
|
|
|
|
fprintf( fp, "#ARTISTTRANSLIT:%s;\n", m_sArtistTranslit );
|
2002-06-30 23:19:33 +00:00
|
|
|
fprintf( fp, "#CREDIT:%s;\n", m_sCredit );
|
|
|
|
|
fprintf( fp, "#BANNER:%s;\n", m_sBannerFile );
|
|
|
|
|
fprintf( fp, "#BACKGROUND:%s;\n", m_sBackgroundFile );
|
|
|
|
|
fprintf( fp, "#CDTITLE:%s;\n", m_sCDTitleFile );
|
2002-07-11 19:02:26 +00:00
|
|
|
fprintf( fp, "#MOVIEBACKGROUND:%s;\n", m_sMovieBackgroundFile );
|
2002-06-30 23:19:33 +00:00
|
|
|
fprintf( fp, "#MUSIC:%s;\n", m_sMusicFile );
|
|
|
|
|
fprintf( fp, "#MUSICBYTES:%u;\n", m_iMusicBytes );
|
2002-07-03 21:27:26 +00:00
|
|
|
fprintf( fp, "#MUSICLENGTH:%.2f;\n", m_fMusicLengthSeconds );
|
2002-07-11 19:02:26 +00:00
|
|
|
fprintf( fp, "#FIRSTBEAT:%.2f;\n", m_fFirstBeat );
|
|
|
|
|
fprintf( fp, "#LASTBEAT:%.2f;\n", m_fLastBeat );
|
2002-07-03 21:27:26 +00:00
|
|
|
fprintf( fp, "#OFFSET:%.2f;\n", m_fBeat0OffsetInSeconds );
|
2002-06-30 23:19:33 +00:00
|
|
|
fprintf( fp, "#SAMPLESTART:%.2f;\n", m_fMusicSampleStartSeconds );
|
|
|
|
|
fprintf( fp, "#SAMPLELENGTH:%.2f;\n", m_fMusicSampleLengthSeconds );
|
2002-08-30 04:28:12 +00:00
|
|
|
fprintf( fp, "#SELECTABLE:" );
|
|
|
|
|
switch(m_SelectionDisplay) {
|
|
|
|
|
default: ASSERT(0); /* fallthrough */
|
|
|
|
|
case SHOW_ALWAYS:
|
|
|
|
|
fprintf( fp, "YES" ); break;
|
|
|
|
|
case SHOW_NEVER:
|
|
|
|
|
fprintf( fp, "NO" ); break;
|
|
|
|
|
case SHOW_ROULETTE:
|
|
|
|
|
fprintf( fp, "ROULETTE" ); break;
|
|
|
|
|
}
|
|
|
|
|
fprintf( fp, ";\n" );
|
2002-06-30 23:19:33 +00:00
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
fprintf( fp, "#BPMS:" );
|
|
|
|
|
for( i=0; i<m_BPMSegments.GetSize(); i++ )
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
2002-07-11 19:02:26 +00:00
|
|
|
BPMSegment &bs = m_BPMSegments[i];
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
fprintf( fp, "%.2f=%.2f", bs.m_fStartBeat, bs.m_fBPM );
|
|
|
|
|
if( i != m_BPMSegments.GetSize()-1 )
|
|
|
|
|
fprintf( fp, "," );
|
|
|
|
|
}
|
|
|
|
|
fprintf( fp, ";\n" );
|
|
|
|
|
|
|
|
|
|
fprintf( fp, "#STOPS:" );
|
|
|
|
|
for( i=0; i<m_StopSegments.GetSize(); i++ )
|
|
|
|
|
{
|
|
|
|
|
StopSegment &fs = m_StopSegments[i];
|
|
|
|
|
|
|
|
|
|
fprintf( fp, "%.2f=%.2f", fs.m_fStartBeat, fs.m_fStopSeconds );
|
|
|
|
|
if( i != m_StopSegments.GetSize()-1 )
|
2002-06-30 23:19:33 +00:00
|
|
|
fprintf( fp, "," );
|
2002-04-16 17:31:00 +00:00
|
|
|
}
|
2002-06-30 23:19:33 +00:00
|
|
|
fprintf( fp, ";\n" );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-08-23 20:18:29 +00:00
|
|
|
fprintf( fp, "#BGCHANGES:" );
|
|
|
|
|
for( i=0; i<m_BackgroundChanges.GetSize(); i++ )
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
2002-08-23 20:18:29 +00:00
|
|
|
BackgroundChange &seg = m_BackgroundChanges[i];
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-08-23 20:18:29 +00:00
|
|
|
fprintf( fp, "%.2f=%s", seg.m_fStartBeat, seg.m_sBGName );
|
|
|
|
|
if( i != m_BackgroundChanges.GetSize()-1 )
|
2002-06-30 23:19:33 +00:00
|
|
|
fprintf( fp, "," );
|
2002-04-16 17:31:00 +00:00
|
|
|
}
|
2002-06-30 23:19:33 +00:00
|
|
|
fprintf( fp, ";\n" );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
|
|
|
|
//
|
2002-06-24 22:04:31 +00:00
|
|
|
// Save all Notes for this file
|
2002-04-16 17:31:00 +00:00
|
|
|
//
|
2002-07-11 19:02:26 +00:00
|
|
|
for( i=0; i<m_apNotes.GetSize(); i++ )
|
2002-08-25 19:00:12 +00:00
|
|
|
{
|
|
|
|
|
Notes* pNotes = m_apNotes[i];
|
|
|
|
|
if( bSavingCache || pNotes->m_sDescription.Find("(autogen)") == -1 ) // If notes aren't autogen
|
|
|
|
|
m_apNotes[i]->WriteSMNotesTag( fp );
|
|
|
|
|
}
|
2002-02-11 04:46:31 +00:00
|
|
|
|
2002-06-30 23:19:33 +00:00
|
|
|
fclose( fp );
|
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-05 14:47:03 +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-05 14:47:03 +00:00
|
|
|
if( !SongHasNotesType(ntMissing) ) // missing Notes of this type
|
2002-08-25 19:00:12 +00:00
|
|
|
{
|
2002-10-05 14:47:03 +00:00
|
|
|
int iNumTracksOfMissing = GAMEMAN->NotesTypeToNumTracks(ntMissing);
|
|
|
|
|
|
|
|
|
|
// look for closest match
|
|
|
|
|
NotesType ntBestMatch = (NotesType)-1;
|
|
|
|
|
int iBestTrackDifference = 10000; // inf
|
|
|
|
|
|
|
|
|
|
for( NotesType nt=(NotesType)0; nt<NUM_NOTES_TYPES; nt=(NotesType)(nt+1) )
|
|
|
|
|
{
|
|
|
|
|
int iNumTracks = GAMEMAN->NotesTypeToNumTracks(nt);
|
|
|
|
|
int iTrackDifference = abs(iNumTracks-iNumTracksOfMissing);
|
2002-10-05 20:03:14 +00:00
|
|
|
CArray<Notes*,Notes*> apNotes;
|
|
|
|
|
this->GetNotesThatMatch( nt, apNotes );
|
|
|
|
|
if( iTrackDifference<iBestTrackDifference && apNotes.GetSize() > 0 && apNotes[0]->m_sDescription.Find("autogen")==-1 )
|
2002-10-05 14:47:03 +00:00
|
|
|
{
|
|
|
|
|
ntBestMatch = nt;
|
|
|
|
|
iBestTrackDifference = iTrackDifference;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( ntBestMatch != -1 )
|
2002-08-25 19:00:12 +00:00
|
|
|
{
|
2002-10-05 14:47:03 +00:00
|
|
|
for( int j=0; j<m_apNotes.GetSize(); j++ )
|
|
|
|
|
{
|
|
|
|
|
Notes* pOriginalNotes = m_apNotes[j];
|
|
|
|
|
if( pOriginalNotes->m_NotesType != ntBestMatch )
|
|
|
|
|
continue; // skip
|
|
|
|
|
|
|
|
|
|
Notes* pNewNotes = new Notes;
|
|
|
|
|
pNewNotes->m_Difficulty = pOriginalNotes->m_Difficulty;
|
|
|
|
|
pNewNotes->m_iMeter = pOriginalNotes->m_iMeter;
|
|
|
|
|
pNewNotes->m_sDescription = pOriginalNotes->m_sDescription + " (autogen)";
|
|
|
|
|
pNewNotes->m_NotesType = ntMissing;
|
|
|
|
|
|
|
|
|
|
NoteData originalNoteData, newNoteData;
|
|
|
|
|
pOriginalNotes->GetNoteData( &originalNoteData );
|
|
|
|
|
newNoteData.LoadTransformedSlidingWindow( &originalNoteData, iNumTracksOfMissing );
|
|
|
|
|
pNewNotes->SetNoteData( &newNoteData );
|
|
|
|
|
this->m_apNotes.Add( 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-06-14 22:25:22 +00:00
|
|
|
CArray<Notes*, Notes*> aNotes;
|
2002-09-06 08:56:28 +00:00
|
|
|
this->GetNotesThatMatch( st, p, aNotes );
|
2002-08-01 13:42:56 +00:00
|
|
|
SortNotesArrayByDifficulty( aNotes );
|
2002-02-11 04:46:31 +00:00
|
|
|
|
2002-06-14 22:25:22 +00:00
|
|
|
for( int i=0; i<aNotes.GetSize(); i++ )
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
2002-09-06 10:15:00 +00:00
|
|
|
const Notes* pNotes = aNotes[i];
|
2002-09-29 05:06:18 +00:00
|
|
|
if( pNotes->m_Difficulty == dc )
|
2002-05-19 01:59:48 +00:00
|
|
|
return pNotes->m_TopGrade;
|
2002-04-16 17:31:00 +00:00
|
|
|
}
|
|
|
|
|
return GRADE_NO_DATA;
|
|
|
|
|
}
|
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
|
|
|
{
|
|
|
|
|
for( int i=0; i<m_apNotes.GetSize(); i++ )
|
|
|
|
|
{
|
|
|
|
|
Notes* pNotes = m_apNotes[i];
|
|
|
|
|
if( pNotes->m_NotesType != nt )
|
|
|
|
|
continue;
|
|
|
|
|
if( pNotes->m_iMeter <= 2 )
|
|
|
|
|
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-02 01:32:43 +00:00
|
|
|
int CompareSongPointersByTitle(const void *arg1, const void *arg2)
|
|
|
|
|
{
|
|
|
|
|
const Song* pSong1 = *(const Song**)arg1;
|
|
|
|
|
const Song* pSong2 = *(const Song**)arg2;
|
|
|
|
|
|
|
|
|
|
//Prefer transliterations to full titles
|
|
|
|
|
CString sTitle1 = pSong1->GetSortTitle();
|
|
|
|
|
CString sTitle2 = pSong2->GetSortTitle();
|
|
|
|
|
|
|
|
|
|
int ret = sTitle1.CompareNoCase(sTitle2);
|
|
|
|
|
if(ret != 0) return ret;
|
|
|
|
|
|
|
|
|
|
/* The titles are the same. Ensure we get a consistent ordering
|
|
|
|
|
* by comparing the unique SongFilePaths. */
|
|
|
|
|
return pSong1->GetSongFilePath().CompareNoCase(pSong2->GetSongFilePath());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SortSongPointerArrayByTitle( CArray<Song*, Song*> &arraySongPointers )
|
|
|
|
|
{
|
|
|
|
|
qsort( arraySongPointers.GetData(), arraySongPointers.GetSize(), sizeof(Song*), CompareSongPointersByTitle );
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-29 23:29:01 +00:00
|
|
|
int CompareSongPointersByDifficulty(const void *arg1, const void *arg2)
|
|
|
|
|
{
|
|
|
|
|
const Song* pSong1 = *(const Song**)arg1;
|
|
|
|
|
const Song* pSong2 = *(const Song**)arg2;
|
|
|
|
|
|
|
|
|
|
CArray<Notes*,Notes*> aNotes1;
|
|
|
|
|
CArray<Notes*,Notes*> aNotes2;
|
|
|
|
|
|
|
|
|
|
pSong1->GetNotesThatMatch( GAMESTATE->GetCurrentStyleDef(), PLAYER_1, aNotes1 );
|
|
|
|
|
pSong2->GetNotesThatMatch( GAMESTATE->GetCurrentStyleDef(), PLAYER_1, aNotes2 );
|
|
|
|
|
|
|
|
|
|
int iEasiestMeter1 = 1000; // infinity
|
|
|
|
|
int iEasiestMeter2 = 1000; // infinity
|
|
|
|
|
|
2002-09-30 06:56:40 +00:00
|
|
|
int i;
|
|
|
|
|
for( i=0; i<aNotes1.GetSize(); i++ )
|
2002-09-29 23:29:01 +00:00
|
|
|
iEasiestMeter1 = min( iEasiestMeter1, aNotes1[i]->m_iMeter );
|
2002-09-30 06:56:40 +00:00
|
|
|
for( i=0; i<aNotes2.GetSize(); i++ )
|
2002-09-29 23:29:01 +00:00
|
|
|
iEasiestMeter2 = min( iEasiestMeter2, aNotes2[i]->m_iMeter );
|
|
|
|
|
|
|
|
|
|
if( iEasiestMeter1 < iEasiestMeter2 )
|
|
|
|
|
return -1;
|
|
|
|
|
else if( iEasiestMeter1 == iEasiestMeter2 )
|
2002-10-02 01:32:43 +00:00
|
|
|
return CompareSongPointersByTitle( arg1, arg2 );
|
2002-09-29 23:29:01 +00:00
|
|
|
else
|
|
|
|
|
return +1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SortSongPointerArrayByDifficulty( CArray<Song*, Song*> &arraySongPointers )
|
|
|
|
|
{
|
|
|
|
|
qsort( arraySongPointers.GetData(), arraySongPointers.GetSize(), sizeof(Song*), CompareSongPointersByDifficulty );
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 10:01:32 +00:00
|
|
|
int CompareSongPointersByBPM(const void *arg1, const void *arg2)
|
|
|
|
|
{
|
2002-08-21 00:48:41 +00:00
|
|
|
const Song* pSong1 = *(const Song**)arg1;
|
|
|
|
|
const Song* pSong2 = *(const Song**)arg2;
|
2002-01-16 10:01:32 +00:00
|
|
|
|
|
|
|
|
float fMinBPM1, fMaxBPM1, fMinBPM2, fMaxBPM2;
|
|
|
|
|
pSong1->GetMinMaxBPM( fMinBPM1, fMaxBPM1 );
|
|
|
|
|
pSong2->GetMinMaxBPM( fMinBPM2, fMaxBPM2 );
|
|
|
|
|
|
|
|
|
|
CString sFilePath1 = pSong1->GetSongFilePath(); // this is unique among songs
|
|
|
|
|
CString sFilePath2 = pSong2->GetSongFilePath();
|
|
|
|
|
|
|
|
|
|
if( fMaxBPM1 < fMaxBPM2 )
|
|
|
|
|
return -1;
|
|
|
|
|
else if( fMaxBPM1 == fMaxBPM2 )
|
2002-03-30 20:00:13 +00:00
|
|
|
return CompareCStringsAsc( (void*)&sFilePath1, (void*)&sFilePath2 );
|
2002-01-16 10:01:32 +00:00
|
|
|
else
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-11 04:46:31 +00:00
|
|
|
void SortSongPointerArrayByBPM( CArray<Song*, Song*> &arraySongPointers )
|
2002-01-16 10:01:32 +00:00
|
|
|
{
|
|
|
|
|
qsort( arraySongPointers.GetData(), arraySongPointers.GetSize(), sizeof(Song*), CompareSongPointersByBPM );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int CompareSongPointersByArtist(const void *arg1, const void *arg2)
|
|
|
|
|
{
|
2002-08-21 00:47:29 +00:00
|
|
|
const Song* pSong1 = *(const Song**)arg1;
|
|
|
|
|
const Song* pSong2 = *(const Song**)arg2;
|
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 )
|
|
|
|
|
return -1;
|
|
|
|
|
else if( sArtist1 == sArtist2 )
|
|
|
|
|
return CompareSongPointersByTitle( arg1, arg2 );
|
|
|
|
|
else
|
|
|
|
|
return 1;
|
2002-01-16 10:01:32 +00:00
|
|
|
}
|
|
|
|
|
|
2002-02-11 04:46:31 +00:00
|
|
|
void SortSongPointerArrayByArtist( CArray<Song*, Song*> &arraySongPointers )
|
2002-01-16 10:01:32 +00:00
|
|
|
{
|
|
|
|
|
qsort( arraySongPointers.GetData(), arraySongPointers.GetSize(), sizeof(Song*), CompareSongPointersByArtist );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CompareSongPointersByGroup(const void *arg1, const void *arg2)
|
|
|
|
|
{
|
2002-08-21 00:47:29 +00:00
|
|
|
const Song* pSong1 = *(const Song**)arg1;
|
|
|
|
|
const Song* pSong2 = *(const Song**)arg2;
|
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 )
|
|
|
|
|
return -1;
|
2002-08-22 21:59:47 +00:00
|
|
|
else if( sGroup1 > sGroup2 )
|
2002-02-24 01:43:11 +00:00
|
|
|
return 1;
|
2002-08-22 21:59:47 +00:00
|
|
|
|
2002-09-29 23:29:01 +00:00
|
|
|
/* Same group; compare by difficulty. */
|
|
|
|
|
return CompareSongPointersByDifficulty( arg1, arg2 );
|
2002-01-16 10:01:32 +00:00
|
|
|
}
|
|
|
|
|
|
2002-02-11 04:46:31 +00:00
|
|
|
void SortSongPointerArrayByGroup( CArray<Song*, Song*> &arraySongPointers )
|
2002-01-16 10:01:32 +00:00
|
|
|
{
|
|
|
|
|
qsort( arraySongPointers.GetData(), arraySongPointers.GetSize(), sizeof(Song*), CompareSongPointersByGroup );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CompareSongPointersByMostPlayed(const void *arg1, const void *arg2)
|
|
|
|
|
{
|
2002-08-21 00:47:29 +00:00
|
|
|
const Song* pSong1 = *(const Song**)arg1;
|
|
|
|
|
const Song* pSong2 = *(const Song**)arg2;
|
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-01-16 10:01:32 +00:00
|
|
|
return -1;
|
|
|
|
|
else if( iNumTimesPlayed1 == iNumTimesPlayed2 )
|
2002-02-24 01:43:11 +00:00
|
|
|
return CompareSongPointersByTitle( arg1, arg2 );
|
2002-01-16 10:01:32 +00:00
|
|
|
else
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-11 04:46:31 +00:00
|
|
|
void SortSongPointerArrayByMostPlayed( CArray<Song*, Song*> &arraySongPointers )
|
2002-01-16 10:01:32 +00:00
|
|
|
{
|
|
|
|
|
qsort( arraySongPointers.GetData(), arraySongPointers.GetSize(), sizeof(Song*), CompareSongPointersByMostPlayed );
|
|
|
|
|
}
|
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
|
|
|
|
|
|
|
|
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::HasMovieBackground() const {return m_sMovieBackgroundFile != ""&& IsAFile(GetMovieBackgroundPath()); };
|
|
|
|
|
bool Song::HasBGChanges() const {return m_BackgroundChanges.GetSize() > 0; };
|
|
|
|
|
|
|
|
|
|
int Song::GetNumTimesPlayed() const
|
|
|
|
|
{
|
|
|
|
|
int iTotalNumTimesPlayed = 0;
|
|
|
|
|
for( int i=0; i<m_apNotes.GetSize(); i++ )
|
|
|
|
|
{
|
|
|
|
|
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::GetBackgroundPath() const
|
|
|
|
|
{
|
|
|
|
|
return m_sSongDir+m_sBackgroundFile;
|
|
|
|
|
}
|
|
|
|
|
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-09-10 08:39:58 +00:00
|
|
|
CString Song::GetMovieBackgroundPath() const
|
|
|
|
|
{
|
|
|
|
|
return m_sSongDir+m_sMovieBackgroundFile;
|
|
|
|
|
}
|