diff --git a/src/ScreenInstallOverlay.cpp b/src/ScreenInstallOverlay.cpp new file mode 100644 index 0000000000..33787607df --- /dev/null +++ b/src/ScreenInstallOverlay.cpp @@ -0,0 +1,400 @@ +#include "global.h" +#include "ScreenInstallOverlay.h" +#include "RageFileManager.h" +#include "ScreenManager.h" +#include "Preference.h" +#include "RageLog.h" +#include "FileDownload.h" +#include "json/Value.h" +#include "JsonUtil.h" +#include "SpecialFiles.h" +class Song; +#include "SongManager.h" +#include "GameState.h" +#include "GameManager.h" +#include "CommonMetrics.h" +#include "SongManager.h" +#include "CommandLineActions.h" +#include "ScreenDimensions.h" +#include "StepMania.h" +#include "ActorUtil.h" + +struct PlayAfterLaunchInfo +{ + RString sSongDir; + RString sTheme; + bool bAnySongChanged; + bool bAnyThemeChanged; + + PlayAfterLaunchInfo() + { + bAnySongChanged = false; + bAnyThemeChanged = false; + } + + void OverlayWith( const PlayAfterLaunchInfo &other ) + { + if( !other.sSongDir.empty() ) sSongDir = other.sSongDir; + if( !other.sTheme.empty() ) sTheme = other.sTheme; + bAnySongChanged |= other.bAnySongChanged; + bAnyThemeChanged |= other.bAnyThemeChanged; + } +}; + +static void Parse( const RString &sDir, PlayAfterLaunchInfo &out ) +{ + vector vsDirParts; + split( sDir, "/", vsDirParts, true ); + if( vsDirParts.size() == 3 && vsDirParts[0].EqualsNoCase("Songs") ) + out.sSongDir = "/" + sDir; + else if( vsDirParts.size() == 2 && vsDirParts[0].EqualsNoCase("Themes") ) + out.sTheme = vsDirParts[1]; +} + +static const RString TEMP_ZIP_MOUNT_POINT = "/@temp-zip/"; +const RString TEMP_OS_MOUNT_POINT = "/@temp-os/"; + +static void InstallSmzip( const RString &sZipFile, PlayAfterLaunchInfo &out ) +{ + if( !FILEMAN->Mount( "zip", sZipFile, TEMP_ZIP_MOUNT_POINT ) ) + FAIL_M("Failed to mount " + sZipFile ); + + vector vsFiles; + { + vector vsRawFiles; + GetDirListingRecursive( TEMP_ZIP_MOUNT_POINT, "*", vsRawFiles); + + vector vsPrettyFiles; + FOREACH_CONST( RString, vsRawFiles, s ) + { + if( GetExtension(*s).EqualsNoCase("ctl") ) + continue; + + vsFiles.push_back( *s); + + RString s2 = s->Right( s->length() - TEMP_ZIP_MOUNT_POINT.length() ); + vsPrettyFiles.push_back( s2 ); + } + sort( vsPrettyFiles.begin(), vsPrettyFiles.end() ); + } + + RString sResult = "Success installing " + sZipFile; + FOREACH_CONST( RString, vsFiles, sSrcFile ) + { + RString sDestFile = *sSrcFile; + sDestFile = sDestFile.Right( sDestFile.length() - TEMP_ZIP_MOUNT_POINT.length() ); + + RString sDir, sThrowAway; + splitpath( sDestFile, sDir, sThrowAway, sThrowAway ); + + Parse( sDir, out ); + out.bAnySongChanged = true; + + FILEMAN->CreateDir( sDir ); + + if( !FileCopy( *sSrcFile, sDestFile ) ) + { + sResult = "Error extracting " + sDestFile; + break; + } + } + FILEMAN->Unmount( "zip", sZipFile, TEMP_ZIP_MOUNT_POINT ); + + SCREENMAN->SystemMessage( sResult ); +} + +void InstallSmzipOsArg( const RString &sOsZipFile, PlayAfterLaunchInfo &out ) +{ + SCREENMAN->SystemMessage("Installing " + sOsZipFile ); + + RString sOsDir, sFilename, sExt; + splitpath( sOsZipFile, sOsDir, sFilename, sExt ); + + if( !FILEMAN->Mount( "dir", sOsDir, TEMP_OS_MOUNT_POINT ) ) + FAIL_M("Failed to mount " + sOsDir ); + InstallSmzip( TEMP_OS_MOUNT_POINT + sFilename + sExt, out ); + + FILEMAN->Unmount( "dir", sOsDir, TEMP_OS_MOUNT_POINT ); +} + +struct FileCopyResult +{ + FileCopyResult( RString _sFile, RString _sComment ) : sFile(_sFile), sComment(_sComment) {} + RString sFile, sComment; +}; + +Preference g_sCookie( "Cookie", "" ); + +class DownloadTask +{ + FileTransfer *m_pTransfer; + vector m_vsQueuedPackageUrls; + RString m_sCurrentPackageTempFile; + enum + { + control, + packages + } m_DownloadState; + PlayAfterLaunchInfo m_playAfterLaunchInfo; +public: + DownloadTask(const RString &sControlFileUri) + { + //SCREENMAN->SystemMessage( "Downloading control file." ); + m_pTransfer = new FileTransfer(); + m_pTransfer->StartDownload( sControlFileUri, "" ); + m_DownloadState = control; + } + ~DownloadTask() + { + SAFE_DELETE(m_pTransfer); + } + RString GetStatus() + { + if( m_pTransfer == NULL ) + return ""; + else + return m_pTransfer->GetStatus(); + } + bool UpdateAndIsFinished( float fDeltaSeconds, PlayAfterLaunchInfo &playAfterLaunchInfo ) + { + m_pTransfer->Update( fDeltaSeconds ); + switch( m_DownloadState ) + { + case control: + if( m_pTransfer->IsFinished() ) + { + SCREENMAN->SystemMessage( "Downloading required .smzip" ); + + RString sResponse = m_pTransfer->GetResponse(); + SAFE_DELETE( m_pTransfer ); + + Json::Value root; + RString sError; + if( !JsonUtil::LoadFromString(root, sResponse, sError) ) + { + SCREENMAN->SystemMessage( sError ); + return true; + } + + // Parse the JSON response, make a list of all packages need to be downloaded. + { + if( root["Cookie"].isString() ) + g_sCookie.Set( root["Cookie"].asString() ); + Json::Value require = root["Require"]; + if( require.isArray() ) + { + for( unsigned i=0; iStartDownload( sUrl, m_sCurrentPackageTempFile ); + } + } + break; + case packages: + { + if( m_pTransfer->IsFinished() ) + { + SAFE_DELETE( m_pTransfer ); + InstallSmzip( m_sCurrentPackageTempFile, m_playAfterLaunchInfo ); + FILEMAN->Remove( m_sCurrentPackageTempFile ); // Harmless if this fails because download didn't finish + } + if( !m_vsQueuedPackageUrls.empty() ) + { + RString sUrl = m_vsQueuedPackageUrls.back(); + m_vsQueuedPackageUrls.pop_back(); + m_sCurrentPackageTempFile = MakeTempFileName(sUrl); + ASSERT(m_pTransfer == NULL); + m_pTransfer = new FileTransfer(); + m_pTransfer->StartDownload( sUrl, m_sCurrentPackageTempFile ); + } + } + break; + } + bool bFinsihed = m_DownloadState == packages && + m_vsQueuedPackageUrls.empty() && + m_pTransfer == NULL; + if( bFinsihed ) + { + playAfterLaunchInfo = m_playAfterLaunchInfo; + return true; + } + else + { + return false; + } + } + static RString MakeTempFileName( RString s ) + { + return SpecialFiles::CACHE_DIR + "Downloads/" + Basename(s); + } +}; +static vector g_pDownloadTasks; + +static bool IsStepManiaProtocol(const RString &arg) +{ + // for now, only load from the StepMania domain until the security implications of this feature are better understood. + //return BeginsWith(arg,"stepmania://beta.stepmania.com/"); + return BeginsWith(arg,"stepmania://"); +} + +static bool IsPackageFile(const RString &arg) +{ + RString ext = GetExtension(arg); + return ext.EqualsNoCase("smzip") || ext.EqualsNoCase("zip"); +} + +PlayAfterLaunchInfo DoInstalls( CommandLineActions::CommandLineArgs args ) +{ + PlayAfterLaunchInfo ret; + for( int i = 0; i<(int)args.argv.size(); i++ ) + { + RString s = args.argv[i]; + if( IsStepManiaProtocol(s) ) + g_pDownloadTasks.push_back( new DownloadTask(s) ); + else if( IsPackageFile(s) ) + InstallSmzipOsArg(s, ret); + } + return ret; +} + +REGISTER_SCREEN_CLASS( ScreenInstallOverlay ); + +ScreenInstallOverlay::~ScreenInstallOverlay() +{ +} +void ScreenInstallOverlay::Init() +{ + Screen::Init(); + + m_textStatus.LoadFromFont( THEME->GetPathF("ScreenInstallOverlay", "status") ); + m_textStatus.SetName("Status"); + ActorUtil::LoadAllCommandsAndSetXY(m_textStatus,"ScreenInstallOverlay"); + this->AddChild( &m_textStatus ); +} + +void ScreenInstallOverlay::Update( float fDeltaTime ) +{ + Screen::Update(fDeltaTime); + PlayAfterLaunchInfo playAfterLaunchInfo; + while( CommandLineActions::ToProcess.size() > 0 ) + { + CommandLineActions::CommandLineArgs args = CommandLineActions::ToProcess.back(); + CommandLineActions::ToProcess.pop_back(); + PlayAfterLaunchInfo pali2 = DoInstalls( args ); + playAfterLaunchInfo.OverlayWith( pali2 ); + } + + for(int i=g_pDownloadTasks.size()-1; i>=0; --i) + { + DownloadTask *p = g_pDownloadTasks[i]; + PlayAfterLaunchInfo pali; + if( p->UpdateAndIsFinished( fDeltaTime, pali) ) + { + playAfterLaunchInfo.OverlayWith(pali); + SAFE_DELETE(p); + g_pDownloadTasks.erase( g_pDownloadTasks.begin()+i ); + } + } + + { + vector vsMessages; + FOREACH_CONST( DownloadTask*, g_pDownloadTasks, pDT ) + { + vsMessages.push_back( (*pDT)->GetStatus() ); + } + m_textStatus.SetText( join("\n", vsMessages) ); + } + + if( playAfterLaunchInfo.bAnySongChanged ) + SONGMAN->Reload( false, NULL ); + + if( !playAfterLaunchInfo.sSongDir.empty() ) + { + Song* pSong = NULL; + GAMESTATE->Reset(); + RString sInitialScreen; + if( playAfterLaunchInfo.sSongDir.length() > 0 ) + pSong = SONGMAN->GetSongFromDir( playAfterLaunchInfo.sSongDir ); + if( pSong ) + { + vector vpStyle; + GAMEMAN->GetStylesForGame( GAMESTATE->m_pCurGame, vpStyle, false ); + GAMESTATE->m_PlayMode.Set( PLAY_MODE_REGULAR ); + GAMESTATE->m_bSideIsJoined[0] = true; + GAMESTATE->m_MasterPlayerNumber = PLAYER_1; + GAMESTATE->m_pCurStyle.Set( vpStyle[0] ); + GAMESTATE->m_pCurSong.Set( pSong ); + GAMESTATE->m_pPreferredSong = pSong; + sInitialScreen = StepMania::GetSelectMusicScreen(); + } + else + { + sInitialScreen = StepMania::GetInitialScreen(); + } + + SCREENMAN->SetNewScreen( sInitialScreen ); + } +} + +/* + * (c) 2001-2005 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. + */ diff --git a/src/ScreenInstallOverlay.h b/src/ScreenInstallOverlay.h new file mode 100644 index 0000000000..313ddd30b6 --- /dev/null +++ b/src/ScreenInstallOverlay.h @@ -0,0 +1,48 @@ +/* ScreenInstallOverlay - package installation processing and messaging. */ + +#ifndef ScreenInstallOverlay_H +#define ScreenInstallOverlay_H + +#include "Screen.h" +#include "BitmapText.h" + +class ScreenInstallOverlay : public Screen +{ +public: + virtual ~ScreenInstallOverlay(); + virtual void Init(); + + void Update( float fDeltaTime ); + +private: + void UpdateText(); + + BitmapText m_textStatus; +}; + +#endif + +/* + * (c) 2001-2005 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. + */