Skip to content

Commit

Permalink
[savestates] Added game state persistence built on a new save game da…
Browse files Browse the repository at this point in the history
…tabase. RetroPlayer can automatically load previous game state on file open, and 9 slots are available for hotkey-based save state storage.
  • Loading branch information
garbear committed Mar 22, 2013
1 parent 203cfa3 commit 3b01427
Show file tree
Hide file tree
Showing 29 changed files with 1,386 additions and 16 deletions.
1 change: 1 addition & 0 deletions Makefile.in
Expand Up @@ -50,6 +50,7 @@ DIRECTORY_ARCHIVES=$(DVDPLAYER_ARCHIVES) \
xbmc/filesystem/filesystem.a \ xbmc/filesystem/filesystem.a \
xbmc/games/games.a \ xbmc/games/games.a \
xbmc/games/libretro/libretro.a \ xbmc/games/libretro/libretro.a \
xbmc/games/savegames/savegames.a \
xbmc/games/tags/gameinfotags.a \ xbmc/games/tags/gameinfotags.a \
xbmc/games/windows/gamewindows.a \ xbmc/games/windows/gamewindows.a \
xbmc/guilib/guilib.a \ xbmc/guilib/guilib.a \
Expand Down
24 changes: 23 additions & 1 deletion language/English/strings.po
Expand Up @@ -5978,7 +5978,29 @@ msgctxt "#15022"
msgid "Maximum rewind time" msgid "Maximum rewind time"
msgstr "" msgstr ""


#empty strings from id 15023 to 15051 #empty string with id 15023

#: xbmc/settings/GUISettings.cpp
msgctxt "#15024"
msgid "Automatically load previous game state"
msgstr ""

#: xbmc/settings/GUISettings.cpp
msgctxt "#15025"
msgid "Save game state every 30 seconds"
msgstr ""

#: xbmc/games/savegames/Savegame.cpp
msgctxt "#15026"
msgid "Autosave on %s"
msgstr ""

#: xbmc/games/savegames/Savegame.cpp
msgctxt "#15027"
msgid "Slot %d"
msgstr ""

#empty strings from id 15028 to 15051


