#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 bFinished = m_DownloadState == packages && m_vsQueuedPackageUrls.empty() && m_pTransfer == NULL; if( bFinished ) { Message msg( "DownloadFinished" ); MESSAGEMAN->Broadcast(msg); 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 ); } bool ScreenInstallOverlay::OverlayInput( const InputEventPlus &input ) { /* if( input.DeviceI.button == g_buttonLogin && input.type == IET_FIRST_PRESS ) { HOOKS->GoToURL("http://www.stepmania.com/launch.php"); return true; } */ return false; } 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->SetMasterPlayerNumber(PLAYER_1); GAMESTATE->m_pCurStyle.Set( vpStyle[0] ); GAMESTATE->m_pCurSong.Set( pSong ); GAMESTATE->m_pPreferredSong = pSong; sInitialScreen = StepMania::GetSelectMusicScreen(); } else { sInitialScreen = StepMania::GetInitialScreen(); } Screen *curScreen = SCREENMAN->GetTopScreen(); if(curScreen->GetScreenType() == game_menu || curScreen->GetScreenType() == attract) 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. */