Merging of the SSCNotes branch to default.
This commit is contained in:
@@ -1237,6 +1237,7 @@
|
||||
</Class>
|
||||
<Class name='Steps'>
|
||||
<Function name='GetDescription'/>
|
||||
<Function name='GetAuthorCredit'/>
|
||||
<Function name='GetDifficulty'/>
|
||||
<Function name='GetFilename'/>
|
||||
<Function name='GetHash'/>
|
||||
|
||||
@@ -3033,6 +3033,9 @@
|
||||
<Function name='GetDescription' return='string' arguments=''>
|
||||
Returns the Steps description.
|
||||
</Function>
|
||||
<Function name='GetAuthorCredit' return='string' arguments=''>
|
||||
Returns the author that made that particular Steps pattern.
|
||||
</Function>
|
||||
<Function name='GetDifficulty' return='Difficulty' arguments=''>
|
||||
Returns the Steps difficulty.
|
||||
</Function>
|
||||
|
||||
@@ -989,6 +989,7 @@ SoundResampleQuality=Resampling Quality
|
||||
SoundVolume=Sound Volume
|
||||
SoundVolumeAttract=Attract Volume
|
||||
Speed=Speed
|
||||
Step Author=Step Author
|
||||
Steps=Steps
|
||||
Stream=Stream
|
||||
Style=Style
|
||||
@@ -1204,6 +1205,7 @@ Enter a new artist.=Enter a new artist.
|
||||
Enter a new genre.=Enter a new genre.
|
||||
Enter a new credit.=Enter a new credit.
|
||||
Enter a new description.=Enter a new description.
|
||||
Enter the author who made this step pattern.=Enter the author who made this step pattern.
|
||||
Enter a new last beat hint.=Enter a new last beat hint.
|
||||
Enter a new main title transliteration.=Enter a new main title transliteration.
|
||||
Enter a new main title.=Enter a new main title.
|
||||
@@ -1373,6 +1375,10 @@ Error deleting the file '%s'.=Error deleting the file "%s".
|
||||
Error renaming file. Destination file '%s' already exists.=Error renaming file. Destination file '%s' already exists.
|
||||
Error writing file '%s'.=Error writing file '%s'.
|
||||
|
||||
[NotesWriterSSC]
|
||||
Error renaming file. Destination file '%s' already exists.=Error renaming file. Destination file '%s' already exists.
|
||||
Error writing file '%s'.=Error writing file '%s'.
|
||||
|
||||
[ScreenOptionsManageProfiles]
|
||||
Are you sure you want to clear all data in the profile '%s'?=Are you sure you want to clear all data in the profile &oq;%s&cq;?
|
||||
Are you sure you want to delete the profile '%s'?=Are you sure you want to delete the profile &oq;%s&cq;?
|
||||
@@ -1604,6 +1610,7 @@ The folder "%s" appears to be a song folder. All song folders must reside in a
|
||||
You must supply a name for your new edit.=You must supply a name for your new edit.
|
||||
The name you chose conflicts with another edit. Please use a different name.=The name you chose conflicts with another edit. Please use a different name.
|
||||
The edit name cannot contain any of the following characters: %s=The edit name can not contain any of the following characters: %s
|
||||
The step author's name cannot contain any of the following characters: %s=The step author's name cannot contain any of the following characters: %s
|
||||
|
||||
[SongSort]
|
||||
FewestPlays=Fewest Plays
|
||||
|
||||
+13
-19
@@ -1,4 +1,4 @@
|
||||
#TITLE:Sync;
|
||||
#TITLE:Sync;
|
||||
#SUBTITLE:;
|
||||
#ARTIST:Unknown artist;
|
||||
#TITLETRANSLIT:;
|
||||
@@ -20,18 +20,15 @@
|
||||
#BGCHANGES:;
|
||||
#KEYSOUNDS:;
|
||||
|
||||
//---------------dance-single - blank----------------
|
||||
#NOTEDATA:;
|
||||
#STEPSTYPE:dance-single:;
|
||||
#DESCRIPTION:blank;
|
||||
#DIFFICULTY:Beginner;
|
||||
#METER:1;
|
||||
#RADARVALUES:0.302,0.205,0.000,0.000,0.000;
|
||||
#NOTES:
|
||||
dance-single:
|
||||
blank:
|
||||
Beginner:
|
||||
1:
|
||||
0.302,0.205,0.000,0.000,0.000:
|
||||
// measure 1
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
, // measure 2
|
||||
1000
|
||||
0001
|
||||
@@ -97,18 +94,15 @@
|
||||
0001
|
||||
;
|
||||
|
||||
//---------------dance-single - blank----------------
|
||||
#NOTEDATA:;
|
||||
#STEPSTYPE:pump-single;
|
||||
#DESCRIPTION:blank;
|
||||
#DIFFICULTY:Beginner;
|
||||
#METER:1;
|
||||
#RADARVALUES:0.302,0.205,0.000,0.000,0.000;
|
||||
#NOTES:
|
||||
pump-single:
|
||||
blank:
|
||||
Beginner:
|
||||
1:
|
||||
0.302,0.205,0.000,0.000,0.000:
|
||||
// measure 1
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
, // measure 2
|
||||
10000
|
||||
00001
|
||||
+13
-56
@@ -6,44 +6,28 @@
|
||||
// a song file here.
|
||||
#TITLE:ScreenHowToPlay steps;
|
||||
#ARTIST:foo;
|
||||
#BPMS:0.000=100.000
|
||||
#BPMS:0.000=100.000;
|
||||
#STOPS:16=2,18=2,20=2,22=2;
|
||||
|
||||
#NOTEDATA:;
|
||||
#STEPSTYPE:dance-single;
|
||||
#DESCRIPTION:BASIC;
|
||||
#DIFFICULTY:easy;
|
||||
#METER:1;
|
||||
#RADARVALUES:0,0,0,0,0;
|
||||
#NOTES:
|
||||
dance-single:
|
||||
BASIC:
|
||||
easy:
|
||||
1:
|
||||
0,0,0,0,0:
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
,
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
,
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
,
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
,
|
||||
0100
|
||||
0000
|
||||
0010
|
||||
0000
|
||||
,
|
||||
1000
|
||||
0000
|
||||
1001
|
||||
0000
|
||||
,
|
||||
1000
|
||||
0100
|
||||
@@ -79,51 +63,30 @@
|
||||
0000
|
||||
,
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
,
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
0000
|
||||
,;
|
||||
|
||||
#NOTEDATA:;
|
||||
#STEPSTYPE:pump-single;
|
||||
#DESCRIPTION:NORMAL;
|
||||
#DIFFICULTY:easy;
|
||||
#METER:1;
|
||||
#RADARVALUES:0,0,0,0,0;
|
||||
#NOTES:
|
||||
pump-single:
|
||||
NORMAL:
|
||||
easy:
|
||||
1:
|
||||
0,0,0,0,0:
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
,
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
,
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
,
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
,
|
||||
01000
|
||||
00000
|
||||
00100
|
||||
00000
|
||||
,
|
||||
10000
|
||||
00000
|
||||
10010
|
||||
00000
|
||||
,
|
||||
10000
|
||||
01000
|
||||
@@ -159,12 +122,6 @@
|
||||
00000
|
||||
,
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
,
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
00000
|
||||
,;
|
||||
@@ -23,6 +23,10 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1215D7A208C6559A00B87D8B /* ScreenGameplayNormal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1215D7A008C6559A00B87D8B /* ScreenGameplayNormal.cpp */; };
|
||||
2BA7925E1304F20D002BD8E1 /* NotesLoaderSSC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2BA7925A1304F20D002BD8E1 /* NotesLoaderSSC.cpp */; };
|
||||
2BA7925F1304F20D002BD8E1 /* NotesLoaderSSC.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BA7925B1304F20D002BD8E1 /* NotesLoaderSSC.h */; };
|
||||
2BA792601304F20D002BD8E1 /* NotesWriterSSC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2BA7925C1304F20D002BD8E1 /* NotesWriterSSC.cpp */; };
|
||||
2BA792611304F20D002BD8E1 /* NotesWriterSSC.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BA7925D1304F20D002BD8E1 /* NotesWriterSSC.h */; };
|
||||
3659B56F0F902143002B6B68 /* CommandLineActions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3659B56D0F902143002B6B68 /* CommandLineActions.cpp */; };
|
||||
3659B5700F902143002B6B68 /* CommandLineActions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3659B56E0F902143002B6B68 /* CommandLineActions.h */; };
|
||||
367D575B0EDE56F200B46814 /* ScreenHighScores.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 367D57590EDE56F200B46814 /* ScreenHighScores.cpp */; };
|
||||
@@ -1768,6 +1772,10 @@
|
||||
1215D7A108C6559A00B87D8B /* ScreenGameplayNormal.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; name = ScreenGameplayNormal.h; path = ../src/ScreenGameplayNormal.h; sourceTree = "<group>"; };
|
||||
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
|
||||
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
||||
2BA7925A1304F20D002BD8E1 /* NotesLoaderSSC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NotesLoaderSSC.cpp; path = ../src/NotesLoaderSSC.cpp; sourceTree = SOURCE_ROOT; };
|
||||
2BA7925B1304F20D002BD8E1 /* NotesLoaderSSC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NotesLoaderSSC.h; path = ../src/NotesLoaderSSC.h; sourceTree = SOURCE_ROOT; };
|
||||
2BA7925C1304F20D002BD8E1 /* NotesWriterSSC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NotesWriterSSC.cpp; path = ../src/NotesWriterSSC.cpp; sourceTree = SOURCE_ROOT; };
|
||||
2BA7925D1304F20D002BD8E1 /* NotesWriterSSC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NotesWriterSSC.h; path = ../src/NotesWriterSSC.h; sourceTree = SOURCE_ROOT; };
|
||||
3659B56D0F902143002B6B68 /* CommandLineActions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CommandLineActions.cpp; sourceTree = "<group>"; };
|
||||
3659B56E0F902143002B6B68 /* CommandLineActions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommandLineActions.h; sourceTree = "<group>"; };
|
||||
367D57590EDE56F200B46814 /* ScreenHighScores.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ScreenHighScores.cpp; path = ../src/ScreenHighScores.cpp; sourceTree = SOURCE_ROOT; };
|
||||
@@ -3779,10 +3787,14 @@
|
||||
EE2153A810A75BEE0074B01B /* NotesLoaderPMS.h */,
|
||||
AA6EFDF504770C79005F316C /* NotesLoaderSM.cpp */,
|
||||
AA6EFDF604770C79005F316C /* NotesLoaderSM.h */,
|
||||
2BA7925A1304F20D002BD8E1 /* NotesLoaderSSC.cpp */,
|
||||
2BA7925B1304F20D002BD8E1 /* NotesLoaderSSC.h */,
|
||||
AA6EFDF704770C79005F316C /* NotesWriterDWI.cpp */,
|
||||
AA6EFDF804770C79005F316C /* NotesWriterDWI.h */,
|
||||
AA6EFDF904770C79005F316C /* NotesWriterSM.cpp */,
|
||||
AA6EFDFA04770C79005F316C /* NotesWriterSM.h */,
|
||||
2BA7925C1304F20D002BD8E1 /* NotesWriterSSC.cpp */,
|
||||
2BA7925D1304F20D002BD8E1 /* NotesWriterSSC.h */,
|
||||
AA6EFDFB04770C79005F316C /* NoteTypes.cpp */,
|
||||
AA6EFDFC04770C79005F316C /* NoteTypes.h */,
|
||||
AA80C75707F20D6600C99272 /* OptionRowHandler.cpp */,
|
||||
@@ -4914,6 +4926,8 @@
|
||||
EE906F7712BF3A6900146C96 /* json_batchallocator.h in Headers */,
|
||||
EE906F8312BF3AF800146C96 /* JsonUtil.h in Headers */,
|
||||
EE906F8B12BF3C7C00146C96 /* FileDownload.h in Headers */,
|
||||
2BA7925F1304F20D002BD8E1 /* NotesLoaderSSC.h in Headers */,
|
||||
2BA792611304F20D002BD8E1 /* NotesWriterSSC.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -6156,6 +6170,8 @@
|
||||
EE906F7D12BF3A6900146C96 /* json_writer.cpp in Sources */,
|
||||
EE906F8212BF3AF800146C96 /* JsonUtil.cpp in Sources */,
|
||||
EE906F8A12BF3C7C00146C96 /* FileDownload.cpp in Sources */,
|
||||
2BA7925E1304F20D002BD8E1 /* NotesLoaderSSC.cpp in Sources */,
|
||||
2BA792601304F20D002BD8E1 /* NotesWriterSSC.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "RageUtil.h"
|
||||
#include "GameState.h"
|
||||
#include "TimingData.h"
|
||||
#include "NotesLoaderSM.h"
|
||||
#include "NotesLoaderSSC.h"
|
||||
#include "PrefsManager.h"
|
||||
#include "RageDisplay.h"
|
||||
#include "AnnouncerManager.h"
|
||||
@@ -141,7 +141,7 @@ static void StartMusic( MusicToPlay &ToPlay )
|
||||
{
|
||||
LOG->Trace( "Found '%s'", ToPlay.m_sTimingFile.c_str() );
|
||||
Song song;
|
||||
if( SMLoader::LoadFromSMFile(ToPlay.m_sTimingFile, song) )
|
||||
if( SSCLoader::LoadFromSSCFile(ToPlay.m_sTimingFile, song) )
|
||||
{
|
||||
ToPlay.HasTiming = true;
|
||||
ToPlay.m_TimingData = song.m_Timing;
|
||||
|
||||
+5
-2
@@ -144,8 +144,11 @@ LyricsLoader.cpp LyricsLoader.h \
|
||||
ModsGroup.cpp ModsGroup.h \
|
||||
NoteData.cpp NoteData.h NoteDataUtil.cpp NoteDataUtil.h NoteDataWithScoring.cpp NoteDataWithScoring.h \
|
||||
NoteTypes.cpp NoteTypes.h NotesLoader.cpp NotesLoader.h \
|
||||
NotesLoaderBMS.cpp NotesLoaderBMS.h NotesLoaderDWI.cpp NotesLoaderDWI.h NotesLoaderKSF.cpp NotesLoaderKSF.h NotesLoaderMidi.cpp NotesLoaderMidi.h \
|
||||
NotesLoaderPMS.cpp NotesLoaderPMS.h NotesLoaderSM.cpp NotesLoaderSM.h NotesWriterDWI.cpp NotesWriterDWI.h NotesWriterSM.cpp NotesWriterSM.h \
|
||||
NotesLoaderBMS.cpp NotesLoaderBMS.h NotesLoaderDWI.cpp NotesLoaderDWI.h \
|
||||
NotesLoaderKSF.cpp NotesLoaderKSF.h NotesLoaderMidi.cpp NotesLoaderMidi.h \
|
||||
NotesLoaderPMS.cpp NotesLoaderPMS.h NotesLoaderSM.cpp NotesLoaderSM.h \
|
||||
NotesLoaderSSC.cpp NotesLoaderSSC.h NotesWriterDWI.cpp NotesWriterDWI.h \
|
||||
NotesWriterSM.cpp NotesWriterSM.h NotesWriterSSC.cpp NotesWriterSSC.h \
|
||||
OptionRowHandler.cpp OptionRowHandler.h OptionsList.cpp OptionsList.h \
|
||||
PlayerAI.cpp PlayerAI.h PlayerNumber.cpp PlayerNumber.h PlayerOptions.cpp PlayerOptions.h \
|
||||
PlayerStageStats.cpp PlayerStageStats.h \
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* MsdFile - Read .SM, .DWI, and .MSD files. */
|
||||
/* MsdFile - Read .SSC, .SM, .DWI, and .MSD files. */
|
||||
|
||||
#ifndef MSDFILE_H
|
||||
#define MSDFILE_H
|
||||
|
||||
+7
-3
@@ -1,6 +1,7 @@
|
||||
#include "global.h"
|
||||
#include "NotesLoader.h"
|
||||
#include "NotesLoaderSM.h"
|
||||
#include "NotesLoaderSSC.h"
|
||||
#include "NotesLoaderDWI.h"
|
||||
#include "NotesLoaderBMS.h"
|
||||
#include "NotesLoaderPMS.h"
|
||||
@@ -30,14 +31,17 @@ bool NotesLoader::LoadFromDir( const RString &sPath, Song &out, set<RString> &Bl
|
||||
vector<RString> list;
|
||||
|
||||
BlacklistedImages.clear();
|
||||
SMLoader::GetApplicableFiles( sPath, list );
|
||||
SSCLoader::GetApplicableFiles( sPath, list );
|
||||
if( !list.empty() )
|
||||
{
|
||||
if( !SMLoader::LoadFromDir(sPath, out) )
|
||||
if( !SSCLoader::LoadFromDir( sPath, out ) )
|
||||
return false;
|
||||
SMLoader::TidyUpData( out, false );
|
||||
SSCLoader::TidyUpData( out, false );
|
||||
return true;
|
||||
}
|
||||
SMLoader::GetApplicableFiles( sPath, list );
|
||||
if (!list.empty() )
|
||||
return SMLoader::LoadFromDir( sPath, out );
|
||||
DWILoader::GetApplicableFiles( sPath, list );
|
||||
if( !list.empty() )
|
||||
return DWILoader::LoadFromDir( sPath, out, BlacklistedImages );
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60KB
|
||||
|
||||
static void LoadFromSMTokens(
|
||||
void SMLoader::LoadFromSMTokens(
|
||||
RString sStepsType,
|
||||
RString sDescription,
|
||||
RString sDifficulty,
|
||||
|
||||
@@ -12,6 +12,9 @@ class TimingData;
|
||||
|
||||
namespace SMLoader
|
||||
{
|
||||
void LoadFromSMTokens( RString sStepsType, RString sDescription, RString sDifficulty,
|
||||
RString sMeter, RString sRadarValues, RString sNoteData, Steps &out );
|
||||
|
||||
bool LoadFromDir( const RString &sPath, Song &out );
|
||||
void TidyUpData( Song &song, bool bFromCache );
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,59 @@
|
||||
/* SSCLoader - Reads a Song and its Steps from a .SSC file. */
|
||||
|
||||
#ifndef NotesLoaderSSC_H
|
||||
#define NotesLoaderSSC_H
|
||||
|
||||
#include "GameConstantsAndTypes.h"
|
||||
|
||||
class MsdFile;
|
||||
class Song;
|
||||
class Steps;
|
||||
class TimingData;
|
||||
|
||||
enum SSCLoadingStates
|
||||
{
|
||||
GETTING_SONG_INFO,
|
||||
GETTING_STEP_INFO,
|
||||
GETTING_STEP_TIMING_INFO,
|
||||
GETTING_NOTE_INFO,
|
||||
NUM_SSCLoadingStates
|
||||
};
|
||||
|
||||
namespace SSCLoader
|
||||
{
|
||||
bool LoadFromDir( const RString &sPath, Song &out );
|
||||
|
||||
bool LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCache = false );
|
||||
void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
bool LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
bool LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
void TidyUpData( Song &song, bool bFromCache );
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2011 Jason Felds
|
||||
* 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.
|
||||
*/
|
||||
+6
-20
@@ -239,7 +239,7 @@ static RString JoinLineList( vector<RString> &lines )
|
||||
return join( "\r\n", lines.begin()+j, lines.end() );
|
||||
}
|
||||
|
||||
static RString GetSMNotesTag( const Song &song, const Steps &in, bool bSavingCache )
|
||||
static RString GetSMNotesTag( const Song &song, const Steps &in )
|
||||
{
|
||||
vector<RString> lines;
|
||||
|
||||
@@ -271,15 +271,11 @@ static RString GetSMNotesTag( const Song &song, const Steps &in, bool bSavingCac
|
||||
return JoinLineList( lines );
|
||||
}
|
||||
|
||||
bool NotesWriterSM::Write( RString sPath, const Song &out, const vector<Steps*>& vpStepsToSave, bool bSavingCache )
|
||||
bool NotesWriterSM::Write( RString sPath, const Song &out )
|
||||
{
|
||||
int flags = RageFile::WRITE;
|
||||
|
||||
/* If we're not saving cache, we're saving real data, so enable SLOW_FLUSH
|
||||
* to prevent data loss. If we're saving cache, this will slow things down
|
||||
* too much. */
|
||||
if( !bSavingCache )
|
||||
flags |= RageFile::SLOW_FLUSH;
|
||||
flags |= RageFile::SLOW_FLUSH;
|
||||
|
||||
RageFile f;
|
||||
if( !f.Open( sPath, flags ) )
|
||||
@@ -289,23 +285,13 @@ bool NotesWriterSM::Write( RString sPath, const Song &out, const vector<Steps*>&
|
||||
}
|
||||
|
||||
WriteGlobalTags( f, out );
|
||||
if( bSavingCache )
|
||||
{
|
||||
f.PutLine( ssprintf( "// cache tags:" ) );
|
||||
f.PutLine( ssprintf( "#FIRSTBEAT:%.3f;", out.m_fFirstBeat ) );
|
||||
f.PutLine( ssprintf( "#LASTBEAT:%.3f;", out.m_fLastBeat ) );
|
||||
f.PutLine( ssprintf( "#SONGFILENAME:%s;", out.m_sSongFileName.c_str() ) );
|
||||
f.PutLine( ssprintf( "#HASMUSIC:%i;", out.m_bHasMusic ) );
|
||||
f.PutLine( ssprintf( "#HASBANNER:%i;", out.m_bHasBanner ) );
|
||||
f.PutLine( ssprintf( "#MUSICLENGTH:%.3f;", out.m_fMusicLengthSeconds ) );
|
||||
f.PutLine( ssprintf( "// end cache tags" ) );
|
||||
}
|
||||
|
||||
// Save specified Steps to this file
|
||||
const vector<Steps*>& vpStepsToSave = out.GetAllSteps();
|
||||
FOREACH_CONST( Steps*, vpStepsToSave, s )
|
||||
{
|
||||
const Steps* pSteps = *s;
|
||||
RString sTag = GetSMNotesTag( out, *pSteps, bSavingCache );
|
||||
RString sTag = GetSMNotesTag( out, *pSteps );
|
||||
f.PutLine( sTag );
|
||||
}
|
||||
if( f.Flush() == -1 )
|
||||
@@ -325,7 +311,7 @@ void NotesWriterSM::GetEditFileContents( const Song *pSong, const Steps *pSteps,
|
||||
if( asParts.size() )
|
||||
sDir = join( "/", asParts.begin()+1, asParts.end() );
|
||||
sOut += ssprintf( "#SONG:%s;\r\n", sDir.c_str() );
|
||||
sOut += GetSMNotesTag( *pSong, *pSteps, false );
|
||||
sOut += GetSMNotesTag( *pSong, *pSteps );
|
||||
}
|
||||
|
||||
RString NotesWriterSM::GetEditFileName( const Song *pSong, const Steps *pSteps )
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ class Song;
|
||||
class Steps;
|
||||
namespace NotesWriterSM
|
||||
{
|
||||
bool Write( RString sPath, const Song &out, const vector<Steps*>& vpStepsToSave, bool bSavingCache );
|
||||
bool Write( RString sPath, const Song &out );
|
||||
void GetEditFileContents( const Song *pSong, const Steps *pSteps, RString &sOut );
|
||||
RString GetEditFileName( const Song *pSong, const Steps *pSteps );
|
||||
bool WriteEditFileToMachine( const Song *pSong, Steps *pSteps, RString &sErrorOut );
|
||||
|
||||
@@ -0,0 +1,508 @@
|
||||
#include "global.h"
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include "NotesWriterSSC.h"
|
||||
#include "BackgroundUtil.h"
|
||||
#include "Foreach.h"
|
||||
#include "GameManager.h"
|
||||
#include "LocalizedString.h"
|
||||
#include "NoteTypes.h"
|
||||
#include "Profile.h"
|
||||
#include "ProfileManager.h"
|
||||
#include "RageFile.h"
|
||||
#include "RageFileManager.h"
|
||||
#include "RageLog.h"
|
||||
#include "RageUtil.h"
|
||||
#include "Song.h"
|
||||
#include "Steps.h"
|
||||
|
||||
static RString BackgroundChangeToString( const BackgroundChange &bgc )
|
||||
{
|
||||
// TODO: Technically we need to double-escape the filename (because it might contain '=') and then
|
||||
// unescape the value returned by the MsdFile.
|
||||
RString s = ssprintf(
|
||||
"%.3f=%s=%.3f=%d=%d=%d=%s=%s=%s=%s=%s",
|
||||
bgc.m_fStartBeat,
|
||||
SmEscape(bgc.m_def.m_sFile1).c_str(),
|
||||
bgc.m_fRate,
|
||||
bgc.m_sTransition == SBT_CrossFade, // backward compat
|
||||
bgc.m_def.m_sEffect == SBE_StretchRewind, // backward compat
|
||||
bgc.m_def.m_sEffect != SBE_StretchNoLoop, // backward compat
|
||||
bgc.m_def.m_sEffect.c_str(),
|
||||
bgc.m_def.m_sFile2.c_str(),
|
||||
bgc.m_sTransition.c_str(),
|
||||
SmEscape(RageColor::NormalizeColorString(bgc.m_def.m_sColor1)).c_str(),
|
||||
SmEscape(RageColor::NormalizeColorString(bgc.m_def.m_sColor2)).c_str()
|
||||
);
|
||||
return s;
|
||||
}
|
||||
|
||||
static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
{
|
||||
//f.PutLine( ssprintf( "#SMVERSION:;", SmEscape(RString("ssc-v1.0")).c_str() ) );
|
||||
f.PutLine( ssprintf( "#TITLE:%s;", SmEscape(out.m_sMainTitle).c_str() ) );
|
||||
f.PutLine( ssprintf( "#SUBTITLE:%s;", SmEscape(out.m_sSubTitle).c_str() ) );
|
||||
f.PutLine( ssprintf( "#ARTIST:%s;", SmEscape(out.m_sArtist).c_str() ) );
|
||||
f.PutLine( ssprintf( "#TITLETRANSLIT:%s;", SmEscape(out.m_sMainTitleTranslit).c_str() ) );
|
||||
f.PutLine( ssprintf( "#SUBTITLETRANSLIT:%s;", SmEscape(out.m_sSubTitleTranslit).c_str() ) );
|
||||
f.PutLine( ssprintf( "#ARTISTTRANSLIT:%s;", SmEscape(out.m_sArtistTranslit).c_str() ) );
|
||||
f.PutLine( ssprintf( "#VERSION:%f;", out.m_fVersion ) );
|
||||
f.PutLine( ssprintf( "#GENRE:%s;", SmEscape(out.m_sGenre).c_str() ) );
|
||||
f.PutLine( ssprintf( "#CREDIT:%s;", SmEscape(out.m_sCredit).c_str() ) );
|
||||
f.PutLine( ssprintf( "#BANNER:%s;", SmEscape(out.m_sBannerFile).c_str() ) );
|
||||
f.PutLine( ssprintf( "#BACKGROUND:%s;", SmEscape(out.m_sBackgroundFile).c_str() ) );
|
||||
f.PutLine( ssprintf( "#LYRICSPATH:%s;", SmEscape(out.m_sLyricsFile).c_str() ) );
|
||||
f.PutLine( ssprintf( "#CDTITLE:%s;", SmEscape(out.m_sCDTitleFile).c_str() ) );
|
||||
f.PutLine( ssprintf( "#MUSIC:%s;", SmEscape(out.m_sMusicFile).c_str() ) );
|
||||
|
||||
{
|
||||
vector<RString> vs;
|
||||
FOREACH_ENUM( InstrumentTrack, it )
|
||||
if( out.HasInstrumentTrack(it) )
|
||||
vs.push_back( InstrumentTrackToString(it) + "=" + out.m_sInstrumentTrackFile[it] );
|
||||
if( !vs.empty() )
|
||||
{
|
||||
RString s = join( ",", vs );
|
||||
f.PutLine( "#INSTRUMENTTRACK:" + s + ";\n" );
|
||||
}
|
||||
}
|
||||
f.PutLine( ssprintf( "#OFFSET:%.6f;", out.m_Timing.m_fBeat0OffsetInSeconds ) );
|
||||
f.PutLine( ssprintf( "#SAMPLESTART:%.6f;", out.m_fMusicSampleStartSeconds ) );
|
||||
f.PutLine( ssprintf( "#SAMPLELENGTH:%.6f;", out.m_fMusicSampleLengthSeconds ) );
|
||||
if( out.m_fSpecifiedLastBeat > 0 )
|
||||
f.PutLine( ssprintf("#LASTBEATHINT:%.6f;", out.m_fSpecifiedLastBeat) );
|
||||
|
||||
f.Write( "#SELECTABLE:" );
|
||||
switch(out.m_SelectionDisplay)
|
||||
{
|
||||
default: ASSERT(0); // fall through
|
||||
case Song::SHOW_ALWAYS: f.Write( "YES" ); break;
|
||||
//case Song::SHOW_NONSTOP: f.Write( "NONSTOP" ); break;
|
||||
case Song::SHOW_NEVER: f.Write( "NO" ); break;
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
switch( out.m_DisplayBPMType )
|
||||
{
|
||||
case Song::DISPLAY_ACTUAL:
|
||||
// write nothing
|
||||
break;
|
||||
case Song::DISPLAY_SPECIFIED:
|
||||
if( out.m_fSpecifiedBPMMin == out.m_fSpecifiedBPMMax )
|
||||
f.PutLine( ssprintf( "#DISPLAYBPM:%.6f;", out.m_fSpecifiedBPMMin ) );
|
||||
else
|
||||
f.PutLine( ssprintf( "#DISPLAYBPM:%.6f:%.6f;", out.m_fSpecifiedBPMMin, out.m_fSpecifiedBPMMax ) );
|
||||
break;
|
||||
case Song::DISPLAY_RANDOM:
|
||||
f.PutLine( ssprintf( "#DISPLAYBPM:*;" ) );
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
f.Write( "#BPMS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_BPMSegments.size(); i++ )
|
||||
{
|
||||
const BPMSegment &bs = out.m_Timing.m_BPMSegments[i];
|
||||
|
||||
f.PutLine( ssprintf( "%.6f=%.6f", NoteRowToBeat(bs.m_iStartRow), bs.GetBPM() ) );
|
||||
if( i != out.m_Timing.m_BPMSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
f.Write( "#STOPS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_StopSegments.size(); i++ )
|
||||
{
|
||||
const StopSegment &fs = out.m_Timing.m_StopSegments[i];
|
||||
|
||||
if(!fs.m_bDelay)
|
||||
{
|
||||
f.PutLine( ssprintf( "%.6f=%.6f", NoteRowToBeat(fs.m_iStartRow), fs.m_fStopSeconds ) );
|
||||
if( i != out.m_Timing.m_StopSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
f.Write( "#DELAYS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_StopSegments.size(); i++ )
|
||||
{
|
||||
const StopSegment &fs = out.m_Timing.m_StopSegments[i];
|
||||
|
||||
if( fs.m_bDelay )
|
||||
{
|
||||
f.PutLine( ssprintf( "%.6f=%.6f", NoteRowToBeat(fs.m_iStartRow), fs.m_fStopSeconds ) );
|
||||
if( i != out.m_Timing.m_StopSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
/*
|
||||
f.Write( "#WARPS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_WarpSegments.size(); i++ )
|
||||
{
|
||||
const WarpSegment &ws = out.m_Timing.m_WarpSegments[i];
|
||||
|
||||
f.PutLine( ssprintf( "%.6f=%.6f", NoteRowToBeat(ws.m_iStartRow), ws.m_fWarpBeats ) );
|
||||
if( i != out.m_Timing.m_WarpSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
*/
|
||||
|
||||
ASSERT( !out.m_Timing.m_vTimeSignatureSegments.empty() );
|
||||
f.Write( "#TIMESIGNATURES:" );
|
||||
FOREACH_CONST( TimeSignatureSegment, out.m_Timing.m_vTimeSignatureSegments, iter )
|
||||
{
|
||||
f.PutLine( ssprintf( "%.6f=%d=%d", NoteRowToBeat(iter->m_iStartRow), iter->m_iNumerator, iter->m_iDenominator ) );
|
||||
vector<TimeSignatureSegment>::const_iterator iter2 = iter;
|
||||
iter2++;
|
||||
if( iter2 != out.m_Timing.m_vTimeSignatureSegments.end() )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
ASSERT( !out.m_Timing.m_TickcountSegments.empty() );
|
||||
f.Write( "#TICKCOUNTS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_TickcountSegments.size(); i++ )
|
||||
{
|
||||
const TickcountSegment &ts = out.m_Timing.m_TickcountSegments[i];
|
||||
|
||||
f.PutLine( ssprintf( "%.6f=%d", NoteRowToBeat(ts.m_iStartRow), ts.m_iTicks ) );
|
||||
if( i != out.m_Timing.m_TickcountSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
/*
|
||||
ASSERT( !out.m_Timing.m_ComboSegments.empty() );
|
||||
f.Write( "#COMBOS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_ComboSegments.size(); i++ )
|
||||
{
|
||||
const ComboSegment &cs = out.m_Timing.m_ComboSegments[i];
|
||||
|
||||
f.PutLine( ssprintf( "%.6f=%d", NoteRowToBeat(cs.m_iStartRow), cs.m_iComboFactor ) );
|
||||
if( i != out.m_Timing.m_ComboSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
*/
|
||||
FOREACH_BackgroundLayer( b )
|
||||
{
|
||||
if( b==0 )
|
||||
f.Write( "#BGCHANGES:" );
|
||||
else if( out.GetBackgroundChanges(b).empty() )
|
||||
continue; // skip
|
||||
else
|
||||
f.Write( ssprintf("#BGCHANGES%d:", b+1) );
|
||||
|
||||
FOREACH_CONST( BackgroundChange, out.GetBackgroundChanges(b), bgc )
|
||||
f.PutLine( BackgroundChangeToString(*bgc)+"," );
|
||||
|
||||
/* If there's an animation plan at all, add a dummy "-nosongbg-" tag to indicate that
|
||||
* this file doesn't want a song BG entry added at the end. See SSCLoader::TidyUpData.
|
||||
* This tag will be removed on load. Add it at a very high beat, so it won't cause
|
||||
* problems if loaded in older versions. */
|
||||
if( b==0 && !out.GetBackgroundChanges(b).empty() )
|
||||
f.PutLine( "99999=-nosongbg-=1.000=0=0=0 // don't automatically add -songbackground-" );
|
||||
f.PutLine( ";" );
|
||||
}
|
||||
|
||||
if( out.GetForegroundChanges().size() )
|
||||
{
|
||||
f.Write( "#FGCHANGES:" );
|
||||
FOREACH_CONST( BackgroundChange, out.GetForegroundChanges(), bgc )
|
||||
{
|
||||
f.PutLine( BackgroundChangeToString(*bgc)+"," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
}
|
||||
|
||||
f.Write( "#KEYSOUNDS:" );
|
||||
for( unsigned i=0; i<out.m_vsKeysoundFile.size(); i++ )
|
||||
{
|
||||
f.Write( out.m_vsKeysoundFile[i] );
|
||||
if( i != out.m_vsKeysoundFile.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
f.Write( "#ATTACKS:" );
|
||||
for( unsigned a=0; a < out.m_sAttackString.size(); a++ )
|
||||
{
|
||||
RString sData = out.m_sAttackString[a];
|
||||
f.Write( ssprintf( "%s", sData.c_str() ) );
|
||||
|
||||
if( a != (out.m_sAttackString.size() - 1) )
|
||||
f.Write( ":" ); // Not the end, so write a divider ':'
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
}
|
||||
|
||||
static RString JoinLineList( vector<RString> &lines )
|
||||
{
|
||||
for( unsigned i = 0; i < lines.size(); ++i )
|
||||
TrimRight( lines[i] );
|
||||
|
||||
/* Skip leading blanks. */
|
||||
unsigned j = 0;
|
||||
while( j < lines.size() && lines.size() == 0 )
|
||||
++j;
|
||||
|
||||
return join( "\r\n", lines.begin()+j, lines.end() );
|
||||
}
|
||||
|
||||
static RString GetSSCNotesTag( const Song &song, const Steps &in, bool bSavingCache )
|
||||
{
|
||||
vector<RString> lines;
|
||||
|
||||
lines.push_back( "" );
|
||||
// Escape to prevent some clown from making a comment of "\r\n;"
|
||||
lines.push_back( ssprintf("//---------------%s - %s----------------",
|
||||
GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName, SmEscape(in.GetDescription()).c_str()) );
|
||||
lines.push_back( "#NOTEDATA:;" ); // our new separator.
|
||||
lines.push_back( ssprintf( "#STEPSTYPE:%s;", GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName ) );
|
||||
lines.push_back( ssprintf( "#DESCRIPTION:%s;", SmEscape(in.GetDescription()).c_str() ) );
|
||||
lines.push_back( ssprintf( "#DIFFICULTY:%s;", DifficultyToString(in.GetDifficulty()).c_str() ) );
|
||||
lines.push_back( ssprintf( "#METER:%d;", in.GetMeter() ) );
|
||||
|
||||
vector<RString> asRadarValues;
|
||||
FOREACH_PlayerNumber( pn )
|
||||
{
|
||||
const RadarValues &rv = in.GetRadarValues( pn );
|
||||
FOREACH_ENUM( RadarCategory, rc )
|
||||
asRadarValues.push_back( ssprintf("%.3f", rv[rc]) );
|
||||
}
|
||||
lines.push_back( ssprintf( "#RADARVALUES:%s:", join(",",asRadarValues).c_str() ) );
|
||||
|
||||
lines.push_back( ssprintf( "#CREDIT:%s;", SmEscape(in.GetCredit()).c_str() ) );
|
||||
|
||||
// TODO: Remove this block, uncommon below block for Split Timing. -Wolfman2000
|
||||
lines.push_back( "#BPMS:;" );
|
||||
lines.push_back( "#STOPS:;" );
|
||||
lines.push_back( "#DELAYS:;" );
|
||||
lines.push_back( "#TIMESIGNATURES:;" );
|
||||
lines.push_back( "#TICKCOUNTS:;" );
|
||||
// lines.push_back( "#COMBOS:;" );
|
||||
|
||||
/*
|
||||
vector<RString> asBPMValues;
|
||||
for( unsigned i=0; i<in.m_Timing.m_BPMSegments.size(); i++ )
|
||||
{
|
||||
const BPMSegment &bs = in.m_Timing.m_BPMSegments[i];
|
||||
asBPMValues.push_back( ssprintf("%.6f=%.6f", NoteRowToBeat(bs.m_iStartRow), bs.GetBPM() ) );
|
||||
}
|
||||
lines.push_back( ssprintf( "#BPMS:%s;", join("\n,", asBPMValues).c_str() ) );
|
||||
|
||||
vector<RString> asStopValues;
|
||||
for( unsigned i=0; i<in.m_Timing.m_StopSegments.size(); i++ )
|
||||
{
|
||||
const StopSegment &fs = in.m_Timing.m_StopSegments[i];
|
||||
|
||||
if(!fs.m_bDelay)
|
||||
{
|
||||
asStopValues.push_back( ssprintf( "%.6f=%.6f", NoteRowToBeat(fs.m_iStartRow), fs.m_fStopSeconds ) );
|
||||
}
|
||||
}
|
||||
lines.push_back( ssprintf( "#STOPS:%s;", join("\n,", asStopValues).c_str() ) );
|
||||
|
||||
vector<RString> asDelayValues;
|
||||
for( unsigned i=0; i<in.m_Timing.m_StopSegments.size(); i++ )
|
||||
{
|
||||
const StopSegment &fs = in.m_Timing.m_StopSegments[i];
|
||||
|
||||
if( fs.m_bDelay )
|
||||
{
|
||||
asDelayValues.push_back( ssprintf( "%.6f=%.6f", NoteRowToBeat(fs.m_iStartRow), fs.m_fStopSeconds ) );
|
||||
}
|
||||
}
|
||||
lines.push_back( ssprintf( "#DELAYS:%s;", join("\n,", asDelayValues).c_str() ) );
|
||||
|
||||
|
||||
ASSERT( !in.m_Timing.m_vTimeSignatureSegments.empty() );
|
||||
vector<RString> asTimeSigValues;
|
||||
FOREACH_CONST( TimeSignatureSegment, in.m_Timing.m_vTimeSignatureSegments, iter )
|
||||
{
|
||||
asTimeSigValues.push_back( ssprintf( "%.6f=%d=%d", NoteRowToBeat(iter->m_iStartRow), iter->m_iNumerator, iter->m_iDenominator ) );
|
||||
vector<TimeSignatureSegment>::const_iterator iter2 = iter;
|
||||
iter2++;
|
||||
}
|
||||
lines.push_back( ssprintf( "#TIMESIGNATURES:%s;", join("\n,", asTimeSigValues).c_str() ) );
|
||||
|
||||
ASSERT( !in.m_Timing.m_TickcountSegments.empty() );
|
||||
vector<RString> asTickValues;
|
||||
for( unsigned i=0; i<in.m_Timing.m_TickcountSegments.size(); i++ )
|
||||
{
|
||||
const TickcountSegment &ts = in.m_Timing.m_TickcountSegments[i];
|
||||
|
||||
asTickValues.push_back( ssprintf( "%.6f=%d", NoteRowToBeat(ts.m_iStartRow), ts.m_iTicks ) );
|
||||
}
|
||||
lines.push_back( ssprintf( "#TICKCOUNTS:%s;", join("\n,", asTickValues).c_str() ) );
|
||||
|
||||
ASSERT( !in.m_Timing.m_ComboSegments.empty() );
|
||||
vector<RString> asComboValues;
|
||||
for( unsigned i=0; i<in.m_Timing.m_ComboSegments.size(); i++ )
|
||||
{
|
||||
const ComboSegment &cs = in.m_Timing.m_ComboSegments[i];
|
||||
|
||||
asComboValues.push_back( ssprintf( "%.6f=%d", NoteRowToBeat(cs.m_iStartRow), cs.m_iComboFactor ) );
|
||||
}
|
||||
lines.push_back( ssprintf( "#COMBOS:%s;", join("\n,", asComboValues).c_str() ) );
|
||||
|
||||
lines.push_back( ssprintf( "#OFFSET:%.6f;", in.m_Timing.m_fBeat0OffsetInSeconds ) );
|
||||
*/
|
||||
|
||||
RString sNoteData;
|
||||
in.GetSMNoteData( sNoteData );
|
||||
|
||||
lines.push_back( song.m_vsKeysoundFile.empty() ? "#NOTES:" : "#NOTES2:" );
|
||||
|
||||
|
||||
split( sNoteData, "\n", lines, true );
|
||||
lines.push_back( ";" );
|
||||
|
||||
return JoinLineList( lines );
|
||||
}
|
||||
|
||||
bool NotesWriterSSC::Write( RString sPath, const Song &out, const vector<Steps*>& vpStepsToSave, bool bSavingCache )
|
||||
{
|
||||
int flags = RageFile::WRITE;
|
||||
|
||||
/* If we're not saving cache, we're saving real data, so enable SLOW_FLUSH
|
||||
* to prevent data loss. If we're saving cache, this will slow things down
|
||||
* too much. */
|
||||
if( !bSavingCache )
|
||||
flags |= RageFile::SLOW_FLUSH;
|
||||
|
||||
RageFile f;
|
||||
if( !f.Open( sPath, flags ) )
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath, "couldn't be opened for writing: %s", f.GetError().c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
WriteGlobalTags( f, out );
|
||||
if( bSavingCache )
|
||||
{
|
||||
f.PutLine( ssprintf( "// cache tags:" ) );
|
||||
f.PutLine( ssprintf( "#FIRSTBEAT:%.3f;", out.m_fFirstBeat ) );
|
||||
f.PutLine( ssprintf( "#LASTBEAT:%.3f;", out.m_fLastBeat ) );
|
||||
f.PutLine( ssprintf( "#SONGFILENAME:%s;", out.m_sSongFileName.c_str() ) );
|
||||
f.PutLine( ssprintf( "#HASMUSIC:%i;", out.m_bHasMusic ) );
|
||||
f.PutLine( ssprintf( "#HASBANNER:%i;", out.m_bHasBanner ) );
|
||||
f.PutLine( ssprintf( "#MUSICLENGTH:%.3f;", out.m_fMusicLengthSeconds ) );
|
||||
f.PutLine( ssprintf( "// end cache tags" ) );
|
||||
}
|
||||
|
||||
// Save specified Steps to this file
|
||||
FOREACH_CONST( Steps*, vpStepsToSave, s )
|
||||
{
|
||||
const Steps* pSteps = *s;
|
||||
RString sTag = GetSSCNotesTag( out, *pSteps, bSavingCache );
|
||||
f.PutLine( sTag );
|
||||
}
|
||||
if( f.Flush() == -1 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NotesWriterSSC::GetEditFileContents( const Song *pSong, const Steps *pSteps, RString &sOut )
|
||||
{
|
||||
sOut = "";
|
||||
RString sDir = pSong->GetSongDir();
|
||||
|
||||
// "Songs/foo/bar"; strip off "Songs/".
|
||||
vector<RString> asParts;
|
||||
split( sDir, "/", asParts );
|
||||
if( asParts.size() )
|
||||
sDir = join( "/", asParts.begin()+1, asParts.end() );
|
||||
sOut += ssprintf( "#SONG:%s;\r\n", sDir.c_str() );
|
||||
sOut += GetSSCNotesTag( *pSong, *pSteps, false );
|
||||
}
|
||||
|
||||
RString NotesWriterSSC::GetEditFileName( const Song *pSong, const Steps *pSteps )
|
||||
{
|
||||
/* Try to make a unique name. This isn't guaranteed. Edit descriptions are
|
||||
* case-sensitive, filenames on disk are usually not, and we decimate certain
|
||||
* characters for FAT filesystems. */
|
||||
RString sFile = pSong->GetTranslitFullTitle() + " - " + pSteps->GetDescription();
|
||||
|
||||
// HACK:
|
||||
if( pSteps->m_StepsType == StepsType_dance_double )
|
||||
sFile += " (doubles)";
|
||||
|
||||
sFile += ".edit";
|
||||
|
||||
MakeValidFilename( sFile );
|
||||
return sFile;
|
||||
}
|
||||
|
||||
static LocalizedString DESTINATION_ALREADY_EXISTS ("NotesWriterSSC", "Error renaming file. Destination file '%s' already exists.");
|
||||
static LocalizedString ERROR_WRITING_FILE ("NotesWriterSSC", "Error writing file '%s'.");
|
||||
bool NotesWriterSSC::WriteEditFileToMachine( const Song *pSong, Steps *pSteps, RString &sErrorOut )
|
||||
{
|
||||
RString sDir = PROFILEMAN->GetProfileDir( ProfileSlot_Machine ) + EDIT_STEPS_SUBDIR;
|
||||
|
||||
RString sPath = sDir + GetEditFileName(pSong,pSteps);
|
||||
|
||||
// Check to make sure that we're not clobering an existing file before opening.
|
||||
bool bFileNameChanging =
|
||||
pSteps->GetSavedToDisk() &&
|
||||
pSteps->GetFilename() != sPath;
|
||||
if( bFileNameChanging && DoesFileExist(sPath) )
|
||||
{
|
||||
sErrorOut = ssprintf( DESTINATION_ALREADY_EXISTS.GetValue(), sPath.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
RageFile f;
|
||||
if( !f.Open(sPath, RageFile::WRITE | RageFile::SLOW_FLUSH) )
|
||||
{
|
||||
sErrorOut = ssprintf( ERROR_WRITING_FILE.GetValue(), sPath.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
RString sTag;
|
||||
GetEditFileContents( pSong, pSteps, sTag );
|
||||
if( f.PutLine(sTag) == -1 || f.Flush() == -1 )
|
||||
{
|
||||
sErrorOut = ssprintf( ERROR_WRITING_FILE.GetValue(), sPath.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If the file name of the edit has changed since the last save, then delete the old
|
||||
* file after saving the new one. If we delete it first, then we'll lose data on error. */
|
||||
|
||||
if( bFileNameChanging )
|
||||
FILEMAN->Remove( pSteps->GetFilename() );
|
||||
pSteps->SetFilename( sPath );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2011 Jason Felds
|
||||
* 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.
|
||||
*/
|
||||
@@ -0,0 +1,41 @@
|
||||
/* NotesWriterSSC - Writes a Song to an .SSC file. */
|
||||
|
||||
#ifndef NOTES_WRITER_SSC_H
|
||||
#define NOTES_WRITER_SSC_H
|
||||
|
||||
class Song;
|
||||
class Steps;
|
||||
namespace NotesWriterSSC
|
||||
{
|
||||
bool Write( RString sPath, const Song &out, const vector<Steps*>& vpStepsToSave, bool bSavingCache );
|
||||
void GetEditFileContents( const Song *pSong, const Steps *pSteps, RString &sOut );
|
||||
RString GetEditFileName( const Song *pSong, const Steps *pSteps );
|
||||
bool WriteEditFileToMachine( const Song *pSong, Steps *pSteps, RString &sErrorOut );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2011 Jason Felds
|
||||
* 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.
|
||||
*/
|
||||
@@ -838,8 +838,8 @@ RageFileBasic *RageFileManager::OpenForWriting( const RString &sPath, int mode,
|
||||
*
|
||||
* The purpose of this is to create files in the expected place. For example, if we
|
||||
* have both C:/games/StepMania and C:/games/DWI loaded, and we're writing
|
||||
* "Songs/Music/Waltz/waltz.sm", and the song was loaded out of
|
||||
* "C:/games/DWI/Songs/Music/Waltz/waltz.dwi", we want to write the new SM into the
|
||||
* "Songs/Music/Waltz/waltz.ssc", and the song was loaded out of
|
||||
* "C:/games/DWI/Songs/Music/Waltz/waltz.dwi", we want to write the new SSC into the
|
||||
* same directory (if possible). Don't split up files in the same directory any
|
||||
* more than we have to.
|
||||
*
|
||||
|
||||
@@ -542,6 +542,7 @@ static MenuDef g_StepsInformation(
|
||||
// xxx: this giant list of numbers SUUUUUUUUUUCKS -aj
|
||||
MenuRowDef( ScreenEdit::meter, "Meter", true, EditMode_Practice, true, false, 0, "1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25" ),
|
||||
MenuRowDef( ScreenEdit::description, "Description", true, EditMode_Practice, true, true, 0, NULL ),
|
||||
MenuRowDef( ScreenEdit::step_credit, "Step Author", true, EditMode_Practice, true, true, 0, NULL ),
|
||||
MenuRowDef( ScreenEdit::predict_meter, "Predicted Meter", false, EditMode_Full, true, true, 0, NULL ),
|
||||
MenuRowDef( ScreenEdit::tap_notes, "Tap Steps", false, EditMode_Full, true, true, 0, NULL ),
|
||||
MenuRowDef( ScreenEdit::jumps, "Jumps", false, EditMode_Full, true, true, 0, NULL ),
|
||||
@@ -2862,6 +2863,12 @@ static void ChangeDescription( const RString &sNew )
|
||||
pSteps->SetDescription(sNew);
|
||||
}
|
||||
|
||||
static void ChangeStepCredit( const RString &sNew )
|
||||
{
|
||||
Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1];
|
||||
pSteps->SetCredit(sNew);
|
||||
}
|
||||
|
||||
static void ChangeMainTitle( const RString &sNew )
|
||||
{
|
||||
Song* pSong = GAMESTATE->m_pCurSong;
|
||||
@@ -3003,6 +3010,8 @@ void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector<int> &iAns
|
||||
g_StepsInformation.rows[predict_meter].SetOneUnthemedChoice( ssprintf("%.2f",pSteps->PredictMeter()) );
|
||||
g_StepsInformation.rows[description].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Full);
|
||||
g_StepsInformation.rows[description].SetOneUnthemedChoice( pSteps->GetDescription() );
|
||||
g_StepsInformation.rows[step_credit].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Full);
|
||||
g_StepsInformation.rows[step_credit].SetOneUnthemedChoice( pSteps->GetCredit() );
|
||||
g_StepsInformation.rows[tap_notes].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumTapNotes()) );
|
||||
g_StepsInformation.rows[jumps].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumJumps()) );
|
||||
g_StepsInformation.rows[hands].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumHands()) );
|
||||
@@ -3466,6 +3475,7 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector<int> &iAns
|
||||
}
|
||||
|
||||
static LocalizedString ENTER_NEW_DESCRIPTION( "ScreenEdit", "Enter a new description." );
|
||||
static LocalizedString ENTER_NEW_STEP_AUTHOR( "ScreenEdit", "Enter the author who made this step pattern." );
|
||||
void ScreenEdit::HandleStepsInformationChoice( StepsInformationChoice c, const vector<int> &iAnswers )
|
||||
{
|
||||
Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1];
|
||||
@@ -3488,6 +3498,17 @@ void ScreenEdit::HandleStepsInformationChoice( StepsInformationChoice c, const v
|
||||
NULL
|
||||
);
|
||||
break;
|
||||
case step_credit:
|
||||
ScreenTextEntry::TextEntry(
|
||||
SM_None,
|
||||
ENTER_NEW_STEP_AUTHOR,
|
||||
m_pSteps->GetCredit(),
|
||||
255,
|
||||
SongUtil::ValidateCurrentStepsCredit,
|
||||
ChangeStepCredit,
|
||||
NULL
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -407,6 +407,7 @@ public:
|
||||
difficulty,
|
||||
meter,
|
||||
description,
|
||||
step_credit,
|
||||
predict_meter,
|
||||
tap_notes,
|
||||
jumps,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "ScreenGameplaySyncMachine.h"
|
||||
#include "NotesLoaderSM.h"
|
||||
#include "NotesLoaderSSC.h"
|
||||
#include "GameState.h"
|
||||
#include "GameManager.h"
|
||||
#include "PrefsManager.h"
|
||||
@@ -18,8 +18,8 @@ void ScreenGameplaySyncMachine::Init()
|
||||
GAMESTATE->SetCurrentStyle( GAMEMAN->GetHowToPlayStyleForGame(GAMESTATE->m_pCurGame) );
|
||||
AdjustSync::ResetOriginalSyncData();
|
||||
|
||||
RString sFile = THEME->GetPathO("ScreenGameplaySyncMachine","music.sm");
|
||||
SMLoader::LoadFromSMFile( sFile, m_Song );
|
||||
RString sFile = THEME->GetPathO("ScreenGameplaySyncMachine","music");
|
||||
SSCLoader::LoadFromSSCFile( sFile, m_Song );
|
||||
m_Song.SetSongDir( Dirname(sFile) );
|
||||
m_Song.TidyUpData();
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "GameState.h"
|
||||
#include "Steps.h"
|
||||
#include "GameManager.h"
|
||||
#include "NotesLoaderSM.h"
|
||||
#include "NotesLoaderSSC.h"
|
||||
#include "GameSoundManager.h"
|
||||
#include "Model.h"
|
||||
#include "ThemeMetric.h"
|
||||
@@ -132,7 +132,7 @@ void ScreenHowToPlay::Init()
|
||||
ActorUtil::LoadAllCommandsAndSetXY( m_pLifeMeterBar, m_sName );
|
||||
m_pLifeMeterBar->FillForHowToPlay( NUM_W2S, NUM_MISSES );
|
||||
|
||||
SMLoader::LoadFromSMFile( THEME->GetPathO(m_sName, "steps"), m_Song, false );
|
||||
SSCLoader::LoadFromSSCFile( THEME->GetPathO(m_sName, "steps"), m_Song, false );
|
||||
m_Song.AddAutoGenNotes();
|
||||
|
||||
const Style* pStyle = GAMESTATE->GetCurrentStyle();
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "PlayerState.h"
|
||||
#include "LocalizedString.h"
|
||||
#include "StepMania.h"
|
||||
#include "NotesLoaderSM.h"
|
||||
#include "NotesLoaderSSC.h"
|
||||
|
||||
static LocalizedString BOOKKEEPING_DATA_CLEARED( "ScreenServiceAction", "Bookkeeping data cleared." );
|
||||
static RString ClearBookkeepingData()
|
||||
@@ -197,7 +197,7 @@ static void CopyEdits( const RString &sFromProfileDir, const RString &sToProfile
|
||||
iNumErrored++;
|
||||
|
||||
// Test whether the song we need for this edit is present and ignore this edit if not present.
|
||||
if( !SMLoader::LoadEditFromFile( sFromDir+*i, ProfileSlot_Machine, false ) )
|
||||
if( !SSCLoader::LoadEditFromFile( sFromDir+*i, ProfileSlot_Machine, false ) )
|
||||
{
|
||||
iNumIgnored++;
|
||||
continue;
|
||||
|
||||
+48
-22
@@ -25,8 +25,10 @@
|
||||
#include "SpecialFiles.h"
|
||||
#include "NotesLoader.h"
|
||||
#include "NotesLoaderSM.h"
|
||||
#include "NotesLoaderSSC.h"
|
||||
#include "NotesWriterDWI.h"
|
||||
#include "NotesWriterSM.h"
|
||||
#include "NotesWriterSSC.h"
|
||||
#include "UnlockManager.h"
|
||||
#include "LyricsLoader.h"
|
||||
|
||||
@@ -34,7 +36,7 @@
|
||||
#include <set>
|
||||
#include <float.h>
|
||||
|
||||
const int FILE_CACHE_VERSION = 162; // increment this to invalidate cache
|
||||
const int FILE_CACHE_VERSION = 163; // increment this to invalidate cache
|
||||
|
||||
const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f;
|
||||
|
||||
@@ -50,6 +52,8 @@ static const char *InstrumentTrackNames[] = {
|
||||
XToString( InstrumentTrack );
|
||||
StringToX( InstrumentTrack );
|
||||
|
||||
const static float VERSION_NUMBER = 0.5f;
|
||||
|
||||
Song::Song()
|
||||
{
|
||||
FOREACH_BackgroundLayer( i )
|
||||
@@ -57,6 +61,7 @@ Song::Song()
|
||||
m_ForegroundChanges = AutoPtrCopyOnWrite<VBackgroundChange>(new VBackgroundChange);
|
||||
|
||||
m_LoadedFromProfile = ProfileSlot_Invalid;
|
||||
m_fVersion = VERSION_NUMBER;
|
||||
m_fMusicSampleStartSeconds = -1;
|
||||
m_fMusicSampleLengthSeconds = DEFAULT_MUSIC_SAMPLE_LENGTH;
|
||||
m_fMusicLengthSeconds = 0;
|
||||
@@ -214,8 +219,8 @@ bool Song::LoadFromSongDir( RString sDir )
|
||||
if( bUseCache )
|
||||
{
|
||||
// LOG->Trace( "Loading '%s' from cache file '%s'.", m_sSongDir.c_str(), GetCacheFilePath().c_str() );
|
||||
SMLoader::LoadFromSMFile( sCacheFilePath, *this, true );
|
||||
SMLoader::TidyUpData( *this, true );
|
||||
SSCLoader::LoadFromSSCFile( sCacheFilePath, *this, true );
|
||||
SSCLoader::TidyUpData( *this, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -707,7 +712,7 @@ void Song::TidyUpData()
|
||||
{
|
||||
/* Generated filename; this doesn't always point to a loadable file,
|
||||
* but instead points to the file we should write changed files to,
|
||||
* and will always be an .SM.
|
||||
* and will always be a .SSC.
|
||||
*
|
||||
* This is a little tricky. We can't always use the song title directly,
|
||||
* since it might contain characters we can't store in filenames. Two
|
||||
@@ -721,23 +726,32 @@ void Song::TidyUpData()
|
||||
* but not KSFs and BMSs.
|
||||
*
|
||||
* So, let's do this (by priority):
|
||||
* 1. If there's an .SM file, use that filename. No reason to use anything
|
||||
* 1. If there's a .SSC file, use that filename. No reason to use anything
|
||||
* else; it's the filename in use.
|
||||
* 2. If there's a .DWI, use it with a changed extension.
|
||||
* 3. Otherwise, use the name of the directory, since it's definitely a valid
|
||||
* 2. If there's an .SM, use it with a changed extension.
|
||||
* 3. If there's a .DWI, use it with a changed extension.
|
||||
* 4. Otherwise, use the name of the directory, since it's definitely a valid
|
||||
* filename, and should always be the title of the song (unlike KSFs). */
|
||||
m_sSongFileName = m_sSongDir;
|
||||
vector<RString> asFileNames;
|
||||
GetDirListing( m_sSongDir+"*.sm", asFileNames );
|
||||
GetDirListing( m_sSongDir+"*.ssc", asFileNames );
|
||||
if( !asFileNames.empty() )
|
||||
{
|
||||
m_sSongFileName += asFileNames[0];
|
||||
else {
|
||||
GetDirListing( m_sSongDir+"*.dwi", asFileNames );
|
||||
if( !asFileNames.empty() ) {
|
||||
m_sSongFileName += SetExtension( asFileNames[0], "sm" );
|
||||
} else {
|
||||
m_sSongFileName += Basename(m_sSongDir);
|
||||
m_sSongFileName += ".sm";
|
||||
}
|
||||
else
|
||||
{
|
||||
GetDirListing( m_sSongDir+"*.sm", asFileNames );
|
||||
if( !asFileNames.empty() )
|
||||
m_sSongFileName += SetExtension( asFileNames[0], "ssc" );
|
||||
else {
|
||||
GetDirListing( m_sSongDir+"*.dwi", asFileNames );
|
||||
if( !asFileNames.empty() ) {
|
||||
m_sSongFileName += SetExtension( asFileNames[0], "ssc" );
|
||||
} else {
|
||||
m_sSongFileName += Basename(m_sSongDir);
|
||||
m_sSongFileName += ".ssc";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -844,9 +858,10 @@ void Song::Save()
|
||||
TranslateTitles();
|
||||
|
||||
// Save the new files. These calls make backups on their own.
|
||||
if( !SaveToSMFile(GetSongFilePath(), false) )
|
||||
if( !SaveToSSCFile(GetSongFilePath(), false) )
|
||||
return;
|
||||
SaveToDWIFile();
|
||||
SaveToSMFile();
|
||||
//SaveToDWIFile();
|
||||
SaveToCacheFile();
|
||||
|
||||
/* We've safely written our files and created backups. Rename non-SM and
|
||||
@@ -871,10 +886,21 @@ void Song::Save()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Song::SaveToSMFile( RString sPath, bool bSavingCache )
|
||||
bool Song::SaveToSMFile()
|
||||
{
|
||||
LOG->Trace( "Song::SaveToSMFile('%s')", sPath.c_str() );
|
||||
const RString sPath = SetExtension( GetSongFilePath(), "sm" );
|
||||
LOG->Trace( "Song::SaveToSMFile(%s)", sPath.c_str() );
|
||||
|
||||
// If the file exists, make a backup.
|
||||
if( IsAFile(sPath) )
|
||||
FileCopy( sPath, sPath + ".old" );
|
||||
return NotesWriterSM::Write( sPath, *this );
|
||||
|
||||
}
|
||||
|
||||
bool Song::SaveToSSCFile( RString sPath, bool bSavingCache )
|
||||
{
|
||||
LOG->Trace( "Song::SaveToSSCFile('%s')", sPath.c_str() );
|
||||
|
||||
// If the file exists, make a backup.
|
||||
if( !bSavingCache && IsAFile(sPath) )
|
||||
@@ -894,7 +920,7 @@ bool Song::SaveToSMFile( RString sPath, bool bSavingCache )
|
||||
vpStepsToSave.push_back( pSteps );
|
||||
}
|
||||
|
||||
if( !NotesWriterSM::Write(sPath, *this, vpStepsToSave, bSavingCache) )
|
||||
if( !NotesWriterSSC::Write(sPath, *this, vpStepsToSave, bSavingCache) )
|
||||
return false;
|
||||
|
||||
if( !bSavingCache && g_BackUpAllSongSaves.Get() )
|
||||
@@ -932,7 +958,7 @@ bool Song::SaveToCacheFile()
|
||||
{
|
||||
SONGINDEX->AddCacheIndex(m_sSongDir, GetHashForDirectory(m_sSongDir));
|
||||
const RString sPath = GetCacheFilePath();
|
||||
if( !SaveToSMFile(sPath, true) )
|
||||
if( !SaveToSSCFile(sPath, true) )
|
||||
return false;
|
||||
|
||||
FOREACH( Steps*, m_vpSteps, pSteps )
|
||||
|
||||
+11
-6
@@ -61,7 +61,7 @@ public:
|
||||
enum SelectionDisplay
|
||||
{
|
||||
SHOW_ALWAYS, // always
|
||||
SHOW_NEVER // never (unless song hiding is turned off)
|
||||
SHOW_NEVER // never (unless song hiding is turned off)
|
||||
} m_SelectionDisplay;
|
||||
|
||||
Song();
|
||||
@@ -78,9 +78,10 @@ public:
|
||||
void ReCalculateRadarValuesAndLastBeat(); // called by TidyUpData, and after saving
|
||||
void TranslateTitles(); // called by TidyUpData
|
||||
|
||||
bool SaveToSMFile( RString sPath, bool bSavingCache );
|
||||
void Save(); // saves SM and DWI
|
||||
bool SaveToSSCFile( RString sPath, bool bSavingCache );
|
||||
void Save(); // saves SSC and SM guaranteed.
|
||||
bool SaveToCacheFile();
|
||||
bool SaveToSMFile();
|
||||
bool SaveToDWIFile();
|
||||
|
||||
const RString &GetSongFilePath() const;
|
||||
@@ -93,9 +94,10 @@ public:
|
||||
// Directory this song data came from:
|
||||
const RString &GetSongDir() const { return m_sSongDir; }
|
||||
|
||||
/* Filename associated with this file. This will always have an .SM extension.
|
||||
* If we loaded an .SM, this will point to it, but if we loaded any other
|
||||
* type, this will point to a generated .SM filename. */
|
||||
/* Filename associated with this file. This will always have a
|
||||
* .SSC extension. If we loaded a .SSC, this will point to it,
|
||||
* but if we loaded any other type, this will point to a
|
||||
* generated .SSC filename. */
|
||||
RString m_sSongFileName;
|
||||
|
||||
RString m_sGroupName;
|
||||
@@ -121,6 +123,9 @@ public:
|
||||
// "title subtitle"
|
||||
RString GetDisplayFullTitle() const;
|
||||
RString GetTranslitFullTitle() const;
|
||||
|
||||
// allow versioning with the song.
|
||||
float m_fVersion;
|
||||
|
||||
RString m_sGenre;
|
||||
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
#include "MsdFile.h"
|
||||
#include "NoteSkinManager.h"
|
||||
#include "NotesLoaderDWI.h"
|
||||
#include "NotesLoaderSM.h"
|
||||
#include "NotesLoaderSSC.h"
|
||||
#include "PrefsManager.h"
|
||||
#include "Profile.h"
|
||||
#include "ProfileManager.h"
|
||||
@@ -1653,7 +1653,7 @@ void SongManager::LoadStepEditsFromProfileDir( const RString &sProfileDir, Profi
|
||||
{
|
||||
RString fn = vsFiles[i];
|
||||
|
||||
SMLoader::LoadEditFromFile( fn, slot, true );
|
||||
SSCLoader::LoadEditFromFile( fn, slot, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+46
-7
@@ -97,7 +97,8 @@ void SongUtil::GetSteps(
|
||||
Difficulty dc,
|
||||
int iMeterLow,
|
||||
int iMeterHigh,
|
||||
const RString &sDescription,
|
||||
const RString &sDescription,
|
||||
const RString &sCredit,
|
||||
bool bIncludeAutoGen,
|
||||
unsigned uHash,
|
||||
int iMaxToGet
|
||||
@@ -119,6 +120,8 @@ void SongUtil::GetSteps(
|
||||
continue;
|
||||
if( sDescription.size() && sDescription != pSteps->GetDescription() )
|
||||
continue;
|
||||
if( sCredit.size() && sCredit != pSteps->GetCredit() )
|
||||
continue;
|
||||
if( uHash != 0 && uHash != pSteps->GetHash() )
|
||||
continue;
|
||||
if( !bIncludeAutoGen && pSteps->IsAutogen() )
|
||||
@@ -141,13 +144,14 @@ Steps* SongUtil::GetOneSteps(
|
||||
Difficulty dc,
|
||||
int iMeterLow,
|
||||
int iMeterHigh,
|
||||
const RString &sDescription,
|
||||
const RString &sDescription,
|
||||
const RString &sCredit,
|
||||
unsigned uHash,
|
||||
bool bIncludeAutoGen
|
||||
)
|
||||
{
|
||||
vector<Steps*> vpSteps;
|
||||
GetSteps( pSong, vpSteps, st, dc, iMeterLow, iMeterHigh, sDescription, bIncludeAutoGen, uHash, 1 ); // get max 1
|
||||
GetSteps( pSong, vpSteps, st, dc, iMeterLow, iMeterHigh, sDescription, sCredit, bIncludeAutoGen, uHash, 1 ); // get max 1
|
||||
if( vpSteps.empty() )
|
||||
return NULL;
|
||||
else
|
||||
@@ -193,13 +197,23 @@ Steps* SongUtil::GetStepsByMeter( const Song *pSong, StepsType st, int iMeterLow
|
||||
Steps* SongUtil::GetStepsByDescription( const Song *pSong, StepsType st, RString sDescription )
|
||||
{
|
||||
vector<Steps*> vNotes;
|
||||
GetSteps( pSong, vNotes, st, Difficulty_Invalid, -1, -1, sDescription );
|
||||
GetSteps( pSong, vNotes, st, Difficulty_Invalid, -1, -1, sDescription, "" );
|
||||
if( vNotes.size() == 0 )
|
||||
return NULL;
|
||||
else
|
||||
return vNotes[0];
|
||||
}
|
||||
|
||||
Steps* SongUtil::GetStepsByCredit( const Song *pSong, StepsType st, RString sCredit )
|
||||
{
|
||||
vector<Steps*> vNotes;
|
||||
GetSteps(pSong, vNotes, st, Difficulty_Invalid, -1, -1, "", sCredit );
|
||||
if( vNotes.size() == 0 )
|
||||
return NULL;
|
||||
else
|
||||
return vNotes[0];
|
||||
}
|
||||
|
||||
|
||||
Steps* SongUtil::GetClosestNotes( const Song *pSong, StepsType st, Difficulty dc, bool bIgnoreLocked )
|
||||
{
|
||||
@@ -279,7 +293,7 @@ static RString RemoveInitialWhitespace( RString s )
|
||||
void SongUtil::DeleteDuplicateSteps( Song *pSong, vector<Steps*> &vSteps )
|
||||
{
|
||||
/* vSteps have the same StepsType and Difficulty. Delete them if they have the
|
||||
* same m_sDescription, m_iMeter and SMNoteData. */
|
||||
* same m_sDescription, m_sCredit, m_iMeter and SMNoteData. */
|
||||
CHECKPOINT;
|
||||
for( unsigned i=0; i<vSteps.size(); i++ )
|
||||
{
|
||||
@@ -293,6 +307,8 @@ void SongUtil::DeleteDuplicateSteps( Song *pSong, vector<Steps*> &vSteps )
|
||||
|
||||
if( s1->GetDescription() != s2->GetDescription() )
|
||||
continue;
|
||||
if( s1->GetCredit() != s2->GetCredit() )
|
||||
continue;
|
||||
if( s1->GetMeter() != s2->GetMeter() )
|
||||
continue;
|
||||
/* Compare, ignoring whitespace. */
|
||||
@@ -303,8 +319,8 @@ void SongUtil::DeleteDuplicateSteps( Song *pSong, vector<Steps*> &vSteps )
|
||||
if( RemoveInitialWhitespace(sSMNoteData1) != RemoveInitialWhitespace(sSMNoteData2) )
|
||||
continue;
|
||||
|
||||
LOG->Trace("Removed %p duplicate steps in song \"%s\" with description \"%s\" and meter \"%i\"",
|
||||
s2, pSong->GetSongDir().c_str(), s1->GetDescription().c_str(), s1->GetMeter() );
|
||||
LOG->Trace("Removed %p duplicate steps in song \"%s\" with description \"%s\", step author \"%s\", and meter \"%i\"",
|
||||
s2, pSong->GetSongDir().c_str(), s1->GetDescription().c_str(), s1->GetCredit().c_str(), s1->GetMeter() );
|
||||
|
||||
pSong->DeleteSteps( s2, false );
|
||||
|
||||
@@ -803,6 +819,29 @@ bool SongUtil::ValidateCurrentStepsDescription( const RString &sAnswer, RString
|
||||
return true;
|
||||
}
|
||||
|
||||
static LocalizedString AUTHOR_NAME_CANNOT_CONTAIN( "SongUtil", "The step author's name cannot contain any of the following characters: %s" );
|
||||
|
||||
bool SongUtil::ValidateCurrentStepsCredit( const RString &sAnswer, RString &sErrorOut )
|
||||
{
|
||||
if( sAnswer.empty() )
|
||||
return true;
|
||||
|
||||
Steps *pSteps = GAMESTATE->m_pCurSteps[PLAYER_1];
|
||||
// If unchanged:
|
||||
if( pSteps->GetCredit() == sAnswer )
|
||||
return true;
|
||||
|
||||
// Borrow from EditDescription testing. Perhaps this should be abstracted? -Wolfman2000
|
||||
static const RString sInvalidChars = "\\/:*?\"<>|";
|
||||
if( strpbrk(sAnswer, sInvalidChars) != NULL )
|
||||
{
|
||||
sErrorOut = ssprintf( AUTHOR_NAME_CANNOT_CONTAIN.GetValue(), sInvalidChars.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SongUtil::GetAllSongGenres( vector<RString> &vsOut )
|
||||
{
|
||||
set<RString> genres;
|
||||
|
||||
+7
-3
@@ -70,7 +70,8 @@ namespace SongUtil
|
||||
Difficulty dc = Difficulty_Invalid,
|
||||
int iMeterLow = -1,
|
||||
int iMeterHigh = -1,
|
||||
const RString &sDescription = "",
|
||||
const RString &sDescription = "",
|
||||
const RString &sCredit = "",
|
||||
bool bIncludeAutoGen = true,
|
||||
unsigned uHash = 0,
|
||||
int iMaxToGet = -1
|
||||
@@ -81,15 +82,17 @@ namespace SongUtil
|
||||
Difficulty dc = Difficulty_Invalid,
|
||||
int iMeterLow = -1,
|
||||
int iMeterHigh = -1,
|
||||
const RString &sDescription = "",
|
||||
const RString &sDescription = "",
|
||||
const RString &sCredit = "",
|
||||
unsigned uHash = 0,
|
||||
bool bIncludeAutoGen = true
|
||||
);
|
||||
Steps* GetStepsByDifficulty( const Song *pSong, StepsType st, Difficulty dc, bool bIncludeAutoGen = true );
|
||||
Steps* GetStepsByMeter( const Song *pSong, StepsType st, int iMeterLow, int iMeterHigh );
|
||||
Steps* GetStepsByDescription( const Song *pSong, StepsType st, RString sDescription );
|
||||
Steps* GetStepsByCredit( const Song *pSong, StepsType st, RString sCredit );
|
||||
Steps* GetClosestNotes( const Song *pSong, StepsType st, Difficulty dc, bool bIgnoreLocked=false );
|
||||
|
||||
|
||||
void AdjustDuplicateSteps( Song *pSong ); // part of TidyUpData
|
||||
void DeleteDuplicateSteps( Song *pSong, vector<Steps*> &vSteps );
|
||||
|
||||
@@ -115,6 +118,7 @@ namespace SongUtil
|
||||
RString MakeUniqueEditDescription( const Song* pSong, StepsType st, const RString &sPreferredDescription );
|
||||
bool ValidateCurrentEditStepsDescription( const RString &sAnswer, RString &sErrorOut );
|
||||
bool ValidateCurrentStepsDescription( const RString &sAnswer, RString &sErrorOut );
|
||||
bool ValidateCurrentStepsCredit( const RString &sAnswer, RString &sErrorOut );
|
||||
|
||||
void GetAllSongGenres( vector<RString> &vsOut );
|
||||
void FilterSongs( const SongCriteria &sc, const vector<Song*> &in, vector<Song*> &out );
|
||||
|
||||
@@ -986,6 +986,12 @@ cl /Zl /nologo /c verstub.cpp /Fo"$(IntDir)"\
|
||||
<File
|
||||
RelativePath="NotesLoaderSM.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesLoaderSSC.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesLoaderSSC.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesWriterDWI.cpp">
|
||||
</File>
|
||||
@@ -998,6 +1004,12 @@ cl /Zl /nologo /c verstub.cpp /Fo"$(IntDir)"\
|
||||
<File
|
||||
RelativePath="NotesWriterSM.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesWriterSSC.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesWriterSSC.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NoteTypes.cpp">
|
||||
</File>
|
||||
|
||||
@@ -1314,6 +1314,15 @@
|
||||
RelativePath="NotesLoaderSM.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesLoaderSSC.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesLoaderSSC.h"
|
||||
>
|
||||
</File>
|
||||
|
||||
<File
|
||||
RelativePath="NotesWriterDWI.cpp"
|
||||
>
|
||||
@@ -1330,6 +1339,15 @@
|
||||
RelativePath="NotesWriterSM.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesWriterSSC.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesWriterSSC.h"
|
||||
>
|
||||
</File>
|
||||
|
||||
<File
|
||||
RelativePath="NoteTypes.cpp"
|
||||
>
|
||||
|
||||
@@ -1456,6 +1456,15 @@
|
||||
RelativePath="NotesLoaderSM.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesLoaderSSC.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesLoaderSSC.h"
|
||||
>
|
||||
</File>
|
||||
|
||||
<File
|
||||
RelativePath="NotesWriterDWI.cpp"
|
||||
>
|
||||
@@ -1472,6 +1481,15 @@
|
||||
RelativePath="NotesWriterSM.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesWriterSSC.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NotesWriterSSC.h"
|
||||
>
|
||||
</File>
|
||||
|
||||
<File
|
||||
RelativePath="NoteTypes.cpp"
|
||||
>
|
||||
|
||||
+11
-3
@@ -20,7 +20,7 @@
|
||||
#include "NoteData.h"
|
||||
#include "GameManager.h"
|
||||
#include "NoteDataUtil.h"
|
||||
#include "NotesLoaderSM.h"
|
||||
#include "NotesLoaderSSC.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -231,7 +231,7 @@ void Steps::Decompress() const
|
||||
{
|
||||
/* We have data on disk and not in memory. Load it. */
|
||||
Song s;
|
||||
if( !SMLoader::LoadFromSMFile(m_sFilename, s, true) )
|
||||
if( !SSCLoader::LoadFromSSCFile(m_sFilename, s, true) )
|
||||
{
|
||||
LOG->Warn( "Couldn't load \"%s\"", m_sFilename.c_str() );
|
||||
return;
|
||||
@@ -322,7 +322,7 @@ void Steps::DeAutogen( bool bCopyNoteData )
|
||||
m_Difficulty = Real()->m_Difficulty;
|
||||
m_iMeter = Real()->m_iMeter;
|
||||
copy( Real()->m_CachedRadarValues, Real()->m_CachedRadarValues + NUM_PLAYERS, m_CachedRadarValues );
|
||||
|
||||
m_sCredit = Real()->m_sCredit;
|
||||
parent = NULL;
|
||||
|
||||
if( bCopyNoteData )
|
||||
@@ -366,6 +366,12 @@ void Steps::SetDifficultyAndDescription( Difficulty dc, RString sDescription )
|
||||
MakeValidEditDescription( m_sDescription );
|
||||
}
|
||||
|
||||
void Steps::SetCredit( RString sCredit )
|
||||
{
|
||||
DeAutogen();
|
||||
m_sCredit = sCredit;
|
||||
}
|
||||
|
||||
bool Steps::MakeValidEditDescription( RString &sPreferredDescription )
|
||||
{
|
||||
if( int(sPreferredDescription.size()) > MAX_EDIT_STEPS_DESCRIPTION_LENGTH )
|
||||
@@ -398,6 +404,7 @@ public:
|
||||
DEFINE_METHOD( GetStepsType, m_StepsType )
|
||||
DEFINE_METHOD( GetDifficulty, GetDifficulty() )
|
||||
DEFINE_METHOD( GetDescription, GetDescription() )
|
||||
DEFINE_METHOD( GetAuthorCredit, GetCredit() )
|
||||
DEFINE_METHOD( GetMeter, GetMeter() )
|
||||
DEFINE_METHOD( GetFilename, GetFilename() )
|
||||
DEFINE_METHOD( IsAutogen, IsAutogen() )
|
||||
@@ -428,6 +435,7 @@ public:
|
||||
ADD_METHOD( GetStepsType );
|
||||
ADD_METHOD( GetDifficulty );
|
||||
ADD_METHOD( GetDescription );
|
||||
ADD_METHOD( GetAuthorCredit );
|
||||
ADD_METHOD( GetMeter );
|
||||
ADD_METHOD( GetFilename );
|
||||
ADD_METHOD( GetRadarValues );
|
||||
|
||||
+4
-1
@@ -40,6 +40,7 @@ public:
|
||||
Difficulty GetDifficulty() const { return Real()->m_Difficulty; }
|
||||
int GetMeter() const { return Real()->m_iMeter; }
|
||||
const RadarValues& GetRadarValues( PlayerNumber pn ) const { return Real()->m_CachedRadarValues[pn]; }
|
||||
RString GetCredit() const { return Real()->m_sCredit; }
|
||||
|
||||
void SetFilename( RString fn ) { m_sFilename = fn; }
|
||||
RString GetFilename() const { return m_sFilename; }
|
||||
@@ -48,6 +49,7 @@ public:
|
||||
void SetDifficulty( Difficulty dc ) { SetDifficultyAndDescription( dc, GetDescription() ); }
|
||||
void SetDescription( RString sDescription ) { SetDifficultyAndDescription( this->GetDifficulty(), sDescription ); }
|
||||
void SetDifficultyAndDescription( Difficulty dc, RString sDescription );
|
||||
void SetCredit( RString sCredit );
|
||||
static bool MakeValidEditDescription( RString &sPreferredDescription ); // return true if was modified
|
||||
|
||||
void SetLoadedFromProfile( ProfileSlot slot ) { m_LoadedFromProfile = slot; }
|
||||
@@ -92,10 +94,11 @@ private:
|
||||
|
||||
/* These values are pulled from the autogen source first, if there is one. */
|
||||
mutable unsigned m_iHash; // only used if m_Difficulty == Difficulty_Edit
|
||||
RString m_sDescription; // Step author, edit name, or something meaningful
|
||||
RString m_sDescription; // Edit name, or something meaningful
|
||||
Difficulty m_Difficulty; // difficulty classification
|
||||
int m_iMeter; // difficulty rating from MIN_METER to MAX_METER
|
||||
RadarValues m_CachedRadarValues[NUM_PLAYERS];
|
||||
RString m_sCredit; // Step author
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+2
-2
@@ -258,11 +258,11 @@ Steps *StepsID::ToSteps( const Song *p, bool bAllowNull ) const
|
||||
Steps *pRet = NULL;
|
||||
if( dc == Difficulty_Edit )
|
||||
{
|
||||
pRet = SongUtil::GetOneSteps( p, st, dc, -1, -1, sDescription, uHash, true );
|
||||
pRet = SongUtil::GetOneSteps( p, st, dc, -1, -1, sDescription, "", uHash, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
pRet = SongUtil::GetOneSteps( p, st, dc, -1, -1, "", 0, true );
|
||||
pRet = SongUtil::GetOneSteps( p, st, dc, -1, -1, "", "", 0, true );
|
||||
}
|
||||
|
||||
if( !bAllowNull && pRet == NULL )
|
||||
|
||||
Reference in New Issue
Block a user