msgctxt "#15052" msgctxt "#15052"
msgid "Password" msgid "Password"
Expand Down
4 changes: 4 additions & 0 deletions project/VS2010Express/XBMC.vcxproj
Expand Up @@ -600,6 +600,8 @@
<ClCompile Include="..\..\xbmc\games\GameFileLoader.cpp" /> <ClCompile Include="..\..\xbmc\games\GameFileLoader.cpp" />
<ClCompile Include="..\..\xbmc\games\GameManager.cpp" /> <ClCompile Include="..\..\xbmc\games\GameManager.cpp" />
<ClCompile Include="..\..\xbmc\games\libretro\LibretroEnvironment.cpp" /> <ClCompile Include="..\..\xbmc\games\libretro\LibretroEnvironment.cpp" />
<ClCompile Include="..\..\xbmc\games\savegames\SavestateDatabase.cpp" />
<ClCompile Include="..\..\xbmc\games\savegames\Savestate.cpp" />
<ClCompile Include="..\..\xbmc\games\SerialState.cpp" /> <ClCompile Include="..\..\xbmc\games\SerialState.cpp" />
<ClCompile Include="..\..\xbmc\games\windows\GUIViewStateWindowGames.cpp" /> <ClCompile Include="..\..\xbmc\games\windows\GUIViewStateWindowGames.cpp" />
<ClCompile Include="..\..\xbmc\games\windows\GUIWindowGames.cpp" /> <ClCompile Include="..\..\xbmc\games\windows\GUIWindowGames.cpp" />
Expand Down Expand Up @@ -1079,6 +1081,8 @@
<ClInclude Include="..\..\xbmc\games\GameManager.h" /> <ClInclude Include="..\..\xbmc\games\GameManager.h" />
<ClInclude Include="..\..\xbmc\games\libretro\libretro.h" /> <ClInclude Include="..\..\xbmc\games\libretro\libretro.h" />
<ClInclude Include="..\..\xbmc\games\libretro\LibretroEnvironment.h" /> <ClInclude Include="..\..\xbmc\games\libretro\LibretroEnvironment.h" />
<ClInclude Include="..\..\xbmc\games\savegames\SavestateDatabase.h" />
<ClInclude Include="..\..\xbmc\games\savegames\Savestate.h" />
<ClInclude Include="..\..\xbmc\games\SerialState.h" /> <ClInclude Include="..\..\xbmc\games\SerialState.h" />
<ClInclude Include="..\..\xbmc\games\windows\GUIViewStateWindowGames.h" /> <ClInclude Include="..\..\xbmc\games\windows\GUIViewStateWindowGames.h" />
<ClInclude Include="..\..\xbmc\games\windows\GUIWindowGames.h" /> <ClInclude Include="..\..\xbmc\games\windows\GUIWindowGames.h" />
Expand Down
15 changes: 15 additions & 0 deletions project/VS2010Express/XBMC.vcxproj.filters
Expand Up @@ -304,6 +304,9 @@
<Filter Include="dbwrappers\test"> <Filter Include="dbwrappers\test">
<UniqueIdentifier>{43dbf5f1-ffdc-4fd5-8f6c-5f38f89b192f}</UniqueIdentifier> <UniqueIdentifier>{43dbf5f1-ffdc-4fd5-8f6c-5f38f89b192f}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="games\savegames">
<UniqueIdentifier>{42e2939e-467c-4c7f-bb3e-b4860c19e9e0}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\xbmc\win32\pch.cpp"> <ClCompile Include="..\..\xbmc\win32\pch.cpp">
Expand Down Expand Up @@ -3063,6 +3066,12 @@
<ClCompile Include="..\..\xbmc\dbwrappers\test\TestDynamicDatabase.cpp"> <ClCompile Include="..\..\xbmc\dbwrappers\test\TestDynamicDatabase.cpp">
<Filter>dbwrappers\test</Filter> <Filter>dbwrappers\test</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\xbmc\games\savegames\SavestateDatabase.cpp">
<Filter>games\savegames</Filter>
</ClCompile>
<ClCompile Include="..\..\xbmc\games\savegames\Savestate.cpp">
<Filter>games\savegames</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\xbmc\win32\pch.h"> <ClInclude Include="..\..\xbmc\win32\pch.h">
Expand Down Expand Up @@ -5972,6 +5981,12 @@
<ClInclude Include="..\..\xbmc\utils\IDeserializable.h"> <ClInclude Include="..\..\xbmc\utils\IDeserializable.h">
<Filter>utils</Filter> <Filter>utils</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\xbmc\games\savegames\SavestateDatabase.h">
<Filter>games\savegames</Filter>
</ClInclude>
<ClInclude Include="..\..\xbmc\games\savegames\Savestate.h">
<Filter>games\savegames</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc"> <ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc">
Expand Down
19 changes: 19 additions & 0 deletions system/keymaps/keyboard.xml
Expand Up @@ -262,6 +262,25 @@
<d>JoypadY</d> <d>JoypadY</d>
<a>JoypadL</a> <a>JoypadL</a>
<s>JoypadR</s> <s>JoypadR</s>
<one>Load1</one>
<two>Load2</two>
<three>Load3</three>
<four>Load4</four>
<five>Load5</five>
<six>Load6</six>
<seven>Load7</seven>
<eight>Load8</eight>
<nine>Load9</nine>
<!-- US keyboard. May need changes for other locales -->
<exclaim>Save1</exclaim>
<at>Save2</at>
<hash>Save3</hash>
<dollar>Save4</dollar>
<percent>Save5</percent>
<caret>Save6</caret>
<ampersand>Save7</ampersand>
<asterisk>Save8</asterisk>
<leftbracket>Save9</leftbracket>
</keyboard> </keyboard>
</FullscreenGame> </FullscreenGame>
<VideoTimeSeek> <VideoTimeSeek>
Expand Down
7 changes: 7 additions & 0 deletions xbmc/Application.cpp
Expand Up @@ -2890,6 +2890,13 @@ bool CApplication::OnAction(const CAction &action)
} }
} }


if (IsPlayingGame() && ((ACTION_SAVE <= action.GetID() && action.GetID() <= ACTION_SAVE9) ||
(ACTION_LOAD <= action.GetID() && action.GetID() <= ACTION_LOAD9)))
{
m_pPlayer->OnAction(action.GetID());
return true;
}

