diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt
index 8411fd121c..c46d1855ea 100644
--- a/Docs/Changelog_sm5.txt
+++ b/Docs/Changelog_sm5.txt
@@ -6,9 +6,11 @@ ________________________________________________________________________________
2015/03/01
----------
-* [SelectMusic] Select Music now plays preview.ogg for a simfile if it exists
- instead of using the song file for the sample music. The sample length
- from the simfile is used and the sample start is treated as 0. [kyzentun]
+* [SelectMusic] Select Music now plays music file named by #PREVIEW tag if
+ it's not blank, instead of using the song file for the sample music. The
+ sample length from the simfile is used and the sample start is treated as
+ 0. [kyzentun]
+* [Song] GetPreviewMusicPath added. [kyzentun]
* [foreach] foreach_ordered lua function now works on tables that have both
number and string keys. Number keys are iterated over first. [kyzentun]
diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml
index 9fe1ca6ca5..90403dbf74 100644
--- a/Docs/Luadoc/Lua.xml
+++ b/Docs/Luadoc/Lua.xml
@@ -1542,6 +1542,7 @@
+
diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml
index 4da6fdb346..faee212099 100644
--- a/Docs/Luadoc/LuaDocumentation.xml
+++ b/Docs/Luadoc/LuaDocumentation.xml
@@ -4464,6 +4464,9 @@ save yourself some time, copy this for undocumented things:
Gets the Song's origin.
+
+ Returns the path to the Song's preview music. This handles the #PREVIEW tag internally, so it works with songs that use it and songs that don't.
+
Returns the path to the Song's preview video, if it exists. (Returns nil otherwise.)
diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini
index 8f23a9e5c8..6d8c5aad46 100644
--- a/Themes/_fallback/Languages/en.ini
+++ b/Themes/_fallback/Languages/en.ini
@@ -1142,6 +1142,7 @@ Predicted Meter=Predicted Meter
Preferences=Preferences
PreloadSounds=Preload Sounds
Premium=Premium
+Preview=Preview
Preview Length=Preview Length
Preview Start=Preview Start
Export Packages=Export Packages
@@ -1497,6 +1498,7 @@ Enter a new artist transliteration.=Enter a new artist transliteration.
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 preview file.=Enter a preview file.
Enter a new description.=Enter a new description.
Enter a new chart name.=Enter the name/title of this chart.
Enter a new chart style.=Enter a new chart style (e.g. "Pad", "Keyboard").
@@ -1997,6 +1999,7 @@ The name you chose conflicts with another edit. Please use a different name.=The
The chart name cannot contain any of the following characters: %s=The chart name cannot contain any of the following characters: %s
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
+The preview file '%s' does not exist.=The preview file '%s' does not exist.
[SongSort]
FewestPlays=Fewest Plays
diff --git a/src/NotesLoaderSSC.cpp b/src/NotesLoaderSSC.cpp
index 5f1d084dfb..ca647b5c55 100644
--- a/src/NotesLoaderSSC.cpp
+++ b/src/NotesLoaderSSC.cpp
@@ -426,6 +426,11 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach
out.m_sMusicFile = sParams[1];
}
+ else if(sValueName == "PREVIEW")
+ {
+ out.m_PreviewFile= sParams[1];
+ }
+
else if( sValueName=="INSTRUMENTTRACK" )
{
SMLoader::ProcessInstrumentTracks( out, sParams[1] );
diff --git a/src/NotesWriterSSC.cpp b/src/NotesWriterSSC.cpp
index 1f5971f149..9e373edbe9 100644
--- a/src/NotesWriterSSC.cpp
+++ b/src/NotesWriterSSC.cpp
@@ -230,6 +230,10 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
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() ) );
+ if(!out.m_PreviewFile.empty())
+ {
+ f.PutLine(ssprintf("#PREVIEW:%s;", SmEscape(out.m_PreviewFile).c_str()));
+ }
{
vector vs = out.GetInstrumentTracksToVectorString();
diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp
index 3caf613f97..763f50fbd2 100644
--- a/src/ScreenEdit.cpp
+++ b/src/ScreenEdit.cpp
@@ -952,6 +952,9 @@ static MenuDef g_SongInformation(
MenuRowDef(ScreenEdit::credit,
"Credit",
true, EditMode_Practice, true, true, 0, NULL ),
+ MenuRowDef(ScreenEdit::preview,
+ "Preview",
+ true, EditMode_Full, true, true, 0, NULL ),
MenuRowDef(ScreenEdit::main_title_transliteration,
"Main title transliteration",
true, EditMode_Practice, true, true, 0, NULL ),
@@ -1477,10 +1480,10 @@ void ScreenEdit::PlayPreviewMusic()
{
SOUND->StopMusic();
SOUND->PlayMusic(
- m_pSong->GetMusicPath(),
+ m_pSong->GetPreviewMusicPath(),
NULL,
false,
- m_pSong->m_fMusicSampleStartSeconds,
+ m_pSong->GetPreviewStartSeconds(),
m_pSong->m_fMusicSampleLengthSeconds,
FADE_IN_PREVIEW,
FADE_OUT_PREVIEW );
@@ -4318,6 +4321,12 @@ static void ChangeCredit( const RString &sNew )
pSong->m_sCredit = sNew;
}
+static void ChangePreview(const RString& sNew)
+{
+ Song* pSong = GAMESTATE->m_pCurSong;
+ pSong->m_PreviewFile= sNew;
+}
+
static void ChangeMainTitleTranslit( const RString &sNew )
{
Song* pSong = GAMESTATE->m_pCurSong;
@@ -4707,6 +4716,7 @@ void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector &iAns
g_SongInformation.rows[artist].SetOneUnthemedChoice( pSong->m_sArtist );
g_SongInformation.rows[genre].SetOneUnthemedChoice( pSong->m_sGenre );
g_SongInformation.rows[credit].SetOneUnthemedChoice( pSong->m_sCredit );
+ g_SongInformation.rows[preview].SetOneUnthemedChoice(pSong->m_PreviewFile);
g_SongInformation.rows[main_title_transliteration].SetOneUnthemedChoice( pSong->m_sMainTitleTranslit );
g_SongInformation.rows[sub_title_transliteration].SetOneUnthemedChoice( pSong->m_sSubTitleTranslit );
g_SongInformation.rows[artist_transliteration].SetOneUnthemedChoice( pSong->m_sArtistTranslit );
@@ -5419,6 +5429,7 @@ static LocalizedString ENTER_SUB_TITLE ("ScreenEdit","Enter a new sub title.")
static LocalizedString ENTER_ARTIST ("ScreenEdit","Enter a new artist.");
static LocalizedString ENTER_GENRE ("ScreenEdit","Enter a new genre.");
static LocalizedString ENTER_CREDIT ("ScreenEdit","Enter a new credit.");
+static LocalizedString ENTER_PREVIEW ("ScreenEdit", "Enter a preview file.");
static LocalizedString ENTER_MAIN_TITLE_TRANSLIT ("ScreenEdit","Enter a new main title transliteration.");
static LocalizedString ENTER_SUB_TITLE_TRANSLIT ("ScreenEdit","Enter a new sub title transliteration.");
static LocalizedString ENTER_ARTIST_TRANSLIT ("ScreenEdit","Enter a new artist transliteration.");
@@ -5447,6 +5458,9 @@ void ScreenEdit::HandleSongInformationChoice( SongInformationChoice c, const vec
case credit:
ScreenTextEntry::TextEntry( SM_None, ENTER_CREDIT, pSong->m_sCredit, 100, NULL, ChangeCredit, NULL );
break;
+ case preview:
+ ScreenTextEntry::TextEntry(SM_None, ENTER_PREVIEW, pSong->m_PreviewFile, 100, SongUtil::ValidateCurrentSongPreview, ChangePreview, NULL);
+ break;
case main_title_transliteration:
ScreenTextEntry::TextEntry( SM_None, ENTER_MAIN_TITLE_TRANSLIT, pSong->m_sMainTitleTranslit, 100, NULL, ChangeMainTitleTranslit, NULL );
break;
diff --git a/src/ScreenEdit.h b/src/ScreenEdit.h
index 73958de654..51832b79f7 100644
--- a/src/ScreenEdit.h
+++ b/src/ScreenEdit.h
@@ -568,6 +568,7 @@ public:
artist,
genre,
credit,
+ preview,
main_title_transliteration,
sub_title_transliteration,
artist_transliteration,
diff --git a/src/ScreenManager.cpp b/src/ScreenManager.cpp
index 230b6a186f..762dc7a58a 100644
--- a/src/ScreenManager.cpp
+++ b/src/ScreenManager.cpp
@@ -742,7 +742,12 @@ void ScreenManager::LoadDelayedScreen()
// Screens may not call SetNewScreen from the ctor or Init(). (We don't do this
// check inside PrepareScreen; that may be called from a thread for concurrent
// loading, and the main thread may call SetNewScreen during that time.)
- ASSERT_M( m_sDelayedScreen.empty(), m_sDelayedScreen );
+ // Emit an error instead of asserting. -Kyz
+ if(!m_sDelayedScreen.empty())
+ {
+ LuaHelpers::ReportScriptError("Setting a new screen during an InitCommand is not allowed.");
+ m_sDelayedScreen= "";
+ }
bLoaded = ActivatePreparedScreenAndBackground( sScreenName );
ASSERT( bLoaded );
diff --git a/src/ScreenNetSelectMusic.cpp b/src/ScreenNetSelectMusic.cpp
index a6a7ac04ba..d153d7f387 100644
--- a/src/ScreenNetSelectMusic.cpp
+++ b/src/ScreenNetSelectMusic.cpp
@@ -548,10 +548,10 @@ void ScreenNetSelectMusic::MusicChanged()
{
SOUND->StopMusic();
SOUND->PlayMusic(
- GAMESTATE->m_pCurSong->GetMusicPath(),
+ GAMESTATE->m_pCurSong->GetPreviewMusicPath(),
NULL,
true,
- GAMESTATE->m_pCurSong->m_fMusicSampleStartSeconds,
+ GAMESTATE->m_pCurSong->GetPreviewStartSeconds(),
GAMESTATE->m_pCurSong->m_fMusicSampleLengthSeconds );
}
}
diff --git a/src/ScreenWithMenuElements.cpp b/src/ScreenWithMenuElements.cpp
index e935cd31ef..88dd8f88f6 100644
--- a/src/ScreenWithMenuElements.cpp
+++ b/src/ScreenWithMenuElements.cpp
@@ -79,17 +79,14 @@ void ScreenWithMenuElements::Init()
// decorations
{
- AutoActor decorations;
- decorations.LoadB( m_sName, "decorations" );
- decorations->SetName("Decorations");
- decorations->SetDrawOrder( DRAW_ORDER_DECORATIONS );
- ActorFrame *pFrame = dynamic_cast(static_cast(decorations));
+ m_Decorations.LoadB( m_sName, "decorations" );
+ m_Decorations->SetName("Decorations");
+ m_Decorations->SetDrawOrder( DRAW_ORDER_DECORATIONS );
+ ActorFrame *pFrame = dynamic_cast(static_cast(m_Decorations));
if( pFrame )
{
- m_vDecorations = pFrame->GetChildren();
- FOREACH( Actor*, m_vDecorations, child )
- this->AddChild( *child );
- pFrame->RemoveAllChildren();
+ this->AddChild(m_Decorations);
+ LOAD_ALL_COMMANDS(m_Decorations);
}
}
@@ -167,8 +164,6 @@ ScreenWithMenuElements::~ScreenWithMenuElements()
if( m_MemoryCardDisplay[p] != NULL )
SAFE_DELETE( m_MemoryCardDisplay[p] );
}
- FOREACH( Actor*, m_vDecorations, actor )
- delete *actor;
}
void ScreenWithMenuElements::SetHelpText( RString s )
diff --git a/src/ScreenWithMenuElements.h b/src/ScreenWithMenuElements.h
index 3b5497e49e..f424d35bbd 100644
--- a/src/ScreenWithMenuElements.h
+++ b/src/ScreenWithMenuElements.h
@@ -45,7 +45,7 @@ protected:
MemoryCardDisplay *m_MemoryCardDisplay[NUM_PLAYERS];
MenuTimer *m_MenuTimer;
AutoActor m_sprOverlay;
- vector m_vDecorations;
+ AutoActor m_Decorations;
Transition m_In;
Transition m_Out;
diff --git a/src/Song.cpp b/src/Song.cpp
index 343a61009c..3d7148d13f 100644
--- a/src/Song.cpp
+++ b/src/Song.cpp
@@ -43,7 +43,7 @@
* @brief The internal version of the cache for StepMania.
*
* Increment this value to invalidate the current cache. */
-const int FILE_CACHE_VERSION = 222;
+const int FILE_CACHE_VERSION = 223;
/** @brief How long does a song sample last by default? */
const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f;
@@ -1479,22 +1479,20 @@ RString Song::GetPreviewVidPath() const
RString Song::GetPreviewMusicPath() const
{
- RString preview= GetSongDir() + "preview.ogg";
- if(FILEMAN->DoesFileExist(preview))
+ if(m_PreviewFile.empty())
{
- return preview;
+ return GetMusicPath();
}
- return GetMusicPath();
+ return GetSongAssetPath(m_PreviewFile, m_sSongDir);
}
float Song::GetPreviewStartSeconds() const
{
- RString preview= GetSongDir() + "preview.ogg";
- if(FILEMAN->DoesFileExist(preview))
+ if(m_PreviewFile.empty())
{
- return 0.0f;
+ return m_fMusicSampleStartSeconds;
}
- return m_fMusicSampleStartSeconds;
+ return 0.0f;
}
RString Song::GetDisplayMainTitle() const
@@ -1814,6 +1812,12 @@ public:
lua_pushnil(L);
return 1;
}
+ static int GetPreviewMusicPath(T* p, lua_State* L)
+ {
+ RString s= p->GetPreviewMusicPath();
+ lua_pushstring(L, s);
+ return 1;
+ }
static int GetJacketPath( T* p, lua_State *L )
{
RString s = p->GetJacketPath();
@@ -1886,7 +1890,7 @@ public:
}
static int GetSampleStart( T* p, lua_State *L )
{
- lua_pushnumber(L, p->m_fMusicSampleStartSeconds);
+ lua_pushnumber(L, p->GetPreviewStartSeconds());
return 1;
}
static int GetSampleLength( T* p, lua_State *L )
@@ -2128,6 +2132,7 @@ public:
ADD_METHOD( ShowInDemonstrationAndRanking );
ADD_METHOD( HasPreviewVid );
ADD_METHOD( GetPreviewVidPath );
+ ADD_METHOD(GetPreviewMusicPath);
}
};
diff --git a/src/Song.h b/src/Song.h
index 76c1bd360d..212219c2e5 100644
--- a/src/Song.h
+++ b/src/Song.h
@@ -224,6 +224,7 @@ public:
RString m_sOrigin; // song origin (for .ssc format)
RString m_sMusicFile;
+ RString m_PreviewFile;
RString m_sInstrumentTrackFile[NUM_InstrumentTrack];
/** @brief The length of the music file. */
diff --git a/src/SongUtil.cpp b/src/SongUtil.cpp
index e5017a44ec..5827f5abb9 100644
--- a/src/SongUtil.cpp
+++ b/src/SongUtil.cpp
@@ -899,6 +899,24 @@ bool SongUtil::ValidateCurrentStepsCredit( const RString &sAnswer, RString &sErr
return true;
}
+static LocalizedString PREVIEW_DOES_NOT_EXIST("SongUtil", "The preview file '%s' does not exist.");
+bool SongUtil::ValidateCurrentSongPreview(const RString& answer, RString& error)
+{
+ if(answer.empty())
+ { return true; }
+ Song* song= GAMESTATE->m_pCurSong;
+ RString real_file= song->m_PreviewFile;
+ song->m_PreviewFile= answer;
+ RString path= song->GetPreviewMusicPath();
+ bool valid= DoesFileExist(path);
+ song->m_PreviewFile= real_file;
+ if(!valid)
+ {
+ error= ssprintf(PREVIEW_DOES_NOT_EXIST.GetValue(), answer.c_str());
+ }
+ return valid;
+}
+
void SongUtil::GetAllSongGenres( vector &vsOut )
{
set genres;
diff --git a/src/SongUtil.h b/src/SongUtil.h
index 97ea6e5616..827f86812e 100644
--- a/src/SongUtil.h
+++ b/src/SongUtil.h
@@ -165,6 +165,7 @@ namespace SongUtil
bool ValidateCurrentStepsDescription( const RString &sAnswer, RString &sErrorOut );
bool ValidateCurrentStepsCredit( const RString &sAnswer, RString &sErrorOut );
bool ValidateCurrentStepsChartName(const RString &answer, RString &error);
+ bool ValidateCurrentSongPreview(const RString& answer, RString& error);
void GetAllSongGenres( vector &vsOut );
/**