2003-02-16 04:01:45 +00:00
|
|
|
#include "global.h"
|
2002-09-06 23:24:40 +00:00
|
|
|
#include "NotesLoaderBMS.h"
|
|
|
|
|
#include "NoteData.h"
|
|
|
|
|
#include "GameConstantsAndTypes.h"
|
|
|
|
|
#include "RageLog.h"
|
|
|
|
|
#include "GameManager.h"
|
|
|
|
|
#include "RageException.h"
|
2003-07-22 07:56:46 +00:00
|
|
|
#include "RageFile.h"
|
2004-04-18 07:34:37 +00:00
|
|
|
#include "StepsUtil.h"
|
2004-05-31 21:55:14 +00:00
|
|
|
#include "song.h"
|
|
|
|
|
#include "Steps.h"
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2003-12-13 03:54:13 +00:00
|
|
|
/* BMS encoding: tap-hold
|
|
|
|
|
4&8panel: Player1 Player2
|
|
|
|
|
Left 11-51 21-61
|
|
|
|
|
Down 13-53 23-63
|
|
|
|
|
Up 15-55 25-65
|
|
|
|
|
Right 16-56 26-66
|
|
|
|
|
|
|
|
|
|
6panel: Player1
|
|
|
|
|
Left 11-51
|
|
|
|
|
Left+Up 12-52
|
|
|
|
|
Down 13-53
|
|
|
|
|
Up 14-54
|
|
|
|
|
Up+Right 15-55
|
|
|
|
|
Right 16-56
|
|
|
|
|
|
|
|
|
|
Notice that 15 and 25 have double meanings! What were they thinking???
|
|
|
|
|
While reading in, use the 6 panel mapping. After reading in, detect if
|
|
|
|
|
only 4 notes are used. If so, shift the Up+Right column back to the Up
|
|
|
|
|
column
|
|
|
|
|
|
|
|
|
|
Hey, folks, BMSes are used for things BESIDES DDR steps,
|
|
|
|
|
and so we're borking up BMSes that are for pnm/bm/etc.
|
|
|
|
|
|
|
|
|
|
pnm-nine: 11-15,22-25
|
|
|
|
|
pnm-five: 13-15,21-22
|
2004-09-27 01:46:57 +00:00
|
|
|
bm-single5: 11-16
|
|
|
|
|
bm-double5: 11-16,21-26
|
2003-12-13 03:54:13 +00:00
|
|
|
bm-single7: 11-16,18-19
|
|
|
|
|
bm-double7: 11-16,18-19,21-26,28-29
|
|
|
|
|
|
|
|
|
|
So the magics for these are:
|
|
|
|
|
pnm-nine: nothing >5, with 12, 14, 22 and/or 24
|
|
|
|
|
pnm-five: nothing >5, with 14 and/or 22
|
|
|
|
|
bm-*: can't tell difference between bm-single and dance-solo
|
|
|
|
|
18/19 marks bm-single7, 28/29 marks bm-double7
|
|
|
|
|
bm-double uses 21-26. */
|
2004-10-24 10:45:30 +00:00
|
|
|
|
2004-09-27 01:46:57 +00:00
|
|
|
enum BmsTrack
|
2004-05-31 22:07:09 +00:00
|
|
|
{
|
2004-09-25 08:00:18 +00:00
|
|
|
BMS_P1_KEY1 = 0,
|
2004-05-31 22:07:09 +00:00
|
|
|
BMS_P1_KEY2,
|
|
|
|
|
BMS_P1_KEY3,
|
|
|
|
|
BMS_P1_KEY4,
|
|
|
|
|
BMS_P1_KEY5,
|
|
|
|
|
BMS_P1_TURN,
|
|
|
|
|
BMS_P1_KEY6,
|
|
|
|
|
BMS_P1_KEY7,
|
|
|
|
|
BMS_P2_KEY1,
|
|
|
|
|
BMS_P2_KEY2,
|
|
|
|
|
BMS_P2_KEY3,
|
|
|
|
|
BMS_P2_KEY4,
|
|
|
|
|
BMS_P2_KEY5,
|
|
|
|
|
BMS_P2_TURN,
|
|
|
|
|
BMS_P2_KEY6,
|
|
|
|
|
BMS_P2_KEY7,
|
2004-10-24 10:45:30 +00:00
|
|
|
// max 4 simultaneous auto keysounds
|
|
|
|
|
BMS_AUTO_KEYSOUND_1,
|
|
|
|
|
BMS_AUTO_KEYSOUND_2,
|
|
|
|
|
BMS_AUTO_KEYSOUND_3,
|
|
|
|
|
BMS_AUTO_KEYSOUND_4,
|
|
|
|
|
BMS_AUTO_KEYSOUND_5,
|
|
|
|
|
BMS_AUTO_KEYSOUND_6,
|
|
|
|
|
BMS_AUTO_KEYSOUND_7,
|
|
|
|
|
BMS_AUTO_KEYSOUND_LAST,
|
2004-09-27 01:46:57 +00:00
|
|
|
NUM_BMS_TRACKS,
|
2004-05-31 22:07:09 +00:00
|
|
|
};
|
|
|
|
|
|
2004-10-24 10:45:30 +00:00
|
|
|
const int NUM_NON_AUTO_KEYSOUND_TRACKS = BMS_AUTO_KEYSOUND_1;
|
|
|
|
|
const int NUM_AUTO_KEYSOUND_TRACKS = NUM_BMS_TRACKS - NUM_NON_AUTO_KEYSOUND_TRACKS;
|
|
|
|
|
|
2004-09-29 07:19:44 +00:00
|
|
|
static bool ConvertRawTrackToTapNote( int iRawTrack, BmsTrack &bmsTrackOut, bool &bIsHoldOut )
|
2004-05-31 22:07:09 +00:00
|
|
|
{
|
2004-09-27 01:46:57 +00:00
|
|
|
if( iRawTrack > 40 )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2004-09-29 07:19:44 +00:00
|
|
|
bIsHoldOut = true;
|
2004-09-27 01:46:57 +00:00
|
|
|
iRawTrack -= 40;
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2004-09-29 07:19:44 +00:00
|
|
|
bIsHoldOut = false;
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-27 01:46:57 +00:00
|
|
|
switch( iRawTrack )
|
|
|
|
|
{
|
2004-10-24 10:45:30 +00:00
|
|
|
case 1: bmsTrackOut = BMS_AUTO_KEYSOUND_1; break;
|
2004-10-23 23:41:49 +00:00
|
|
|
case 11: bmsTrackOut = BMS_P1_KEY1; break;
|
|
|
|
|
case 12: bmsTrackOut = BMS_P1_KEY2; break;
|
|
|
|
|
case 13: bmsTrackOut = BMS_P1_KEY3; break;
|
|
|
|
|
case 14: bmsTrackOut = BMS_P1_KEY4; break;
|
|
|
|
|
case 15: bmsTrackOut = BMS_P1_KEY5; break;
|
|
|
|
|
case 16: bmsTrackOut = BMS_P1_TURN; break;
|
|
|
|
|
case 18: bmsTrackOut = BMS_P1_KEY6; break;
|
|
|
|
|
case 19: bmsTrackOut = BMS_P1_KEY7; break;
|
|
|
|
|
case 21: bmsTrackOut = BMS_P2_KEY1; break;
|
|
|
|
|
case 22: bmsTrackOut = BMS_P2_KEY2; break;
|
|
|
|
|
case 23: bmsTrackOut = BMS_P2_KEY3; break;
|
|
|
|
|
case 24: bmsTrackOut = BMS_P2_KEY4; break;
|
|
|
|
|
case 25: bmsTrackOut = BMS_P2_KEY5; break;
|
|
|
|
|
case 26: bmsTrackOut = BMS_P2_TURN; break;
|
|
|
|
|
case 28: bmsTrackOut = BMS_P2_KEY6; break;
|
|
|
|
|
case 29: bmsTrackOut = BMS_P2_KEY7; break;
|
2004-09-27 01:46:57 +00:00
|
|
|
default: // unknown track
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static StepsType DetermineStepsType( int iPlayer, const NoteData &nd )
|
|
|
|
|
{
|
|
|
|
|
ASSERT( NUM_BMS_TRACKS == nd.GetNumTracks() );
|
|
|
|
|
|
2004-10-24 10:45:30 +00:00
|
|
|
bool bTrackHasNote[NUM_NON_AUTO_KEYSOUND_TRACKS];
|
2004-09-27 01:46:57 +00:00
|
|
|
ZERO( bTrackHasNote );
|
|
|
|
|
|
2004-09-29 06:43:57 +00:00
|
|
|
int iLastRow = nd.GetLastRow();
|
2004-10-24 10:45:30 +00:00
|
|
|
for( int t=0; t<NUM_NON_AUTO_KEYSOUND_TRACKS; t++ )
|
2004-09-27 01:46:57 +00:00
|
|
|
{
|
2004-09-29 06:43:57 +00:00
|
|
|
for( int r=0; r<=iLastRow; r++ )
|
2004-09-27 01:46:57 +00:00
|
|
|
{
|
|
|
|
|
if( nd.GetTapNoteX(t, r).type != TapNote::empty )
|
|
|
|
|
{
|
|
|
|
|
bTrackHasNote[t] = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int iNumNonEmptyTracks = 0;
|
2004-10-24 10:45:30 +00:00
|
|
|
for( int t=0; t<NUM_NON_AUTO_KEYSOUND_TRACKS; t++ )
|
2004-09-27 01:46:57 +00:00
|
|
|
if( bTrackHasNote[t] )
|
|
|
|
|
iNumNonEmptyTracks++;
|
|
|
|
|
|
|
|
|
|
switch( iPlayer )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2004-09-27 01:46:57 +00:00
|
|
|
case 1: // "1 player"
|
|
|
|
|
/* Track counts:
|
|
|
|
|
4 - DDR
|
|
|
|
|
5 - PNM 5-key
|
|
|
|
|
6 - DDR Solo, BM 5-key
|
|
|
|
|
8 - BM 7-key
|
|
|
|
|
9 - PNM 9-key */
|
|
|
|
|
switch( iNumNonEmptyTracks )
|
|
|
|
|
{
|
|
|
|
|
case 4: return STEPS_TYPE_DANCE_SINGLE;
|
|
|
|
|
case 5: return STEPS_TYPE_PNM_FIVE;
|
|
|
|
|
case 6:
|
|
|
|
|
// FIXME: There's no way to distinguish between these types.
|
|
|
|
|
// They use the same tracks. Assume it's a BM type since they
|
|
|
|
|
// are more common.
|
|
|
|
|
//return STEPS_TYPE_DANCE_SOLO;
|
|
|
|
|
return STEPS_TYPE_BM_SINGLE5;
|
|
|
|
|
case 8: return STEPS_TYPE_BM_SINGLE7;
|
|
|
|
|
case 9: return STEPS_TYPE_PNM_NINE;
|
|
|
|
|
default: return STEPS_TYPE_INVALID;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 2: // couple/battle
|
|
|
|
|
return STEPS_TYPE_DANCE_COUPLE;
|
|
|
|
|
case 3: // double
|
|
|
|
|
/* Track counts:
|
|
|
|
|
8 - DDR Double
|
|
|
|
|
12 - BM Double 5-key
|
|
|
|
|
16 - BM Double 7-key */
|
|
|
|
|
switch( iNumNonEmptyTracks )
|
|
|
|
|
{
|
|
|
|
|
case 8: return STEPS_TYPE_BM_SINGLE7;
|
|
|
|
|
case 12: return STEPS_TYPE_BM_DOUBLE5;
|
|
|
|
|
case 16: return STEPS_TYPE_BM_DOUBLE7;
|
|
|
|
|
default: return STEPS_TYPE_INVALID;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
LOG->Warn( "Invalid #PLAYER value %d", iPlayer );
|
|
|
|
|
return STEPS_TYPE_INVALID;
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out, const map<CString,int> &mapWavIdToKeysoundIndex )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2003-08-03 00:13:55 +00:00
|
|
|
LOG->Trace( "Steps::LoadFromBMSFile( '%s' )", sPath.c_str() );
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2003-08-07 06:36:34 +00:00
|
|
|
out.m_StepsType = STEPS_TYPE_INVALID;
|
2002-09-07 05:04:04 +00:00
|
|
|
|
2004-09-27 01:46:57 +00:00
|
|
|
// BMS player code. Fill in below and use to determine StepsType.
|
|
|
|
|
int iPlayer = -1;
|
|
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
NoteData ndNotes;
|
|
|
|
|
ndNotes.SetNumTracks( NUM_BMS_TRACKS );
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2004-03-08 05:11:26 +00:00
|
|
|
RageFile file;
|
|
|
|
|
if( !file.Open(sPath) )
|
2004-07-13 00:03:47 +00:00
|
|
|
RageException::Throw( "Failed to open \"%s\" for reading: %s", sPath.c_str(), file.GetError().c_str() );
|
2004-10-23 23:41:49 +00:00
|
|
|
|
2004-03-08 05:11:26 +00:00
|
|
|
while( !file.AtEOF() )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2004-06-06 21:04:48 +00:00
|
|
|
CString line;
|
|
|
|
|
if( file.GetLine( line ) == -1 )
|
|
|
|
|
{
|
|
|
|
|
LOG->Warn( "Error reading \"%s\": %s", sPath.c_str(), file.GetError().c_str() );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2003-02-16 05:12:37 +00:00
|
|
|
StripCrnl(line);
|
2002-09-06 23:24:40 +00:00
|
|
|
CString value_name; // fill these in
|
2003-12-12 05:37:52 +00:00
|
|
|
CString value_data;
|
2002-09-06 23:24:40 +00:00
|
|
|
|
|
|
|
|
// 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;
|
2003-12-12 05:37:52 +00:00
|
|
|
|
2002-09-06 23:24:40 +00:00
|
|
|
int iIndexOfSeparator = min( iIndexOfFirstSpace, iIndexOfFirstColon );
|
|
|
|
|
|
|
|
|
|
if( iIndexOfSeparator != 10000 )
|
|
|
|
|
{
|
2003-01-03 05:29:45 +00:00
|
|
|
value_name = line.substr( 0, iIndexOfSeparator );
|
2002-09-06 23:24:40 +00:00
|
|
|
value_data = line; // the rest
|
2003-01-03 05:29:45 +00:00
|
|
|
value_data.erase(0,iIndexOfSeparator+1);
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
else // no separator
|
|
|
|
|
{
|
|
|
|
|
value_name = line;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value_name.MakeLower();
|
|
|
|
|
|
2003-12-12 05:37:52 +00:00
|
|
|
if( -1 != value_name.Find("#player") )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2004-09-27 01:46:57 +00:00
|
|
|
iPlayer = atoi(value_data);
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
if( -1 != value_name.Find("#title") )
|
|
|
|
|
{
|
2003-01-02 22:10:51 +00:00
|
|
|
value_data.MakeLower();
|
2003-12-12 05:37:52 +00:00
|
|
|
|
2003-08-03 00:13:55 +00:00
|
|
|
// extract the Steps description (looks like 'Music <BASIC>')
|
2003-01-02 22:10:51 +00:00
|
|
|
int iPosOpenBracket = value_data.Find( "<" );
|
2002-09-06 23:24:40 +00:00
|
|
|
if( iPosOpenBracket == -1 )
|
2003-01-02 22:10:51 +00:00
|
|
|
iPosOpenBracket = value_data.Find( "(" );
|
|
|
|
|
int iPosCloseBracket = value_data.Find( ">" );
|
2002-09-06 23:24:40 +00:00
|
|
|
if( iPosCloseBracket == -1 )
|
2003-01-02 22:10:51 +00:00
|
|
|
iPosCloseBracket = value_data.Find( ")" );
|
2002-09-06 23:24:40 +00:00
|
|
|
|
|
|
|
|
if( iPosOpenBracket != -1 && iPosCloseBracket != -1 )
|
2003-12-12 05:37:52 +00:00
|
|
|
value_data = value_data.substr( iPosOpenBracket+1,
|
2003-12-13 03:54:13 +00:00
|
|
|
iPosCloseBracket-iPosOpenBracket-1 );
|
2003-08-03 00:13:55 +00:00
|
|
|
LOG->Trace( "Steps description found to be '%s'", value_data.c_str() );
|
2003-01-02 22:10:51 +00:00
|
|
|
|
|
|
|
|
out.SetDescription(value_data);
|
2002-09-06 23:24:40 +00:00
|
|
|
|
|
|
|
|
// if there's a 6 in the description, it's probably part of "6panel" or "6-panel"
|
2003-01-02 22:10:51 +00:00
|
|
|
if( value_data.Find("6") != -1 )
|
2003-08-07 06:36:34 +00:00
|
|
|
out.m_StepsType = STEPS_TYPE_DANCE_SOLO;
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
2003-12-12 05:37:52 +00:00
|
|
|
if( -1 != value_name.Find("#playlevel") )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2003-01-02 22:10:51 +00:00
|
|
|
out.SetMeter(atoi(value_data));
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
2004-09-29 07:19:44 +00:00
|
|
|
else if( value_name.size() == 6 && value_name[0] == '#'
|
2003-01-03 05:29:45 +00:00
|
|
|
&& IsAnInt( value_name.substr(1,3) )
|
|
|
|
|
&& IsAnInt( value_name.substr(4,2) ) ) // this is step or offset data. Looks like "#00705"
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2004-09-27 01:46:57 +00:00
|
|
|
int iMeasureNo = atoi( value_name.substr(1,3).c_str() );
|
|
|
|
|
int iRawTrackNum = atoi( value_name.substr(4,2).c_str() );
|
2003-10-28 06:36:11 +00:00
|
|
|
|
2002-09-06 23:24:40 +00:00
|
|
|
CString &sNoteData = value_data;
|
2004-09-29 07:19:44 +00:00
|
|
|
vector<TapNote> vTapNotes;
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2003-01-03 05:29:45 +00:00
|
|
|
for( int i=0; i+1<sNoteData.GetLength(); i+=2 )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2004-09-29 07:19:44 +00:00
|
|
|
CString sNoteId = sNoteData.substr(i,2);
|
|
|
|
|
if( sNoteId != "00" )
|
|
|
|
|
{
|
|
|
|
|
TapNote tn = TAP_ORIGINAL_TAP;
|
2004-10-23 23:41:49 +00:00
|
|
|
map<CString,int>::const_iterator it = mapWavIdToKeysoundIndex.find(sNoteId);
|
2004-09-29 07:19:44 +00:00
|
|
|
if( it != mapWavIdToKeysoundIndex.end() )
|
|
|
|
|
{
|
|
|
|
|
tn.bKeysound = true;
|
2004-10-23 23:41:49 +00:00
|
|
|
tn.iKeysoundIndex = it->second;
|
2004-09-29 07:19:44 +00:00
|
|
|
}
|
|
|
|
|
vTapNotes.push_back( tn );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
vTapNotes.push_back( TAP_EMPTY );
|
|
|
|
|
}
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-29 07:19:44 +00:00
|
|
|
const unsigned uNumNotesInThisMeasure = vTapNotes.size();
|
2003-10-28 06:36:11 +00:00
|
|
|
|
2004-09-29 07:19:44 +00:00
|
|
|
for( unsigned j=0; j<uNumNotesInThisMeasure; j++ )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2004-09-29 07:19:44 +00:00
|
|
|
if( vTapNotes[j].type != TapNote::empty )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2004-09-29 07:19:44 +00:00
|
|
|
float fPercentThroughMeasure = (float)j/(float)uNumNotesInThisMeasure;
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2004-10-24 17:36:13 +00:00
|
|
|
int row = (int) ( (iMeasureNo + fPercentThroughMeasure)
|
|
|
|
|
* BEATS_PER_MEASURE * ROWS_PER_BEAT );
|
|
|
|
|
|
|
|
|
|
// some BMS files seem to have funky alignment, causing us to write gigantic cache files.
|
|
|
|
|
// Try to correct for this by quantizing.
|
2004-10-24 17:44:51 +00:00
|
|
|
row = Quantize( row, ROWS_PER_MEASURE/64 );
|
2004-10-24 17:36:13 +00:00
|
|
|
|
2004-09-27 01:46:57 +00:00
|
|
|
BmsTrack bmsTrack;
|
2004-09-29 07:19:44 +00:00
|
|
|
bool bIsHold;
|
|
|
|
|
if( ConvertRawTrackToTapNote(iRawTrackNum, bmsTrack, bIsHold) )
|
|
|
|
|
{
|
2004-10-24 10:45:30 +00:00
|
|
|
TapNote tn = vTapNotes[j];
|
|
|
|
|
|
|
|
|
|
if( bmsTrack == BMS_AUTO_KEYSOUND_1 )
|
|
|
|
|
{
|
|
|
|
|
tn.type = TapNote::autoKeysound;
|
|
|
|
|
|
|
|
|
|
// shift the auto keysound as far right as possible
|
|
|
|
|
int iLastEmptyTrack = -1;
|
|
|
|
|
if( ndNotes.GetTapLastEmptyTrack(row,iLastEmptyTrack) &&
|
|
|
|
|
iLastEmptyTrack >= BMS_AUTO_KEYSOUND_1 )
|
|
|
|
|
{
|
|
|
|
|
bmsTrack = (BmsTrack)iLastEmptyTrack;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// no room for this note. Drop it.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if( bIsHold )
|
2004-10-23 23:41:49 +00:00
|
|
|
{
|
2004-10-24 10:45:30 +00:00
|
|
|
tn.type = TapNote::hold_head;
|
2004-10-23 23:41:49 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2004-10-24 10:45:30 +00:00
|
|
|
tn.type = TapNote::tap;
|
2004-10-23 23:41:49 +00:00
|
|
|
}
|
2004-10-24 10:45:30 +00:00
|
|
|
|
|
|
|
|
ndNotes.SetTapNote( bmsTrack, row, tn );
|
2004-09-29 07:19:44 +00:00
|
|
|
}
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-12-12 05:37:52 +00:00
|
|
|
|
2004-10-24 10:45:30 +00:00
|
|
|
// we're done reading in all of the BMS values
|
|
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
out.m_StepsType = DetermineStepsType( iPlayer, ndNotes );
|
2003-10-28 06:36:11 +00:00
|
|
|
|
2003-08-07 06:36:34 +00:00
|
|
|
if( out.m_StepsType == STEPS_TYPE_INVALID )
|
2003-07-12 19:06:04 +00:00
|
|
|
{
|
2004-03-08 05:11:26 +00:00
|
|
|
LOG->Warn( "Couldn't determine note type of file '%s'", sPath.c_str() );
|
2003-07-12 19:06:04 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2004-10-24 10:45:30 +00:00
|
|
|
// shift all of the autokeysound tracks onto the main tracks
|
|
|
|
|
for( int t=BMS_AUTO_KEYSOUND_1; t<BMS_AUTO_KEYSOUND_1+NUM_AUTO_KEYSOUND_TRACKS; t++ )
|
|
|
|
|
{
|
|
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK( ndNotes, t, row )
|
|
|
|
|
{
|
|
|
|
|
TapNote tn = ndNotes.GetTapNote( t, row );
|
|
|
|
|
int iEmptyTrack;
|
|
|
|
|
if( ndNotes.GetTapFirstEmptyTrack(row, iEmptyTrack) )
|
|
|
|
|
{
|
|
|
|
|
ndNotes.SetTapNote( iEmptyTrack, row, tn );
|
|
|
|
|
ndNotes.SetTapNote( t, row, TAP_EMPTY );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LOG->Warn( "No room to shift." );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int iNumNewTracks = GameManager::StepsTypeToNumTracks( out.m_StepsType );
|
|
|
|
|
vector<int> iTransformNewToOld;
|
|
|
|
|
iTransformNewToOld.resize( iNumNewTracks, -1 );
|
2002-09-07 05:04:04 +00:00
|
|
|
|
2003-08-07 06:36:34 +00:00
|
|
|
switch( out.m_StepsType )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2003-12-13 03:54:13 +00:00
|
|
|
// fix PNM &c.
|
2003-08-07 06:16:17 +00:00
|
|
|
case STEPS_TYPE_DANCE_SINGLE:
|
2003-10-28 06:36:11 +00:00
|
|
|
iTransformNewToOld[0] = BMS_P1_KEY1;
|
|
|
|
|
iTransformNewToOld[1] = BMS_P1_KEY3;
|
|
|
|
|
iTransformNewToOld[2] = BMS_P1_KEY5;
|
|
|
|
|
iTransformNewToOld[3] = BMS_P1_TURN;
|
2002-09-06 23:24:40 +00:00
|
|
|
break;
|
2003-08-07 06:16:17 +00:00
|
|
|
case STEPS_TYPE_DANCE_DOUBLE:
|
|
|
|
|
case STEPS_TYPE_DANCE_COUPLE:
|
2003-10-28 06:36:11 +00:00
|
|
|
iTransformNewToOld[0] = BMS_P1_KEY1;
|
|
|
|
|
iTransformNewToOld[1] = BMS_P1_KEY3;
|
|
|
|
|
iTransformNewToOld[2] = BMS_P1_KEY5;
|
|
|
|
|
iTransformNewToOld[3] = BMS_P1_TURN;
|
|
|
|
|
iTransformNewToOld[4] = BMS_P2_KEY1;
|
|
|
|
|
iTransformNewToOld[5] = BMS_P2_KEY3;
|
|
|
|
|
iTransformNewToOld[6] = BMS_P2_KEY5;
|
|
|
|
|
iTransformNewToOld[7] = BMS_P2_TURN;
|
2002-09-06 23:24:40 +00:00
|
|
|
break;
|
2003-08-07 06:16:17 +00:00
|
|
|
case STEPS_TYPE_DANCE_SOLO:
|
2004-09-25 17:41:31 +00:00
|
|
|
case STEPS_TYPE_BM_SINGLE5:
|
2004-09-27 01:49:18 +00:00
|
|
|
// Hey! Why are these exactly the same? :-)
|
2003-10-28 06:36:11 +00:00
|
|
|
iTransformNewToOld[0] = BMS_P1_KEY1;
|
|
|
|
|
iTransformNewToOld[1] = BMS_P1_KEY2;
|
|
|
|
|
iTransformNewToOld[2] = BMS_P1_KEY3;
|
|
|
|
|
iTransformNewToOld[3] = BMS_P1_KEY4;
|
|
|
|
|
iTransformNewToOld[4] = BMS_P1_KEY5;
|
|
|
|
|
iTransformNewToOld[5] = BMS_P1_TURN;
|
2002-09-06 23:24:40 +00:00
|
|
|
break;
|
2003-10-28 06:36:11 +00:00
|
|
|
case STEPS_TYPE_PNM_FIVE:
|
|
|
|
|
iTransformNewToOld[0] = BMS_P1_KEY3;
|
|
|
|
|
iTransformNewToOld[1] = BMS_P1_KEY4;
|
|
|
|
|
iTransformNewToOld[2] = BMS_P1_KEY5;
|
2003-12-12 05:37:52 +00:00
|
|
|
// fix these columns!
|
|
|
|
|
iTransformNewToOld[3] = BMS_P2_KEY2;
|
|
|
|
|
iTransformNewToOld[4] = BMS_P2_KEY3;
|
|
|
|
|
break;
|
2003-10-28 06:36:11 +00:00
|
|
|
case STEPS_TYPE_PNM_NINE:
|
2003-12-12 05:37:52 +00:00
|
|
|
iTransformNewToOld[0] = BMS_P1_KEY1; // lwhite
|
|
|
|
|
iTransformNewToOld[1] = BMS_P1_KEY2; // lyellow
|
|
|
|
|
iTransformNewToOld[2] = BMS_P1_KEY3; // lgreen
|
|
|
|
|
iTransformNewToOld[3] = BMS_P1_KEY4; // lblue
|
|
|
|
|
iTransformNewToOld[4] = BMS_P1_KEY5; // red
|
|
|
|
|
// fix these columns!
|
|
|
|
|
iTransformNewToOld[5] = BMS_P2_KEY2; // rblue
|
|
|
|
|
iTransformNewToOld[6] = BMS_P2_KEY3; // rgreen
|
|
|
|
|
iTransformNewToOld[7] = BMS_P2_KEY4; // ryellow
|
|
|
|
|
iTransformNewToOld[8] = BMS_P2_KEY5; // rwhite
|
2003-10-28 06:36:11 +00:00
|
|
|
break;
|
2004-09-25 17:41:31 +00:00
|
|
|
case STEPS_TYPE_BM_DOUBLE5:
|
2003-10-28 06:36:11 +00:00
|
|
|
iTransformNewToOld[0] = BMS_P1_KEY1;
|
|
|
|
|
iTransformNewToOld[1] = BMS_P1_KEY2;
|
|
|
|
|
iTransformNewToOld[2] = BMS_P1_KEY3;
|
|
|
|
|
iTransformNewToOld[3] = BMS_P1_KEY4;
|
|
|
|
|
iTransformNewToOld[4] = BMS_P1_KEY5;
|
|
|
|
|
iTransformNewToOld[5] = BMS_P1_TURN;
|
|
|
|
|
iTransformNewToOld[6] = BMS_P2_KEY1;
|
|
|
|
|
iTransformNewToOld[7] = BMS_P2_KEY2;
|
|
|
|
|
iTransformNewToOld[8] = BMS_P2_KEY3;
|
|
|
|
|
iTransformNewToOld[9] = BMS_P2_KEY4;
|
|
|
|
|
iTransformNewToOld[10] = BMS_P2_KEY5;
|
|
|
|
|
iTransformNewToOld[11] = BMS_P2_TURN;
|
|
|
|
|
break;
|
2004-09-25 17:41:31 +00:00
|
|
|
case STEPS_TYPE_BM_SINGLE7:
|
2003-10-28 06:36:11 +00:00
|
|
|
iTransformNewToOld[0] = BMS_P1_KEY1;
|
|
|
|
|
iTransformNewToOld[1] = BMS_P1_KEY2;
|
|
|
|
|
iTransformNewToOld[2] = BMS_P1_KEY3;
|
|
|
|
|
iTransformNewToOld[3] = BMS_P1_KEY4;
|
|
|
|
|
iTransformNewToOld[4] = BMS_P1_KEY5;
|
|
|
|
|
iTransformNewToOld[5] = BMS_P1_KEY6;
|
|
|
|
|
iTransformNewToOld[6] = BMS_P1_KEY7;
|
|
|
|
|
iTransformNewToOld[7] = BMS_P1_TURN;
|
2003-10-29 09:02:14 +00:00
|
|
|
break;
|
2004-09-25 17:41:31 +00:00
|
|
|
case STEPS_TYPE_BM_DOUBLE7:
|
2003-10-28 06:36:11 +00:00
|
|
|
iTransformNewToOld[0] = BMS_P1_KEY1;
|
|
|
|
|
iTransformNewToOld[1] = BMS_P1_KEY2;
|
|
|
|
|
iTransformNewToOld[2] = BMS_P1_KEY3;
|
|
|
|
|
iTransformNewToOld[3] = BMS_P1_KEY4;
|
|
|
|
|
iTransformNewToOld[4] = BMS_P1_KEY5;
|
|
|
|
|
iTransformNewToOld[5] = BMS_P1_KEY6;
|
|
|
|
|
iTransformNewToOld[6] = BMS_P1_KEY7;
|
|
|
|
|
iTransformNewToOld[7] = BMS_P1_TURN;
|
|
|
|
|
iTransformNewToOld[8] = BMS_P2_KEY1;
|
|
|
|
|
iTransformNewToOld[9] = BMS_P2_KEY2;
|
|
|
|
|
iTransformNewToOld[10] = BMS_P2_KEY3;
|
|
|
|
|
iTransformNewToOld[11] = BMS_P2_KEY4;
|
|
|
|
|
iTransformNewToOld[12] = BMS_P2_KEY5;
|
|
|
|
|
iTransformNewToOld[13] = BMS_P2_KEY6;
|
|
|
|
|
iTransformNewToOld[14] = BMS_P2_KEY7;
|
|
|
|
|
iTransformNewToOld[15] = BMS_P2_TURN;
|
2003-10-29 09:02:14 +00:00
|
|
|
break;
|
2002-09-06 23:24:40 +00:00
|
|
|
default:
|
2003-07-12 19:06:04 +00:00
|
|
|
ASSERT(0);
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
NoteData noteData2;
|
|
|
|
|
noteData2.SetNumTracks( iNumNewTracks );
|
2004-10-24 19:21:44 +00:00
|
|
|
noteData2.LoadTransformed( ndNotes, iNumNewTracks, &*iTransformNewToOld.begin() );
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
out.SetNoteData( noteData2 );
|
2002-09-06 23:24:40 +00:00
|
|
|
|
|
|
|
|
out.TidyUpData();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-11 05:15:46 +00:00
|
|
|
void BMSLoader::GetApplicableFiles( CString sPath, CStringArray &out )
|
|
|
|
|
{
|
|
|
|
|
GetDirListing( sPath + CString("*.bms"), out );
|
2004-09-25 08:00:18 +00:00
|
|
|
GetDirListing( sPath + CString("*.bme"), out );
|
2002-09-11 05:15:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BMSLoader::LoadFromDir( CString sDir, Song &out )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2003-04-25 00:01:35 +00:00
|
|
|
LOG->Trace( "Song::LoadFromBMSDir(%s)", sDir.c_str() );
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2004-09-29 07:19:44 +00:00
|
|
|
ASSERT( out.m_vsKeysoundFile.empty() );
|
|
|
|
|
|
2002-09-06 23:24:40 +00:00
|
|
|
CStringArray arrayBMSFileNames;
|
2002-09-11 05:15:46 +00:00
|
|
|
GetApplicableFiles( sDir, arrayBMSFileNames );
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2003-07-12 19:06:04 +00:00
|
|
|
/* We should have at least one; if we had none, we shouldn't have been
|
|
|
|
|
* called to begin with. */
|
|
|
|
|
ASSERT( arrayBMSFileNames.size() );
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2004-09-29 07:19:44 +00:00
|
|
|
// This maps from a BMS wav ID (e.g. "1A") to an entry in the Song's
|
|
|
|
|
// keysound vector. Fill this in below while parsing the song data.
|
2004-10-23 23:41:49 +00:00
|
|
|
map<CString,int> mapWavIdToKeysoundIndex;
|
2004-04-17 01:47:55 +00:00
|
|
|
|
2002-12-13 23:41:11 +00:00
|
|
|
CString sPath = out.GetSongDir() + arrayBMSFileNames[0];
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2004-06-06 21:04:48 +00:00
|
|
|
RageFile file;
|
|
|
|
|
if( !file.Open(sPath) )
|
2004-07-13 00:03:47 +00:00
|
|
|
RageException::Throw( "Failed to open \"%s\" for reading: %s", sPath.c_str(), file.GetError().c_str() );
|
|
|
|
|
while( !file.AtEOF() )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2004-06-06 21:04:48 +00:00
|
|
|
CString line;
|
|
|
|
|
if( file.GetLine( line ) == -1 )
|
|
|
|
|
{
|
|
|
|
|
LOG->Warn( "Error reading \"%s\": %s", sPath.c_str(), file.GetError().c_str() );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2003-02-16 05:12:37 +00:00
|
|
|
StripCrnl(line);
|
2002-09-06 23:24:40 +00:00
|
|
|
CString value_name; // fill these in
|
2003-12-12 05:37:52 +00:00
|
|
|
CString value_data;
|
2002-09-06 23:24:40 +00:00
|
|
|
|
|
|
|
|
// 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;
|
2003-12-12 05:37:52 +00:00
|
|
|
|
2002-09-06 23:24:40 +00:00
|
|
|
int iIndexOfSeparator = min( iIndexOfFirstSpace, iIndexOfFirstColon );
|
|
|
|
|
|
|
|
|
|
if( iIndexOfSeparator != 10000 )
|
|
|
|
|
{
|
2003-01-03 05:29:45 +00:00
|
|
|
value_name = line.substr( 0, iIndexOfSeparator );
|
2002-09-06 23:24:40 +00:00
|
|
|
value_data = line; // the rest
|
2003-01-03 05:29:45 +00:00
|
|
|
value_data.erase(0,iIndexOfSeparator+1);
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
else // no separator
|
|
|
|
|
{
|
|
|
|
|
value_name = line;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
value_name.MakeLower();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// handle the data
|
2003-12-12 05:37:52 +00:00
|
|
|
if( value_name == "#title" )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2003-08-03 00:13:55 +00:00
|
|
|
// strip Steps type out of description leaving only song title - looks like 'B4U <BASIC>'
|
2004-06-16 07:03:25 +00:00
|
|
|
size_t iIndex = value_data.find_last_of('<');
|
2003-01-03 05:29:45 +00:00
|
|
|
if( iIndex == value_data.npos )
|
|
|
|
|
iIndex = value_data.find_last_of('(');
|
|
|
|
|
if( iIndex != value_data.npos )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
|
|
|
|
value_data = value_data.Left( iIndex );
|
2003-12-12 05:37:52 +00:00
|
|
|
GetMainAndSubTitlesFromFullTitle( value_data, out.m_sMainTitle,
|
2003-12-13 03:54:13 +00:00
|
|
|
out.m_sSubTitle );
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
out.m_sMainTitle = value_data;
|
|
|
|
|
}
|
2003-12-12 05:37:52 +00:00
|
|
|
else if( value_name == "#artist" )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
|
|
|
|
out.m_sArtist = value_data;
|
|
|
|
|
}
|
2003-12-12 05:37:52 +00:00
|
|
|
else if( value_name == "#bpm" )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2004-08-10 20:57:59 +00:00
|
|
|
BPMSegment newSeg( 0, strtof(value_data, NULL) );
|
2002-09-06 23:24:40 +00:00
|
|
|
out.AddBPMSegment( newSeg );
|
2003-12-12 05:37:52 +00:00
|
|
|
|
2002-09-06 23:24:40 +00:00
|
|
|
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", newSeg.m_fStartBeat, newSeg.m_fBPM );
|
|
|
|
|
}
|
2003-12-12 05:37:52 +00:00
|
|
|
else if( value_name == "#backbmp" )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
|
|
|
|
out.m_sBackgroundFile = value_data;
|
|
|
|
|
}
|
2003-12-12 05:37:52 +00:00
|
|
|
else if( value_name == "#wav" )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
|
|
|
|
out.m_sMusicFile = value_data;
|
|
|
|
|
}
|
2004-09-29 07:19:44 +00:00
|
|
|
else if( value_name.size() == 6 && value_name.Left(4) == "#wav" ) // this is keysound file name. Looks like "#WAV1A"
|
|
|
|
|
{
|
|
|
|
|
CString sWavID = value_name.Right(2);
|
|
|
|
|
sWavID.MakeUpper(); // HACK: undo the MakeLower()
|
|
|
|
|
out.m_vsKeysoundFile.push_back( value_data );
|
|
|
|
|
mapWavIdToKeysoundIndex[ sWavID ] = out.m_vsKeysoundFile.size()-1;
|
2004-10-05 10:36:11 +00:00
|
|
|
LOG->Trace( "Inserting keysound index %lu '%s'", out.m_vsKeysoundFile.size()-1, sWavID.c_str() );
|
2004-09-29 07:19:44 +00:00
|
|
|
}
|
|
|
|
|
else if( value_name.size() == 6 && value_name[0] == '#'
|
2003-01-03 05:29:45 +00:00
|
|
|
&& IsAnInt( value_name.substr(1,3) )
|
|
|
|
|
&& IsAnInt( value_name.substr(4,2) ) ) // this is step or offset data. Looks like "#00705"
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2003-01-03 05:29:45 +00:00
|
|
|
int iMeasureNo = atoi( value_name.substr(1,3).c_str() );
|
|
|
|
|
int iBMSTrackNo = atoi( value_name.substr(4,2).c_str() );
|
2002-09-06 23:24:40 +00:00
|
|
|
|
|
|
|
|
CString sNoteData = value_data;
|
2003-01-03 05:56:28 +00:00
|
|
|
vector<int> arrayNotes;
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2003-01-03 05:29:45 +00:00
|
|
|
for( int i=0; i+1<sNoteData.GetLength(); i+=2 )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2003-01-03 05:29:45 +00:00
|
|
|
CString sNote = sNoteData.substr(i,2);
|
2002-09-06 23:24:40 +00:00
|
|
|
int iNote;
|
|
|
|
|
sscanf( sNote, "%x", &iNote ); // data is in hexadecimal
|
2002-10-31 04:23:39 +00:00
|
|
|
arrayNotes.push_back( iNote );
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
|
2002-10-31 02:54:49 +00:00
|
|
|
const unsigned iNumNotesInThisMeasure = arrayNotes.size();
|
|
|
|
|
for( unsigned j=0; j<iNumNotesInThisMeasure; j++ )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
|
|
|
|
if( arrayNotes[j] == 0 )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
float fPercentThroughMeasure = (float)j/(float)iNumNotesInThisMeasure;
|
|
|
|
|
|
|
|
|
|
// index is in quarter beats starting at beat 0
|
|
|
|
|
int iStepIndex = (int) ( (iMeasureNo + fPercentThroughMeasure)
|
|
|
|
|
* BEATS_PER_MEASURE * ROWS_PER_BEAT );
|
|
|
|
|
|
|
|
|
|
switch( iBMSTrackNo )
|
|
|
|
|
{
|
|
|
|
|
case 1: { // background music track
|
|
|
|
|
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;
|
2003-12-18 04:48:26 +00:00
|
|
|
fBPS = out.m_Timing.m_BPMSegments[0].m_fBPM/60.0f;
|
|
|
|
|
out.m_Timing.m_fBeat0OffsetInSeconds = fBeatOffset / fBPS;
|
2002-09-06 23:24:40 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 3: { // bpm change
|
|
|
|
|
BPMSegment newSeg( NoteRowToBeat(iStepIndex), (float)arrayNotes[j] );
|
|
|
|
|
out.AddBPMSegment( newSeg );
|
|
|
|
|
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", newSeg.m_fStartBeat, newSeg.m_fBPM );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 8: { // indirect bpm
|
2003-12-13 03:54:13 +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. */
|
2002-09-06 23:24:40 +00:00
|
|
|
CString sTagToLookFor = ssprintf( "#BPM%02x", arrayNotes[j] );
|
|
|
|
|
float fBPM = -1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// open the song file again and and look for this tag's value
|
2003-11-30 06:20:25 +00:00
|
|
|
/* I don't like this. I think we should just seek back to the beginning
|
2003-12-13 03:54:13 +00:00
|
|
|
* rather than open the file again. However, I'm not changing the logic,
|
2003-11-30 06:20:25 +00:00
|
|
|
* only the implementation. -- Steve
|
|
|
|
|
*/
|
2004-09-25 02:50:31 +00:00
|
|
|
RageFile file;
|
|
|
|
|
if( !file.Open(sPath) )
|
|
|
|
|
RageException::Throw( "Failed to open %s for reading: %s", sPath.c_str(), file.GetError().c_str() );
|
2003-11-30 06:20:25 +00:00
|
|
|
while (!file.AtEOF())
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2004-06-06 21:04:48 +00:00
|
|
|
CString line;
|
|
|
|
|
file.GetLine( line );
|
2003-02-16 05:12:37 +00:00
|
|
|
StripCrnl(line);
|
2002-09-06 23:24:40 +00:00
|
|
|
CString value_name; // fill these in
|
2003-12-12 05:37:52 +00:00
|
|
|
CString value_data;
|
2002-09-06 23:24:40 +00:00
|
|
|
|
|
|
|
|
// 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;
|
2003-12-12 05:37:52 +00:00
|
|
|
|
2002-09-06 23:24:40 +00:00
|
|
|
int iIndexOfSeparator = min( iIndexOfFirstSpace, iIndexOfFirstColon );
|
|
|
|
|
|
|
|
|
|
if( iIndexOfSeparator != 10000 )
|
|
|
|
|
{
|
2003-01-03 05:29:45 +00:00
|
|
|
value_name = line.substr( 0, iIndexOfSeparator );
|
2002-09-06 23:24:40 +00:00
|
|
|
value_data = line; // the rest
|
2003-01-03 05:29:45 +00:00
|
|
|
value_data.erase(0,iIndexOfSeparator+1);
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
else // no separator
|
|
|
|
|
value_name = line;
|
|
|
|
|
|
|
|
|
|
if( 0==stricmp(value_name, sTagToLookFor) )
|
|
|
|
|
{
|
2004-08-10 20:57:59 +00:00
|
|
|
fBPM = strtof( value_data, NULL );
|
2002-09-06 23:24:40 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( fBPM == -1 ) // we didn't find the line we were looking for
|
2004-03-08 05:11:26 +00:00
|
|
|
LOG->Warn( "Couldn't find tag '%s' in '%s'.", sTagToLookFor.c_str(), sPath.c_str() );
|
2002-09-06 23:24:40 +00:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
BPMSegment newSeg( NoteRowToBeat(iStepIndex), fBPM );
|
|
|
|
|
out.AddBPMSegment( newSeg );
|
2003-12-13 03:54:13 +00:00
|
|
|
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", newSeg.m_fStartBeat, newSeg.m_fBPM );
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 9: { // stop
|
2003-12-13 03:54:13 +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. */
|
2002-09-06 23:24:40 +00:00
|
|
|
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
|
2004-09-25 02:50:31 +00:00
|
|
|
RageFile file;
|
|
|
|
|
if( !file.Open(sPath) )
|
|
|
|
|
RageException::Throw( "Failed to open %s for reading: %s", sPath.c_str(), file.GetError().c_str() );
|
2003-11-30 06:20:25 +00:00
|
|
|
while (!file.AtEOF())
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2004-06-06 21:04:48 +00:00
|
|
|
CString line;
|
|
|
|
|
file.GetLine( line );
|
2003-02-16 05:12:37 +00:00
|
|
|
StripCrnl(line);
|
2002-09-06 23:24:40 +00:00
|
|
|
CString value_name; // fill these in
|
2003-12-12 05:37:52 +00:00
|
|
|
CString value_data;
|
2002-09-06 23:24:40 +00:00
|
|
|
|
|
|
|
|
// 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;
|
2003-12-12 05:37:52 +00:00
|
|
|
|
2002-09-06 23:24:40 +00:00
|
|
|
int iIndexOfSeparator = min( iIndexOfFirstSpace, iIndexOfFirstColon );
|
|
|
|
|
|
|
|
|
|
if( iIndexOfSeparator != 10000 )
|
|
|
|
|
{
|
2003-01-03 05:29:45 +00:00
|
|
|
value_name = line.substr( 0, iIndexOfSeparator );
|
2002-09-06 23:24:40 +00:00
|
|
|
value_data = line; // the rest
|
2003-01-03 05:29:45 +00:00
|
|
|
value_data.erase(0,iIndexOfSeparator+1);
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
else // no separator
|
|
|
|
|
{
|
|
|
|
|
value_name = line;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( 0==stricmp(value_name, sTagToLookFor) )
|
|
|
|
|
{
|
|
|
|
|
// find the BPM at the time of this freeze
|
|
|
|
|
float fBPM = -1;
|
2003-12-18 04:48:26 +00:00
|
|
|
for( unsigned i=0; i<out.m_Timing.m_BPMSegments.size()-1; i++ )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2003-12-18 04:48:26 +00:00
|
|
|
if( out.m_Timing.m_BPMSegments[i].m_fStartBeat <= fFreezeStartBeat &&
|
|
|
|
|
out.m_Timing.m_BPMSegments[i+1].m_fStartBeat > fFreezeStartBeat )
|
2002-09-06 23:24:40 +00:00
|
|
|
{
|
2003-12-18 04:48:26 +00:00
|
|
|
fBPM = out.m_Timing.m_BPMSegments[i].m_fBPM;
|
2002-09-06 23:24:40 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// the BPM segment of this beat is the last BPM segment
|
|
|
|
|
if( fBPM == -1 )
|
2003-12-18 04:48:26 +00:00
|
|
|
fBPM = out.m_Timing.m_BPMSegments[out.m_Timing.m_BPMSegments.size()-1].m_fBPM;
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2004-08-10 20:57:59 +00:00
|
|
|
fFreezeSecs = strtof(value_data,NULL)/(fBPM*0.81f); // I have no idea what units these are in, so I experimented until finding this factor.
|
2002-09-06 23:24:40 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( fFreezeSecs == -1 ) // we didn't find the line we were looking for
|
|
|
|
|
{
|
2004-03-08 05:11:26 +00:00
|
|
|
LOG->Warn( "Couldn't find tag '%s' in '%s'.", sTagToLookFor.c_str(), sPath.c_str() );
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
StopSegment newSeg( fFreezeStartBeat, fFreezeSecs );
|
|
|
|
|
out.AddStopSegment( newSeg );
|
2003-12-13 03:54:13 +00:00
|
|
|
LOG->Trace( "Inserting new Freeze at beat %f, secs %f", newSeg.m_fStartBeat, newSeg.m_fStopSeconds );
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-21 07:53:39 +00:00
|
|
|
for( unsigned i=0; i<out.m_Timing.m_BPMSegments.size(); i++ )
|
2003-12-12 05:37:52 +00:00
|
|
|
LOG->Trace( "There is a BPM change at beat %f, BPM %f, index %d",
|
2003-12-18 04:48:26 +00:00
|
|
|
out.m_Timing.m_BPMSegments[i].m_fStartBeat, out.m_Timing.m_BPMSegments[i].m_fBPM, i );
|
2002-09-06 23:24:40 +00:00
|
|
|
|
2004-09-29 07:19:44 +00:00
|
|
|
|
|
|
|
|
// Now that we've parsed the keysound data, load the Steps from the rest
|
|
|
|
|
// of the .bms files.
|
|
|
|
|
for( unsigned i=0; i<arrayBMSFileNames.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
Steps* pNewNotes = new Steps;
|
|
|
|
|
|
|
|
|
|
const bool ok = LoadFromBMSFile(
|
|
|
|
|
out.GetSongDir() + arrayBMSFileNames[i],
|
|
|
|
|
*pNewNotes,
|
|
|
|
|
mapWavIdToKeysoundIndex );
|
|
|
|
|
if( ok )
|
|
|
|
|
out.AddSteps( pNewNotes );
|
|
|
|
|
else
|
|
|
|
|
delete pNewNotes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SlideDuplicateDifficulties( out );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2002-10-11 20:47:28 +00:00
|
|
|
return true;
|
2002-09-06 23:24:40 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-12 05:37:52 +00:00
|
|
|
|
2004-04-17 01:47:55 +00:00
|
|
|
void BMSLoader::SlideDuplicateDifficulties( Song &p )
|
|
|
|
|
{
|
|
|
|
|
/* BMS files have to guess the Difficulty from the meter; this is inaccurate,
|
|
|
|
|
* and often leads to duplicates. Slide duplicate difficulties upwards. We
|
|
|
|
|
* only do this with BMS files, since a very common bug was having *all*
|
|
|
|
|
* difficulties slid upwards due to (for example) having two beginner steps.
|
|
|
|
|
* We do a second pass in Song::TidyUpData to eliminate any remaining duplicates
|
|
|
|
|
* after this. */
|
|
|
|
|
for( int i=0; i<NUM_STEPS_TYPES; i++ )
|
|
|
|
|
{
|
|
|
|
|
StepsType st = (StepsType)i;
|
|
|
|
|
|
|
|
|
|
for( unsigned j=0; j<=DIFFICULTY_CHALLENGE; j++ ) // not DIFFICULTY_EDIT
|
|
|
|
|
{
|
|
|
|
|
Difficulty dc = (Difficulty)j;
|
|
|
|
|
|
|
|
|
|
vector<Steps*> vSteps;
|
|
|
|
|
p.GetSteps( vSteps, st, dc );
|
|
|
|
|
|
2004-04-18 07:34:37 +00:00
|
|
|
StepsUtil::SortNotesArrayByDifficulty( vSteps );
|
2004-04-17 01:47:55 +00:00
|
|
|
for( unsigned k=1; k<vSteps.size(); k++ )
|
|
|
|
|
{
|
|
|
|
|
Steps* pSteps = vSteps[k];
|
|
|
|
|
|
|
|
|
|
Difficulty dc2 = min( (Difficulty)(dc+1), DIFFICULTY_CHALLENGE );
|
|
|
|
|
pSteps->SetDifficulty( dc2 );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-05-31 21:55:14 +00:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* (c) 2001-2004 Chris Danford, Glenn Maynard
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
|
|
|
* whom the Software is furnished to do so, provided that the above
|
|
|
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
|
|
|
* the Software and that both the above copyright notice(s) and this
|
|
|
|
|
* permission notice appear in supporting documentation.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
|
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
|
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
|
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
|
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*/
|