CFileItem* CSavestateDatabase::CreateFileItem(const CVariant& object) const { using namespace ADDON; CSavestate save; save.Deserialize(object); CFileItem* item = new CFileItem(save.Label()); item->SetPath(save.Path()); if (!save.Thumbnail().empty()) item->SetArt("thumb", save.Thumbnail()); else { AddonPtr addon; if (CAddonMgr::GetInstance().GetAddon(save.GameClient(), addon, ADDON_GAMEDLL)) item->SetArt("thumb", addon->Icon()); } // Use the slot number as the second label if (save.Type() == SAVETYPE::SLOT) item->SetLabel2(StringUtils::Format("%u", save.Slot())); item->m_dateTime = save.Timestamp(); item->SetProperty(FILEITEM_PROPERTY_SAVESTATE_DURATION, static_cast<uint64_t>(save.PlaytimeWallClock())); item->GetGameInfoTag()->SetGameClient(save.GameClient()); item->m_dwSize = save.Size(); item->m_bIsFolder = false; return item; }
bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options) { CFileItem fileCopy(file); // When playing a game, set the game client that we'll use to open the game // Currently this may prompt the user, the goal is to figure this out silently if (!GAME::CGameUtils::FillInGameClient(fileCopy, true)) { CLog::Log(LOGINFO, "RetroPlayer: No compatible game client selected, aborting playback"); return false; } // Check if we should open in standalone mode const bool bStandalone = fileCopy.GetPath().empty(); m_processInfo.reset(CRPProcessInfo::CreateInstance()); if (!m_processInfo) { CLog::Log(LOGERROR, "Failed to create RetroPlayer - no process info registered"); return false; } m_processInfo->SetDataCache(&CServiceBroker::GetDataCacheCore()); m_processInfo->ResetInfo(); m_renderManager.reset(new CRPRenderManager(*m_processInfo)); CSingleLock lock(m_mutex); if (IsPlaying()) CloseFile(); PrintGameInfo(fileCopy); bool bSuccess = false; std::string gameClientId = fileCopy.GetGameInfoTag()->GetGameClient(); ADDON::AddonPtr addon; if (gameClientId.empty()) { CLog::Log(LOGERROR, "Can't play game, no game client was passed to RetroPlayer!"); } else if (!CServiceBroker::GetAddonMgr().GetAddon(gameClientId, addon, ADDON::ADDON_GAMEDLL)) { CLog::Log(LOGERROR, "Can't find add-on %s for game file!", gameClientId.c_str()); } else { m_gameClient = std::static_pointer_cast<CGameClient>(addon); if (m_gameClient->Initialize()) { m_audio.reset(new CRetroPlayerAudio(*m_processInfo)); m_video.reset(new CRetroPlayerVideo(*m_renderManager, *m_processInfo)); m_input.reset(new CRetroPlayerInput(CServiceBroker::GetPeripherals())); if (!bStandalone) { std::string redactedPath = CURL::GetRedacted(fileCopy.GetPath()); CLog::Log(LOGINFO, "RetroPlayer: Opening: %s", redactedPath.c_str()); bSuccess = m_gameClient->OpenFile(fileCopy, m_audio.get(), m_video.get(), m_input.get()); } else { CLog::Log(LOGINFO, "RetroPlayer: Opening standalone"); bSuccess = m_gameClient->OpenStandalone(m_audio.get(), m_video.get(), m_input.get()); } if (bSuccess) CLog::Log(LOGDEBUG, "RetroPlayer: Using game client %s", gameClientId.c_str()); else CLog::Log(LOGERROR, "RetroPlayer: Failed to open file using %s", gameClientId.c_str()); } else CLog::Log(LOGERROR, "RetroPlayer: Failed to initialize %s", gameClientId.c_str()); } if (bSuccess && !bStandalone) { std::string savestatePath = CSavestateUtils::MakeMetadataPath(fileCopy.GetPath()); CSavestate save; if (save.Deserialize(savestatePath)) { // Check if game client is the same if (save.GameClient() != m_gameClient->ID()) { ADDON::AddonPtr addon; if (CServiceBroker::GetAddonMgr().GetAddon(save.GameClient(), addon)) { // Warn the user that continuing with a different game client will // overwrite the save bool dummy; if (!CGUIDialogYesNo::ShowAndGetInput(438, StringUtils::Format(g_localizeStrings.Get(35217), addon->Name()), dummy, 222, 35218, 0)) bSuccess = false; } } } if (bSuccess) { std::string redactedSavestatePath = CURL::GetRedacted(savestatePath); CLog::Log(LOGDEBUG, "RetroPlayer: Loading savestate %s", redactedSavestatePath.c_str()); if (!SetPlayerState(savestatePath)) CLog::Log(LOGERROR, "RetroPlayer: Failed to load savestate"); } } if (bSuccess) { RegisterWindowCallbacks(); SetSpeedInternal(1.0); m_callback.OnPlayBackStarted(fileCopy); if (!bStandalone) m_autoSave.reset(new CRetroPlayerAutoSave(*m_gameClient)); m_processInfo->SetVideoFps(static_cast<float>(m_gameClient->Timing().GetFrameRate())); } else { m_gameClient.reset(); m_audio.reset(); m_video.reset(); } return bSuccess; }
std::string CGUIDialogSelectGameClient::ShowAndGetGameClient(const std::string &gamePath, const GameClientVector& candidates, const GameClientVector& installable) { std::string gameClient; LogGameClients(candidates, installable); std::string extension = URIUtils::GetExtension(gamePath); std::string xmlPath = CSavestateUtils::MakeMetadataPath(gamePath); // Load savestate CSavestate save; CSavestateDatabase db; CLog::Log(LOGDEBUG, "Select game client dialog: Loading savestate metadata %s", CURL::GetRedacted(xmlPath).c_str()); const bool bLoaded = db.GetSavestate(xmlPath, save); // Get savestate game client std::string saveGameClient; if (bLoaded) { saveGameClient = save.GameClient(); CLog::Log(LOGDEBUG, "Select game client dialog: Auto-selecting %s", saveGameClient.c_str()); } // "Select emulator for {0:s}" CGUIDialogSelect *dialog = GetDialog(StringUtils::Format(g_localizeStrings.Get(35258), extension)); if (dialog != nullptr) { // Turn the addons into items CFileItemList items; for (const auto &candidate : candidates) { CFileItemPtr item(XFILE::CAddonsDirectory::FileItemFromAddon(candidate, candidate->ID())); item->SetLabel2(g_localizeStrings.Get(35257)); // "Installed" if (item->GetPath() == saveGameClient) item->SetLabel2(item->GetLabel2() + ", " + g_localizeStrings.Get(35259)); // "Saved" items.Add(std::move(item)); } for (const auto &addon : installable) { CFileItemPtr item(XFILE::CAddonsDirectory::FileItemFromAddon(addon, addon->ID())); items.Add(std::move(item)); } items.Sort(SortByLabel, SortOrderAscending); dialog->SetItems(items); for (int i = 0; i < items.Size(); i++) { if (items[i]->GetPath() == saveGameClient) dialog->SetSelected(i); } dialog->Open(); // If the "Get More" button has been pressed, show a list of installable addons if (dialog->IsConfirmed()) { int selectedIndex = dialog->GetSelectedItem(); if (0 <= selectedIndex && selectedIndex < items.Size()) { gameClient = items[selectedIndex]->GetPath(); CLog::Log(LOGDEBUG, "Select game client dialog: User selected emulator %s", gameClient.c_str()); if (Install(gameClient)) { // If the addon is disabled we need to enable it if (!Enable(gameClient)) CLog::Log(LOGDEBUG, "Failed to enable game client %s", gameClient.c_str()); } else CLog::Log(LOGDEBUG, "Failed to install game client: %s", gameClient.c_str()); } else { CLog::Log(LOGDEBUG, "Select game client dialog: User selected invalid emulator %d", selectedIndex); } } else { CLog::Log(LOGDEBUG, "Select game client dialog: User cancelled game client installation"); } } return gameClient; }