if (g_peripherals.OnAction(action)) if (g_peripherals.OnAction(action))
return true; return true;


Expand Down
2 changes: 2 additions & 0 deletions xbmc/DatabaseManager.cpp
Expand Up @@ -27,6 +27,7 @@
#include "video/VideoDatabase.h" #include "video/VideoDatabase.h"
#include "pvr/PVRDatabase.h" #include "pvr/PVRDatabase.h"
#include "epg/EpgDatabase.h" #include "epg/EpgDatabase.h"
#include "games/savegames/SavestateDatabase.h"
#include "settings/AdvancedSettings.h" #include "settings/AdvancedSettings.h"


using namespace std; using namespace std;
Expand Down Expand Up @@ -63,6 +64,7 @@ void CDatabaseManager::Initialize(bool addonsOnly)
{ CVideoDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseVideo); } { CVideoDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseVideo); }
{ CPVRDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseTV); } { CPVRDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseTV); }
{ CEpgDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseEpg); } { CEpgDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseEpg); }
{ CSavestateDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseSavestates); }
CLog::Log(LOGDEBUG, "%s, updating databases... DONE", __FUNCTION__); CLog::Log(LOGDEBUG, "%s, updating databases... DONE", __FUNCTION__);
} }


Expand Down
4 changes: 4 additions & 0 deletions xbmc/FileItem.cpp
Expand Up @@ -534,6 +534,7 @@ const CFileItem& CFileItem::operator=(const CFileItem& item)
m_lStartOffset = item.m_lStartOffset; m_lStartOffset = item.m_lStartOffset;
m_lStartPartNumber = item.m_lStartPartNumber; m_lStartPartNumber = item.m_lStartPartNumber;
m_lEndOffset = item.m_lEndOffset; m_lEndOffset = item.m_lEndOffset;
m_startSaveState = item.m_startSaveState;
m_strDVDLabel = item.m_strDVDLabel; m_strDVDLabel = item.m_strDVDLabel;
m_strTitle = item.m_strTitle; m_strTitle = item.m_strTitle;
m_iprogramCount = item.m_iprogramCount; m_iprogramCount = item.m_iprogramCount;
Expand Down Expand Up @@ -571,6 +572,7 @@ void CFileItem::Reset()
m_lStartOffset = 0; m_lStartOffset = 0;
m_lStartPartNumber = 1; m_lStartPartNumber = 1;
m_lEndOffset = 0; m_lEndOffset = 0;
m_startSaveState.Empty();
m_iprogramCount = 0; m_iprogramCount = 0;
m_idepth = 1; m_idepth = 1;
m_iLockMode = LOCK_MODE_EVERYONE; m_iLockMode = LOCK_MODE_EVERYONE;
Expand Down Expand Up @@ -621,6 +623,7 @@ void CFileItem::Archive(CArchive& ar)
ar << m_lStartOffset; ar << m_lStartOffset;
ar << m_lStartPartNumber; ar << m_lStartPartNumber;
ar << m_lEndOffset; ar << m_lEndOffset;
ar << m_startSaveState;
ar << m_iLockMode; ar << m_iLockMode;
ar << m_strLockCode; ar << m_strLockCode;
ar << m_iBadPwdCount; ar << m_iBadPwdCount;
Expand Down Expand Up @@ -675,6 +678,7 @@ void CFileItem::Archive(CArchive& ar)
ar >> m_lStartOffset; ar >> m_lStartOffset;
ar >> m_lStartPartNumber; ar >> m_lStartPartNumber;
ar >> m_lEndOffset; ar >> m_lEndOffset;
ar >> m_startSaveState;
int temp; int temp;
ar >> temp; ar >> temp;
m_iLockMode = (LockType)temp; m_iLockMode = (LockType)temp;
Expand Down
1 change: 1 addition & 0 deletions xbmc/FileItem.h
Expand Up @@ -398,6 +398,7 @@ class CFileItem :
int m_lStartOffset; int m_lStartOffset;
int m_lStartPartNumber; int m_lStartPartNumber;
int m_lEndOffset; int m_lEndOffset;
CStdString m_startSaveState;
LockType m_iLockMode; LockType m_iLockMode;
CStdString m_strLockCode; CStdString m_strLockCode;
int m_iHasLock; // 0 - no lock 1 - lock, but unlocked 2 - locked int m_iHasLock; // 0 - no lock 1 - lock, but unlocked 2 - locked
Expand Down
6 changes: 6 additions & 0 deletions xbmc/GUIInfoManager.cpp
Expand Up @@ -4346,6 +4346,12 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info, CStdSt
if (item->GetMusicInfoTag()->GetDuration() > 0) if (item->GetMusicInfoTag()->GetDuration() > 0)
duration = StringUtils::SecondsToTimeString(item->GetMusicInfoTag()->GetDuration()); duration = StringUtils::SecondsToTimeString(item->GetMusicInfoTag()->GetDuration());
} }
else if (item->HasProperty("duration"))
{
// The "duration" property is set for savestate file items, as they
// have no dedicated info tag.
duration = StringUtils::SecondsToTimeString((long)item->GetProperty("duration").asInteger());
}
return duration; return duration;
} }
case LISTITEM_PLOT: case LISTITEM_PLOT:
Expand Down
55 changes: 55 additions & 0 deletions xbmc/cores/RetroPlayer/RetroPlayer.cpp
Expand Up @@ -33,6 +33,7 @@
#include "guilib/GUIWindowManager.h" #include "guilib/GUIWindowManager.h"
#include "guilib/Key.h" #include "guilib/Key.h"
#include "settings/GUISettings.h" #include "settings/GUISettings.h"
#include "threads/SystemClock.h" // Should auto-save tracking be in GameClient.cpp?
#include "URL.h" #include "URL.h"
#include "utils/log.h" #include "utils/log.h"
#include "utils/StringUtils.h" #include "utils/StringUtils.h"
Expand Down Expand Up @@ -72,7 +73,17 @@ bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options
m_bStop = false; m_bStop = false;


