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-06-14 22:25:22 +00:00
|
|
|
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-07-31 19:40:40 +00:00
|
|
|
const int FILE_CACHE_VERSION = 53; // increment this to force a cache reload (useful when the SM file format changes)
|
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**
|
|
|
|
|
BPMSegment* seg1 = (BPMSegment*)arg1;
|
|
|
|
|
BPMSegment* seg2 = (BPMSegment*)arg2;
|
|
|
|
|
|
|
|
|
|
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-07-11 19:02:26 +00:00
|
|
|
StopSegment* seg1 = (StopSegment*)arg1;
|
|
|
|
|
StopSegment* seg2 = (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 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CompareAnimationSegments(const void *arg1, const void *arg2)
|
|
|
|
|
{
|
|
|
|
|
// arg1 and arg2 are of type Step**
|
|
|
|
|
AnimationSegment* seg1 = (AnimationSegment*)arg1;
|
|
|
|
|
AnimationSegment* seg2 = (AnimationSegment*)arg2;
|
|
|
|
|
|
|
|
|
|
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 SortAnimationSegmentsArray( CArray<AnimationSegment,AnimationSegment&> &arrayAnimationSegments )
|
|
|
|
|
{
|
|
|
|
|
qsort( arrayAnimationSegments.GetData(), arrayAnimationSegments.GetSize(), sizeof(AnimationSegment), CompareAnimationSegments );
|
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;
|
|
|
|
|
m_fMusicSampleLengthSeconds = 16; // 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;
|
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 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Song::AddAnimationSegment( AnimationSegment seg )
|
2002-06-24 22:04:31 +00:00
|
|
|
{
|
2002-07-11 19:02:26 +00:00
|
|
|
m_AnimationSegments.Add( seg );
|
|
|
|
|
SortAnimationSegmentsArray( m_AnimationSegments );
|
2002-06-24 22:04:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-06-30 23:19:33 +00:00
|
|
|
void Song::GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut )
|
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
|
|
|
|
|
{
|
|
|
|
|
BPMSegment &this_seg = m_BPMSegments[i];
|
|
|
|
|
|
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-07-11 19:02:26 +00:00
|
|
|
for( int 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;
|
2001-12-28 10:15:59 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// this BPMSegment IS the current segment
|
|
|
|
|
|
|
|
|
|
float fBeatEstimate = fStartBeatThisSegment + fElapsedTime*fBPS;
|
|
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
for( int 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
|
|
|
if( m_StopSegments[j].m_fStartBeat <= fBeatEstimate )
|
2001-12-28 10:15:59 +00:00
|
|
|
{
|
2002-07-11 19:02:26 +00:00
|
|
|
fElapsedTime -= m_StopSegments[j].m_fStopSeconds;
|
2001-12-28 10:15:59 +00:00
|
|
|
// re-estimate
|
|
|
|
|
fBeatEstimate = fStartBeatThisSegment + fElapsedTime*fBPS;
|
2002-07-11 19:02:26 +00:00
|
|
|
if( fBeatEstimate < m_StopSegments[j].m_fStartBeat )
|
2001-12-28 10:15:59 +00:00
|
|
|
{
|
2002-07-11 19:02:26 +00:00
|
|
|
fBeatOut = m_StopSegments[j].m_fStartBeat;
|
2001-12-28 10:15:59 +00:00
|
|
|
fBPSOut = fBPS;
|
2002-06-30 23:19:33 +00:00
|
|
|
bFreezeOut = true;
|
2001-12-28 10:15:59 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fBeatOut = fBeatEstimate;
|
2001-12-19 01:50:57 +00:00
|
|
|
fBPSOut = fBPS;
|
2002-06-30 23:19:33 +00:00
|
|
|
bFreezeOut = false;
|
2001-12-19 01:50:57 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2001-12-19 01:50:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-05-19 01:59:48 +00:00
|
|
|
// This is a super hack, but it's only called from ScreenEdit, so it's OK :-)
|
2002-04-16 17:31:00 +00:00
|
|
|
// Writing an inverse function of GetBeatAndBPSFromElapsedTime() is impossible,
|
|
|
|
|
// so do a binary search to get close to the correct elapsed time.
|
|
|
|
|
float Song::GetElapsedTimeFromBeat( float fBeat )
|
|
|
|
|
{
|
|
|
|
|
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-07-11 19:02:26 +00:00
|
|
|
void Song::GetMainAndSubTitlesFromFullTitle( const CString sFullTitle, CString &sMainTitleOut, CString &sSubTitleOut )
|
2002-05-01 19:14:55 +00:00
|
|
|
{
|
2002-07-11 19:02:26 +00:00
|
|
|
const CString sLeftSeps[] = { " -", " ~", " (", " [" };
|
|
|
|
|
int iNumSeps = sizeof(sLeftSeps)/sizeof(CString);
|
|
|
|
|
for( int i=0; i<iNumSeps; i++ )
|
2002-05-01 19:14:55 +00:00
|
|
|
{
|
2002-06-24 22:04:31 +00:00
|
|
|
int iBeginIndex = sFullTitle.Find( sLeftSeps[i] );
|
2002-05-01 19:14:55 +00:00
|
|
|
if( iBeginIndex == -1 )
|
|
|
|
|
continue;
|
2002-07-11 19:02:26 +00:00
|
|
|
sMainTitleOut = sFullTitle.Left( iBeginIndex );
|
|
|
|
|
sSubTitleOut = sFullTitle.Mid( iBeginIndex+1, sFullTitle.GetLength()-iBeginIndex+1 );
|
2002-05-01 19:14:55 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
sMainTitleOut = sFullTitle;
|
|
|
|
|
sSubTitleOut = "";
|
|
|
|
|
};
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-05-19 01:59:48 +00:00
|
|
|
CString Song::GetCacheFilePath()
|
|
|
|
|
{
|
2002-06-30 23:19:33 +00:00
|
|
|
return ssprintf( "Cache\\%d", GetHashForString(m_sSongDir) );
|
2002-05-19 01:59:48 +00:00
|
|
|
}
|
|
|
|
|
|
2002-07-02 00:27:58 +00:00
|
|
|
CString Song::GetSongFilePath()
|
|
|
|
|
{
|
|
|
|
|
CStringArray asFileNames;
|
|
|
|
|
GetDirListing( m_sSongDir+"*.sm", asFileNames );
|
|
|
|
|
if( asFileNames.GetSize() > 0 )
|
|
|
|
|
return m_sSongDir + asFileNames[0];
|
|
|
|
|
else
|
|
|
|
|
return m_sSongDir + GetFullTitle() + ".sm";
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-03 10:52:42 +00:00
|
|
|
bool Song::LoadFromSongDir( CString sDir )
|
|
|
|
|
{
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Song::LoadFromSongDir(%s)", sDir );
|
2001-11-03 10:52:42 +00:00
|
|
|
|
|
|
|
|
// make sure there is a trailing '\\' at the end of sDir
|
|
|
|
|
if( sDir.Right(1) != "\\" )
|
|
|
|
|
sDir += "\\";
|
|
|
|
|
|
|
|
|
|
// save song dir
|
|
|
|
|
m_sSongDir = sDir;
|
|
|
|
|
|
2002-07-02 00:27:58 +00:00
|
|
|
// save group name
|
|
|
|
|
CStringArray sDirectoryParts;
|
2002-08-01 05:11:11 +00:00
|
|
|
split( m_sSongDir, "\\", sDirectoryParts, false );
|
|
|
|
|
m_sGroupName = sDirectoryParts[sDirectoryParts.GetSize()-3]; // second from last item
|
2002-07-02 00:27:58 +00:00
|
|
|
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-05-19 01:59:48 +00:00
|
|
|
//
|
|
|
|
|
// First look in the cache for this song (without loading NoteData)
|
|
|
|
|
//
|
2002-06-30 23:19:33 +00:00
|
|
|
IniFile ini;
|
|
|
|
|
ini.SetPath( "Cache\\index.cache" );
|
|
|
|
|
if( !ini.ReadFile() )
|
|
|
|
|
goto load_without_cache;
|
2002-05-19 01:59:48 +00:00
|
|
|
|
2002-06-30 23:19:33 +00:00
|
|
|
int iCacheVersion;
|
|
|
|
|
ini.GetValueI( "Cache", "CacheVersion", iCacheVersion );
|
|
|
|
|
if( iCacheVersion != FILE_CACHE_VERSION )
|
|
|
|
|
{
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Cache format is out of date. Deleting all cache files." );
|
2002-06-30 23:19:33 +00:00
|
|
|
CStringArray asCacheFileNames;
|
|
|
|
|
GetDirListing( "Cache\\*.*", asCacheFileNames );
|
|
|
|
|
for( int i=0; i<asCacheFileNames.GetSize(); i++ )
|
|
|
|
|
DeleteFile( "Cache\\" + asCacheFileNames[i] );
|
|
|
|
|
goto load_without_cache;
|
|
|
|
|
}
|
2002-05-19 01:59:48 +00:00
|
|
|
|
2002-06-30 23:19:33 +00:00
|
|
|
int iDirHash;
|
|
|
|
|
ini.GetValueI( "Cache", m_sSongDir, iDirHash );
|
|
|
|
|
if( GetHashForDirectory(m_sSongDir) == iDirHash ) // this cache is up to date
|
|
|
|
|
{
|
|
|
|
|
if( !DoesFileExist(GetCacheFilePath()) )
|
|
|
|
|
goto load_without_cache;
|
2002-07-23 01:41:40 +00:00
|
|
|
LoadFromSMFile( GetCacheFilePath() );
|
2002-07-02 00:27:58 +00:00
|
|
|
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Loading '%s' from cache file '%s'.", m_sSongDir, GetCacheFilePath() );
|
2002-06-30 23:19:33 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
load_without_cache:
|
2002-05-19 01:59:48 +00:00
|
|
|
//
|
|
|
|
|
// There was no entry in the cache for this song.
|
|
|
|
|
// Let's load it from a file, then write a cache entry.
|
|
|
|
|
//
|
2001-11-03 10:52:42 +00:00
|
|
|
CStringArray arrayBMSFileNames;
|
|
|
|
|
GetDirListing( sDir + CString("*.bms"), arrayBMSFileNames );
|
|
|
|
|
int iNumBMSFiles = arrayBMSFileNames.GetSize();
|
|
|
|
|
|
2002-07-31 19:40:40 +00:00
|
|
|
CStringArray arrayKSFFileNames;
|
|
|
|
|
GetDirListing( sDir + CString("*.ksf"), arrayKSFFileNames );
|
|
|
|
|
int iNumKSFFiles = arrayKSFFileNames.GetSize();
|
|
|
|
|
|
2001-12-19 01:50:57 +00:00
|
|
|
CStringArray arrayDWIFileNames;
|
|
|
|
|
GetDirListing( sDir + CString("*.dwi"), arrayDWIFileNames );
|
|
|
|
|
int iNumDWIFiles = arrayDWIFileNames.GetSize();
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-06-30 23:19:33 +00:00
|
|
|
CStringArray arraySMFileNames;
|
|
|
|
|
GetDirListing( sDir + CString("*.sm"), arraySMFileNames );
|
|
|
|
|
int iNumSMFiles = arraySMFileNames.GetSize();
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-06-30 23:19:33 +00:00
|
|
|
if( iNumSMFiles > 1 )
|
|
|
|
|
throw RageException( "There is more than one SM file in '%s'. There should be only one!", sDir );
|
2002-04-16 17:31:00 +00:00
|
|
|
else if( iNumDWIFiles > 1 )
|
2002-06-14 22:25:22 +00:00
|
|
|
throw RageException( "There is more than one DWI file in '%s'. There should be only one!", sDir );
|
2002-06-30 23:19:33 +00:00
|
|
|
else if( iNumSMFiles == 1 )
|
2002-07-03 03:13:13 +00:00
|
|
|
LoadFromSMFile( sDir + arraySMFileNames[0] ); // do load note data
|
2001-12-19 01:50:57 +00:00
|
|
|
else if( iNumDWIFiles == 1 )
|
2002-04-16 17:31:00 +00:00
|
|
|
LoadFromDWIFile( sDir + arrayDWIFileNames[0] );
|
2001-12-28 10:15:59 +00:00
|
|
|
else if( iNumBMSFiles > 0 )
|
2002-04-16 17:31:00 +00:00
|
|
|
LoadFromBMSDir( sDir );
|
2002-07-31 19:40:40 +00:00
|
|
|
else if( iNumKSFFiles > 0 )
|
|
|
|
|
LoadFromKSFDir( sDir );
|
2001-11-03 10:52:42 +00:00
|
|
|
else
|
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-05-19 01:59:48 +00:00
|
|
|
TidyUpData();
|
|
|
|
|
|
2002-07-03 03:13:13 +00:00
|
|
|
SaveToCacheFile(); // save a cahce file so we don't have to parse it all over again next time
|
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
|
|
|
|
|
|
|
|
|
2002-04-16 17:31:00 +00:00
|
|
|
bool Song::LoadFromBMSDir( CString sDir )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Song::LoadFromBMSDir(%s)", sDir );
|
2001-12-28 10:15:59 +00:00
|
|
|
|
|
|
|
|
CStringArray arrayBMSFileNames;
|
|
|
|
|
GetDirListing( sDir + CString("*.bms"), arrayBMSFileNames );
|
|
|
|
|
|
|
|
|
|
if( arrayBMSFileNames.GetSize() == 0 )
|
2002-06-27 17:49:10 +00:00
|
|
|
throw RageException( "Couldn't find any BMS files in '%s'", sDir );
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2002-05-19 01:59:48 +00:00
|
|
|
// load the Notes from the rest of the BMS files
|
2001-12-28 10:15:59 +00:00
|
|
|
for( int i=0; i<arrayBMSFileNames.GetSize(); i++ )
|
|
|
|
|
{
|
2002-06-14 22:25:22 +00:00
|
|
|
Notes* pNewNotes = new Notes;
|
|
|
|
|
pNewNotes->LoadFromBMSFile( m_sSongDir + arrayBMSFileNames[i] );
|
2002-07-11 19:02:26 +00:00
|
|
|
m_apNotes.Add( pNewNotes );
|
2001-12-28 10:15:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-07-02 00:27:58 +00:00
|
|
|
CString sPath = m_sSongDir + arrayBMSFileNames[0];
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2002-01-24 08:01:24 +00:00
|
|
|
CStdioFile file;
|
2002-07-02 00:27:58 +00:00
|
|
|
if( !file.Open( sPath, CFile::modeRead|CFile::shareDenyNone ) )
|
2002-01-24 08:01:24 +00:00
|
|
|
{
|
2002-07-02 00:27:58 +00:00
|
|
|
throw RageException( "Failed to open %s.", sPath );
|
2002-01-24 08:01:24 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString line;
|
|
|
|
|
while( file.ReadString(line) ) // foreach line
|
|
|
|
|
{
|
|
|
|
|
CString value_name; // fill these in
|
|
|
|
|
CString value_data;
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2002-01-24 08:01:24 +00:00
|
|
|
// BMS value names can be separated by a space or a colon.
|
|
|
|
|
int iIndexOfFirstColon = line.Find( ":" );
|
|
|
|
|
int iIndexOfFirstSpace = line.Find( " " );
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-01-24 08:01:24 +00:00
|
|
|
if( iIndexOfFirstColon == -1 )
|
|
|
|
|
iIndexOfFirstColon = 10000;
|
|
|
|
|
if( iIndexOfFirstSpace == -1 )
|
|
|
|
|
iIndexOfFirstSpace = 10000;
|
|
|
|
|
|
|
|
|
|
int iIndexOfSeparator = min( iIndexOfFirstSpace, iIndexOfFirstColon );
|
|
|
|
|
|
|
|
|
|
if( iIndexOfSeparator != 10000 )
|
|
|
|
|
{
|
|
|
|
|
value_name = line.Mid( 0, iIndexOfSeparator );
|
|
|
|
|
value_data = line; // the rest
|
|
|
|
|
value_data.Delete(0,iIndexOfSeparator+1);
|
|
|
|
|
}
|
|
|
|
|
else // no separator
|
|
|
|
|
{
|
|
|
|
|
value_name = line;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
value_name.MakeLower();
|
2001-11-03 10:52:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
// handle the data
|
2002-06-27 17:49:10 +00:00
|
|
|
if( value_name == "#title" )
|
2002-01-24 08:01:24 +00:00
|
|
|
{
|
2002-06-27 17:49:10 +00:00
|
|
|
// strip Notes type out of description leaving only song title - looks like 'B4U <BASIC>'
|
|
|
|
|
int iIndex = value_data.ReverseFind('<');
|
|
|
|
|
if( iIndex == -1 )
|
|
|
|
|
iIndex = value_data.ReverseFind('(');
|
|
|
|
|
if( iIndex != -1 )
|
|
|
|
|
{
|
2002-06-27 19:14:23 +00:00
|
|
|
value_data = value_data.Left( iIndex );
|
2002-06-27 17:49:10 +00:00
|
|
|
GetMainAndSubTitlesFromFullTitle( value_data, m_sMainTitle, m_sSubTitle );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
m_sMainTitle = value_data;
|
2002-01-24 08:01:24 +00:00
|
|
|
}
|
2002-06-27 17:49:10 +00:00
|
|
|
else if( value_name == "#artist" )
|
2002-01-24 08:01:24 +00:00
|
|
|
{
|
|
|
|
|
m_sArtist = value_data;
|
2001-11-04 19:34:28 +00:00
|
|
|
}
|
2002-06-27 17:49:10 +00:00
|
|
|
else if( value_name == "#bpm" )
|
2002-01-24 08:01:24 +00:00
|
|
|
{
|
2002-06-27 17:49:10 +00:00
|
|
|
BPMSegment newSeg( 0, (float)atof(value_data) );
|
|
|
|
|
AddBPMSegment( newSeg );
|
|
|
|
|
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", newSeg.m_fStartBeat, newSeg.m_fBPM );
|
2001-11-27 22:47:30 +00:00
|
|
|
}
|
2002-06-27 17:49:10 +00:00
|
|
|
else if( value_name == "#backbmp" )
|
2002-01-24 08:01:24 +00:00
|
|
|
{
|
2002-06-29 11:59:09 +00:00
|
|
|
m_sBackgroundFile = value_data;
|
2002-01-24 08:01:24 +00:00
|
|
|
}
|
2002-06-27 17:49:10 +00:00
|
|
|
else if( value_name == "#wav" )
|
2002-01-24 08:01:24 +00:00
|
|
|
{
|
2002-06-29 11:59:09 +00:00
|
|
|
m_sMusicFile = value_data;
|
2002-01-24 08:01:24 +00:00
|
|
|
}
|
|
|
|
|
else if( value_name.Left(1) == "#"
|
|
|
|
|
&& IsAnInt( value_name.Mid(1,3) )
|
|
|
|
|
&& IsAnInt( value_name.Mid(4,2) ) ) // this is step or offset data. Looks like "#00705"
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2001-11-04 19:34:28 +00:00
|
|
|
int iMeasureNo = atoi( value_name.Mid(1,3) );
|
2002-06-27 17:49:10 +00:00
|
|
|
int iBMSTrackNo = atoi( value_name.Mid(4,2) );
|
2001-11-27 22:47:30 +00:00
|
|
|
|
2002-01-24 08:01:24 +00:00
|
|
|
CString sNoteData = value_data;
|
2001-12-19 01:50:57 +00:00
|
|
|
CArray<int, int> arrayNotes;
|
2001-11-03 10:52:42 +00:00
|
|
|
|
|
|
|
|
for( int i=0; i<sNoteData.GetLength(); i+=2 )
|
|
|
|
|
{
|
2001-12-19 01:50:57 +00:00
|
|
|
CString sNote = sNoteData.Mid(i,2);
|
|
|
|
|
int iNote;
|
|
|
|
|
sscanf( sNote, "%x", &iNote ); // data is in hexadecimal
|
|
|
|
|
arrayNotes.Add( iNote );
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int iNumNotesInThisMeasure = arrayNotes.GetSize();
|
2002-07-31 19:40:40 +00:00
|
|
|
//LOG->Trace( "%s:%s: iMeasureNo = %d, iBMSTrackNo = %d, iNumNotesInThisMeasure = %d",
|
2002-06-27 17:49:10 +00:00
|
|
|
// valuename, sNoteData, iMeasureNo, iBMSTrackNo, iNumNotesInThisMeasure );
|
2001-11-03 10:52:42 +00:00
|
|
|
for( int j=0; j<iNumNotesInThisMeasure; j++ )
|
|
|
|
|
{
|
2001-12-19 01:50:57 +00:00
|
|
|
if( arrayNotes[j] != 0 )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2001-11-27 22:47:30 +00:00
|
|
|
float fPercentThroughMeasure = (float)j/(float)iNumNotesInThisMeasure;
|
2001-11-03 10:52:42 +00:00
|
|
|
|
|
|
|
|
// index is in quarter beats starting at beat 0
|
|
|
|
|
int iStepIndex = (int) ( (iMeasureNo + fPercentThroughMeasure)
|
2001-11-04 19:34:28 +00:00
|
|
|
* BEATS_PER_MEASURE * ELEMENTS_PER_BEAT );
|
|
|
|
|
|
2002-06-27 17:49:10 +00:00
|
|
|
switch( iBMSTrackNo )
|
2001-11-04 19:34:28 +00:00
|
|
|
{
|
2002-06-27 17:49:10 +00:00
|
|
|
case 1: // background music track
|
2002-07-03 21:27:26 +00:00
|
|
|
{
|
|
|
|
|
float fBeatOffset = fBeatOffset = NoteRowToBeat( (float)iStepIndex );
|
|
|
|
|
if( fBeatOffset > 10 ) // some BPMs's play the music again at the end. Why? Who knows...
|
|
|
|
|
break;
|
|
|
|
|
float fBPS;
|
|
|
|
|
fBPS = m_BPMSegments[0].m_fBPM/60.0f;
|
|
|
|
|
m_fBeat0OffsetInSeconds = fBeatOffset / fBPS;
|
2002-07-31 19:40:40 +00:00
|
|
|
//LOG->Trace( "Found offset to be index %d, beat %f", iStepIndex, NoteRowToBeat(iStepIndex) );
|
2002-07-03 21:27:26 +00:00
|
|
|
}
|
2001-11-27 22:47:30 +00:00
|
|
|
break;
|
2002-06-27 17:49:10 +00:00
|
|
|
case 3: // bpm change
|
|
|
|
|
{
|
|
|
|
|
BPMSegment newSeg( NoteRowToBeat(iStepIndex), (float)arrayNotes[j] );
|
|
|
|
|
AddBPMSegment( newSeg );
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", newSeg.m_fStartBeat, newSeg.m_fBPM );
|
2002-06-27 17:49:10 +00:00
|
|
|
}
|
|
|
|
|
break;
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2002-06-27 17:49:10 +00:00
|
|
|
// Let me just take a moment to express how frustrated I am with the new,
|
|
|
|
|
// poorly-designed changes to the BMS format.
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhhhhhhhhh!!!!!!!!!!!!!!!
|
|
|
|
|
//
|
|
|
|
|
// Thank you.
|
|
|
|
|
|
|
|
|
|
case 8: // indirect bpm
|
|
|
|
|
{
|
|
|
|
|
// This is a very inefficient way to parse, but it doesn't matter much
|
|
|
|
|
// because this is only parsed on the first run after the song is installed.
|
|
|
|
|
CString sTagToLookFor = ssprintf( "#BPM%02x", arrayNotes[j] );
|
|
|
|
|
float fBPM = -1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// open the song file again and and look for this tag's value
|
|
|
|
|
CStdioFile file;
|
2002-07-02 00:27:58 +00:00
|
|
|
if( !file.Open( sPath, CFile::modeRead|CFile::shareDenyNone ) )
|
2002-06-27 17:49:10 +00:00
|
|
|
{
|
2002-07-02 00:27:58 +00:00
|
|
|
throw RageException( "Failed to open %s.", sPath );
|
2002-06-27 17:49:10 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString line;
|
|
|
|
|
while( file.ReadString(line) ) // foreach line
|
|
|
|
|
{
|
|
|
|
|
CString value_name; // fill these in
|
|
|
|
|
CString value_data;
|
|
|
|
|
|
|
|
|
|
// BMS value names can be separated by a space or a colon.
|
|
|
|
|
int iIndexOfFirstColon = line.Find( ":" );
|
|
|
|
|
int iIndexOfFirstSpace = line.Find( " " );
|
|
|
|
|
|
|
|
|
|
if( iIndexOfFirstColon == -1 )
|
|
|
|
|
iIndexOfFirstColon = 10000;
|
|
|
|
|
if( iIndexOfFirstSpace == -1 )
|
|
|
|
|
iIndexOfFirstSpace = 10000;
|
|
|
|
|
|
|
|
|
|
int iIndexOfSeparator = min( iIndexOfFirstSpace, iIndexOfFirstColon );
|
|
|
|
|
|
|
|
|
|
if( iIndexOfSeparator != 10000 )
|
|
|
|
|
{
|
|
|
|
|
value_name = line.Mid( 0, iIndexOfSeparator );
|
|
|
|
|
value_data = line; // the rest
|
|
|
|
|
value_data.Delete(0,iIndexOfSeparator+1);
|
|
|
|
|
}
|
|
|
|
|
else // no separator
|
|
|
|
|
{
|
|
|
|
|
value_name = line;
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
if( 0==stricmp(value_name, sTagToLookFor) == 0 )
|
2002-06-27 17:49:10 +00:00
|
|
|
{
|
|
|
|
|
fBPM = (float)atof( value_data );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( fBPM == -1 ) // we didn't find the line we were looking for
|
|
|
|
|
{
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "WARNING: Couldn't find tag '%s' in '%s'.", sTagToLookFor, sPath );
|
2002-06-27 17:49:10 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
BPMSegment newSeg( NoteRowToBeat(iStepIndex), fBPM );
|
|
|
|
|
AddBPMSegment( newSeg );
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", newSeg.m_fStartBeat, newSeg.m_fBPM );
|
2002-06-27 17:49:10 +00:00
|
|
|
}
|
2002-07-02 00:27:58 +00:00
|
|
|
|
|
|
|
|
file.Close();
|
2002-06-27 17:49:10 +00:00
|
|
|
}
|
|
|
|
|
break;
|
2002-07-27 19:29:51 +00:00
|
|
|
case 9: // stop
|
2002-06-27 17:49:10 +00:00
|
|
|
{
|
|
|
|
|
// This is a very inefficient way to parse, but it doesn't
|
|
|
|
|
// matter much because this is only parsed on the first run after the song is installed.
|
|
|
|
|
CString sTagToLookFor = ssprintf( "#STOP%02x", arrayNotes[j] );
|
|
|
|
|
float fFreezeStartBeat = NoteRowToBeat(iStepIndex);
|
|
|
|
|
float fFreezeSecs = -1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// open the song file again and and look for this tag's value
|
|
|
|
|
CStdioFile file;
|
2002-07-02 00:27:58 +00:00
|
|
|
if( !file.Open( sPath, CFile::modeRead|CFile::shareDenyNone ) )
|
|
|
|
|
throw RageException( "Failed to open %s.", sPath );
|
2002-06-27 17:49:10 +00:00
|
|
|
|
|
|
|
|
CString line;
|
|
|
|
|
while( file.ReadString(line) ) // foreach line
|
|
|
|
|
{
|
|
|
|
|
CString value_name; // fill these in
|
|
|
|
|
CString value_data;
|
|
|
|
|
|
|
|
|
|
// BMS value names can be separated by a space or a colon.
|
|
|
|
|
int iIndexOfFirstColon = line.Find( ":" );
|
|
|
|
|
int iIndexOfFirstSpace = line.Find( " " );
|
|
|
|
|
|
|
|
|
|
if( iIndexOfFirstColon == -1 )
|
|
|
|
|
iIndexOfFirstColon = 10000;
|
|
|
|
|
if( iIndexOfFirstSpace == -1 )
|
|
|
|
|
iIndexOfFirstSpace = 10000;
|
|
|
|
|
|
|
|
|
|
int iIndexOfSeparator = min( iIndexOfFirstSpace, iIndexOfFirstColon );
|
|
|
|
|
|
|
|
|
|
if( iIndexOfSeparator != 10000 )
|
|
|
|
|
{
|
|
|
|
|
value_name = line.Mid( 0, iIndexOfSeparator );
|
|
|
|
|
value_data = line; // the rest
|
|
|
|
|
value_data.Delete(0,iIndexOfSeparator+1);
|
|
|
|
|
}
|
|
|
|
|
else // no separator
|
|
|
|
|
{
|
|
|
|
|
value_name = line;
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-27 19:29:51 +00:00
|
|
|
if( 0==stricmp(value_name, sTagToLookFor) )
|
2002-06-27 17:49:10 +00:00
|
|
|
{
|
|
|
|
|
// find the BPM at the time of this freeze
|
|
|
|
|
float fBPM = -1;
|
|
|
|
|
for( int i=0; i<m_BPMSegments.GetSize()-1; i++ )
|
|
|
|
|
{
|
|
|
|
|
if( m_BPMSegments[i].m_fStartBeat <= fFreezeStartBeat &&
|
|
|
|
|
m_BPMSegments[i+1].m_fStartBeat > fFreezeStartBeat )
|
|
|
|
|
{
|
|
|
|
|
fBPM = m_BPMSegments[i].m_fBPM;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// the BPM segment of this beat is the last BPM segment
|
|
|
|
|
if( fBPM == -1 )
|
|
|
|
|
fBPM = m_BPMSegments[m_BPMSegments.GetSize()-1].m_fBPM;
|
|
|
|
|
|
|
|
|
|
fFreezeSecs = (float)atof(value_data)/(fBPM*0.81f); // I have no idea what units these are in, so I experimented until finding this factor.
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( fFreezeSecs == -1 ) // we didn't find the line we were looking for
|
|
|
|
|
{
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "WARNING: Couldn't find tag '%s' in '%s'.", sTagToLookFor, sPath );
|
2002-06-27 17:49:10 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-07-11 19:02:26 +00:00
|
|
|
StopSegment newSeg( fFreezeStartBeat, fFreezeSecs );
|
|
|
|
|
AddStopSegment( newSeg );
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Inserting new Freeze at beat %f, secs %f", newSeg.m_fStartBeat, newSeg.m_fStopSeconds );
|
2002-06-27 17:49:10 +00:00
|
|
|
}
|
2002-07-02 00:27:58 +00:00
|
|
|
|
|
|
|
|
file.Close();
|
2002-06-27 17:49:10 +00:00
|
|
|
}
|
2001-11-04 19:34:28 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-12-28 10:15:59 +00:00
|
|
|
for( i=0; i<m_BPMSegments.GetSize(); i++ )
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "There is a BPM change at beat %f, BPM %f, index %d",
|
2001-12-19 01:50:57 +00:00
|
|
|
m_BPMSegments[i].m_fStartBeat, m_BPMSegments[i].m_fBPM, i );
|
|
|
|
|
|
2002-07-02 00:27:58 +00:00
|
|
|
file.Close();
|
2001-12-19 01:50:57 +00:00
|
|
|
|
2001-11-03 10:52:42 +00:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-04-16 17:31:00 +00:00
|
|
|
bool Song::LoadFromDWIFile( CString sPath )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Song::LoadFromDWIFile(%s)", sPath );
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-07-31 19:40:40 +00:00
|
|
|
MsdFile msd;
|
|
|
|
|
bool bResult = msd.ReadFile( sPath );
|
|
|
|
|
if( !bResult )
|
|
|
|
|
throw RageException( "Error opening file '%s'.", sPath );
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-07-31 19:40:40 +00:00
|
|
|
for( int i=0; i<msd.m_iNumValues; i++ )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2002-07-31 19:40:40 +00:00
|
|
|
int iNumParams = msd.m_iNumParams[i];
|
|
|
|
|
CString* sParams = msd.m_sValuesAndParams[i];
|
|
|
|
|
CString sValueName = sParams[0];
|
2001-12-19 01:50:57 +00:00
|
|
|
|
2001-11-03 10:52:42 +00:00
|
|
|
// handle the data
|
2002-08-01 03:15:27 +00:00
|
|
|
if( 0==stricmp(sValueName,"FILE") )
|
2002-07-31 19:40:40 +00:00
|
|
|
m_sMusicFile = sParams[1];
|
2001-12-19 01:50:57 +00:00
|
|
|
|
2002-08-01 03:15:27 +00:00
|
|
|
else if( 0==stricmp(sValueName,"TITLE") )
|
2002-07-31 19:40:40 +00:00
|
|
|
GetMainAndSubTitlesFromFullTitle( sParams[1], m_sMainTitle, m_sSubTitle );
|
2002-05-28 20:01:22 +00:00
|
|
|
|
2002-08-01 03:15:27 +00:00
|
|
|
else if( 0==stricmp(sValueName,"ARTIST") )
|
2002-07-31 19:40:40 +00:00
|
|
|
m_sArtist = sParams[1];
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-08-01 03:15:27 +00:00
|
|
|
else if( 0==stricmp(sValueName,"CDTITLE") )
|
2002-07-31 19:40:40 +00:00
|
|
|
m_sCDTitleFile = sParams[1];
|
2002-07-11 19:02:26 +00:00
|
|
|
|
2002-08-01 03:15:27 +00:00
|
|
|
else if( 0==stricmp(sValueName,"BPM") )
|
2002-07-31 19:40:40 +00:00
|
|
|
AddBPMSegment( BPMSegment(0, (float)atof(sParams[1])) );
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2002-08-01 03:15:27 +00:00
|
|
|
else if( 0==stricmp(sValueName,"GAP") )
|
2001-12-19 01:50:57 +00:00
|
|
|
// the units of GAP is 1/1000 second
|
2002-07-31 19:40:40 +00:00
|
|
|
m_fBeat0OffsetInSeconds = -atoi( sParams[1] ) / 1000.0f;
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-08-01 03:15:27 +00:00
|
|
|
else if( 0==stricmp(sValueName,"SAMPLESTART") )
|
2002-07-31 19:40:40 +00:00
|
|
|
m_fMusicSampleStartSeconds = TimeToSeconds( sParams[1] );
|
2002-04-28 20:42:32 +00:00
|
|
|
|
2002-08-01 03:15:27 +00:00
|
|
|
else if( 0==stricmp(sValueName,"SAMPLELENGTH") )
|
2002-07-31 19:40:40 +00:00
|
|
|
m_fMusicSampleLengthSeconds = TimeToSeconds( sParams[1] );
|
2002-04-28 20:42:32 +00:00
|
|
|
|
2002-08-01 03:15:27 +00:00
|
|
|
else if( 0==stricmp(sValueName,"FREEZE") )
|
2001-12-19 01:50:57 +00:00
|
|
|
{
|
|
|
|
|
CStringArray arrayFreezeExpressions;
|
2002-07-31 19:40:40 +00:00
|
|
|
split( sParams[1], ",", arrayFreezeExpressions );
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2001-12-19 01:50:57 +00:00
|
|
|
for( int f=0; f<arrayFreezeExpressions.GetSize(); f++ )
|
|
|
|
|
{
|
|
|
|
|
CStringArray arrayFreezeValues;
|
|
|
|
|
split( arrayFreezeExpressions[f], "=", arrayFreezeValues );
|
2002-02-28 19:40:40 +00:00
|
|
|
float fIndex = atoi( arrayFreezeValues[0] ) * ELEMENTS_PER_BEAT / 4.0f;
|
2002-05-19 01:59:48 +00:00
|
|
|
float fFreezeBeat = NoteRowToBeat( fIndex );
|
2002-01-16 19:46:22 +00:00
|
|
|
float fFreezeSeconds = (float)atof( arrayFreezeValues[1] ) / 1000.0f;
|
2001-12-19 01:50:57 +00:00
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
AddStopSegment( StopSegment(fFreezeBeat, fFreezeSeconds) );
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Adding a freeze segment: beat: %f, seconds = %f", fFreezeBeat, fFreezeSeconds );
|
2001-12-19 01:50:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-08-01 03:15:27 +00:00
|
|
|
else if( 0==stricmp(sValueName,"CHANGEBPM") || 0==stricmp(sValueName,"BPMCHANGE") )
|
2001-12-19 01:50:57 +00:00
|
|
|
{
|
|
|
|
|
CStringArray arrayBPMChangeExpressions;
|
2002-07-31 19:40:40 +00:00
|
|
|
split( sParams[1], ",", arrayBPMChangeExpressions );
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2001-12-19 01:50:57 +00:00
|
|
|
for( int b=0; b<arrayBPMChangeExpressions.GetSize(); b++ )
|
|
|
|
|
{
|
|
|
|
|
CStringArray arrayBPMChangeValues;
|
|
|
|
|
split( arrayBPMChangeExpressions[b], "=", arrayBPMChangeValues );
|
2002-02-28 19:40:40 +00:00
|
|
|
float fIndex = atoi( arrayBPMChangeValues[0] ) * ELEMENTS_PER_BEAT / 4.0f;
|
2002-05-19 01:59:48 +00:00
|
|
|
float fBeat = NoteRowToBeat( fIndex );
|
2002-07-11 19:02:26 +00:00
|
|
|
float fNewBPM = (float)atof( arrayBPMChangeValues[1] );
|
2001-12-19 01:50:57 +00:00
|
|
|
|
2002-06-24 22:04:31 +00:00
|
|
|
AddBPMSegment( BPMSegment(fBeat, fNewBPM) );
|
2001-12-19 01:50:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-08-01 03:15:27 +00:00
|
|
|
else if( 0==stricmp(sValueName,"SINGLE") ||
|
|
|
|
|
0==stricmp(sValueName,"DOUBLE") ||
|
|
|
|
|
0==stricmp(sValueName,"COUPLE") ||
|
|
|
|
|
0==stricmp(sValueName,"SOLO"))
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2002-06-14 22:25:22 +00:00
|
|
|
Notes* pNewNotes = new Notes;
|
|
|
|
|
pNewNotes->LoadFromDWITokens(
|
2002-07-31 19:40:40 +00:00
|
|
|
sParams[0],
|
|
|
|
|
sParams[1],
|
|
|
|
|
sParams[2],
|
|
|
|
|
sParams[3],
|
|
|
|
|
(iNumParams==5) ? sParams[4] : ""
|
2002-03-30 20:00:13 +00:00
|
|
|
);
|
2002-07-11 19:02:26 +00:00
|
|
|
m_apNotes.Add( pNewNotes );
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
// do nothing. We don't care about this value name
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-07-03 03:13:13 +00:00
|
|
|
bool Song::LoadFromSMFile( CString sPath )
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Song::LoadFromSMDir(%s)", sPath );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-02 00:27:58 +00:00
|
|
|
m_BPMSegments.RemoveAll();
|
2002-07-11 19:02:26 +00:00
|
|
|
m_StopSegments.RemoveAll();
|
2002-07-02 00:27:58 +00:00
|
|
|
|
2002-04-16 17:31:00 +00:00
|
|
|
int i;
|
|
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
MsdFile msd;
|
|
|
|
|
bool bResult = msd.ReadFile( sPath );
|
2002-07-31 19:40:40 +00:00
|
|
|
if( !bResult )
|
|
|
|
|
throw RageException( "Error opening file '%s'.", sPath );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
for( i=0; i<msd.m_iNumValues; i++ )
|
2002-06-24 22:04:31 +00:00
|
|
|
{
|
2002-07-23 01:41:40 +00:00
|
|
|
int iNumParams = msd.m_iNumParams[i];
|
|
|
|
|
CString* sParams = msd.m_sValuesAndParams[i];
|
|
|
|
|
CString sValueName = sParams[0];
|
2002-04-16 17:31:00 +00:00
|
|
|
|
|
|
|
|
// handle the data
|
2002-07-23 01:41:40 +00:00
|
|
|
if( 0==stricmp(sValueName,"TITLE") )
|
|
|
|
|
GetMainAndSubTitlesFromFullTitle( sParams[1], m_sMainTitle, m_sSubTitle );
|
2002-04-28 20:42:32 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"SUBTITLE") )
|
|
|
|
|
m_sSubTitle = sParams[1];
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"ARTIST") )
|
|
|
|
|
m_sArtist = sParams[1];
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"CREDIT") )
|
|
|
|
|
m_sCredit = sParams[1];
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"BANNER") )
|
|
|
|
|
m_sBannerFile = sParams[1];
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"BACKGROUND") )
|
|
|
|
|
m_sBackgroundFile = sParams[1];
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"CDTITLE") )
|
|
|
|
|
m_sCDTitleFile = sParams[1];
|
2002-06-29 11:59:09 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"MOVIEBACKGROUND") )
|
|
|
|
|
m_sMovieBackgroundFile = sParams[1];
|
2002-07-11 19:02:26 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"MUSIC") )
|
|
|
|
|
m_sMusicFile = sParams[1];
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"MUSICBYTES") )
|
|
|
|
|
m_iMusicBytes = atoi( sParams[1] );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"MUSICLENGTH") )
|
|
|
|
|
m_fMusicLengthSeconds = (float)atof( sParams[1] );
|
2002-07-03 21:27:26 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"FIRSTBEAT") )
|
|
|
|
|
m_fFirstBeat = (float)atof( sParams[1] );
|
2002-07-11 19:02:26 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"LASTBEAT") )
|
|
|
|
|
m_fLastBeat = (float)atof( sParams[1] );
|
2002-07-11 19:02:26 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"SAMPLESTART") )
|
|
|
|
|
m_fMusicSampleStartSeconds = TimeToSeconds( sParams[1] );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"SAMPLELENGTH") )
|
|
|
|
|
m_fMusicSampleLengthSeconds = TimeToSeconds( sParams[1] );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"OFFSET") )
|
|
|
|
|
m_fBeat0OffsetInSeconds = (float)atof( sParams[1] );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"STOPS") || 0==stricmp(sValueName,"FREEZES") )
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
|
|
|
|
CStringArray arrayFreezeExpressions;
|
2002-07-23 01:41:40 +00:00
|
|
|
split( sParams[1], ",", arrayFreezeExpressions );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
|
|
|
|
for( int f=0; f<arrayFreezeExpressions.GetSize(); f++ )
|
|
|
|
|
{
|
|
|
|
|
CStringArray arrayFreezeValues;
|
|
|
|
|
split( arrayFreezeExpressions[f], "=", arrayFreezeValues );
|
|
|
|
|
float fFreezeBeat = (float)atof( arrayFreezeValues[0] );
|
|
|
|
|
float fFreezeSeconds = (float)atof( arrayFreezeValues[1] );
|
|
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
StopSegment new_seg;
|
2002-04-16 17:31:00 +00:00
|
|
|
new_seg.m_fStartBeat = fFreezeBeat;
|
2002-07-11 19:02:26 +00:00
|
|
|
new_seg.m_fStopSeconds = fFreezeSeconds;
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Adding a freeze segment: beat: %f, seconds = %f", new_seg.m_fStartBeat, new_seg.m_fStopSeconds );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
AddStopSegment( new_seg );
|
2002-04-16 17:31:00 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"BPMS") )
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
|
|
|
|
CStringArray arrayBPMChangeExpressions;
|
2002-07-23 01:41:40 +00:00
|
|
|
split( sParams[1], ",", arrayBPMChangeExpressions );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
|
|
|
|
for( int b=0; b<arrayBPMChangeExpressions.GetSize(); b++ )
|
|
|
|
|
{
|
|
|
|
|
CStringArray arrayBPMChangeValues;
|
|
|
|
|
split( arrayBPMChangeExpressions[b], "=", arrayBPMChangeValues );
|
|
|
|
|
float fBeat = (float)atof( arrayBPMChangeValues[0] );
|
2002-07-11 19:02:26 +00:00
|
|
|
float fNewBPM = (float)atof( arrayBPMChangeValues[1] );
|
2002-04-16 17:31:00 +00:00
|
|
|
|
|
|
|
|
BPMSegment new_seg;
|
|
|
|
|
new_seg.m_fStartBeat = fBeat;
|
|
|
|
|
new_seg.m_fBPM = fNewBPM;
|
|
|
|
|
|
2002-07-02 00:27:58 +00:00
|
|
|
AddBPMSegment( new_seg );
|
2002-04-16 17:31:00 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"ANIMATIONS") )
|
2002-07-11 19:02:26 +00:00
|
|
|
{
|
|
|
|
|
CStringArray arrayAnimationExpressions;
|
2002-07-23 01:41:40 +00:00
|
|
|
split( sParams[1], ",", arrayAnimationExpressions );
|
2002-07-11 19:02:26 +00:00
|
|
|
|
|
|
|
|
for( int b=0; b<arrayAnimationExpressions.GetSize(); b++ )
|
|
|
|
|
{
|
|
|
|
|
CStringArray arrayAnimationValues;
|
|
|
|
|
split( arrayAnimationExpressions[b], "=", arrayAnimationValues );
|
|
|
|
|
float fBeat = (float)atof( arrayAnimationValues[0] );
|
|
|
|
|
CString sAnimName = arrayAnimationValues[1];
|
|
|
|
|
sAnimName.MakeLower();
|
|
|
|
|
|
|
|
|
|
AddAnimationSegment( AnimationSegment(fBeat, sAnimName) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
else if( 0==stricmp(sValueName,"NOTES") )
|
2002-06-30 23:19:33 +00:00
|
|
|
{
|
2002-07-23 01:41:40 +00:00
|
|
|
Notes* pNewNotes = new Notes;
|
|
|
|
|
ASSERT( pNewNotes );
|
|
|
|
|
m_apNotes.Add( pNewNotes );
|
2002-07-02 00:27:58 +00:00
|
|
|
|
2002-07-23 01:41:40 +00:00
|
|
|
if( iNumParams != 7 )
|
|
|
|
|
throw RageException( "The song file '%s' is has %d fields in a #NOTES tag, but should have %d.", sPath, iNumParams, 7 );
|
2002-06-30 23:19:33 +00:00
|
|
|
|
|
|
|
|
pNewNotes->LoadFromSMTokens(
|
2002-07-23 01:41:40 +00:00
|
|
|
sParams[1],
|
|
|
|
|
sParams[2],
|
|
|
|
|
sParams[3],
|
|
|
|
|
sParams[4],
|
|
|
|
|
sParams[5],
|
|
|
|
|
sParams[6]
|
2002-07-03 03:13:13 +00:00
|
|
|
);
|
2002-06-30 23:19:33 +00:00
|
|
|
}
|
|
|
|
|
|
2002-04-16 17:31:00 +00:00
|
|
|
else
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "Unexpected value named '%s'", sValueName );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool Song::LoadFromKSFDir( CString sDir )
|
|
|
|
|
{
|
|
|
|
|
LOG->Trace( "Song::LoadFromKSFDir(%s)", sDir );
|
|
|
|
|
|
|
|
|
|
CStringArray arrayKSFFileNames;
|
|
|
|
|
GetDirListing( sDir + CString("*.ksf"), arrayKSFFileNames );
|
|
|
|
|
|
|
|
|
|
if( arrayKSFFileNames.GetSize() == 0 )
|
|
|
|
|
throw RageException( "Couldn't find any KSF files in '%s'", sDir );
|
|
|
|
|
|
|
|
|
|
// load the Notes from the rest of the KSF files
|
|
|
|
|
for( int i=0; i<arrayKSFFileNames.GetSize(); i++ )
|
|
|
|
|
{
|
|
|
|
|
Notes* pNewNotes = new Notes;
|
|
|
|
|
pNewNotes->LoadFromKSFFile( m_sSongDir + arrayKSFFileNames[i] );
|
|
|
|
|
m_apNotes.Add( pNewNotes );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString sPath = m_sSongDir + arrayKSFFileNames[0];
|
|
|
|
|
|
|
|
|
|
MsdFile msd;
|
|
|
|
|
bool bResult = msd.ReadFile( sPath );
|
|
|
|
|
if( !bResult )
|
|
|
|
|
throw RageException( "Error opening file '%s'.", sPath );
|
|
|
|
|
|
|
|
|
|
for( i=0; i<msd.m_iNumValues; i++ )
|
|
|
|
|
{
|
|
|
|
|
int iNumParams = msd.m_iNumParams[i];
|
|
|
|
|
CString* sParams = msd.m_sValuesAndParams[i];
|
|
|
|
|
CString sValueName = sParams[0];
|
|
|
|
|
|
|
|
|
|
// handle the data
|
|
|
|
|
if( 0==stricmp(sValueName,"TITLE") )
|
|
|
|
|
{
|
|
|
|
|
//title is usually in format "artist - songtitle"
|
|
|
|
|
CStringArray asBits;
|
|
|
|
|
split( sParams[1], " - ", asBits, false );
|
|
|
|
|
|
|
|
|
|
if( asBits.GetSize() == 2 )
|
|
|
|
|
{
|
|
|
|
|
m_sArtist = asBits[0];
|
|
|
|
|
m_sMainTitle = asBits[1];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_sMainTitle = asBits[0];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if( 0==stricmp(sValueName,"BPM") )
|
|
|
|
|
AddBPMSegment( BPMSegment(0, (float)atof(sParams[1])) );
|
|
|
|
|
|
|
|
|
|
else if( 0==stricmp(sValueName,"STARTTIME") )
|
|
|
|
|
m_fBeat0OffsetInSeconds = -(float)atof(sParams[1])/100;
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
LOG->Trace( "Unexpected value named '%s'", sValueName );
|
2002-04-16 17:31:00 +00:00
|
|
|
}
|
|
|
|
|
|
2002-07-31 19:40:40 +00:00
|
|
|
m_sMusicFile = "song.mp3";
|
|
|
|
|
|
2002-04-16 17:31:00 +00:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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-06-14 22:25:22 +00:00
|
|
|
throw RageException( "No #BPM specified in '%s.'", GetSongFilePath() );
|
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];
|
2001-11-25 04:31:44 +00:00
|
|
|
else
|
2002-06-29 11:59:09 +00:00
|
|
|
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-07-11 19:02:26 +00:00
|
|
|
RageSoundStream sound;
|
|
|
|
|
sound.Load( GetMusicPath() );
|
|
|
|
|
m_fMusicLengthSeconds = sound.GetLengthSeconds();
|
|
|
|
|
|
2002-05-19 01:59:48 +00:00
|
|
|
|
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-05-27 08:23:27 +00:00
|
|
|
void Song::GetNotesThatMatch( NotesType nt, CArray<Notes*, Notes*>& arrayAddTo )
|
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
|
|
|
|
|
if( m_apNotes[i]->m_NotesType == nt )
|
|
|
|
|
arrayAddTo.Add( m_apNotes[i] );
|
2001-12-28 10:15:59 +00:00
|
|
|
}
|
|
|
|
|
|
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-06-30 23:19:33 +00:00
|
|
|
//
|
|
|
|
|
// First look in the cache for this song (without loading NoteData)
|
|
|
|
|
//
|
|
|
|
|
IniFile ini;
|
|
|
|
|
ini.SetPath( "Cache\\index.cache" );
|
|
|
|
|
ini.ReadFile(); // don't care if this fails
|
|
|
|
|
|
|
|
|
|
ini.SetValueI( "Cache", "CacheVersion", FILE_CACHE_VERSION );
|
|
|
|
|
ini.SetValueI( "Cache", m_sSongDir, GetHashForDirectory(m_sSongDir) );
|
|
|
|
|
ini.WriteFile();
|
|
|
|
|
SaveToSMFile( GetCacheFilePath() );
|
2002-05-19 01:59:48 +00:00
|
|
|
}
|
2002-02-11 04:46:31 +00:00
|
|
|
|
2002-05-19 01:59:48 +00:00
|
|
|
|
2002-06-30 23:19:33 +00:00
|
|
|
void Song::SaveToSMFile( CString sPath )
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
2002-06-30 23:19:33 +00:00
|
|
|
if( sPath == "" )
|
2002-07-02 00:27:58 +00:00
|
|
|
{
|
2002-06-30 23:19:33 +00:00
|
|
|
sPath = GetSongFilePath();
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-02 00:27:58 +00:00
|
|
|
//
|
|
|
|
|
// rename all old files to avoid confusion
|
|
|
|
|
//
|
|
|
|
|
CStringArray arrayOldFileNames;
|
|
|
|
|
GetDirListing( m_sSongDir + "*.bms", arrayOldFileNames );
|
|
|
|
|
GetDirListing( m_sSongDir + "*.dwi", arrayOldFileNames );
|
2002-07-31 19:40:40 +00:00
|
|
|
GetDirListing( m_sSongDir + "*.ksf", arrayOldFileNames );
|
2002-07-02 00:27:58 +00:00
|
|
|
|
|
|
|
|
for( int i=0; i<arrayOldFileNames.GetSize(); i++ )
|
|
|
|
|
{
|
|
|
|
|
CString sOldPath = m_sSongDir + arrayOldFileNames[i];
|
|
|
|
|
CString sNewPath = sOldPath + ".old";
|
|
|
|
|
MoveFile( sOldPath, sNewPath );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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 );
|
|
|
|
|
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-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-07-11 19:02:26 +00:00
|
|
|
fprintf( fp, "#ANIMATIONS:" );
|
|
|
|
|
for( i=0; i<m_AnimationSegments.GetSize(); i++ )
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
2002-07-11 19:02:26 +00:00
|
|
|
AnimationSegment &seg = m_AnimationSegments[i];
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2002-07-11 19:02:26 +00:00
|
|
|
fprintf( fp, "%.2f=%s", seg.m_fStartBeat, seg.m_sAnimationName );
|
|
|
|
|
if( i != m_AnimationSegments.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++ )
|
|
|
|
|
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-05-27 08:23:27 +00:00
|
|
|
Grade Song::GetGradeForDifficultyClass( NotesType nt, DifficultyClass dc )
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
2002-06-14 22:25:22 +00:00
|
|
|
CArray<Notes*, Notes*> aNotes;
|
|
|
|
|
this->GetNotesThatMatch( nt, 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-06-14 22:25:22 +00:00
|
|
|
Notes* pNotes = aNotes[i];
|
2002-05-19 01:59:48 +00:00
|
|
|
if( pNotes->m_DifficultyClass == dc )
|
|
|
|
|
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-02-11 04:46:31 +00:00
|
|
|
/////////////////////////////////////
|
|
|
|
|
// Sorting
|
|
|
|
|
/////////////////////////////////////
|
2002-01-16 10:01:32 +00:00
|
|
|
|
|
|
|
|
int CompareSongPointersByTitle(const void *arg1, const void *arg2)
|
|
|
|
|
{
|
|
|
|
|
Song* pSong1 = *(Song**)arg1;
|
|
|
|
|
Song* pSong2 = *(Song**)arg2;
|
|
|
|
|
|
2002-06-29 11:59:09 +00:00
|
|
|
CString sTitle1 = pSong1->GetFullTitle();
|
|
|
|
|
CString sTitle2 = pSong2->GetFullTitle();
|
2002-01-16 10:01:32 +00:00
|
|
|
|
|
|
|
|
CString sFilePath1 = pSong1->GetSongFilePath(); // this is unique among songs
|
|
|
|
|
CString sFilePath2 = pSong2->GetSongFilePath();
|
|
|
|
|
|
|
|
|
|
CString sCompareString1 = sTitle1 + sFilePath1;
|
|
|
|
|
CString sCompareString2 = sTitle2 + sFilePath2;
|
|
|
|
|
|
2002-03-30 20:00:13 +00:00
|
|
|
return CompareCStringsAsc( (void*)&sCompareString1, (void*)&sCompareString2 );
|
2002-01-16 10:01:32 +00:00
|
|
|
}
|
|
|
|
|
|
2002-02-11 04:46:31 +00:00
|
|
|
void SortSongPointerArrayByTitle( CArray<Song*, Song*> &arraySongPointers )
|
2002-01-16 10:01:32 +00:00
|
|
|
{
|
|
|
|
|
qsort( arraySongPointers.GetData(), arraySongPointers.GetSize(), sizeof(Song*), CompareSongPointersByTitle );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CompareSongPointersByBPM(const void *arg1, const void *arg2)
|
|
|
|
|
{
|
|
|
|
|
Song* pSong1 = *(Song**)arg1;
|
|
|
|
|
Song* pSong2 = *(Song**)arg2;
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
Song* pSong1 = *(Song**)arg1;
|
|
|
|
|
Song* pSong2 = *(Song**)arg2;
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
Song* pSong1 = *(Song**)arg1;
|
|
|
|
|
Song* pSong2 = *(Song**)arg2;
|
|
|
|
|
|
2002-06-29 11:59:09 +00:00
|
|
|
CString sGroup1 = pSong1->m_sGroupName;
|
|
|
|
|
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;
|
|
|
|
|
else if( sGroup1 == sGroup2 )
|
|
|
|
|
return CompareSongPointersByTitle( arg1, arg2 );
|
|
|
|
|
else
|
|
|
|
|
return 1;
|
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)
|
|
|
|
|
{
|
|
|
|
|
Song* pSong1 = *(Song**)arg1;
|
|
|
|
|
Song* pSong2 = *(Song**)arg2;
|
|
|
|
|
|
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-02-24 01:43:11 +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 );
|
|
|
|
|
}
|