if (IsRunning()) if (IsRunning())
{
// If the same file was provided, load the appropriate save state
if (m_gameClient && file.GetPath().Equals(m_file.GetPath()))
{
if (!file.m_startSaveState.empty())
return m_gameClient->Load(file.m_startSaveState);
else
return m_gameClient->AutoLoad();
}
CloseFile(); CloseFile();
}


// Get game info tag (from a mutable file item, if necessary) // Get game info tag (from a mutable file item, if necessary)
const GAME_INFO::CGameInfoTag *tag = file.GetGameInfoTag(); const GAME_INFO::CGameInfoTag *tag = file.GetGameInfoTag();
Expand Down Expand Up @@ -203,6 +214,7 @@ bool CRetroPlayer::InstallGameClient(CFileItem file, GameClientPtr &result) cons
} }
} }
} }

file.ClearProperty("gameclient"); // don't want this to interfere later on file.ClearProperty("gameclient"); // don't want this to interfere later on
} }


Expand Down Expand Up @@ -401,6 +413,38 @@ bool CRetroPlayer::OnAction(const CAction &action)
if (!IsPlaying()) if (!IsPlaying())
return false; return false;


switch (action.GetID())
{
case ACTION_SAVE:
return m_gameClient->AutoSave();
case ACTION_SAVE1:
case ACTION_SAVE2:
case ACTION_SAVE3:
case ACTION_SAVE4:
case ACTION_SAVE5:
case ACTION_SAVE6:
case ACTION_SAVE7:
case ACTION_SAVE8:
case ACTION_SAVE9:
return m_gameClient->Save(action.GetID() - ACTION_SAVE1 + 1);
case ACTION_LOAD:
if (m_playSpeed <= 0)
ToFFRW(1);
return m_gameClient->AutoLoad();
case ACTION_LOAD1:
case ACTION_LOAD2:
case ACTION_LOAD3:
case ACTION_LOAD4:
case ACTION_LOAD5:
case ACTION_LOAD6:
case ACTION_LOAD7:
case ACTION_LOAD8:
case ACTION_LOAD9:
if (m_playSpeed <= 0)
ToFFRW(1);
return m_gameClient->Load(action.GetID() - ACTION_LOAD1 + 1);
}

return false; return false;
} }


Expand Down Expand Up @@ -441,6 +485,8 @@ void CRetroPlayer::Process()


m_video.GoForth(framerate, m_PlayerOptions.fullscreen); m_video.GoForth(framerate, m_PlayerOptions.fullscreen);


unsigned int saveTimer = XbmcThreads::SystemClockMillis();

const double frametime = 1000 * 1000 / framerate; // microseconds const double frametime = 1000 * 1000 / framerate; // microseconds
double nextpts = CDVDClock::GetAbsoluteClock() + frametime; double nextpts = CDVDClock::GetAbsoluteClock() + frametime;
CLog::Log(LOGDEBUG, "RetroPlayer: Beginning loop de loop"); CLog::Log(LOGDEBUG, "RetroPlayer: Beginning loop de loop");
Expand Down Expand Up @@ -469,6 +515,13 @@ void CRetroPlayer::Process()
// If the game client uses single frame audio, render those now // If the game client uses single frame audio, render those now
m_audio.Flush(); m_audio.Flush();


if (g_guiSettings.GetBool("games.autosave") &&
XbmcThreads::SystemClockMillis() - saveTimer > 30000) // every 30 seconds
{
m_gameClient->AutoSave();
saveTimer = XbmcThreads::SystemClockMillis();
}

// Slow down (increase nextpts) if we're playing catchup after stalling // Slow down (increase nextpts) if we're playing catchup after stalling
if (nextpts < CDVDClock::GetAbsoluteClock()) if (nextpts < CDVDClock::GetAbsoluteClock())
nextpts = CDVDClock::GetAbsoluteClock(); nextpts = CDVDClock::GetAbsoluteClock();
Expand All @@ -485,6 +538,8 @@ void CRetroPlayer::Process()
} }


m_bStop = true; m_bStop = true;

// Save the game before the video cuts out
m_gameClient->CloseFile(); m_gameClient->CloseFile();


m_video.StopThread(true); m_video.StopThread(true);
Expand Down
3 changes: 3 additions & 0 deletions xbmc/cores/RetroPlayer/RetroPlayer.h
Expand Up @@ -113,6 +113,9 @@ class CRetroPlayer : public IPlayer, public CThread
virtual int64_t GetTime(); virtual int64_t GetTime();
virtual int64_t GetTotalTime(); virtual int64_t GetTotalTime();


bool Save(unsigned int slot) { return m_gameClient && m_gameClient->Save(slot); }
bool Save(const CStdString &label) { return m_gameClient && m_gameClient->Save(label); }
bool Load(const CStdString &saveStatePath) { return m_gameClient && m_gameClient->Load(saveStatePath); }


protected: protected:
virtual void Process(); virtual void Process();
Expand Down
2 changes: 2 additions & 0 deletions xbmc/filesystem/SpecialProtocol.cpp
Expand Up @@ -134,6 +134,8 @@ CStdString CSpecialProtocol::TranslatePath(const CURL &url)
URIUtils::AddFileToFolder(g_settings.GetDatabaseFolder(), FileName, translatedPath); URIUtils::AddFileToFolder(g_settings.GetDatabaseFolder(), FileName, translatedPath);
else if (RootDir.Equals("thumbnails")) else if (RootDir.Equals("thumbnails"))
URIUtils::AddFileToFolder(g_settings.GetThumbnailsFolder(), FileName, translatedPath); URIUtils::AddFileToFolder(g_settings.GetThumbnailsFolder(), FileName, translatedPath);
else if (RootDir.Equals("savegames"))
URIUtils::AddFileToFolder(g_settings.GetSavegamesFolder(), FileName, translatedPath);
else if (RootDir.Equals("recordings") || RootDir.Equals("cdrips")) else if (RootDir.Equals("recordings") || RootDir.Equals("cdrips"))
URIUtils::AddFileToFolder(g_guiSettings.GetString("audiocds.recordingpath", false), FileName, translatedPath); URIUtils::AddFileToFolder(g_guiSettings.GetString("audiocds.recordingpath", false), FileName, translatedPath);
else if (RootDir.Equals("screenshots")) else if (RootDir.Equals("screenshots"))
Expand Down

0 comments on commit 3b01427

Please sign in to comment.