aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett Brown <garbearucla@gmail.com>2014-03-02 17:04:45 -0800
committerGarrett Brown <themagnificentmrb@gmail.com>2016-12-01 18:08:29 -0800
commite2563f6529ea0f08fe153d6baa9dff0935ea8d65 (patch)
treea96d39b1dc64a0da9e13570ed4b869faede717a1
parent918e289427c97e9f108b1c9e2219537644ad4955 (diff)
[retroplayer] Game add-ons
Thanks to Themaister for rewind functionality, fetzerch for mouse support, file length check and cmake modifications, topfs2 for fixing a crash when loading game clients, eibma for fixing linux compilation errors, a1rwulf for catching a missing callback symbol and fixing some rebase errors, and to notspiff for helping with the rebrand and cmake.
-rw-r--r--.gitignore4
-rw-r--r--Kodi.xcodeproj/project.pbxproj387
-rw-r--r--Makefile.in17
-rw-r--r--addons/kodi.game/addon.xml4
-rw-r--r--addons/kodi.resource/games.xsd9
-rw-r--r--addons/library.kodi.game/.gitignore5
-rw-r--r--addons/resource.language.en_gb/resources/strings.po144
-rw-r--r--configure.ac1
-rw-r--r--doxygen_resources/pages/mainpage.dox17
-rw-r--r--lib/addons/library.kodi.game/CMakeLists.txt2
-rw-r--r--lib/addons/library.kodi.game/Makefile.in29
-rw-r--r--lib/addons/library.kodi.game/libKODI_game.cpp164
-rw-r--r--lib/addons/library.kodi.game/project/VS2010Express/libKODI_game.vcxproj84
-rw-r--r--lib/addons/library.kodi.game/project/VS2010Express/libKODI_game.vcxproj.filters14
-rw-r--r--project/Win32BuildSetup/genNsisIncludes.bat16
-rw-r--r--project/Win32BuildSetup/genNsisInstaller.nsi1
-rw-r--r--project/cmake/installdata/common/addons.txt1
-rw-r--r--project/cmake/scripts/linux/Install.cmake1
-rw-r--r--project/cmake/treedata/common/addons.txt1
-rw-r--r--project/cmake/treedata/common/games.txt5
-rw-r--r--project/cmake/treedata/common/subdirs.txt3
-rw-r--r--system/settings/settings.xml31
-rw-r--r--xbmc/Application.cpp11
-rw-r--r--xbmc/DatabaseManager.cpp1
-rw-r--r--xbmc/FileItem.cpp13
-rw-r--r--xbmc/GUIInfoManager.cpp6
-rw-r--r--xbmc/addons/Addon.cpp3
-rw-r--r--xbmc/addons/AddonBuilder.cpp13
-rw-r--r--xbmc/addons/BinaryAddonCache.cpp7
-rw-r--r--xbmc/addons/CMakeLists.txt2
-rw-r--r--xbmc/addons/DllGameClient.h29
-rw-r--r--xbmc/addons/GUIDialogAddonInfo.cpp12
-rw-r--r--xbmc/addons/GUIWindowAddonBrowser.cpp2
-rw-r--r--xbmc/addons/GameResource.cpp35
-rw-r--r--xbmc/addons/GameResource.h41
-rw-r--r--xbmc/addons/IAddon.h3
-rw-r--r--xbmc/addons/Makefile1
-rw-r--r--xbmc/addons/PluginSource.cpp5
-rw-r--r--xbmc/addons/PluginSource.h2
-rw-r--r--xbmc/addons/addon-bindings.mk5
-rw-r--r--xbmc/addons/binary/interfaces/AddonInterfaces.cpp34
-rw-r--r--xbmc/addons/binary/interfaces/AddonInterfaces.h11
-rw-r--r--xbmc/addons/binary/interfaces/api1/Game/AddonCallbacksGame.cpp213
-rw-r--r--xbmc/addons/binary/interfaces/api1/Game/AddonCallbacksGame.h72
-rw-r--r--xbmc/addons/binary/interfaces/api1/Game/CMakeLists.txt9
-rw-r--r--xbmc/addons/binary/interfaces/api1/Game/Makefile7
-rw-r--r--xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_callbacks.h161
-rw-r--r--xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_dll.h287
-rw-r--r--xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h487
-rw-r--r--xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_game.h238
-rw-r--r--xbmc/filesystem/AddonsDirectory.cpp219
-rw-r--r--xbmc/games/CMakeLists.txt7
-rw-r--r--xbmc/games/GameSettings.cpp6
-rw-r--r--xbmc/games/GameSettings.h4
-rw-r--r--xbmc/games/GameTypes.h32
-rw-r--r--xbmc/games/GameUtils.cpp260
-rw-r--r--xbmc/games/GameUtils.h73
-rw-r--r--xbmc/games/Makefile1
-rw-r--r--xbmc/games/addons/CMakeLists.txt18
-rw-r--r--xbmc/games/addons/GameClient.cpp918
-rw-r--r--xbmc/games/addons/GameClient.h173
-rw-r--r--xbmc/games/addons/GameClientCallbacks.h55
-rw-r--r--xbmc/games/addons/GameClientInput.cpp193
-rw-r--r--xbmc/games/addons/GameClientInput.h68
-rw-r--r--xbmc/games/addons/GameClientKeyboard.cpp104
-rw-r--r--xbmc/games/addons/GameClientKeyboard.h60
-rw-r--r--xbmc/games/addons/GameClientMouse.cpp127
-rw-r--r--xbmc/games/addons/GameClientMouse.h63
-rw-r--r--xbmc/games/addons/GameClientProperties.cpp218
-rw-r--r--xbmc/games/addons/GameClientProperties.h90
-rw-r--r--xbmc/games/addons/GameClientTiming.cpp53
-rw-r--r--xbmc/games/addons/GameClientTiming.h79
-rw-r--r--xbmc/games/addons/GameClientTranslator.cpp142
-rw-r--r--xbmc/games/addons/GameClientTranslator.h98
-rw-r--r--xbmc/games/addons/Makefile12
-rw-r--r--xbmc/games/addons/playback/CMakeLists.txt9
-rw-r--r--xbmc/games/addons/playback/GameClientRealtimePlayback.h44
-rw-r--r--xbmc/games/addons/playback/GameClientReversiblePlayback.cpp338
-rw-r--r--xbmc/games/addons/playback/GameClientReversiblePlayback.h94
-rw-r--r--xbmc/games/addons/playback/GameLoop.cpp135
-rw-r--r--xbmc/games/addons/playback/GameLoop.h73
-rw-r--r--xbmc/games/addons/playback/IGameClientPlayback.h49
-rw-r--r--xbmc/games/addons/playback/Makefile7
-rw-r--r--xbmc/games/addons/savestates/BasicMemoryStream.cpp66
-rw-r--r--xbmc/games/addons/savestates/BasicMemoryStream.h56
-rw-r--r--xbmc/games/addons/savestates/CMakeLists.txt23
-rw-r--r--xbmc/games/addons/savestates/DeltaPairMemoryStream.cpp107
-rw-r--r--xbmc/games/addons/savestates/DeltaPairMemoryStream.h75
-rw-r--r--xbmc/games/addons/savestates/IMemoryStream.h152
-rw-r--r--xbmc/games/addons/savestates/LinearMemoryStream.cpp115
-rw-r--r--xbmc/games/addons/savestates/LinearMemoryStream.h76
-rw-r--r--xbmc/games/addons/savestates/Makefile14
-rw-r--r--xbmc/games/addons/savestates/Savestate.cpp264
-rw-r--r--xbmc/games/addons/savestates/Savestate.h103
-rw-r--r--xbmc/games/addons/savestates/SavestateDatabase.cpp110
-rw-r--r--xbmc/games/addons/savestates/SavestateDatabase.h55
-rw-r--r--xbmc/games/addons/savestates/SavestateDefines.h43
-rw-r--r--xbmc/games/addons/savestates/SavestateReader.cpp79
-rw-r--r--xbmc/games/addons/savestates/SavestateReader.h48
-rw-r--r--xbmc/games/addons/savestates/SavestateTranslator.cpp46
-rw-r--r--xbmc/games/addons/savestates/SavestateTranslator.h34
-rw-r--r--xbmc/games/addons/savestates/SavestateUtils.cpp149
-rw-r--r--xbmc/games/addons/savestates/SavestateUtils.h55
-rw-r--r--xbmc/games/addons/savestates/SavestateWriter.cpp171
-rw-r--r--xbmc/games/addons/savestates/SavestateWriter.h51
-rw-r--r--xbmc/games/controllers/ControllerDefinitions.h1
-rw-r--r--xbmc/games/controllers/ControllerTranslator.cpp2
-rw-r--r--xbmc/games/controllers/windows/GUIConfigurationWizard.cpp9
-rw-r--r--xbmc/games/controllers/windows/GUIConfigurationWizard.h7
-rw-r--r--xbmc/games/dialogs/CMakeLists.txt7
-rw-r--r--xbmc/games/dialogs/GUIDialogSelectGameClient.cpp169
-rw-r--r--xbmc/games/dialogs/GUIDialogSelectGameClient.h44
-rw-r--r--xbmc/games/dialogs/Makefile6
-rw-r--r--xbmc/games/ports/CMakeLists.txt7
-rw-r--r--xbmc/games/ports/Makefile7
-rw-r--r--xbmc/games/ports/PortManager.cpp175
-rw-r--r--xbmc/games/ports/PortManager.h91
-rw-r--r--xbmc/games/ports/PortMapper.cpp84
-rw-r--r--xbmc/games/ports/PortMapper.h44
-rw-r--r--xbmc/guilib/WindowIDs.h3
-rw-r--r--xbmc/input/InputManager.cpp78
-rw-r--r--xbmc/input/InputManager.h61
-rw-r--r--xbmc/input/joysticks/CMakeLists.txt3
-rw-r--r--xbmc/input/joysticks/DefaultJoystick.cpp7
-rw-r--r--xbmc/input/joysticks/DefaultJoystick.h4
-rw-r--r--xbmc/input/joysticks/IButtonSequence.h33
-rw-r--r--xbmc/input/joysticks/JoystickEasterEgg.cpp79
-rw-r--r--xbmc/input/joysticks/JoystickEasterEgg.h47
-rw-r--r--xbmc/input/joysticks/JoystickTypes.h2
-rw-r--r--xbmc/input/joysticks/Makefile1
-rw-r--r--xbmc/input/keyboard/CMakeLists.txt7
-rw-r--r--xbmc/input/keyboard/KeyboardEasterEgg.cpp68
-rw-r--r--xbmc/input/keyboard/KeyboardEasterEgg.h47
-rw-r--r--xbmc/input/keyboard/Makefile6
-rw-r--r--xbmc/input/mouse/CMakeLists.txt8
-rw-r--r--xbmc/input/mouse/IMouseButtonMap.h75
-rw-r--r--xbmc/input/mouse/IMouseDriverHandler.h59
-rw-r--r--xbmc/input/mouse/IMouseInputHandler.h69
-rw-r--r--xbmc/input/mouse/Makefile6
-rw-r--r--xbmc/input/mouse/MouseWindowingButtonMap.cpp83
-rw-r--r--xbmc/input/mouse/MouseWindowingButtonMap.h49
-rw-r--r--xbmc/input/mouse/generic/CMakeLists.txt5
-rw-r--r--xbmc/input/mouse/generic/Makefile6
-rw-r--r--xbmc/input/mouse/generic/MouseInputHandling.cpp68
-rw-r--r--xbmc/input/mouse/generic/MouseInputHandling.h55
-rw-r--r--xbmc/interfaces/builtins/AddonBuiltins.cpp23
-rw-r--r--xbmc/interfaces/json-rpc/AddonsOperations.cpp3
-rw-r--r--xbmc/peripherals/Peripherals.cpp2
-rw-r--r--xbmc/peripherals/Peripherals.h2
-rw-r--r--xbmc/profiles/ProfilesManager.cpp9
-rw-r--r--xbmc/profiles/ProfilesManager.h1
-rw-r--r--xbmc/settings/AdvancedSettings.cpp17
-rw-r--r--xbmc/settings/AdvancedSettings.h1
-rw-r--r--xbmc/settings/Settings.cpp5
-rw-r--r--xbmc/settings/Settings.h3
-rw-r--r--xbmc/utils/Observer.h4
156 files changed, 9954 insertions, 62 deletions
diff --git a/.gitignore b/.gitignore
index 44ef5abaa2..3a617d0fa4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -129,6 +129,7 @@ cmake_install.cmake
/addons/pvr.*
/addons/adsp.*
/addons/peripheral.*
+/addons/game.*
/addons/xbmc.addon/addon.xml
/addons/xbmc.json/addon.xml
/addons/kodi.guilib/addon.xml
@@ -151,6 +152,7 @@ cmake_install.cmake
/lib/addons/library.xbmc.pvr/Makefile
/lib/addons/library.xbmc.codec/Makefile
/lib/addons/library.kodi.peripheral/Makefile
+/lib/addons/library.kodi.game/Makefile
/lib/addons/library.xbmc.addon/project/VS2010Express/Release
/lib/addons/library.xbmc.addon/project/VS2010Express/Debug
/lib/addons/library.kodi.adsp/project/VS2010Express/Release
@@ -165,6 +167,8 @@ cmake_install.cmake
/lib/addons/library.kodi.inputstream/Makefile
/lib/addons/library.kodi.peripheral/project/VS2010Express/Release
/lib/addons/library.kodi.peripheral/project/VS2010Express/Debug
+/lib/addons/library.kodi.game/project/VS2010Express/Release
+/lib/addons/library.kodi.game/project/VS2010Express/Debug
# /lib/cpluff/
/lib/cpluff/ABOUT-NLS
diff --git a/Kodi.xcodeproj/project.pbxproj b/Kodi.xcodeproj/project.pbxproj
index 203bcdc719..6ad12ab571 100644
--- a/Kodi.xcodeproj/project.pbxproj
+++ b/Kodi.xcodeproj/project.pbxproj
@@ -261,6 +261,62 @@
6838CF821D6665510057F17B /* DeadzoneFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6838CF7F1D6665510057F17B /* DeadzoneFilter.cpp */; };
6861B9EA1CC248EE00F62655 /* DriverReceiving.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6861B9E81CC248EE00F62655 /* DriverReceiving.cpp */; };
6861B9EB1CC248EE00F62655 /* DriverReceiving.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6861B9E81CC248EE00F62655 /* DriverReceiving.cpp */; };
+ 6890C1DA1DDBDBE500F8F362 /* BasicMemoryStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1C61DDBDBE500F8F362 /* BasicMemoryStream.cpp */; };
+ 6890C1DB1DDBDBE500F8F362 /* BasicMemoryStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1C61DDBDBE500F8F362 /* BasicMemoryStream.cpp */; };
+ 6890C1DC1DDBDBE500F8F362 /* DeltaPairMemoryStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1C81DDBDBE500F8F362 /* DeltaPairMemoryStream.cpp */; };
+ 6890C1DD1DDBDBE500F8F362 /* DeltaPairMemoryStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1C81DDBDBE500F8F362 /* DeltaPairMemoryStream.cpp */; };
+ 6890C1DE1DDBDBE500F8F362 /* LinearMemoryStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1CB1DDBDBE500F8F362 /* LinearMemoryStream.cpp */; };
+ 6890C1DF1DDBDBE500F8F362 /* LinearMemoryStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1CB1DDBDBE500F8F362 /* LinearMemoryStream.cpp */; };
+ 6890C1E01DDBDBE500F8F362 /* Savestate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1CD1DDBDBE500F8F362 /* Savestate.cpp */; };
+ 6890C1E11DDBDBE500F8F362 /* Savestate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1CD1DDBDBE500F8F362 /* Savestate.cpp */; };
+ 6890C1E21DDBDBE500F8F362 /* SavestateDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1CF1DDBDBE500F8F362 /* SavestateDatabase.cpp */; };
+ 6890C1E31DDBDBE500F8F362 /* SavestateDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1CF1DDBDBE500F8F362 /* SavestateDatabase.cpp */; };
+ 6890C1E41DDBDBE500F8F362 /* SavestateReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1D21DDBDBE500F8F362 /* SavestateReader.cpp */; };
+ 6890C1E51DDBDBE500F8F362 /* SavestateReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1D21DDBDBE500F8F362 /* SavestateReader.cpp */; };
+ 6890C1E61DDBDBE500F8F362 /* SavestateTranslator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1D41DDBDBE500F8F362 /* SavestateTranslator.cpp */; };
+ 6890C1E71DDBDBE500F8F362 /* SavestateTranslator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1D41DDBDBE500F8F362 /* SavestateTranslator.cpp */; };
+ 6890C1E81DDBDBE500F8F362 /* SavestateUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1D61DDBDBE500F8F362 /* SavestateUtils.cpp */; };
+ 6890C1E91DDBDBE500F8F362 /* SavestateUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1D61DDBDBE500F8F362 /* SavestateUtils.cpp */; };
+ 6890C1EA1DDBDBE500F8F362 /* SavestateWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1D81DDBDBE500F8F362 /* SavestateWriter.cpp */; };
+ 6890C1EB1DDBDBE500F8F362 /* SavestateWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1D81DDBDBE500F8F362 /* SavestateWriter.cpp */; };
+ 6890C1F21DDBDBEA00F8F362 /* GameClientReversiblePlayback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1ED1DDBDBEA00F8F362 /* GameClientReversiblePlayback.cpp */; };
+ 6890C1F31DDBDBEA00F8F362 /* GameClientReversiblePlayback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1ED1DDBDBEA00F8F362 /* GameClientReversiblePlayback.cpp */; };
+ 6890C1F41DDBDBEA00F8F362 /* GameLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1EF1DDBDBEA00F8F362 /* GameLoop.cpp */; };
+ 6890C1F51DDBDBEA00F8F362 /* GameLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1EF1DDBDBEA00F8F362 /* GameLoop.cpp */; };
+ 6890C2051DDBDBFC00F8F362 /* GameClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1F61DDBDBFC00F8F362 /* GameClient.cpp */; };
+ 6890C2061DDBDBFC00F8F362 /* GameClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1F61DDBDBFC00F8F362 /* GameClient.cpp */; };
+ 6890C2071DDBDBFC00F8F362 /* GameClientInput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1F91DDBDBFC00F8F362 /* GameClientInput.cpp */; };
+ 6890C2081DDBDBFC00F8F362 /* GameClientInput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1F91DDBDBFC00F8F362 /* GameClientInput.cpp */; };
+ 6890C2091DDBDBFC00F8F362 /* GameClientKeyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1FB1DDBDBFC00F8F362 /* GameClientKeyboard.cpp */; };
+ 6890C20A1DDBDBFC00F8F362 /* GameClientKeyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1FB1DDBDBFC00F8F362 /* GameClientKeyboard.cpp */; };
+ 6890C20B1DDBDBFC00F8F362 /* GameClientMouse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1FD1DDBDBFC00F8F362 /* GameClientMouse.cpp */; };
+ 6890C20C1DDBDBFC00F8F362 /* GameClientMouse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1FD1DDBDBFC00F8F362 /* GameClientMouse.cpp */; };
+ 6890C20D1DDBDBFC00F8F362 /* GameClientProperties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1FF1DDBDBFC00F8F362 /* GameClientProperties.cpp */; };
+ 6890C20E1DDBDBFC00F8F362 /* GameClientProperties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C1FF1DDBDBFC00F8F362 /* GameClientProperties.cpp */; };
+ 6890C20F1DDBDBFC00F8F362 /* GameClientTiming.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2011DDBDBFC00F8F362 /* GameClientTiming.cpp */; };
+ 6890C2101DDBDBFC00F8F362 /* GameClientTiming.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2011DDBDBFC00F8F362 /* GameClientTiming.cpp */; };
+ 6890C2111DDBDBFC00F8F362 /* GameClientTranslator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2031DDBDBFC00F8F362 /* GameClientTranslator.cpp */; };
+ 6890C2121DDBDBFC00F8F362 /* GameClientTranslator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2031DDBDBFC00F8F362 /* GameClientTranslator.cpp */; };
+ 6890C21D1DDBDCA200F8F362 /* GameResource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C21B1DDBDCA200F8F362 /* GameResource.cpp */; };
+ 6890C21E1DDBDCA200F8F362 /* GameResource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C21B1DDBDCA200F8F362 /* GameResource.cpp */; };
+ 6890C2271DDBDCF500F8F362 /* AddonCallbacksGame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2251DDBDCF500F8F362 /* AddonCallbacksGame.cpp */; };
+ 6890C2281DDBDCF500F8F362 /* AddonCallbacksGame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2251DDBDCF500F8F362 /* AddonCallbacksGame.cpp */; };
+ 6890C22F1DDBDD2D00F8F362 /* GameUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C22D1DDBDD2D00F8F362 /* GameUtils.cpp */; };
+ 6890C2301DDBDD2D00F8F362 /* GameUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C22D1DDBDD2D00F8F362 /* GameUtils.cpp */; };
+ 6890C2381DDBDD4400F8F362 /* GUIDialogSelectGameClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2341DDBDD4400F8F362 /* GUIDialogSelectGameClient.cpp */; };
+ 6890C2391DDBDD4400F8F362 /* GUIDialogSelectGameClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2341DDBDD4400F8F362 /* GUIDialogSelectGameClient.cpp */; };
+ 6890C23F1DDBDD6A00F8F362 /* PortManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C23B1DDBDD6A00F8F362 /* PortManager.cpp */; };
+ 6890C2401DDBDD6A00F8F362 /* PortManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C23B1DDBDD6A00F8F362 /* PortManager.cpp */; };
+ 6890C2411DDBDD6A00F8F362 /* PortMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C23D1DDBDD6A00F8F362 /* PortMapper.cpp */; };
+ 6890C2421DDBDD6A00F8F362 /* PortMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C23D1DDBDD6A00F8F362 /* PortMapper.cpp */; };
+ 6890C2461DDBDD9300F8F362 /* JoystickEasterEgg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2441DDBDD9200F8F362 /* JoystickEasterEgg.cpp */; };
+ 6890C2471DDBDD9300F8F362 /* JoystickEasterEgg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2441DDBDD9200F8F362 /* JoystickEasterEgg.cpp */; };
+ 6890C24A1DDBDDA400F8F362 /* KeyboardEasterEgg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2481DDBDDA400F8F362 /* KeyboardEasterEgg.cpp */; };
+ 6890C24B1DDBDDA400F8F362 /* KeyboardEasterEgg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2481DDBDDA400F8F362 /* KeyboardEasterEgg.cpp */; };
+ 6890C2501DDBDDC900F8F362 /* MouseInputHandling.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C24E1DDBDDC900F8F362 /* MouseInputHandling.cpp */; };
+ 6890C2511DDBDDC900F8F362 /* MouseInputHandling.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C24E1DDBDDC900F8F362 /* MouseInputHandling.cpp */; };
+ 6890C2571DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2551DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp */; };
+ 6890C2581DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2551DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp */; };
68AE5BA51C92412900C4D527 /* AddonCallbacksPeripheral.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68AE5BA31C92412900C4D527 /* AddonCallbacksPeripheral.cpp */; };
68AE5BA61C92412900C4D527 /* AddonCallbacksPeripheral.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68AE5BA31C92412900C4D527 /* AddonCallbacksPeripheral.cpp */; };
68AE5BBD1C9241DF00C4D527 /* DefaultJoystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68AE5BAC1C9241DF00C4D527 /* DefaultJoystick.cpp */; };
@@ -325,8 +381,6 @@
68AE5C341C9243A000C4D527 /* ControllerTranslator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68AE5C2A1C9243A000C4D527 /* ControllerTranslator.cpp */; };
68B7E5E81D5FA9B300A5AEC0 /* GUIFeatureControls.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68B7E5E61D5FA9B300A5AEC0 /* GUIFeatureControls.cpp */; };
68B7E5E91D5FA9B300A5AEC0 /* GUIFeatureControls.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68B7E5E61D5FA9B300A5AEC0 /* GUIFeatureControls.cpp */; };
- 68D9167A1DD0430E00058B06 /* GUIDialogNewJoystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D916781DD0430E00058B06 /* GUIDialogNewJoystick.cpp */; };
- 68D9167B1DD0430E00058B06 /* GUIDialogNewJoystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D916781DD0430E00058B06 /* GUIDialogNewJoystick.cpp */; };
68D77CFF1CD1C5E4004C1735 /* GameSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D77CFD1CD1C5E4004C1735 /* GameSettings.cpp */; };
68D77D001CD1C5E4004C1735 /* GameSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D77CFD1CD1C5E4004C1735 /* GameSettings.cpp */; };
68D77D0A1CD1C64C004C1735 /* JoystickEmulation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D77D061CD1C64C004C1735 /* JoystickEmulation.cpp */; };
@@ -337,6 +391,8 @@
68D77D131CD1C68A004C1735 /* PeripheralJoystickEmulation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D77D101CD1C68A004C1735 /* PeripheralJoystickEmulation.cpp */; };
68D77D171CD1C6D9004C1735 /* GameInfoTag.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D77D151CD1C6D9004C1735 /* GameInfoTag.cpp */; };
68D77D181CD1C6D9004C1735 /* GameInfoTag.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D77D151CD1C6D9004C1735 /* GameInfoTag.cpp */; };
+ 68D9167A1DD0430E00058B06 /* GUIDialogNewJoystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D916781DD0430E00058B06 /* GUIDialogNewJoystick.cpp */; };
+ 68D9167B1DD0430E00058B06 /* GUIDialogNewJoystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D916781DD0430E00058B06 /* GUIDialogNewJoystick.cpp */; };
761170901C8B85F8006C6366 /* AddonGUIRenderingControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7611708C1C8B85F8006C6366 /* AddonGUIRenderingControl.cpp */; };
761170911C8B85F8006C6366 /* AddonGUIWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7611708E1C8B85F8006C6366 /* AddonGUIWindow.cpp */; };
76AEFB361C8F79BD00EF2EC0 /* AddonInterfaces.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EDED2E991C878F61000F5E80 /* AddonInterfaces.cpp */; };
@@ -2786,6 +2842,83 @@
6861B9E91CC248EE00F62655 /* DriverReceiving.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DriverReceiving.h; path = joysticks/generic/DriverReceiving.h; sourceTree = "<group>"; };
6861B9EC1CC248F600F62655 /* IDriverReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDriverReceiver.h; path = joysticks/IDriverReceiver.h; sourceTree = "<group>"; };
6861B9ED1CC248F600F62655 /* IInputReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IInputReceiver.h; path = joysticks/IInputReceiver.h; sourceTree = "<group>"; };
+ 6890C1C61DDBDBE500F8F362 /* BasicMemoryStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BasicMemoryStream.cpp; path = games/addons/savestates/BasicMemoryStream.cpp; sourceTree = "<group>"; };
+ 6890C1C71DDBDBE500F8F362 /* BasicMemoryStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BasicMemoryStream.h; path = games/addons/savestates/BasicMemoryStream.h; sourceTree = "<group>"; };
+ 6890C1C81DDBDBE500F8F362 /* DeltaPairMemoryStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DeltaPairMemoryStream.cpp; path = games/addons/savestates/DeltaPairMemoryStream.cpp; sourceTree = "<group>"; };
+ 6890C1C91DDBDBE500F8F362 /* DeltaPairMemoryStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DeltaPairMemoryStream.h; path = games/addons/savestates/DeltaPairMemoryStream.h; sourceTree = "<group>"; };
+ 6890C1CA1DDBDBE500F8F362 /* IMemoryStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMemoryStream.h; path = games/addons/savestates/IMemoryStream.h; sourceTree = "<group>"; };
+ 6890C1CB1DDBDBE500F8F362 /* LinearMemoryStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LinearMemoryStream.cpp; path = games/addons/savestates/LinearMemoryStream.cpp; sourceTree = "<group>"; };
+ 6890C1CC1DDBDBE500F8F362 /* LinearMemoryStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LinearMemoryStream.h; path = games/addons/savestates/LinearMemoryStream.h; sourceTree = "<group>"; };
+ 6890C1CD1DDBDBE500F8F362 /* Savestate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Savestate.cpp; path = games/addons/savestates/Savestate.cpp; sourceTree = "<group>"; };
+ 6890C1CE1DDBDBE500F8F362 /* Savestate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Savestate.h; path = games/addons/savestates/Savestate.h; sourceTree = "<group>"; };
+ 6890C1CF1DDBDBE500F8F362 /* SavestateDatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SavestateDatabase.cpp; path = games/addons/savestates/SavestateDatabase.cpp; sourceTree = "<group>"; };
+ 6890C1D01DDBDBE500F8F362 /* SavestateDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SavestateDatabase.h; path = games/addons/savestates/SavestateDatabase.h; sourceTree = "<group>"; };
+ 6890C1D11DDBDBE500F8F362 /* SavestateDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SavestateDefines.h; path = games/addons/savestates/SavestateDefines.h; sourceTree = "<group>"; };
+ 6890C1D21DDBDBE500F8F362 /* SavestateReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SavestateReader.cpp; path = games/addons/savestates/SavestateReader.cpp; sourceTree = "<group>"; };
+ 6890C1D31DDBDBE500F8F362 /* SavestateReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SavestateReader.h; path = games/addons/savestates/SavestateReader.h; sourceTree = "<group>"; };
+ 6890C1D41DDBDBE500F8F362 /* SavestateTranslator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SavestateTranslator.cpp; path = games/addons/savestates/SavestateTranslator.cpp; sourceTree = "<group>"; };
+ 6890C1D51DDBDBE500F8F362 /* SavestateTranslator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SavestateTranslator.h; path = games/addons/savestates/SavestateTranslator.h; sourceTree = "<group>"; };
+ 6890C1D61DDBDBE500F8F362 /* SavestateUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SavestateUtils.cpp; path = games/addons/savestates/SavestateUtils.cpp; sourceTree = "<group>"; };
+ 6890C1D71DDBDBE500F8F362 /* SavestateUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SavestateUtils.h; path = games/addons/savestates/SavestateUtils.h; sourceTree = "<group>"; };
+ 6890C1D81DDBDBE500F8F362 /* SavestateWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SavestateWriter.cpp; path = games/addons/savestates/SavestateWriter.cpp; sourceTree = "<group>"; };
+ 6890C1D91DDBDBE500F8F362 /* SavestateWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SavestateWriter.h; path = games/addons/savestates/SavestateWriter.h; sourceTree = "<group>"; };
+ 6890C1EC1DDBDBEA00F8F362 /* GameClientRealtimePlayback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameClientRealtimePlayback.h; path = games/addons/playback/GameClientRealtimePlayback.h; sourceTree = "<group>"; };
+ 6890C1ED1DDBDBEA00F8F362 /* GameClientReversiblePlayback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GameClientReversiblePlayback.cpp; path = games/addons/playback/GameClientReversiblePlayback.cpp; sourceTree = "<group>"; };
+ 6890C1EE1DDBDBEA00F8F362 /* GameClientReversiblePlayback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameClientReversiblePlayback.h; path = games/addons/playback/GameClientReversiblePlayback.h; sourceTree = "<group>"; };
+ 6890C1EF1DDBDBEA00F8F362 /* GameLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GameLoop.cpp; path = games/addons/playback/GameLoop.cpp; sourceTree = "<group>"; };
+ 6890C1F01DDBDBEA00F8F362 /* GameLoop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameLoop.h; path = games/addons/playback/GameLoop.h; sourceTree = "<group>"; };
+ 6890C1F11DDBDBEA00F8F362 /* IGameClientPlayback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGameClientPlayback.h; path = games/addons/playback/IGameClientPlayback.h; sourceTree = "<group>"; };
+ 6890C1F61DDBDBFC00F8F362 /* GameClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GameClient.cpp; path = games/addons/GameClient.cpp; sourceTree = "<group>"; };
+ 6890C1F71DDBDBFC00F8F362 /* GameClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameClient.h; path = games/addons/GameClient.h; sourceTree = "<group>"; };
+ 6890C1F81DDBDBFC00F8F362 /* GameClientCallbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameClientCallbacks.h; path = games/addons/GameClientCallbacks.h; sourceTree = "<group>"; };
+ 6890C1F91DDBDBFC00F8F362 /* GameClientInput.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GameClientInput.cpp; path = games/addons/GameClientInput.cpp; sourceTree = "<group>"; };
+ 6890C1FA1DDBDBFC00F8F362 /* GameClientInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameClientInput.h; path = games/addons/GameClientInput.h; sourceTree = "<group>"; };
+ 6890C1FB1DDBDBFC00F8F362 /* GameClientKeyboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GameClientKeyboard.cpp; path = games/addons/GameClientKeyboard.cpp; sourceTree = "<group>"; };
+ 6890C1FC1DDBDBFC00F8F362 /* GameClientKeyboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameClientKeyboard.h; path = games/addons/GameClientKeyboard.h; sourceTree = "<group>"; };
+ 6890C1FD1DDBDBFC00F8F362 /* GameClientMouse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GameClientMouse.cpp; path = games/addons/GameClientMouse.cpp; sourceTree = "<group>"; };
+ 6890C1FE1DDBDBFC00F8F362 /* GameClientMouse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameClientMouse.h; path = games/addons/GameClientMouse.h; sourceTree = "<group>"; };
+ 6890C1FF1DDBDBFC00F8F362 /* GameClientProperties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GameClientProperties.cpp; path = games/addons/GameClientProperties.cpp; sourceTree = "<group>"; };
+ 6890C2001DDBDBFC00F8F362 /* GameClientProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameClientProperties.h; path = games/addons/GameClientProperties.h; sourceTree = "<group>"; };
+ 6890C2011DDBDBFC00F8F362 /* GameClientTiming.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GameClientTiming.cpp; path = games/addons/GameClientTiming.cpp; sourceTree = "<group>"; };
+ 6890C2021DDBDBFC00F8F362 /* GameClientTiming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameClientTiming.h; path = games/addons/GameClientTiming.h; sourceTree = "<group>"; };
+ 6890C2031DDBDBFC00F8F362 /* GameClientTranslator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GameClientTranslator.cpp; path = games/addons/GameClientTranslator.cpp; sourceTree = "<group>"; };
+ 6890C2041DDBDBFC00F8F362 /* GameClientTranslator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameClientTranslator.h; path = games/addons/GameClientTranslator.h; sourceTree = "<group>"; };
+ 6890C2181DDBDC5300F8F362 /* IArchivable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IArchivable.h; sourceTree = "<group>"; };
+ 6890C2191DDBDC6E00F8F362 /* ISerializable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ISerializable.h; sourceTree = "<group>"; };
+ 6890C21A1DDBDC7400F8F362 /* IXmlDeserializable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IXmlDeserializable.h; sourceTree = "<group>"; };
+ 6890C21B1DDBDCA200F8F362 /* GameResource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameResource.cpp; sourceTree = "<group>"; };
+ 6890C21C1DDBDCA200F8F362 /* GameResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameResource.h; sourceTree = "<group>"; };
+ 6890C21F1DDBDCB000F8F362 /* DllGameClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DllGameClient.h; sourceTree = "<group>"; };
+ 6890C2201DDBDCB700F8F362 /* DllAudioDSP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DllAudioDSP.h; sourceTree = "<group>"; };
+ 6890C2211DDBDCBC00F8F362 /* DllLibCPluff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DllLibCPluff.h; sourceTree = "<group>"; };
+ 6890C2221DDBDCC900F8F362 /* DllPeripheral.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DllPeripheral.h; sourceTree = "<group>"; };
+ 6890C2231DDBDCCE00F8F362 /* DllPVRClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DllPVRClient.h; sourceTree = "<group>"; };
+ 6890C2251DDBDCF500F8F362 /* AddonCallbacksGame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddonCallbacksGame.cpp; path = addons/binary/interfaces/api1/Game/AddonCallbacksGame.cpp; sourceTree = "<group>"; };
+ 6890C2261DDBDCF500F8F362 /* AddonCallbacksGame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddonCallbacksGame.h; path = addons/binary/interfaces/api1/Game/AddonCallbacksGame.h; sourceTree = "<group>"; };
+ 6890C2291DDBDD0900F8F362 /* kodi_game_callbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = kodi_game_callbacks.h; path = "kodi-addon-dev-kit/include/kodi/kodi_game_callbacks.h"; sourceTree = "<group>"; };
+ 6890C22A1DDBDD0900F8F362 /* kodi_game_dll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = kodi_game_dll.h; path = "kodi-addon-dev-kit/include/kodi/kodi_game_dll.h"; sourceTree = "<group>"; };
+ 6890C22B1DDBDD0900F8F362 /* kodi_game_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = kodi_game_types.h; path = "kodi-addon-dev-kit/include/kodi/kodi_game_types.h"; sourceTree = "<group>"; };
+ 6890C22C1DDBDD2D00F8F362 /* GameTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameTypes.h; path = games/GameTypes.h; sourceTree = "<group>"; };
+ 6890C22D1DDBDD2D00F8F362 /* GameUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GameUtils.cpp; path = games/GameUtils.cpp; sourceTree = "<group>"; };
+ 6890C22E1DDBDD2D00F8F362 /* GameUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameUtils.h; path = games/GameUtils.h; sourceTree = "<group>"; };
+ 6890C2341DDBDD4400F8F362 /* GUIDialogSelectGameClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GUIDialogSelectGameClient.cpp; path = games/dialogs/GUIDialogSelectGameClient.cpp; sourceTree = "<group>"; };
+ 6890C2351DDBDD4400F8F362 /* GUIDialogSelectGameClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GUIDialogSelectGameClient.h; path = games/dialogs/GUIDialogSelectGameClient.h; sourceTree = "<group>"; };
+ 6890C23B1DDBDD6A00F8F362 /* PortManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PortManager.cpp; path = games/ports/PortManager.cpp; sourceTree = "<group>"; };
+ 6890C23C1DDBDD6A00F8F362 /* PortManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PortManager.h; path = games/ports/PortManager.h; sourceTree = "<group>"; };
+ 6890C23D1DDBDD6A00F8F362 /* PortMapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PortMapper.cpp; path = games/ports/PortMapper.cpp; sourceTree = "<group>"; };
+ 6890C23E1DDBDD6A00F8F362 /* PortMapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PortMapper.h; path = games/ports/PortMapper.h; sourceTree = "<group>"; };
+ 6890C2431DDBDD8900F8F362 /* IButtonSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IButtonSequence.h; path = joysticks/IButtonSequence.h; sourceTree = "<group>"; };
+ 6890C2441DDBDD9200F8F362 /* JoystickEasterEgg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JoystickEasterEgg.cpp; path = joysticks/JoystickEasterEgg.cpp; sourceTree = "<group>"; };
+ 6890C2451DDBDD9200F8F362 /* JoystickEasterEgg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JoystickEasterEgg.h; path = joysticks/JoystickEasterEgg.h; sourceTree = "<group>"; };
+ 6890C2481DDBDDA400F8F362 /* KeyboardEasterEgg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = KeyboardEasterEgg.cpp; path = keyboard/KeyboardEasterEgg.cpp; sourceTree = "<group>"; };
+ 6890C2491DDBDDA400F8F362 /* KeyboardEasterEgg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KeyboardEasterEgg.h; path = keyboard/KeyboardEasterEgg.h; sourceTree = "<group>"; };
+ 6890C24E1DDBDDC900F8F362 /* MouseInputHandling.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MouseInputHandling.cpp; path = mouse/generic/MouseInputHandling.cpp; sourceTree = "<group>"; };
+ 6890C24F1DDBDDC900F8F362 /* MouseInputHandling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MouseInputHandling.h; path = mouse/generic/MouseInputHandling.h; sourceTree = "<group>"; };
+ 6890C2521DDBDDD500F8F362 /* IMouseButtonMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMouseButtonMap.h; path = mouse/IMouseButtonMap.h; sourceTree = "<group>"; };
+ 6890C2531DDBDDD500F8F362 /* IMouseDriverHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMouseDriverHandler.h; path = mouse/IMouseDriverHandler.h; sourceTree = "<group>"; };
+ 6890C2541DDBDDD500F8F362 /* IMouseInputHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMouseInputHandler.h; path = mouse/IMouseInputHandler.h; sourceTree = "<group>"; };
+ 6890C2551DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MouseWindowingButtonMap.cpp; path = mouse/MouseWindowingButtonMap.cpp; sourceTree = "<group>"; };
+ 6890C2561DDBDDD500F8F362 /* MouseWindowingButtonMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MouseWindowingButtonMap.h; path = mouse/MouseWindowingButtonMap.h; sourceTree = "<group>"; };
68AE5BA01C923E5300C4D527 /* kodi_vfs_utils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kodi_vfs_utils.hpp; path = "kodi-addon-dev-kit/include/kodi/kodi_vfs_utils.hpp"; sourceTree = "<group>"; };
68AE5BA31C92412900C4D527 /* AddonCallbacksPeripheral.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddonCallbacksPeripheral.cpp; path = addons/binary/interfaces/api1/Peripheral/AddonCallbacksPeripheral.cpp; sourceTree = "<group>"; };
68AE5BA41C92412900C4D527 /* AddonCallbacksPeripheral.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddonCallbacksPeripheral.h; path = addons/binary/interfaces/api1/Peripheral/AddonCallbacksPeripheral.h; sourceTree = "<group>"; };
@@ -2866,8 +2999,6 @@
68AE5C2C1C9243A000C4D527 /* ControllerTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ControllerTypes.h; path = games/controllers/ControllerTypes.h; sourceTree = "<group>"; };
68B7E5E61D5FA9B300A5AEC0 /* GUIFeatureControls.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GUIFeatureControls.cpp; path = games/controllers/guicontrols/GUIFeatureControls.cpp; sourceTree = "<group>"; };
68B7E5E71D5FA9B300A5AEC0 /* GUIFeatureControls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GUIFeatureControls.h; path = games/controllers/guicontrols/GUIFeatureControls.h; sourceTree = "<group>"; };
- 68D916781DD0430E00058B06 /* GUIDialogNewJoystick.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GUIDialogNewJoystick.cpp; path = joysticks/dialogs/GUIDialogNewJoystick.cpp; sourceTree = "<group>"; };
- 68D916791DD0430E00058B06 /* GUIDialogNewJoystick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GUIDialogNewJoystick.h; path = joysticks/dialogs/GUIDialogNewJoystick.h; sourceTree = "<group>"; };
68D77CFD1CD1C5E4004C1735 /* GameSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GameSettings.cpp; path = games/GameSettings.cpp; sourceTree = "<group>"; };
68D77CFE1CD1C5E4004C1735 /* GameSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameSettings.h; path = games/GameSettings.h; sourceTree = "<group>"; };
68D77D021CD1C638004C1735 /* IKeyboardHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IKeyboardHandler.h; path = keyboard/IKeyboardHandler.h; sourceTree = "<group>"; };
@@ -2879,6 +3010,8 @@
68D77D111CD1C68A004C1735 /* PeripheralJoystickEmulation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeripheralJoystickEmulation.h; sourceTree = "<group>"; };
68D77D151CD1C6D9004C1735 /* GameInfoTag.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GameInfoTag.cpp; path = games/tags/GameInfoTag.cpp; sourceTree = "<group>"; };
68D77D161CD1C6D9004C1735 /* GameInfoTag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameInfoTag.h; path = games/tags/GameInfoTag.h; sourceTree = "<group>"; };
+ 68D916781DD0430E00058B06 /* GUIDialogNewJoystick.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GUIDialogNewJoystick.cpp; path = joysticks/dialogs/GUIDialogNewJoystick.cpp; sourceTree = "<group>"; };
+ 68D916791DD0430E00058B06 /* GUIDialogNewJoystick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GUIDialogNewJoystick.h; path = joysticks/dialogs/GUIDialogNewJoystick.h; sourceTree = "<group>"; };
6E97BDBF0DA2B620003A2A89 /* EventClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventClient.h; sourceTree = "<group>"; };
6E97BDC00DA2B620003A2A89 /* EventPacket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventPacket.h; sourceTree = "<group>"; };
6E97BDC10DA2B620003A2A89 /* EventServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventServer.h; sourceTree = "<group>"; };
@@ -5214,8 +5347,15 @@
9A2FAD681C972BE10049652A /* ContextMenus.cpp */,
9A2FAD691C972BE10049652A /* ContextMenus.h */,
18B49FF61152BFA5001AF8A6 /* DllAddon.h */,
+ 6890C2201DDBDCB700F8F362 /* DllAudioDSP.h */,
+ 6890C21F1DDBDCB000F8F362 /* DllGameClient.h */,
+ 6890C2211DDBDCBC00F8F362 /* DllLibCPluff.h */,
+ 6890C2221DDBDCC900F8F362 /* DllPeripheral.h */,
+ 6890C2231DDBDCCE00F8F362 /* DllPVRClient.h */,
DF209C101DB2A0C200515D1F /* FilesystemInstaller.cpp */,
DF209C111DB2A0C300515D1F /* FilesystemInstaller.h */,
+ 6890C21B1DDBDCA200F8F362 /* GameResource.cpp */,
+ 6890C21C1DDBDCA200F8F362 /* GameResource.h */,
18B7C38612942090009E7A26 /* GUIDialogAddonInfo.cpp */,
18B7C38712942090009E7A26 /* GUIDialogAddonInfo.h */,
18B7C8F912942718009E7A26 /* GUIDialogAddonSettings.cpp */,
@@ -5505,6 +5645,7 @@
children = (
68AE5BAB1C92419500C4D527 /* joysticks */,
68D77D011CD1C627004C1735 /* keyboard */,
+ 6890C24C1DDBDDAF00F8F362 /* mouse */,
E4991332174E5E5C00741B6D /* touch */,
18B7C8CB12942546009E7A26 /* ButtonTranslator.cpp */,
18B7C8CC12942546009E7A26 /* ButtonTranslator.h */,
@@ -6187,15 +6328,121 @@
name = dialogs;
sourceTree = "<group>";
};
- 6827D71B1DD03E5300BE9114 /* dialogs */ = {
+ 6890C1C31DDBDBB400F8F362 /* addons */ = {
isa = PBXGroup;
children = (
- 6827D71C1DD03E5F00BE9114 /* GUIDialogNewJoystick.cpp */,
- 6827D71D1DD03E5F00BE9114 /* GUIDialogNewJoystick.h */,
+ 6890C1C51DDBDBCE00F8F362 /* playback */,
+ 6890C1C41DDBDBC800F8F362 /* savestates */,
+ 6890C1F61DDBDBFC00F8F362 /* GameClient.cpp */,
+ 6890C1F71DDBDBFC00F8F362 /* GameClient.h */,
+ 6890C1F81DDBDBFC00F8F362 /* GameClientCallbacks.h */,
+ 6890C1F91DDBDBFC00F8F362 /* GameClientInput.cpp */,
+ 6890C1FA1DDBDBFC00F8F362 /* GameClientInput.h */,
+ 6890C1FB1DDBDBFC00F8F362 /* GameClientKeyboard.cpp */,
+ 6890C1FC1DDBDBFC00F8F362 /* GameClientKeyboard.h */,
+ 6890C1FD1DDBDBFC00F8F362 /* GameClientMouse.cpp */,
+ 6890C1FE1DDBDBFC00F8F362 /* GameClientMouse.h */,
+ 6890C1FF1DDBDBFC00F8F362 /* GameClientProperties.cpp */,
+ 6890C2001DDBDBFC00F8F362 /* GameClientProperties.h */,
+ 6890C2011DDBDBFC00F8F362 /* GameClientTiming.cpp */,
+ 6890C2021DDBDBFC00F8F362 /* GameClientTiming.h */,
+ 6890C2031DDBDBFC00F8F362 /* GameClientTranslator.cpp */,
+ 6890C2041DDBDBFC00F8F362 /* GameClientTranslator.h */,
+ );
+ name = addons;
+ sourceTree = "<group>";
+ };
+ 6890C1C41DDBDBC800F8F362 /* savestates */ = {
+ isa = PBXGroup;
+ children = (
+ 6890C1C61DDBDBE500F8F362 /* BasicMemoryStream.cpp */,
+ 6890C1C71DDBDBE500F8F362 /* BasicMemoryStream.h */,
+ 6890C1C81DDBDBE500F8F362 /* DeltaPairMemoryStream.cpp */,
+ 6890C1C91DDBDBE500F8F362 /* DeltaPairMemoryStream.h */,
+ 6890C1CA1DDBDBE500F8F362 /* IMemoryStream.h */,
+ 6890C1CB1DDBDBE500F8F362 /* LinearMemoryStream.cpp */,
+ 6890C1CC1DDBDBE500F8F362 /* LinearMemoryStream.h */,
+ 6890C1CD1DDBDBE500F8F362 /* Savestate.cpp */,
+ 6890C1CE1DDBDBE500F8F362 /* Savestate.h */,
+ 6890C1CF1DDBDBE500F8F362 /* SavestateDatabase.cpp */,
+ 6890C1D01DDBDBE500F8F362 /* SavestateDatabase.h */,
+ 6890C1D11DDBDBE500F8F362 /* SavestateDefines.h */,
+ 6890C1D21DDBDBE500F8F362 /* SavestateReader.cpp */,
+ 6890C1D31DDBDBE500F8F362 /* SavestateReader.h */,
+ 6890C1D41DDBDBE500F8F362 /* SavestateTranslator.cpp */,
+ 6890C1D51DDBDBE500F8F362 /* SavestateTranslator.h */,
+ 6890C1D61DDBDBE500F8F362 /* SavestateUtils.cpp */,
+ 6890C1D71DDBDBE500F8F362 /* SavestateUtils.h */,
+ 6890C1D81DDBDBE500F8F362 /* SavestateWriter.cpp */,
+ 6890C1D91DDBDBE500F8F362 /* SavestateWriter.h */,
+ );
+ name = savestates;
+ sourceTree = "<group>";
+ };
+ 6890C1C51DDBDBCE00F8F362 /* playback */ = {
+ isa = PBXGroup;
+ children = (
+ 6890C1EC1DDBDBEA00F8F362 /* GameClientRealtimePlayback.h */,
+ 6890C1ED1DDBDBEA00F8F362 /* GameClientReversiblePlayback.cpp */,
+ 6890C1EE1DDBDBEA00F8F362 /* GameClientReversiblePlayback.h */,
+ 6890C1EF1DDBDBEA00F8F362 /* GameLoop.cpp */,
+ 6890C1F01DDBDBEA00F8F362 /* GameLoop.h */,
+ 6890C1F11DDBDBEA00F8F362 /* IGameClientPlayback.h */,
+ );
+ name = playback;
+ sourceTree = "<group>";
+ };
+ 6890C2241DDBDCE300F8F362 /* Game */ = {
+ isa = PBXGroup;
+ children = (
+ 6890C2251DDBDCF500F8F362 /* AddonCallbacksGame.cpp */,
+ 6890C2261DDBDCF500F8F362 /* AddonCallbacksGame.h */,
+ );
+ name = Game;
+ sourceTree = "<group>";
+ };
+ 6890C2311DDBDD3500F8F362 /* dialogs */ = {
+ isa = PBXGroup;
+ children = (
+ 6890C2341DDBDD4400F8F362 /* GUIDialogSelectGameClient.cpp */,
+ 6890C2351DDBDD4400F8F362 /* GUIDialogSelectGameClient.h */,
);
name = dialogs;
sourceTree = "<group>";
};
+ 6890C23A1DDBDD4E00F8F362 /* ports */ = {
+ isa = PBXGroup;
+ children = (
+ 6890C23B1DDBDD6A00F8F362 /* PortManager.cpp */,
+ 6890C23C1DDBDD6A00F8F362 /* PortManager.h */,
+ 6890C23D1DDBDD6A00F8F362 /* PortMapper.cpp */,
+ 6890C23E1DDBDD6A00F8F362 /* PortMapper.h */,
+ );
+ name = ports;
+ sourceTree = "<group>";
+ };
+ 6890C24C1DDBDDAF00F8F362 /* mouse */ = {
+ isa = PBXGroup;
+ children = (
+ 6890C24D1DDBDDC200F8F362 /* generic */,
+ 6890C2521DDBDDD500F8F362 /* IMouseButtonMap.h */,
+ 6890C2531DDBDDD500F8F362 /* IMouseDriverHandler.h */,
+ 6890C2541DDBDDD500F8F362 /* IMouseInputHandler.h */,
+ 6890C2551DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp */,
+ 6890C2561DDBDDD500F8F362 /* MouseWindowingButtonMap.h */,
+ );
+ name = mouse;
+ sourceTree = "<group>";
+ };
+ 6890C24D1DDBDDC200F8F362 /* generic */ = {
+ isa = PBXGroup;
+ children = (
+ 6890C24E1DDBDDC900F8F362 /* MouseInputHandling.cpp */,
+ 6890C24F1DDBDDC900F8F362 /* MouseInputHandling.h */,
+ );
+ name = generic;
+ sourceTree = "<group>";
+ };
68AE5BA21C92410300C4D527 /* Peripheral */ = {
isa = PBXGroup;
children = (
@@ -6220,6 +6467,9 @@
68AE5BB01C9241DF00C4D527 /* IButtonMap.h */,
6819C8B61D76492500A60E30 /* IButtonMapCallback.h */,
68AE5BB11C9241DF00C4D527 /* IButtonMapper.h */,
+ 6890C2431DDBDD8900F8F362 /* IButtonSequence.h */,
+ 6890C2441DDBDD9200F8F362 /* JoystickEasterEgg.cpp */,
+ 6890C2451DDBDD9200F8F362 /* JoystickEasterEgg.h */,
68AE5BB21C9241DF00C4D527 /* IDriverHandler.h */,
6861B9EC1CC248F600F62655 /* IDriverReceiver.h */,
68AE5BB31C9241DF00C4D527 /* IInputHandler.h */,
@@ -6274,10 +6524,16 @@
68AE5BF91C92431F00C4D527 /* games */ = {
isa = PBXGroup;
children = (
+ 6890C1C31DDBDBB400F8F362 /* addons */,
68AE5BFA1C92433900C4D527 /* controllers */,
+ 6890C2311DDBDD3500F8F362 /* dialogs */,
+ 6890C23A1DDBDD4E00F8F362 /* ports */,
68D77D141CD1C6C7004C1735 /* tags */,
68D77CFD1CD1C5E4004C1735 /* GameSettings.cpp */,
68D77CFE1CD1C5E4004C1735 /* GameSettings.h */,
+ 6890C22C1DDBDD2D00F8F362 /* GameTypes.h */,
+ 6890C22D1DDBDD2D00F8F362 /* GameUtils.cpp */,
+ 6890C22E1DDBDD2D00F8F362 /* GameUtils.h */,
);
name = games;
sourceTree = "<group>";
@@ -6338,37 +6594,13 @@
name = windows;
sourceTree = "<group>";
};
- 68D916771DD0430300058B06 /* dialogs */ = {
- isa = PBXGroup;
- children = (
- 68D916781DD0430E00058B06 /* GUIDialogNewJoystick.cpp */,
- 68D916791DD0430E00058B06 /* GUIDialogNewJoystick.h */,
- );
- name = dialogs;
- sourceTree = "<group>";
- };
- 76EC19BB1D15510D00D4AF19 /* AudioDSPAddons */ = {
- isa = PBXGroup;
- children = (
- 76EC19BC1D15514200D4AF19 /* ActiveAEDSP.cpp */,
- 76EC19BD1D15514200D4AF19 /* ActiveAEDSP.h */,
- 76EC19BE1D15514200D4AF19 /* ActiveAEDSPAddon.cpp */,
- 76EC19BF1D15514200D4AF19 /* ActiveAEDSPAddon.h */,
- 76EC19C01D15514200D4AF19 /* ActiveAEDSPDatabase.cpp */,
- 76EC19C11D15514200D4AF19 /* ActiveAEDSPDatabase.h */,
- 76EC19C21D15514200D4AF19 /* ActiveAEDSPMode.cpp */,
- 76EC19C31D15514200D4AF19 /* ActiveAEDSPMode.h */,
- 76EC19C41D15514200D4AF19 /* ActiveAEDSPProcess.cpp */,
- 76EC19C51D15514200D4AF19 /* ActiveAEDSPProcess.h */,
- );
- name = AudioDSPAddons;
- sourceTree = "<group>";
- };
68D77D011CD1C627004C1735 /* keyboard */ = {
isa = PBXGroup;
children = (
68D77D031CD1C63B004C1735 /* generic */,
68D77D021CD1C638004C1735 /* IKeyboardHandler.h */,
+ 6890C2481DDBDDA400F8F362 /* KeyboardEasterEgg.cpp */,
+ 6890C2491DDBDDA400F8F362 /* KeyboardEasterEgg.h */,
);
name = keyboard;
sourceTree = "<group>";
@@ -6391,6 +6623,32 @@
name = tags;
sourceTree = "<group>";
};
+ 68D916771DD0430300058B06 /* dialogs */ = {
+ isa = PBXGroup;
+ children = (
+ 68D916781DD0430E00058B06 /* GUIDialogNewJoystick.cpp */,
+ 68D916791DD0430E00058B06 /* GUIDialogNewJoystick.h */,
+ );
+ name = dialogs;
+ sourceTree = "<group>";
+ };
+ 76EC19BB1D15510D00D4AF19 /* AudioDSPAddons */ = {
+ isa = PBXGroup;
+ children = (
+ 76EC19BC1D15514200D4AF19 /* ActiveAEDSP.cpp */,
+ 76EC19BD1D15514200D4AF19 /* ActiveAEDSP.h */,
+ 76EC19BE1D15514200D4AF19 /* ActiveAEDSPAddon.cpp */,
+ 76EC19BF1D15514200D4AF19 /* ActiveAEDSPAddon.h */,
+ 76EC19C01D15514200D4AF19 /* ActiveAEDSPDatabase.cpp */,
+ 76EC19C11D15514200D4AF19 /* ActiveAEDSPDatabase.h */,
+ 76EC19C21D15514200D4AF19 /* ActiveAEDSPMode.cpp */,
+ 76EC19C31D15514200D4AF19 /* ActiveAEDSPMode.h */,
+ 76EC19C41D15514200D4AF19 /* ActiveAEDSPProcess.cpp */,
+ 76EC19C51D15514200D4AF19 /* ActiveAEDSPProcess.h */,
+ );
+ name = AudioDSPAddons;
+ sourceTree = "<group>";
+ };
76F4C37B1C8E927A00A1E64B /* InputStream */ = {
isa = PBXGroup;
children = (
@@ -8934,10 +9192,13 @@
DF56EF231A798A5E00CAAEFB /* HttpRangeUtils.h */,
DF52769C151BAEDA00B5B63B /* HttpResponse.cpp */,
DF52769D151BAEDA00B5B63B /* HttpResponse.h */,
+ 6890C2181DDBDC5300F8F362 /* IArchivable.h */,
E38E1E4C0D25F9FD00618676 /* InfoLoader.cpp */,
E38E1E4D0D25F9FD00618676 /* InfoLoader.h */,
DFAF6A4C16EBAE3800D6AE12 /* IRssObserver.h */,
+ 6890C2191DDBDC6E00F8F362 /* ISerializable.h */,
36A9443E15821E5400727135 /* ISortable.h */,
+ 6890C21A1DDBDC7400F8F362 /* IXmlDeserializable.h */,
7CAA205B107AFC280096DE39 /* Job.h */,
F57B6F7E1071B8B500079ACB /* JobManager.cpp */,
F57B6F7F1071B8B500079ACB /* JobManager.h */,
@@ -9209,6 +9470,9 @@
EDE8C70F1C7F618500A86ECC /* kodi_audiodec_dll.h */,
EDE8C7101C7F618500A86ECC /* kodi_audiodec_types.h */,
EDE8C7111C7F618500A86ECC /* kodi_audioengine_types.h */,
+ 6890C2291DDBDD0900F8F362 /* kodi_game_callbacks.h */,
+ 6890C22A1DDBDD0900F8F362 /* kodi_game_dll.h */,
+ 6890C22B1DDBDD0900F8F362 /* kodi_game_types.h */,
7CEE107B1C970BB800E0D426 /* kodi_inputstream_dll.h */,
7CEE107C1C970BB800E0D426 /* kodi_inputstream_types.h */,
68AE5BA71C92414B00C4D527 /* kodi_peripheral_callbacks.h */,
@@ -9269,6 +9533,7 @@
EDED2E851C878D3F000F5E80 /* AudioDSP */,
EDED2E831C878D04000F5E80 /* AudioEngine */,
EDED2E821C878CF6000F5E80 /* Codec */,
+ 6890C2241DDBDCE300F8F362 /* Game */,
EDED2E801C878CB4000F5E80 /* GUI */,
76F4C37B1C8E927A00A1E64B /* InputStream */,
68AE5BA21C92410300C4D527 /* Peripheral */,
@@ -9848,6 +10113,7 @@
7C8E02431BA35D0B0072E8B2 /* SkinBuiltins.cpp in Sources */,
E38E1F3C0D25F9FD00618676 /* Autorun.cpp in Sources */,
E38E1F3D0D25F9FD00618676 /* AutoSwitch.cpp in Sources */,
+ 6890C1E81DDBDBE500F8F362 /* SavestateUtils.cpp in Sources */,
E38E1F3E0D25F9FD00618676 /* BackgroundInfoLoader.cpp in Sources */,
E38E1F450D25F9FD00618676 /* CDDARipper.cpp in Sources */,
E38E1F460D25F9FD00618676 /* Encoder.cpp in Sources */,
@@ -9882,6 +10148,7 @@
E38E1F890D25F9FD00618676 /* DVDOverlayCodecText.cpp in Sources */,
E38E1F8D0D25F9FD00618676 /* DVDVideoCodecFFmpeg.cpp in Sources */,
E38E1F8F0D25F9FD00618676 /* DVDVideoPPFFmpeg.cpp in Sources */,
+ 6890C1E21DDBDBE500F8F362 /* SavestateDatabase.cpp in Sources */,
E38E1F910D25F9FD00618676 /* DVDDemux.cpp in Sources */,
7C8E023A1BA35D0B0072E8B2 /* PlayerBuiltins.cpp in Sources */,
80204F121C91CD3600E8C88B /* InputStreamMultiSource.cpp in Sources */,
@@ -9906,6 +10173,7 @@
395C2A141A9F072400EBC7AD /* ResourceFile.cpp in Sources */,
E38E1FAA0D25F9FD00618676 /* VideoPlayerVideo.cpp in Sources */,
DF91E93E1C0A26350011084D /* SDLMain.mm in Sources */,
+ 6890C1DC1DDBDBE500F8F362 /* DeltaPairMemoryStream.cpp in Sources */,
E38E1FAB0D25F9FD00618676 /* DVDStreamInfo.cpp in Sources */,
E38E1FAC0D25F9FD00618676 /* DVDFactorySubtitle.cpp in Sources */,
E38E1FAD0D25F9FD00618676 /* DVDSubtitleLineCollection.cpp in Sources */,
@@ -9919,6 +10187,8 @@
68D77D0E1CD1C672004C1735 /* PeripheralBusApplication.cpp in Sources */,
E38E1FF00D25F9FD00618676 /* VideoFilterShader.cpp in Sources */,
E38E1FF10D25F9FD00618676 /* YUV2RGBShader.cpp in Sources */,
+ 6890C2051DDBDBFC00F8F362 /* GameClient.cpp in Sources */,
+ 6890C20D1DDBDBFC00F8F362 /* GameClientProperties.cpp in Sources */,
2AFBB94C1CC608A200BAB340 /* GUIEPGGridContainerModel.cpp in Sources */,
E38E1FF70D25F9FD00618676 /* CueDocument.cpp in Sources */,
E38E1FF80D25F9FD00618676 /* Database.cpp in Sources */,
@@ -9929,6 +10199,7 @@
EDED2E971C878EF8000F5E80 /* AddonCallbacksAudioDSP.cpp in Sources */,
E38E1FFC0D25F9FD00618676 /* DynamicDll.cpp in Sources */,
E38E1FFF0D25F9FD00618676 /* FileItem.cpp in Sources */,
+ 6890C1F41DDBDBEA00F8F362 /* GameLoop.cpp in Sources */,
E38E20020D25F9FD00618676 /* CacheStrategy.cpp in Sources */,
E38E20030D25F9FD00618676 /* CDDADirectory.cpp in Sources */,
E38E20040D25F9FD00618676 /* cddb.cpp in Sources */,
@@ -9995,6 +10266,7 @@
68AE5BC31C9241DF00C4D527 /* JoystickTranslator.cpp in Sources */,
7CFC08381C5BA7D0000E5E73 /* DVDDemuxClient.cpp in Sources */,
E38E20670D25F9FD00618676 /* DirectoryNodeRecentlyAddedMovies.cpp in Sources */,
+ 6890C2411DDBDD6A00F8F362 /* PortMapper.cpp in Sources */,
E38E20680D25F9FD00618676 /* DirectoryNodeRecentlyAddedMusicVideos.cpp in Sources */,
E38E20690D25F9FD00618676 /* DirectoryNodeRoot.cpp in Sources */,
E38E206A0D25F9FD00618676 /* DirectoryNodeSeasons.cpp in Sources */,
@@ -10008,6 +10280,7 @@
E38E20740D25F9FD00618676 /* VirtualDirectory.cpp in Sources */,
E38E20770D25F9FD00618676 /* ZipDirectory.cpp in Sources */,
395C2A041A9CD25100EBC7AD /* ContextItemAddonInvoker.cpp in Sources */,
+ 6890C1DA1DDBDBE500F8F362 /* BasicMemoryStream.cpp in Sources */,
395C2A091A9F06EB00EBC7AD /* LanguageResource.cpp in Sources */,
E38E20780D25F9FD00618676 /* ZipManager.cpp in Sources */,
E38E207B0D25F9FD00618676 /* GUIDialogBoxBase.cpp in Sources */,
@@ -10102,6 +10375,7 @@
E38E22480D25F9FE00618676 /* isnt.cpp in Sources */,
E38E22490D25F9FE00618676 /* log.cpp in Sources */,
E38E224B0D25F9FE00618676 /* match.cpp in Sources */,
+ 6890C2071DDBDBFC00F8F362 /* GameClientInput.cpp in Sources */,
E38E224D0D25F9FE00618676 /* options.cpp in Sources */,
395C29BC1A94733100EBC7AD /* Key.cpp in Sources */,
E38E224E0D25F9FE00618676 /* pathfn.cpp in Sources */,
@@ -10139,6 +10413,7 @@
E38E227E0D25F9FE00618676 /* MusicDatabase.cpp in Sources */,
E38E227F0D25F9FE00618676 /* MusicInfoLoader.cpp in Sources */,
E38E22800D25F9FE00618676 /* MusicInfoScanner.cpp in Sources */,
+ 6890C1E01DDBDBE500F8F362 /* Savestate.cpp in Sources */,
E38E22970D25F9FE00618676 /* NfoFile.cpp in Sources */,
E38E22A00D25F9FE00618676 /* PartyModeManager.cpp in Sources */,
B542632B197D353B00726998 /* PosixInterfaceForCLog.cpp in Sources */,
@@ -10253,6 +10528,7 @@
E46F7C2D0F77219700C25D29 /* ZeroconfOSX.cpp in Sources */,
83E0B2490F7C95FF0091643F /* Atomics.cpp in Sources */,
F5AACA680FB3DE2D00DBB77C /* GUIDialogSelect.cpp in Sources */,
+ 6890C1E41DDBDBE500F8F362 /* SavestateReader.cpp in Sources */,
F5AACA970FB3E2B800DBB77C /* GUIDialogSlider.cpp in Sources */,
F59876C00FBA351D008EF4FB /* VideoReferenceClock.cpp in Sources */,
F5987F050FBDF274008EF4FB /* DPMSSupport.cpp in Sources */,
@@ -10306,14 +10582,17 @@
7C45DBE910F325C400D4BBF3 /* DAVDirectory.cpp in Sources */,
7CAA57471C8AF6C20032A326 /* DebugRenderer.cpp in Sources */,
7C4B64A71C86F712000E1F74 /* InputStreamAddon.cpp in Sources */,
+ 6890C1F21DDBDBEA00F8F362 /* GameClientReversiblePlayback.cpp in Sources */,
F592568810FBF2E100D2C91D /* ConvolutionKernels.cpp in Sources */,
F5DC87E2110A287400EE1B15 /* RingBuffer.cpp in Sources */,
F5F244651110DC6B009126C6 /* FileOperationJob.cpp in Sources */,
+ 6890C24A1DDBDDA400F8F362 /* KeyboardEasterEgg.cpp in Sources */,
DF0E4AC51AD597ED00A75430 /* VideoPlayerRadioRDS.cpp in Sources */,
F5F245EE1112C9AB009126C6 /* FileUtils.cpp in Sources */,
7C1870621CA1664D00114E45 /* PVRClient.cpp in Sources */,
F5A7A702112893E50059D6AA /* AnnouncementManager.cpp in Sources */,
F5A7A85B112908F00059D6AA /* WebServer.cpp in Sources */,
+ 6890C2501DDBDDC900F8F362 /* MouseInputHandling.cpp in Sources */,
7C7B2B301134F36400713D6D /* mysqldataset.cpp in Sources */,
F5A7B37E113AFB900059D6AA /* SFTPDirectory.cpp in Sources */,
F5A7B42C113CBB950059D6AA /* AddonsDirectory.cpp in Sources */,
@@ -10405,6 +10684,7 @@
18B7C7E01294222E009E7A26 /* GUISpinControlEx.cpp in Sources */,
18B7C7E21294222E009E7A26 /* GUIStaticItem.cpp in Sources */,
18B7C7E31294222E009E7A26 /* GUITextBox.cpp in Sources */,
+ 6890C20B1DDBDBFC00F8F362 /* GameClientMouse.cpp in Sources */,
18B7C7E41294222E009E7A26 /* GUITextLayout.cpp in Sources */,
18B7C7E51294222E009E7A26 /* GUITexture.cpp in Sources */,
18B7C7E61294222E009E7A26 /* GUITextureD3D.cpp in Sources */,
@@ -10451,6 +10731,7 @@
18B7C933129428CA009E7A26 /* PlayListM3U.cpp in Sources */,
18B7C934129428CA009E7A26 /* PlayListPLS.cpp in Sources */,
18B7C935129428CA009E7A26 /* PlayListURL.cpp in Sources */,
+ 6890C2091DDBDBFC00F8F362 /* GameClientKeyboard.cpp in Sources */,
18B7C936129428CA009E7A26 /* PlayListWPL.cpp in Sources */,
18B7C937129428CA009E7A26 /* PlayListXML.cpp in Sources */,
18B7C938129428CA009E7A26 /* SmartPlayList.cpp in Sources */,
@@ -10510,6 +10791,7 @@
DF98D98C1434F47D00A6EBE1 /* SkinVariable.cpp in Sources */,
F5E10537140AA38100175026 /* PeripheralBusUSB.cpp in Sources */,
F5E10538140AA38100175026 /* PeripheralBus.cpp in Sources */,
+ 6890C1EA1DDBDBE500F8F362 /* SavestateWriter.cpp in Sources */,
F5E1053B140AA38100175026 /* Peripheral.cpp in Sources */,
F5E1053C140AA38100175026 /* PeripheralBluetooth.cpp in Sources */,
F5E1053E140AA38100175026 /* PeripheralDisk.cpp in Sources */,
@@ -10563,6 +10845,7 @@
DF93D6AB1444A8B1007C6459 /* ShoutcastFile.cpp in Sources */,
DF93D6AD1444A8B1007C6459 /* SMBFile.cpp in Sources */,
DF93D6AE1444A8B1007C6459 /* SpecialProtocolFile.cpp in Sources */,
+ 6890C23F1DDBDD6A00F8F362 /* PortManager.cpp in Sources */,
DF6A0D811A4584E80075BBFC /* OverrideDirectory.cpp in Sources */,
2AC7EB601C34893700BDAA95 /* PVRRecordingsPath.cpp in Sources */,
DF93D6B11444A8B1007C6459 /* UDFFile.cpp in Sources */,
@@ -10595,6 +10878,7 @@
C84828C5156CFCD8005A996F /* PVRChannelGroupInternal.cpp in Sources */,
C84828C6156CFCD8005A996F /* PVRChannelGroups.cpp in Sources */,
C84828C7156CFCD8005A996F /* PVRChannelGroupsContainer.cpp in Sources */,
+ 6890C21D1DDBDCA200F8F362 /* GameResource.cpp in Sources */,
C84828C8156CFCD8005A996F /* GUIDialogPVRChannelManager.cpp in Sources */,
C84828C9156CFCD8005A996F /* GUIDialogPVRChannelsOSD.cpp in Sources */,
C84828CC156CFCD8005A996F /* GUIDialogPVRGroupManager.cpp in Sources */,
@@ -10611,6 +10895,8 @@
C84828D8156CFCD8005A996F /* PVRRecording.cpp in Sources */,
7CED593D1CD341280093F573 /* VTB.cpp in Sources */,
C84828D9156CFCD8005A996F /* PVRRecordings.cpp in Sources */,
+ 6890C2571DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp in Sources */,
+ 6890C20F1DDBDBFC00F8F362 /* GameClientTiming.cpp in Sources */,
C84828DB156CFCD8005A996F /* PVRTimerInfoTag.cpp in Sources */,
C84828DC156CFCD8005A996F /* PVRTimers.cpp in Sources */,
C84828DD156CFCD8005A996F /* GUIViewStatePVR.cpp in Sources */,
@@ -10742,6 +11028,7 @@
DF8990211709BB5400B35C21 /* ViewStateSettings.cpp in Sources */,
395C29D51A98A11C00EBC7AD /* WsgiErrorStream.cpp in Sources */,
DF28EDA2170E1A11005FA9D2 /* GUIDialogLockSettings.cpp in Sources */,
+ 6890C2461DDBDD9300F8F362 /* JoystickEasterEgg.cpp in Sources */,
DF28EDA3170E1A11005FA9D2 /* GUIDialogProfileSettings.cpp in Sources */,
DF28EDA6170E1A11005FA9D2 /* Profile.cpp in Sources */,
DF28EDA7170E1A11005FA9D2 /* ProfilesManager.cpp in Sources */,
@@ -10749,12 +11036,14 @@
DF28EE03170E1E51005FA9D2 /* DisplaySettings.cpp in Sources */,
7C87B2CE162CE39600EF897D /* PlayerController.cpp in Sources */,
F52CC5F01713AAA200113454 /* DirectoryNodeGrouped.cpp in Sources */,
+ 6890C2271DDBDCF500F8F362 /* AddonCallbacksGame.cpp in Sources */,
F52CC6AA1713BD2B00113454 /* DirectoryNodeGrouped.cpp in Sources */,
DFD717271C09EDCE0025D964 /* MessagePrinter.cpp in Sources */,
DFECFADF172D9C5100A43CF7 /* GUIControlSettings.cpp in Sources */,
DFECFB09172D9CAB00A43CF7 /* SettingAddon.cpp in Sources */,
DFECFB0C172D9CAB00A43CF7 /* SettingControl.cpp in Sources */,
DFECFB0E172D9CAB00A43CF7 /* SettingPath.cpp in Sources */,
+ 6890C2111DDBDBFC00F8F362 /* GameClientTranslator.cpp in Sources */,
395F6DDD1A8133360088CC74 /* GUIDialogSimpleMenu.cpp in Sources */,
DFECFB1C172D9D0100A43CF7 /* BooleanLogic.cpp in Sources */,
DFECFB4C172D9D6D00A43CF7 /* NetworkServices.cpp in Sources */,
@@ -10766,6 +11055,7 @@
9A2FAD6A1C972BE10049652A /* ContextMenus.cpp in Sources */,
DFE4095B17417FDF00473BD9 /* LegacyPathTranslation.cpp in Sources */,
39C38CCA1BBFF1EE000F59F5 /* InputCodingTableKorean.cpp in Sources */,
+ 6890C1E61DDBDBE500F8F362 /* SavestateTranslator.cpp in Sources */,
0E3036EC1760F68A00D93596 /* FavouritesDirectory.cpp in Sources */,
68AE5BE11C92421800C4D527 /* AddonButtonMapping.cpp in Sources */,
68AE5C091C92437900C4D527 /* GUIControllerList.cpp in Sources */,
@@ -10796,6 +11086,7 @@
7C140989183224B8009F9411 /* ISetting.cpp in Sources */,
7CAEF0D21D0E9F0D00B1316C /* GUIDialogCMSSettings.cpp in Sources */,
7C14098C183224B8009F9411 /* ISettingControl.cpp in Sources */,
+ 6890C2381DDBDD4400F8F362 /* GUIDialogSelectGameClient.cpp in Sources */,
68AE5BDF1C92421800C4D527 /* AddonButtonMap.cpp in Sources */,
7C14098F183224B8009F9411 /* Setting.cpp in Sources */,
7CAEF0CE1D0E9E3800B1316C /* ColorManager.cpp in Sources */,
@@ -10862,6 +11153,7 @@
7CCDA16B192753E30074CF51 /* PltService.cpp in Sources */,
7C2ED53D1C7F7A9800C04032 /* ProcessInfo.cpp in Sources */,
7CCDA174192753E30074CF51 /* PltSsdp.cpp in Sources */,
+ 6890C1DE1DDBDBE500F8F362 /* LinearMemoryStream.cpp in Sources */,
DF0D9F511D63961B006A7DBB /* PlatformDarwinOSX.cpp in Sources */,
7CCDA17D192753E30074CF51 /* PltStateVariable.cpp in Sources */,
7CCDA186192753E30074CF51 /* PltTaskManager.cpp in Sources */,
@@ -10960,6 +11252,7 @@
7CAA469019427AED00008885 /* PosixDirectory.cpp in Sources */,
76EC19CA1D15514200D4AF19 /* ActiveAEDSPProcess.cpp in Sources */,
42DAC16E1A6E789E0066B4C8 /* PVRActionListener.cpp in Sources */,
+ 6890C22F1DDBDD2D00F8F362 /* GameUtils.cpp in Sources */,
DF033D381946612400BFC82E /* AEDeviceEnumerationOSX.cpp in Sources */,
7C525DF5195E2D8100BE3482 /* SaveFileStateJob.cpp in Sources */,
7C908894196358A8003D0619 /* auto_buffer.cpp in Sources */,
@@ -10983,6 +11276,7 @@
7C921F931CD6042500684D0B /* FrameBufferObject.cpp in Sources */,
E499114F174E5CC300741B6D /* archive.cpp in Sources */,
68AE5BF01C92424400C4D527 /* PeripheralJoystick.cpp in Sources */,
+ 6890C1E11DDBDBE500F8F362 /* Savestate.cpp in Sources */,
E4991150174E5CC300741B6D /* arcread.cpp in Sources */,
E4991151174E5CC300741B6D /* cmddata.cpp in Sources */,
E4991152174E5CC300741B6D /* consio.cpp in Sources */,
@@ -10991,6 +11285,7 @@
68AE5BC01C9241DF00C4D527 /* DriverPrimitive.cpp in Sources */,
E4991155174E5CC300741B6D /* encname.cpp in Sources */,
E4991156174E5CC300741B6D /* errhnd.cpp in Sources */,
+ 6890C21E1DDBDCA200F8F362 /* GameResource.cpp in Sources */,
E4991157174E5CC300741B6D /* extinfo.cpp in Sources */,
E4991158174E5CC300741B6D /* extract.cpp in Sources */,
68AE5BCF1C9241F800C4D527 /* ButtonMapping.cpp in Sources */,
@@ -11041,6 +11336,7 @@
E4991183174E5CE000741B6D /* GUIDialogAddonInfo.cpp in Sources */,
68AE5C081C92437900C4D527 /* GUIConfigurationWizard.cpp in Sources */,
E4991184174E5CE000741B6D /* GUIDialogAddonSettings.cpp in Sources */,
+ 6890C24B1DDBDDA400F8F362 /* KeyboardEasterEgg.cpp in Sources */,
E4991185174E5CE000741B6D /* GUIViewStateAddonBrowser.cpp in Sources */,
E4991186174E5CE000741B6D /* GUIWindowAddonBrowser.cpp in Sources */,
E4991187174E5CE000741B6D /* PluginSource.cpp in Sources */,
@@ -11104,6 +11400,7 @@
E49911D4174E5D2E00741B6D /* DVDDemuxCDDA.cpp in Sources */,
E49911D5174E5D2E00741B6D /* DVDDemuxFFmpeg.cpp in Sources */,
E49911D9174E5D2E00741B6D /* DVDDemuxUtils.cpp in Sources */,
+ 6890C2121DDBDBFC00F8F362 /* GameClientTranslator.cpp in Sources */,
E49911DA174E5D2E00741B6D /* DVDDemuxVobsub.cpp in Sources */,
E49911DB174E5D2E00741B6D /* DVDFactoryDemuxer.cpp in Sources */,
E49911DC174E5D3700741B6D /* DVDFactoryInputStream.cpp in Sources */,
@@ -11132,6 +11429,7 @@
E49911F3174E5D3E00741B6D /* DVDSubtitleTagMicroDVD.cpp in Sources */,
E49911F4174E5D3E00741B6D /* DVDSubtitleTagSami.cpp in Sources */,
E49911F5174E5D4500741B6D /* DVDAudio.cpp in Sources */,
+ 6890C2061DDBDBFC00F8F362 /* GameClient.cpp in Sources */,
E49911F6174E5D4500741B6D /* DVDClock.cpp in Sources */,
E49911F7174E5D4500741B6D /* DVDDemuxSPU.cpp in Sources */,
E49911F8174E5D4500741B6D /* DVDFileInfo.cpp in Sources */,
@@ -11140,6 +11438,7 @@
E49911FC174E5D4500741B6D /* DVDOverlayContainer.cpp in Sources */,
DF4A3BB31B4B0FC100F9CDC0 /* ApplicationMessenger.cpp in Sources */,
DF29BCF21B5D911800904347 /* EventLog.cpp in Sources */,
+ 6890C2281DDBDCF500F8F362 /* AddonCallbacksGame.cpp in Sources */,
E49911FF174E5D4500741B6D /* VideoPlayer.cpp in Sources */,
E4991200174E5D4500741B6D /* VideoPlayerAudio.cpp in Sources */,
E4991201174E5D4500741B6D /* VideoPlayerSubtitle.cpp in Sources */,
@@ -11162,6 +11461,7 @@
E499121E174E5D5A00741B6D /* VideoFilterShader.cpp in Sources */,
E499121F174E5D5A00741B6D /* YUV2RGBShader.cpp in Sources */,
E4991220174E5D5A00741B6D /* BaseRenderer.cpp in Sources */,
+ 6890C1DB1DDBDBE500F8F362 /* BasicMemoryStream.cpp in Sources */,
E4991222174E5D5A00741B6D /* OverlayRenderer.cpp in Sources */,
E4991223174E5D5A00741B6D /* OverlayRendererGL.cpp in Sources */,
E4991224174E5D5A00741B6D /* OverlayRendererUtil.cpp in Sources */,
@@ -11174,6 +11474,7 @@
E499122B174E5D6100741B6D /* qry_dat.cpp in Sources */,
E499122C174E5D6100741B6D /* sqlitedataset.cpp in Sources */,
E499122D174E5D6800741B6D /* Epg.cpp in Sources */,
+ 6890C2101DDBDBFC00F8F362 /* GameClientTiming.cpp in Sources */,
B542632C197D353B00726998 /* PosixInterfaceForCLog.cpp in Sources */,
E499122E174E5D6800741B6D /* EpgContainer.cpp in Sources */,
E499122F174E5D6800741B6D /* EpgDatabase.cpp in Sources */,
@@ -11198,6 +11499,7 @@
E499123F174E5D7E00741B6D /* GUIDialogMediaSource.cpp in Sources */,
76AEFB3C1C8F79D100EF2EC0 /* AddonGUIWindow.cpp in Sources */,
395C29E41A98A15700EBC7AD /* HTTPPythonHandler.cpp in Sources */,
+ 6890C2391DDBDD4400F8F362 /* GUIDialogSelectGameClient.cpp in Sources */,
DFE704181D15803F004EAA9D /* ActiveAEDSP.cpp in Sources */,
E4991241174E5D7E00741B6D /* GUIDialogNumeric.cpp in Sources */,
E4991242174E5D7E00741B6D /* GUIDialogOK.cpp in Sources */,
@@ -11205,6 +11507,7 @@
E4991244174E5D7E00741B6D /* GUIDialogPlayerControls.cpp in Sources */,
E4991245174E5D7E00741B6D /* GUIDialogProgress.cpp in Sources */,
E4991246174E5D7E00741B6D /* GUIDialogSeekBar.cpp in Sources */,
+ 6890C2471DDBDD9300F8F362 /* JoystickEasterEgg.cpp in Sources */,
E4991247174E5D7E00741B6D /* GUIDialogSelect.cpp in Sources */,
E4991248174E5D7E00741B6D /* GUIDialogSlider.cpp in Sources */,
E4991249174E5D7E00741B6D /* GUIDialogSmartPlaylistEditor.cpp in Sources */,
@@ -11386,6 +11689,7 @@
E49912F5174E5DAD00741B6D /* GUIFontTTF.cpp in Sources */,
E49912F6174E5DAD00741B6D /* GUIFontTTFDX.cpp in Sources */,
E49912F7174E5DAD00741B6D /* GUIFontTTFGL.cpp in Sources */,
+ 6890C2581DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp in Sources */,
E49912F8174E5DAD00741B6D /* GUIImage.cpp in Sources */,
E49912F9174E5DAD00741B6D /* GUIIncludes.cpp in Sources */,
E49912FA174E5DAD00741B6D /* GUIInfoTypes.cpp in Sources */,
@@ -11395,6 +11699,7 @@
E49912FE174E5DAD00741B6D /* GUIListContainer.cpp in Sources */,
E49912FF174E5DAD00741B6D /* GUIListGroup.cpp in Sources */,
E4991300174E5DAD00741B6D /* GUIListItem.cpp in Sources */,
+ 6890C2301DDBDD2D00F8F362 /* GameUtils.cpp in Sources */,
E4991301174E5DAD00741B6D /* GUIListItemLayout.cpp in Sources */,
DFD717611C0A031B0025D964 /* XBMCApplication.m in Sources */,
E4991302174E5DAD00741B6D /* GUIListLabel.cpp in Sources */,
@@ -11427,6 +11732,7 @@
395C2A121A9F072400EBC7AD /* ResourceDirectory.cpp in Sources */,
E4991318174E5DAD00741B6D /* GUITextureD3D.cpp in Sources */,
E4991319174E5DAD00741B6D /* GUITextureGL.cpp in Sources */,
+ 6890C1E91DDBDBE500F8F362 /* SavestateUtils.cpp in Sources */,
E499131A174E5DAD00741B6D /* GUITextureGLES.cpp in Sources */,
2AFBB94D1CC608A200BAB340 /* GUIEPGGridContainerModel.cpp in Sources */,
E499131B174E5DAD00741B6D /* GUIToggleButtonControl.cpp in Sources */,
@@ -11448,12 +11754,14 @@
E4991328174E5DAD00741B6D /* Texture.cpp in Sources */,
E4991329174E5DAD00741B6D /* TextureBundle.cpp in Sources */,
397877D61AAAF87700F98A45 /* Speed.cpp in Sources */,
+ 6890C1E71DDBDBE500F8F362 /* SavestateTranslator.cpp in Sources */,
DF6F52AF1AF6D03F001BC57D /* dacp.cpp in Sources */,
E499132A174E5DAD00741B6D /* TextureBundleXBT.cpp in Sources */,
E499132C174E5DAD00741B6D /* TextureDX.cpp in Sources */,
E499132D174E5DAD00741B6D /* TextureGL.cpp in Sources */,
E499132E174E5DAD00741B6D /* TextureManager.cpp in Sources */,
E499132F174E5DAD00741B6D /* VisibleEffect.cpp in Sources */,
+ 6890C1E51DDBDBE500F8F362 /* SavestateReader.cpp in Sources */,
E4991330174E5DAD00741B6D /* XBTF.cpp in Sources */,
DFC6F4B71AFF7CB10039A7FA /* kiss_fft.c in Sources */,
E4991331174E5DAD00741B6D /* XBTFReader.cpp in Sources */,
@@ -11483,6 +11791,7 @@
E499135E174E5EEF00741B6D /* AddonModuleXbmc.cpp in Sources */,
E499135F174E5EEF00741B6D /* AddonModuleXbmcaddon.cpp in Sources */,
2A7B2BDD1BD6F16600044BCD /* PVRSettings.cpp in Sources */,
+ 6890C1EB1DDBDBE500F8F362 /* SavestateWriter.cpp in Sources */,
E4991360174E5EEF00741B6D /* AddonModuleXbmcgui.cpp in Sources */,
E4991361174E5EEF00741B6D /* AddonModuleXbmcplugin.cpp in Sources */,
E4991362174E5EEF00741B6D /* AddonModuleXbmcvfs.cpp in Sources */,
@@ -11540,6 +11849,7 @@
E49913A1174E5F0E00741B6D /* MusicThumbLoader.cpp in Sources */,
E49913A2174E5F0E00741B6D /* Song.cpp in Sources */,
E49913A3174E5F2100741B6D /* HTTPImageHandler.cpp in Sources */,
+ 6890C1F51DDBDBEA00F8F362 /* GameLoop.cpp in Sources */,
E49913A4174E5F2100741B6D /* HTTPJsonRpcHandler.cpp in Sources */,
E49913A5174E5F2100741B6D /* HTTPVfsHandler.cpp in Sources */,
E49913A6174E5F2100741B6D /* HTTPWebinterfaceAddonsHandler.cpp in Sources */,
@@ -11581,6 +11891,7 @@
E49913C2174E5F3C00741B6D /* TCPServer.cpp in Sources */,
E49913C3174E5F3C00741B6D /* UdpClient.cpp in Sources */,
DFEB902919E9337200728978 /* AEResampleFactory.cpp in Sources */,
+ 6890C1DF1DDBDBE500F8F362 /* LinearMemoryStream.cpp in Sources */,
E49913C4174E5F3C00741B6D /* WakeOnAccess.cpp in Sources */,
E49913C5174E5F3C00741B6D /* WebServer.cpp in Sources */,
E49913C6174E5F3C00741B6D /* Zeroconf.cpp in Sources */,
@@ -11625,6 +11936,7 @@
E49913E6174E5F8D00741B6D /* PlayListXML.cpp in Sources */,
DFACDB8E1D6CAD06003BBB92 /* Platform.cpp in Sources */,
E49913E7174E5F8D00741B6D /* SmartPlayList.cpp in Sources */,
+ 6890C2511DDBDDC900F8F362 /* MouseInputHandling.cpp in Sources */,
E49913E8174E5F9900741B6D /* CocoaPowerSyscall.cpp in Sources */,
E49913E9174E5F9900741B6D /* DPMSSupport.cpp in Sources */,
E49913EA174E5F9900741B6D /* PowerManager.cpp in Sources */,
@@ -11663,7 +11975,9 @@
E4991409174E5FB900741B6D /* GUIWindowPVRChannels.cpp in Sources */,
E499140B174E5FB900741B6D /* GUIWindowPVRGuide.cpp in Sources */,
E499140C174E5FB900741B6D /* GUIWindowPVRRecordings.cpp in Sources */,
+ 6890C20A1DDBDBFC00F8F362 /* GameClientKeyboard.cpp in Sources */,
E499140D174E5FB900741B6D /* GUIWindowPVRSearch.cpp in Sources */,
+ 6890C1F31DDBDBEA00F8F362 /* GameClientReversiblePlayback.cpp in Sources */,
E499140E174E5FB900741B6D /* GUIWindowPVRTimers.cpp in Sources */,
E499140F174E5FB900741B6D /* PVRDatabase.cpp in Sources */,
E4991410174E5FB900741B6D /* PVRGUIInfo.cpp in Sources */,
@@ -11683,6 +11997,7 @@
E499141E174E603C00741B6D /* AdvancedSettings.cpp in Sources */,
E499141F174E603C00741B6D /* DisplaySettings.cpp in Sources */,
E4991421174E603C00741B6D /* MediaSettings.cpp in Sources */,
+ 6890C20C1DDBDBFC00F8F362 /* GameClientMouse.cpp in Sources */,
E4991422174E603C00741B6D /* MediaSourceSettings.cpp in Sources */,
E4991424174E603C00741B6D /* SettingAddon.cpp in Sources */,
39C38CE21BCD600E000F59F5 /* FFmpegImage.cpp in Sources */,
@@ -11829,6 +12144,7 @@
DF91E93B1C0A21D60011084D /* xbmc.cpp in Sources */,
E499152C174E640800741B6D /* Application.cpp in Sources */,
DFD7172D1C09F5CF0025D964 /* XbmcContext.cpp in Sources */,
+ 6890C1DD1DDBDBE500F8F362 /* DeltaPairMemoryStream.cpp in Sources */,
E499152E174E642900741B6D /* AppParamParser.cpp in Sources */,
E499152F174E642900741B6D /* Autorun.cpp in Sources */,
E4991530174E642900741B6D /* AutoSwitch.cpp in Sources */,
@@ -11882,6 +12198,7 @@
E499155C174E656E00741B6D /* LanguageHook.cpp in Sources */,
E499155D174E656E00741B6D /* ListItem.cpp in Sources */,
E499155E174E656E00741B6D /* ModuleXbmc.cpp in Sources */,
+ 6890C2421DDBDD6A00F8F362 /* PortMapper.cpp in Sources */,
E499155F174E656E00741B6D /* ModuleXbmcgui.cpp in Sources */,
E4991560174E656E00741B6D /* ModuleXbmcplugin.cpp in Sources */,
E4991561174E656E00741B6D /* ModuleXbmcvfs.cpp in Sources */,
@@ -12003,6 +12320,7 @@
7CCDA199192753E30074CF51 /* PltUPnP.cpp in Sources */,
DF54F7FF1B6580AD000FCBA4 /* ContextMenuItem.cpp in Sources */,
7CCDA1A2192753E30074CF51 /* PltMediaConnect.cpp in Sources */,
+ 6890C20E1DDBDBFC00F8F362 /* GameClientProperties.cpp in Sources */,
7CCDA1AB192753E30074CF51 /* PltXbox360.cpp in Sources */,
7CCDA1B0192753E30074CF51 /* X_MS_MediaReceiverRegistrarSCPD.cpp in Sources */,
7CCDA1BB192753E30074CF51 /* AVTransportSCPD.cpp in Sources */,
@@ -12034,6 +12352,7 @@
7CCDA7A4192756250074CF51 /* NptCrypto.cpp in Sources */,
7CCDA7A7192756250074CF51 /* NptDataBuffer.cpp in Sources */,
68AE5BA61C92412900C4D527 /* AddonCallbacksPeripheral.cpp in Sources */,
+ 6890C2081DDBDBFC00F8F362 /* GameClientInput.cpp in Sources */,
7CCDA7B0192756250074CF51 /* NptDebug.cpp in Sources */,
7CCDA7B9192756250074CF51 /* NptDigest.cpp in Sources */,
7CCDA7BC192756250074CF51 /* NptDynamicLibraries.cpp in Sources */,
@@ -12048,6 +12367,7 @@
7CCDA7E6192756250074CF51 /* NptLogging.cpp in Sources */,
7CCDA7E9192756250074CF51 /* NptMessaging.cpp in Sources */,
7CCDA7F2192756250074CF51 /* NptNetwork.cpp in Sources */,
+ 6890C1E31DDBDBE500F8F362 /* SavestateDatabase.cpp in Sources */,
7CCDA7FB192756250074CF51 /* NptQueue.cpp in Sources */,
3961C43A1ABC0A46002DBBFB /* UISoundsResource.cpp in Sources */,
7CCDA804192756250074CF51 /* NptResults.cpp in Sources */,
@@ -12084,6 +12404,7 @@
7CCDACA819275D1F0074CF51 /* NptStdcDebug.cpp in Sources */,
DF29BCFB1B5D911800904347 /* GUIViewStateEventLog.cpp in Sources */,
7CCDACB119275D1F0074CF51 /* NptStdcEnvironment.cpp in Sources */,
+ 6890C2401DDBDD6A00F8F362 /* PortManager.cpp in Sources */,
7CCDACC219275D790074CF51 /* NptAppleAutoreleasePool.mm in Sources */,
7CCDACCB19275D790074CF51 /* NptAppleLogConfig.mm in Sources */,
7CAA469119427AED00008885 /* PosixDirectory.cpp in Sources */,
diff --git a/Makefile.in b/Makefile.in
index 3038a9b85d..f28925e9f2 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -30,6 +30,7 @@ DIRECTORY_ARCHIVES=$(VideoPlayer_ARCHIVES) \
xbmc/addons/binary/interfaces/api1/AudioDSP/addon-callbacks-audiodsp.a \
xbmc/addons/binary/interfaces/api1/AudioEngine/addon-callbacks-audioengine.a \
xbmc/addons/binary/interfaces/api1/Codec/addon-callbacks-codec.a \
+ xbmc/addons/binary/interfaces/api1/Game/addon-callbacks-game.a \
xbmc/addons/binary/interfaces/api1/GUI/addon-callbacks-gui.a \
xbmc/addons/binary/interfaces/api1/InputStream/addon-callbacks-inputstream.a \
xbmc/addons/binary/interfaces/api1/Peripheral/addon-callbacks-peripheral.a \
@@ -53,11 +54,16 @@ DIRECTORY_ARCHIVES=$(VideoPlayer_ARCHIVES) \
xbmc/filesystem/MusicDatabaseDirectory/musicdatabasedirectory.a \
xbmc/filesystem/VideoDatabaseDirectory/videodatabasedirectory.a \
xbmc/filesystem/filesystem.a \
+ xbmc/games/addons/gameaddons.a \
+ xbmc/games/addons/playback/gameaddonplayback.a \
+ xbmc/games/addons/savestates/gamesavestates.a \
xbmc/games/controllers/controllers.a \
xbmc/games/controllers/dialogs/controllerdialogs.a \
xbmc/games/controllers/guicontrols/controllerguicontrols.a \
xbmc/games/controllers/windows/controllerwindows.a \
+ xbmc/games/dialogs/gamedialogs.a \
xbmc/games/games.a \
+ xbmc/games/ports/gameports.a \
xbmc/games/tags/gameinfotags.a \
xbmc/guilib/guilib.a \
xbmc/input/input.a \
@@ -153,7 +159,10 @@ ifeq ($(findstring osx,@ARCH@),osx)
DIRECTORY_ARCHIVES += xbmc/input/joysticks/input_joysticks.a
DIRECTORY_ARCHIVES += xbmc/input/joysticks/dialogs/input_joystick_dialogs.a
DIRECTORY_ARCHIVES += xbmc/input/joysticks/generic/input_joysticks_generic.a
+DIRECTORY_ARCHIVES += xbmc/input/keyboard/input_keyboard.a
DIRECTORY_ARCHIVES += xbmc/input/keyboard/generic/input_keyboard_generic.a
+DIRECTORY_ARCHIVES += xbmc/input/mouse/input_mouse.a
+DIRECTORY_ARCHIVES += xbmc/input/mouse/generic/input_mouse_generic.a
DIRECTORY_ARCHIVES += xbmc/network/osx/network.a
DIRECTORY_ARCHIVES += xbmc/network/linux/network_linux.a
DIRECTORY_ARCHIVES += xbmc/powermanagement/osx/powermanagement.a
@@ -170,8 +179,11 @@ ifeq (@USE_ANDROID@,1)
DIRECTORY_ARCHIVES += xbmc/input/joysticks/input_joysticks.a
DIRECTORY_ARCHIVES += xbmc/input/joysticks/dialogs/input_joystick_dialogs.a
DIRECTORY_ARCHIVES += xbmc/input/joysticks/generic/input_joysticks_generic.a
+DIRECTORY_ARCHIVES += xbmc/input/keyboard/input_keyboard.a
DIRECTORY_ARCHIVES += xbmc/input/keyboard/generic/input_keyboard_generic.a
DIRECTORY_ARCHIVES += xbmc/input/linux/input_linux.a
+DIRECTORY_ARCHIVES += xbmc/input/mouse/input_mouse.a
+DIRECTORY_ARCHIVES += xbmc/input/mouse/generic/input_mouse_generic.a
DIRECTORY_ARCHIVES += xbmc/input/touch/input_touch.a
DIRECTORY_ARCHIVES += xbmc/input/touch/generic/input_touch_generic.a
DIRECTORY_ARCHIVES += xbmc/network/linux/network_linux.a
@@ -182,8 +194,11 @@ else
DIRECTORY_ARCHIVES += xbmc/input/joysticks/input_joysticks.a
DIRECTORY_ARCHIVES += xbmc/input/joysticks/dialogs/input_joystick_dialogs.a
DIRECTORY_ARCHIVES += xbmc/input/joysticks/generic/input_joysticks_generic.a
+DIRECTORY_ARCHIVES += xbmc/input/keyboard/input_keyboard.a
DIRECTORY_ARCHIVES += xbmc/input/keyboard/generic/input_keyboard_generic.a
DIRECTORY_ARCHIVES += xbmc/input/linux/input_linux.a
+DIRECTORY_ARCHIVES += xbmc/input/mouse/input_mouse.a
+DIRECTORY_ARCHIVES += xbmc/input/mouse/generic/input_mouse_generic.a
DIRECTORY_ARCHIVES += xbmc/input/touch/input_touch.a
DIRECTORY_ARCHIVES += xbmc/input/touch/generic/input_touch_generic.a
DIRECTORY_ARCHIVES += xbmc/network/linux/network_linux.a
@@ -227,6 +242,7 @@ LIBADDON_DIRS=\
lib/addons/library.kodi.guilib \
lib/addons/library.kodi.inputstream \
lib/addons/library.kodi.peripheral \
+ lib/addons/library.kodi.game \
ESTUARY_MEDIA=addons/skin.estuary/media
SKIN_DIRS=$(ESTUARY_MEDIA)
@@ -347,6 +363,7 @@ libaddon: exports
$(MAKE) -C lib/addons/library.kodi.adsp
$(MAKE) -C lib/addons/library.kodi.audioengine
$(MAKE) -C lib/addons/library.xbmc.codec
+ $(MAKE) -C lib/addons/library.kodi.game
$(MAKE) -C lib/addons/library.kodi.guilib
$(MAKE) -C lib/addons/library.kodi.peripheral
$(MAKE) -C lib/addons/library.xbmc.pvr
diff --git a/addons/kodi.game/addon.xml b/addons/kodi.game/addon.xml
index 44f2ca5bb5..e50d0395dc 100644
--- a/addons/kodi.game/addon.xml
+++ b/addons/kodi.game/addon.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<addon id="kodi.game" version="1.0.14" provider-name="Team-Kodi">
- <backwards-compatibility abi="1.0.14"/>
+<addon id="kodi.game" version="1.0.28" provider-name="Team-Kodi">
+ <backwards-compatibility abi="1.0.28"/>
<requires>
<import addon="xbmc.core" version="0.1.0"/>
</requires>
diff --git a/addons/kodi.resource/games.xsd b/addons/kodi.resource/games.xsd
new file mode 100644
index 0000000000..eb9809b51c
--- /dev/null
+++ b/addons/kodi.resource/games.xsd
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "http://www.w3.org/2001/XMLSchema.dtd">
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:element name="extension">
+ <xs:complexType>
+ <xs:attribute name="point" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
diff --git a/addons/library.kodi.game/.gitignore b/addons/library.kodi.game/.gitignore
new file mode 100644
index 0000000000..76bedaeabb
--- /dev/null
+++ b/addons/library.kodi.game/.gitignore
@@ -0,0 +1,5 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
+
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po
index 1018dd5a5d..427f88e605 100644
--- a/addons/resource.language.en_gb/resources/strings.po
+++ b/addons/resource.language.en_gb/resources/strings.po
@@ -16070,7 +16070,7 @@ msgstr ""
#empty strings from id 35019 to 35048
#. Name of game add-ons category
-#: xbmc/filesystem/AddonsDirectory.cpp
+#: xbmc/addons/Addon.cpp
msgctxt "#35049"
msgid "Game add-ons"
msgstr ""
@@ -16267,6 +16267,16 @@ msgctxt "#35102"
msgid "Disable joystick when this device is present"
msgstr ""
+#. Label for mouse buttons. Used in the controller mapping dialog.
+msgctxt "#35103"
+msgid "Buttons"
+msgstr ""
+
+#. Label for relative mounters like the mouse pointer. Used in the controller mapping dialog.
+msgctxt "#35104"
+msgid "Pointers"
+msgstr ""
+
#empty strings from id 35103 to 35149
#. Name of keyboard category in the settings category window
@@ -16275,7 +16285,7 @@ msgctxt "#35150"
msgid "Keyboard"
msgstr ""
-#. Name of group for configuring keyboard players
+#. Name of group in "Games -> Keyboard" for configuring keyboard players
#: system/settings/settings.xml
msgctxt "#35151"
msgid "Player configuration"
@@ -16372,7 +16382,135 @@ msgctxt "#35200"
msgid "ALL YOUR BASE ARE BELONG[CR]TO US"
msgstr ""
-#empty strings from id 35201 to 35504
+#. Name of group in "Games -> General" for configuring gameplay
+#: system/settings/settings.xml
+msgctxt "#35201"
+msgid "Gameplay"
+msgstr ""
+
+#empty string with id 35202
+
+#: system/settings/settings.xml
+msgctxt "#35203"
+msgid "Enable rewind if supported"
+msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#35204"
+msgid "Enable real-time rewinding during game play, if supported. Press rewind or manually seek backwards using the seek bar."
+msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#35205"
+msgid "Maximum rewind time"
+msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#35206"
+msgid "Maximum time possible to rewind, if supported. Large rewind histories can use a lot of RAM."
+msgstr ""
+
+#. Description of add-on category for emulators
+#: xbmc/filesystem/AddonsDirectory.cpp
+msgctxt "#35207"
+msgid "Emulators"
+msgstr ""
+
+#. Description of add-on category for standalone games
+#: xbmc/filesystem/AddonsDirectory.cpp
+msgctxt "#35208"
+msgid "Standalone games"
+msgstr ""
+
+#: xbmc/addons/Addon.cpp
+msgctxt "#35209"
+msgid "Game resources"
+msgstr ""
+
+#. Dialog title when gameplay fails
+#: xbmc/games/addons/GameClient.cpp
+#: xbmc/games/GameManager.cpp
+msgctxt "#35210"
+msgid "Failed to play game"
+msgstr ""
+
+#. Error dialog text when the game requires restricted files to play. %s - emulator ID
+#: xbmc/games/addons/GameClient.cpp
+msgctxt "#35211"
+msgid "This game requires the following add-on: %s"
+msgstr ""
+
+#. Error dialog text when launching a game and no emulators are compatible
+#: xbmc/games/GameUtils.cpp
+msgctxt "#35212"
+msgid "This game isn't compatible with any available emulators."
+msgstr ""
+
+#. Error dialog text when launching a game and the emulator has an internal error. %s - emulator name
+#: xbmc/games/GameClient.cpp
+msgctxt "#35213"
+msgid "The emulator \"%s\" had an internal error."
+msgstr ""
+
+#. Error dialog text when the game can't be played because it's not on a hard drive or partition
+#: xbmc/games/GameUtils.cpp
+msgctxt "#35214"
+msgid "This game can only be played directly from a hard drive or partition. Compressed files must be extracted."
+msgstr ""
+
+#. Error dialog text when libretro support is disabled
+#: xbmc/games/addons/GameClientProperties.cpp
+msgctxt "#35215"
+msgid "This game depends on a disabled add-on. Would you like to enable it?"
+msgstr ""
+
+#. Description of add-on category for game support add-ons like game.libretro (the libretro wrapper)
+#: xbmc/filesystem/AddonsDirectory.cpp
+msgctxt "#35216"
+msgid "Support add-ons"
+msgstr ""
+
+#empty strings from id 35217 to 35219
+
+#. Description of add-on category for game providers
+#: xbmc/filesystem/AddonsDirectory.cpp
+msgctxt "#35220"
+msgid "Game providers"
+msgstr ""
+
+#empty strings from id 35221 to 35249
+
+#: xbmc/windows/GUIMediaWindow.cpp
+msgctxt "#35250"
+msgid "Add games..."
+msgstr ""
+
+#: xbmc/dialogs/GUIDialogMediaSource.cpp
+msgctxt "#35251"
+msgid "Add game source"
+msgstr ""
+
+#: xbmc/dialogs/GUIDialogMediaSource.cpp
+msgctxt "#35252"
+msgid "Edit game source"
+msgstr ""
+
+#: xbmc/games/windows/GUIWindowGames.cpp
+msgctxt "#35253"
+msgid "Install emulator"
+msgstr ""
+
+#: xbmc/games/windows/GUIWindowGames.cpp
+msgctxt "#35254"
+msgid "Manage emulators"
+msgstr ""
+
+#: xbmc/games/dialogs/GUIDialogSelectGameClient.cpp
+msgctxt "#35255"
+msgid "Browse all emulators"
+msgstr ""
+
+#empty strings from id 35256 to 35504
#. connection state "host unreachable"
#: xbmc/addons/AddonCallbacksPVR.cpp
diff --git a/configure.ac b/configure.ac
index 87bfbbfe8b..1da41576dd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2194,6 +2194,7 @@ OUTPUT_FILES="Makefile \
lib/addons/library.kodi.adsp/Makefile \
lib/addons/library.kodi.audioengine/Makefile \
lib/addons/library.xbmc.codec/Makefile \
+ lib/addons/library.kodi.game/Makefile \
lib/addons/library.kodi.guilib/Makefile \
lib/addons/library.kodi.peripheral/Makefile \
lib/addons/library.xbmc.pvr/Makefile \
diff --git a/doxygen_resources/pages/mainpage.dox b/doxygen_resources/pages/mainpage.dox
index 1c42e87cf2..5017adfe18 100644
--- a/doxygen_resources/pages/mainpage.dox
+++ b/doxygen_resources/pages/mainpage.dox
@@ -148,6 +148,18 @@
\defgroup mouse Mouse
\ingroup input
Everything around mouse
+
+ Mouse input is processed by \ref CInputManager and forwarded to
+ registered mouse handlers (e.g. game clients) or as actions to the UI:
+
+ - If no mouse handlers are registered or if they don't consume events,
+ the mouse input events are forwarded to the UI via \ref CInputManager::ProcessMouse.
+ - Clients (e.g. game clients implementing \ref MOUSE::IMouseInputHandler) call
+ \ref CInputManager::RegisterMouseHandler to register themselves as eligible
+ for mouse input events.
+ - Mouse events (from \ref CInputManager::OnEvent) are collected via implementations of
+ \ref MOUSE::IMouseDriverHandler and transformed into higher level features by
+ \ref MOUSE::IMouseButtonMap instances before they are sent to the handlers.
*/
/*!
@@ -169,6 +181,11 @@
*/
/*!
+ \defgroup games
+ Everything about RetroPlayer.
+*/
+
+/*!
\defgroup interface Interfaces
Everything around interfaces
*/
diff --git a/lib/addons/library.kodi.game/CMakeLists.txt b/lib/addons/library.kodi.game/CMakeLists.txt
new file mode 100644
index 0000000000..05fd1d0201
--- /dev/null
+++ b/lib/addons/library.kodi.game/CMakeLists.txt
@@ -0,0 +1,2 @@
+project(KODI_game)
+core_add_addon_library(${PROJECT_NAME})
diff --git a/lib/addons/library.kodi.game/Makefile.in b/lib/addons/library.kodi.game/Makefile.in
new file mode 100644
index 0000000000..3644126f19
--- /dev/null
+++ b/lib/addons/library.kodi.game/Makefile.in
@@ -0,0 +1,29 @@
+ARCH=@ARCH@
+INCLUDES=-I. -I../../../xbmc/addons/include
+DEFINES+=
+CXXFLAGS=-fPIC
+LIBNAME=libKODI_game
+OBJS=$(LIBNAME).o
+
+ifeq ($(findstring osx,$(ARCH)), osx)
+LIB_SHARED=../../../addons/library.kodi.game/$(LIBNAME)-$(ARCH).dylib
+else
+LIB_SHARED=../../../addons/library.kodi.game/$(LIBNAME)-$(ARCH).so
+endif
+
+all: $(LIB_SHARED)
+
+$(LIB_SHARED): $(OBJS)
+ifeq ($(findstring osx,$(ARCH)), osx)
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -dynamiclib -o $@ $(OBJS)
+else
+ $(CXX) $(CFLAGS) $(LDFLAGS) -shared -g -o $(LIB_SHARED) $(OBJS)
+endif
+
+CLEAN_FILES = \
+ $(LIB_SHARED) \
+
+DISTCLEAN_FILES= \
+ Makefile \
+
+include ../../../Makefile.include
diff --git a/lib/addons/library.kodi.game/libKODI_game.cpp b/lib/addons/library.kodi.game/libKODI_game.cpp
new file mode 100644
index 0000000000..cbcedc4c89
--- /dev/null
+++ b/lib/addons/library.kodi.game/libKODI_game.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2014-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "addons/binary/interfaces/AddonInterfaces.h"
+#include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h"
+
+#include <stdio.h>
+
+#ifdef _WIN32
+ #include <windows.h>
+ #define DLLEXPORT __declspec(dllexport)
+#else
+ #define DLLEXPORT
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+DLLEXPORT CB_GameLib* GAME_register_me(AddonCB* frontend)
+{
+ CB_GameLib* cb = NULL;
+ if (!frontend)
+ fprintf(stderr, "ERROR: GAME_register_frontend is called with NULL handle!!!\n");
+ else
+ {
+ cb = frontend->GameLib_RegisterMe(frontend->addonData);
+ if (!cb)
+ fprintf(stderr, "ERROR: GAME_register_frontend can't get callback table from frontend!!!\n");
+ }
+ return cb;
+}
+
+DLLEXPORT void GAME_unregister_me(AddonCB* frontend, CB_GameLib* cb)
+{
+ if (frontend == NULL || cb == NULL)
+ return;
+ return frontend->GameLib_UnRegisterMe(frontend->addonData, cb);
+}
+
+DLLEXPORT void GAME_close_game(AddonCB* frontend, CB_GameLib* cb)
+{
+ if (frontend == NULL || cb == NULL)
+ return;
+ return cb->CloseGame(frontend->addonData);
+}
+
+DLLEXPORT int GAME_open_pixel_stream(AddonCB* frontend, CB_GameLib* cb, GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation)
+{
+ if (frontend == NULL || cb == NULL)
+ return -1;
+
+ return cb->OpenPixelStream(frontend->addonData, format, width, height, rotation);
+}
+
+DLLEXPORT int GAME_open_video_stream(AddonCB* frontend, CB_GameLib* cb, GAME_VIDEO_CODEC codec)
+{
+ if (frontend == NULL || cb == NULL)
+ return -1;
+
+ return cb->OpenVideoStream(frontend->addonData, codec);
+}
+
+DLLEXPORT int GAME_open_pcm_stream(AddonCB* frontend, CB_GameLib* cb, GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channel_map)
+{
+ if (frontend == NULL || cb == NULL)
+ return -1;
+
+ return cb->OpenPCMStream(frontend->addonData, format, channel_map);
+}
+
+DLLEXPORT int GAME_open_audio_stream(AddonCB* frontend, CB_GameLib* cb, GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channel_map)
+{
+ if (frontend == NULL || cb == NULL)
+ return -1;
+
+ return cb->OpenAudioStream(frontend->addonData, codec, channel_map);
+}
+
+DLLEXPORT void GAME_add_stream_data(AddonCB* frontend, CB_GameLib* cb, GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size)
+{
+ if (frontend == NULL || cb == NULL)
+ return;
+
+ return cb->AddStreamData(frontend->addonData, stream, data, size);
+}
+
+DLLEXPORT void GAME_close_stream(AddonCB* frontend, CB_GameLib* cb, GAME_STREAM_TYPE stream)
+{
+ if (frontend == NULL || cb == NULL)
+ return;
+
+ return cb->CloseStream(frontend->addonData, stream);
+}
+
+DLLEXPORT void GAME_enable_hardware_rendering(AddonCB* frontend, CB_GameLib* cb, game_hw_info* hw_info)
+{
+ if (frontend == NULL || cb == NULL)
+ return;
+ return cb->EnableHardwareRendering(frontend->addonData, hw_info);
+}
+
+DLLEXPORT uintptr_t GAME_hw_get_current_framebuffer(AddonCB* frontend, CB_GameLib* cb)
+{
+ if (frontend == NULL || cb == NULL)
+ return 0;
+ return cb->HwGetCurrentFramebuffer(frontend->addonData);
+}
+
+DLLEXPORT game_proc_address_t GAME_hw_get_proc_address(AddonCB* frontend, CB_GameLib* cb, const char* sym)
+{
+ if (frontend == NULL || cb == NULL)
+ return NULL;
+ return cb->HwGetProcAddress(frontend->addonData, sym);
+}
+
+DLLEXPORT void GAME_render_frame(AddonCB* frontend, CB_GameLib* cb)
+{
+ if (frontend == NULL || cb == NULL)
+ return;
+ cb->RenderFrame(frontend->addonData);
+}
+
+DLLEXPORT bool GAME_open_port(AddonCB* frontend, CB_GameLib* cb, unsigned int port)
+{
+ if (frontend == NULL || cb == NULL)
+ return false;
+ return cb->OpenPort(frontend->addonData, port);
+}
+
+DLLEXPORT void GAME_close_port(AddonCB* frontend, CB_GameLib* cb, unsigned int port)
+{
+ if (frontend == NULL || cb == NULL)
+ return;
+ return cb->ClosePort(frontend->addonData, port);
+}
+
+DLLEXPORT bool GAME_input_event(AddonCB* frontend, CB_GameLib* cb, const game_input_event* event)
+{
+ if (frontend == NULL || cb == NULL)
+ return false;
+ return cb->InputEvent(frontend->addonData, event);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/lib/addons/library.kodi.game/project/VS2010Express/libKODI_game.vcxproj b/lib/addons/library.kodi.game/project/VS2010Express/libKODI_game.vcxproj
new file mode 100644
index 0000000000..72cf3e72c2
--- /dev/null
+++ b/lib/addons/library.kodi.game/project/VS2010Express/libKODI_game.vcxproj
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{1400C916-B4AD-41D7-ACEE-A853F3B89B38}</ProjectGuid>
+ <RootNamespace>XBMC_VDR</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ <ProjectName>libKODI_game</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(SolutionDir)\XBMC.core-defaults.props" />
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ <PlatformToolset>v140</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(SolutionDir)\XBMC.defaults.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\library.kodi.game\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\library.kodi.game\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir>
+ <IncludePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\library.xbmc.addon\;$(IncludePath)</IncludePath>
+ <IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\library.xbmc.addon\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_USRDLL;_WIN32PC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ </ClCompile>
+ <Link>
+ <OutputFile>..\..\..\..\..\addons\library.kodi.game\$(ProjectName).dll</OutputFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_USRDLL;XBMC__WIN32PC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ </ClCompile>
+ <Link>
+ <OutputFile>..\..\..\..\..\addons\library.kodi.game\$(ProjectName).dll</OutputFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\libKODI_game.cpp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/lib/addons/library.kodi.game/project/VS2010Express/libKODI_game.vcxproj.filters b/lib/addons/library.kodi.game/project/VS2010Express/libKODI_game.vcxproj.filters
new file mode 100644
index 0000000000..3609c12382
--- /dev/null
+++ b/lib/addons/library.kodi.game/project/VS2010Express/libKODI_game.vcxproj.filters
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\libKODI_game.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/project/Win32BuildSetup/genNsisIncludes.bat b/project/Win32BuildSetup/genNsisIncludes.bat
index 2e481e61a2..e05b04ea98 100644
--- a/project/Win32BuildSetup/genNsisIncludes.bat
+++ b/project/Win32BuildSetup/genNsisIncludes.bat
@@ -28,6 +28,22 @@ IF EXIST BUILD_WIN32\addons\pvr.* (
)
SET Counter=1
+IF EXIST BUILD_WIN32\addons\game.libretro.* (
+ ECHO SectionGroup "Game Add-ons" SecGameAddons >> game-addons.nsi
+ FOR /F "tokens=*" %%P IN ('dir /B /AD BUILD_WIN32\addons\game.libretro.*') DO (
+ FOR /f "delims=<" %%N in ('powershell.exe -ExecutionPolicy Unrestricted -command "& {[xml]$a = get-content BUILD_WIN32\addons\%%P\addon.xml;$a.addon.name}"') do (
+ ECHO Section "%%N" SecGameAddons!Counter! >> game-addons.nsi
+ ECHO SectionIn 1 2 >> game-addons.nsi
+ ECHO SetOutPath "$INSTDIR\addons\%%P" >> game-addons.nsi
+ ECHO File /r "${app_root}\addons\%%P\*.*" >> game-addons.nsi
+ ECHO SectionEnd >> game-addons.nsi
+ SET /A Counter = !Counter! + 1
+ )
+ )
+ ECHO SectionGroupEnd >> game-addons.nsi
+)
+
+SET Counter=1
IF EXIST BUILD_WIN32\addons\audiodecoder.* (
ECHO SectionGroup "Audio Decoder Add-ons" SecAudioDecoderAddons >> audiodecoder-addons.nsi
FOR /F "tokens=*" %%P IN ('dir /B /AD BUILD_WIN32\addons\audiodecoder.*') DO (
diff --git a/project/Win32BuildSetup/genNsisInstaller.nsi b/project/Win32BuildSetup/genNsisInstaller.nsi
index c6dd8a1f55..a5741774ea 100644
--- a/project/Win32BuildSetup/genNsisInstaller.nsi
+++ b/project/Win32BuildSetup/genNsisInstaller.nsi
@@ -230,6 +230,7 @@ SectionEnd
!include /nonfatal "audiodecoder-addons.nsi"
!include /nonfatal "audioencoder-addons.nsi"
!include /nonfatal "audiodsp-addons.nsi"
+!include /nonfatal "game-addons.nsi"
!include /nonfatal "inputstream-addons.nsi"
!include /nonfatal "pvr-addons.nsi"
!include /nonfatal "screensaver-addons.nsi"
diff --git a/project/cmake/installdata/common/addons.txt b/project/cmake/installdata/common/addons.txt
index 1cbe4d690a..72ec5f1fbe 100644
--- a/project/cmake/installdata/common/addons.txt
+++ b/project/cmake/installdata/common/addons.txt
@@ -20,6 +20,7 @@ addons/xbmc.python/*
addons/xbmc.webinterface/*
addons/library.kodi.adsp/*
addons/library.kodi.audioengine/*
+addons/library.kodi.game/*
addons/library.kodi.guilib/*
addons/library.kodi.inputstream/*
addons/library.kodi.peripheral/*
diff --git a/project/cmake/scripts/linux/Install.cmake b/project/cmake/scripts/linux/Install.cmake
index d8df3598d9..e685854198 100644
--- a/project/cmake/scripts/linux/Install.cmake
+++ b/project/cmake/scripts/linux/Install.cmake
@@ -160,6 +160,7 @@ install(FILES ${CORE_SOURCE_DIR}/xbmc/addons/kodi-addon-dev-kit/include/kodi/kod
${CORE_SOURCE_DIR}/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_codec_types.h
${CORE_SOURCE_DIR}/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxPacket.h
${CORE_SOURCE_DIR}/xbmc/filesystem/IFileTypes.h
+ ${CORE_SOURCE_DIR}/xbmc/input/XBMC_vkeys.h
DESTINATION ${includedir}/${APP_NAME_LC}
COMPONENT kodi-addon-dev)
diff --git a/project/cmake/treedata/common/addons.txt b/project/cmake/treedata/common/addons.txt
index 76410b7ed6..9d76cf375b 100644
--- a/project/cmake/treedata/common/addons.txt
+++ b/project/cmake/treedata/common/addons.txt
@@ -1,5 +1,6 @@
lib/addons/library.kodi.adsp KODI_adsp
lib/addons/library.kodi.audioengine KODI_audioengine
+lib/addons/library.kodi.game KODI_game
lib/addons/library.kodi.guilib KODI_guilib
lib/addons/library.kodi.inputstream KODI_inputstream
lib/addons/library.kodi.peripheral KODI_peripheral
diff --git a/project/cmake/treedata/common/games.txt b/project/cmake/treedata/common/games.txt
index 2217c6debc..4a0e37bdb8 100644
--- a/project/cmake/treedata/common/games.txt
+++ b/project/cmake/treedata/common/games.txt
@@ -1,6 +1,11 @@
xbmc/games games
+xbmc/games/addons games/addons
+xbmc/games/addons/playback games/addons/playback
+xbmc/games/addons/savestates games/addons/savestates
xbmc/games/controllers games/controllers
xbmc/games/controllers/dialogs games/controllers/dialogs
xbmc/games/controllers/guicontrols games/controllers/guicontrols
xbmc/games/controllers/windows games/controllers/windows
+xbmc/games/dialogs games/dialogs
+xbmc/games/ports games/ports
xbmc/games/tags games/tags
diff --git a/project/cmake/treedata/common/subdirs.txt b/project/cmake/treedata/common/subdirs.txt
index 89c8c0d46f..af916110a4 100644
--- a/project/cmake/treedata/common/subdirs.txt
+++ b/project/cmake/treedata/common/subdirs.txt
@@ -5,6 +5,7 @@ xbmc/addons/binary/interfaces/api1/Addon api1AddonCallbacks_Addon
xbmc/addons/binary/interfaces/api1/AudioDSP api1AddonCallbacks_AudioDSP
xbmc/addons/binary/interfaces/api1/AudioEngine api1AddonCallbacks_AudioEngine
xbmc/addons/binary/interfaces/api1/Codec api1AddonCallbacks_Codec
+xbmc/addons/binary/interfaces/api1/Game api1AddonCallbacks_Game
xbmc/addons/binary/interfaces/api1/GUI api1AddonCallbacks_GUI
xbmc/addons/binary/interfaces/api1/InputStream api1AddonCallbacks_InputStream
xbmc/addons/binary/interfaces/api1/Peripheral api1AddonCallbacks_Peripheral
@@ -20,6 +21,8 @@ xbmc/input/joysticks/dialogs input/joysticks/dialogs
xbmc/input/joysticks/generic input/joysticks/generic
xbmc/input/keyboard input/keyboard
xbmc/input/keyboard/generic input/keyboard/generic
+xbmc/input/mouse input/mouse
+xbmc/input/mouse/generic input/mouse/generic
xbmc/listproviders listproviders
xbmc/media media
xbmc/messaging messaging
diff --git a/system/settings/settings.xml b/system/settings/settings.xml
index e876fc0420..a9c53839ee 100644
--- a/system/settings/settings.xml
+++ b/system/settings/settings.xml
@@ -1980,6 +1980,37 @@
</category>
</section>
<section id="games" label="15016" help="35200">
+ <category id="gamesgeneral" label="16000">
+ <group id="1" label="35201">
+ <setting id="gamesgeneral.enable" type="boolean">
+ <visible>false</visible>
+ <level>0</level>
+ <default>false</default>
+ <control type="toggle" />
+ </setting>
+ <setting id="gamesgeneral.enablerewind" type="boolean" label="35203" help="35204">
+ <level>0</level>
+ <default>true</default>
+ <control type="toggle" />
+ </setting>
+ <setting id="gamesgeneral.rewindtime" type="integer" label="35205" help="35206">
+ <level>2</level>
+ <default>60</default>
+ <constraints>
+ <minimum>10</minimum>
+ <step>10</step>
+ <maximum>600</maximum>
+ </constraints>
+ <dependencies>
+ <dependency type="enable" setting="gamesgeneral.enablerewind">true</dependency>
+ </dependencies>
+ <control type="slider" format="integer">
+ <popup>true</popup>
+ <formatlabel>14045</formatlabel>
+ </control>
+ </setting>
+ </group>
+ </category>
<category id="gameskeyboard" label="35150">
<group id="1" label="128">
<setting id="gameskeyboard.enablekeyboard" type="boolean" label="35152" help="35153">
diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index f3887822cb..23f03ddcbb 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -3065,6 +3065,17 @@ bool CApplication::PlayMedia(const CFileItem& item, const std::string &player, i
return g_PVRManager.PlayMedia(item);
}
+ CURL path(item.GetPath());
+ if (path.GetProtocol() == "game")
+ {
+ AddonPtr addon;
+ if (CAddonMgr::GetInstance().GetAddon(path.GetHostName(), addon, ADDON_GAMEDLL))
+ {
+ CFileItem addonItem(addon);
+ return PlayFile(addonItem, player, false) == PLAYBACK_OK;
+ }
+ }
+
//nothing special just play
return PlayFile(item, player, false) == PLAYBACK_OK;
}
diff --git a/xbmc/DatabaseManager.cpp b/xbmc/DatabaseManager.cpp
index cfd839f4cf..dd330fb9ff 100644
--- a/xbmc/DatabaseManager.cpp
+++ b/xbmc/DatabaseManager.cpp
@@ -27,6 +27,7 @@
#include "video/VideoDatabase.h"
#include "pvr/PVRDatabase.h"
#include "epg/EpgDatabase.h"
+#include "games/addons/savestates/SavestateDatabase.h"
#include "settings/AdvancedSettings.h"
#include "cores/AudioEngine/Engines/ActiveAE/AudioDSPAddons/ActiveAEDSP.h"
diff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cpp
index 9f1c9e33e2..b525b0bdff 100644
--- a/xbmc/FileItem.cpp
+++ b/xbmc/FileItem.cpp
@@ -36,6 +36,8 @@
#include "filesystem/MusicDatabaseDirectory.h"
#include "filesystem/VideoDatabaseDirectory.h"
#include "filesystem/VideoDatabaseDirectory/QueryParams.h"
+#include "games/addons/GameClient.h"
+#include "games/GameUtils.h"
#include "games/tags/GameInfoTag.h"
#include "music/tags/MusicInfoTagLoaderFactory.h"
#include "CueDocument.h"
@@ -811,6 +813,9 @@ bool CFileItem::IsVideo() const
return true;
}
+ //! @todo If the file is a zip file, ask the game clients if any support this
+ // file before assuming it is video.
+
return URIUtils::HasExtension(m_strPath, g_advancedSettings.m_videoExtensions);
}
@@ -890,6 +895,9 @@ bool CFileItem::IsAudio() const
return true;
}
+ //! @todo If the file is a zip file, ask the game clients if any support this
+ // file before assuming it is audio
+
return URIUtils::HasExtension(m_strPath, g_advancedSettings.GetMusicExtensions());
}
@@ -907,7 +915,10 @@ bool CFileItem::IsGame() const
if (HasPictureInfoTag())
return false;
- return false;
+ if (HasAddonInfo())
+ return CGameUtils::IsStandaloneGame(std::const_pointer_cast<ADDON::IAddon>(GetAddonInfo()));
+
+ return CGameUtils::HasGameExtension(m_strPath);
}
bool CFileItem::IsPicture() const
diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp
index dca216c771..169241b2be 100644
--- a/xbmc/GUIInfoManager.cpp
+++ b/xbmc/GUIInfoManager.cpp
@@ -42,6 +42,7 @@
#include "pictures/GUIWindowSlideShow.h"
#include "pictures/PictureInfoTag.h"
#include "music/tags/MusicInfoTag.h"
+#include "games/addons/savestates/SavestateDefines.h"
#include "guilib/IGUIContainer.h"
#include "guilib/GUIWindowManager.h"
#include "PlayListPlayer.h"
@@ -10024,6 +10025,11 @@ std::string CGUIInfoManager::GetItemLabel(const CFileItem *item, int info, std::
if (item->GetMusicInfoTag()->GetDuration() > 0)
duration = StringUtils::SecondsToTimeString(item->GetMusicInfoTag()->GetDuration());
}
+ else if (item->HasProperty(FILEITEM_PROPERTY_SAVESTATE_DURATION))
+ {
+ long iDuration = static_cast<long>(item->GetProperty(FILEITEM_PROPERTY_SAVESTATE_DURATION).asInteger());
+ duration = StringUtils::SecondsToTimeString(iDuration);
+ }
return duration;
}
case LISTITEM_PLOT:
diff --git a/xbmc/addons/Addon.cpp b/xbmc/addons/Addon.cpp
index a9ffe7f741..c24396f955 100644
--- a/xbmc/addons/Addon.cpp
+++ b/xbmc/addons/Addon.cpp
@@ -93,17 +93,20 @@ static const TypeMapping types[] =
{"xbmc.webinterface", ADDON_WEB_INTERFACE, 199, "DefaultAddonWebSkin.png" },
{"xbmc.addon.repository", ADDON_REPOSITORY, 24011, "DefaultAddonRepository.png" },
{"xbmc.pvrclient", ADDON_PVRDLL, 24019, "DefaultAddonPVRClient.png" },
+ {"kodi.gameclient", ADDON_GAMEDLL, 35049, "DefaultAddonGame.png" },
{"kodi.peripheral", ADDON_PERIPHERALDLL, 35010, "DefaultAddonPeripheral.png" },
{"xbmc.addon.video", ADDON_VIDEO, 1037, "DefaultAddonVideo.png" },
{"xbmc.addon.audio", ADDON_AUDIO, 1038, "DefaultAddonMusic.png" },
{"xbmc.addon.image", ADDON_IMAGE, 1039, "DefaultAddonPicture.png" },
{"xbmc.addon.executable", ADDON_EXECUTABLE, 1043, "DefaultAddonProgram.png" },
+ {"kodi.addon.game", ADDON_GAME, 35049, "DefaultAddonGame.png" },
{"xbmc.audioencoder", ADDON_AUDIOENCODER, 200, "DefaultAddonAudioEncoder.png" },
{"kodi.audiodecoder", ADDON_AUDIODECODER, 201, "DefaultAddonAudioDecoder.png" },
{"xbmc.service", ADDON_SERVICE, 24018, "DefaultAddonService.png" },
{"kodi.resource.images", ADDON_RESOURCE_IMAGES, 24035, "DefaultAddonImages.png" },
{"kodi.resource.language", ADDON_RESOURCE_LANGUAGE, 24026, "DefaultAddonLanguage.png" },
{"kodi.resource.uisounds", ADDON_RESOURCE_UISOUNDS, 24006, "DefaultAddonUISounds.png" },
+ {"kodi.resource.games", ADDON_RESOURCE_GAMES, 35209, "DefaultAddonGame.png" },
{"kodi.adsp", ADDON_ADSPDLL, 24135, "DefaultAddonAudioDSP.png" },
{"kodi.inputstream", ADDON_INPUTSTREAM, 24048, "DefaultAddonInputstream.png" },
};
diff --git a/xbmc/addons/AddonBuilder.cpp b/xbmc/addons/AddonBuilder.cpp
index 835b439585..f7b9f68372 100644
--- a/xbmc/addons/AddonBuilder.cpp
+++ b/xbmc/addons/AddonBuilder.cpp
@@ -22,6 +22,7 @@
#include "addons/AudioDecoder.h"
#include "addons/AudioEncoder.h"
#include "addons/ContextMenuAddon.h"
+#include "addons/GameResource.h"
#include "addons/ImageResource.h"
#include "addons/InputStream.h"
#include "addons/LanguageResource.h"
@@ -35,6 +36,7 @@
#include "addons/Visualisation.h"
#include "addons/Webinterface.h"
#include "cores/AudioEngine/Engines/ActiveAE/AudioDSPAddons/ActiveAEDSP.h"
+#include "games/addons/GameClient.h"
#include "games/controllers/Controller.h"
#include "peripherals/addons/PeripheralAddon.h"
#include "addons/PVRClient.h"
@@ -88,7 +90,8 @@ std::shared_ptr<IAddon> CAddonBuilder::Build()
type == ADDON_AUDIOENCODER ||
type == ADDON_AUDIODECODER ||
type == ADDON_INPUTSTREAM ||
- type == ADDON_PERIPHERALDLL)
+ type == ADDON_PERIPHERALDLL ||
+ type == ADDON_GAMEDLL)
{
std::string value = CAddonMgr::GetInstance().GetPlatformLibraryName(m_extPoint->plugin->extensions->configuration);
if (value.empty())
@@ -137,10 +140,14 @@ std::shared_ptr<IAddon> CAddonBuilder::Build()
return CInputStream::FromExtension(std::move(m_props), m_extPoint);
case ADDON_PERIPHERALDLL:
return PERIPHERALS::CPeripheralAddon::FromExtension(std::move(m_props), m_extPoint);
+ case ADDON_GAMEDLL:
+ return GAME::CGameClient::FromExtension(std::move(m_props), m_extPoint);
case ADDON_SKIN:
return CSkinInfo::FromExtension(std::move(m_props), m_extPoint);
case ADDON_RESOURCE_IMAGES:
return CImageResource::FromExtension(std::move(m_props), m_extPoint);
+ case ADDON_RESOURCE_GAMES:
+ return CGameResource::FromExtension(std::move(m_props), m_extPoint);
case ADDON_RESOURCE_LANGUAGE:
return CLanguageResource::FromExtension(std::move(m_props), m_extPoint);
case ADDON_RESOURCE_UISOUNDS:
@@ -203,6 +210,8 @@ AddonPtr CAddonBuilder::FromProps(AddonProps addonProps)
return AddonPtr(new CAudioDecoder(std::move(addonProps)));
case ADDON_RESOURCE_IMAGES:
return AddonPtr(new CImageResource(std::move(addonProps)));
+ case ADDON_RESOURCE_GAMES:
+ return AddonPtr(new CGameResource(std::move(addonProps)));
case ADDON_RESOURCE_LANGUAGE:
return AddonPtr(new CLanguageResource(std::move(addonProps)));
case ADDON_RESOURCE_UISOUNDS:
@@ -217,6 +226,8 @@ AddonPtr CAddonBuilder::FromProps(AddonProps addonProps)
return AddonPtr(new PERIPHERALS::CPeripheralAddon(std::move(addonProps), false, false)); //! @todo implement
case ADDON_GAME_CONTROLLER:
return AddonPtr(new GAME::CController(std::move(addonProps)));
+ case ADDON_GAMEDLL:
+ return AddonPtr(new GAME::CGameClient(std::move(addonProps)));
default:
break;
}
diff --git a/xbmc/addons/BinaryAddonCache.cpp b/xbmc/addons/BinaryAddonCache.cpp
index 196a2eede1..fe64687d3e 100644
--- a/xbmc/addons/BinaryAddonCache.cpp
+++ b/xbmc/addons/BinaryAddonCache.cpp
@@ -32,7 +32,12 @@ CBinaryAddonCache::~CBinaryAddonCache()
void CBinaryAddonCache::Init()
{
- m_addonsToCache = {ADDON_AUDIODECODER, ADDON_INPUTSTREAM, ADDON_PVRDLL};
+ m_addonsToCache = {
+ ADDON_AUDIODECODER,
+ ADDON_INPUTSTREAM,
+ ADDON_PVRDLL,
+ ADDON_GAMEDLL,
+ };
CAddonMgr::GetInstance().Events().Subscribe(this, &CBinaryAddonCache::OnEvent);
Update();
}
diff --git a/xbmc/addons/CMakeLists.txt b/xbmc/addons/CMakeLists.txt
index 8ea5f11f49..927d05db77 100644
--- a/xbmc/addons/CMakeLists.txt
+++ b/xbmc/addons/CMakeLists.txt
@@ -12,6 +12,7 @@ set(SOURCES Addon.cpp
ContextMenuAddon.cpp
ContextMenus.cpp
FilesystemInstaller.cpp
+ GameResource.cpp
GUIDialogAddonInfo.cpp
GUIDialogAddonSettings.cpp
GUIViewStateAddonBrowser.cpp
@@ -51,6 +52,7 @@ set(HEADERS Addon.h
DllLibCPluff.h
DllPVRClient.h
FilesystemInstaller.h
+ GameResource.h
GUIDialogAddonInfo.h
GUIDialogAddonSettings.h
GUIViewStateAddonBrowser.h
diff --git a/xbmc/addons/DllGameClient.h b/xbmc/addons/DllGameClient.h
new file mode 100644
index 0000000000..a34a1e1349
--- /dev/null
+++ b/xbmc/addons/DllGameClient.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "DllAddon.h"
+#include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h"
+
+class DllGameClient : public DllAddon<GameClient, game_client_properties>
+{
+ // this is populated via macro calls in DllAddon.h
+};
+
diff --git a/xbmc/addons/GUIDialogAddonInfo.cpp b/xbmc/addons/GUIDialogAddonInfo.cpp
index ee49ec4739..c79b183730 100644
--- a/xbmc/addons/GUIDialogAddonInfo.cpp
+++ b/xbmc/addons/GUIDialogAddonInfo.cpp
@@ -33,6 +33,7 @@
#include "dialogs/GUIDialogOK.h"
#include "dialogs/GUIDialogSelect.h"
#include "dialogs/GUIDialogYesNo.h"
+#include "games/GameUtils.h"
#include "GUIUserMessages.h"
#include "guilib/GUIWindowManager.h"
#include "input/Key.h"
@@ -377,7 +378,16 @@ bool CGUIDialogAddonInfo::CanOpen() const
bool CGUIDialogAddonInfo::CanRun() const
{
- return m_localAddon && m_localAddon->Type() == ADDON_SCRIPT;
+ if (m_localAddon)
+ {
+ if (m_localAddon->Type() == ADDON_SCRIPT)
+ return true;
+
+ if (GAME::CGameUtils::IsStandaloneGame(m_localAddon))
+ return true;
+ }
+
+ return false;
}
bool CGUIDialogAddonInfo::CanUse() const
diff --git a/xbmc/addons/GUIWindowAddonBrowser.cpp b/xbmc/addons/GUIWindowAddonBrowser.cpp
index 68cb844cf9..84a34a13a0 100644
--- a/xbmc/addons/GUIWindowAddonBrowser.cpp
+++ b/xbmc/addons/GUIWindowAddonBrowser.cpp
@@ -403,6 +403,8 @@ int CGUIWindowAddonBrowser::SelectAddonID(const std::vector<ADDON::TYPE> &types,
CAddonsDirectory::GetScriptsAndPlugins("image", typeAddons);
else if (*type == ADDON_VIDEO)
CAddonsDirectory::GetScriptsAndPlugins("video", typeAddons);
+ else if (*type == ADDON_GAME)
+ CAddonsDirectory::GetScriptsAndPlugins("game", typeAddons);
else
CAddonMgr::GetInstance().GetAddons(typeAddons, *type);
diff --git a/xbmc/addons/GameResource.cpp b/xbmc/addons/GameResource.cpp
new file mode 100644
index 0000000000..ee6be8f70a
--- /dev/null
+++ b/xbmc/addons/GameResource.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GameResource.h"
+
+#include <utility>
+
+using namespace ADDON;
+
+CGameResource::CGameResource(AddonProps props) :
+ CResource(std::move(props))
+{
+}
+
+std::unique_ptr<CGameResource> CGameResource::FromExtension(AddonProps props, const cp_extension_t* ext)
+{
+ return std::unique_ptr<CGameResource>(new CGameResource(std::move(props)));
+}
diff --git a/xbmc/addons/GameResource.h b/xbmc/addons/GameResource.h
new file mode 100644
index 0000000000..3ccd24248a
--- /dev/null
+++ b/xbmc/addons/GameResource.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "addons/Resource.h"
+
+#include <memory>
+
+namespace ADDON
+{
+
+class CGameResource : public CResource
+{
+public:
+ CGameResource(AddonProps props);
+ virtual ~CGameResource() = default;
+
+ static std::unique_ptr<CGameResource> FromExtension(AddonProps props, const cp_extension_t* ext);
+
+ // implementation of CResource
+ virtual bool IsAllowed(const std::string& file) const override { return true; }
+};
+
+}
diff --git a/xbmc/addons/IAddon.h b/xbmc/addons/IAddon.h
index 1b1dfb3f76..7e4f827a13 100644
--- a/xbmc/addons/IAddon.h
+++ b/xbmc/addons/IAddon.h
@@ -41,6 +41,7 @@ namespace ADDON
ADDON_PVRDLL,
ADDON_ADSPDLL,
ADDON_INPUTSTREAM,
+ ADDON_GAMEDLL,
ADDON_PERIPHERALDLL,
ADDON_SCRIPT,
ADDON_SCRIPT_WEATHER,
@@ -62,10 +63,12 @@ namespace ADDON
ADDON_RESOURCE_IMAGES,
ADDON_RESOURCE_LANGUAGE,
ADDON_RESOURCE_UISOUNDS,
+ ADDON_RESOURCE_GAMES,
ADDON_VIDEO, // virtual addon types
ADDON_AUDIO,
ADDON_IMAGE,
ADDON_EXECUTABLE,
+ ADDON_GAME,
ADDON_SCRAPER_LIBRARY,
ADDON_SCRIPT_LIBRARY,
ADDON_SCRIPT_MODULE,
diff --git a/xbmc/addons/Makefile b/xbmc/addons/Makefile
index ea68de3afd..65d30ca2d4 100644
--- a/xbmc/addons/Makefile
+++ b/xbmc/addons/Makefile
@@ -12,6 +12,7 @@ SRCS=Addon.cpp \
ContextMenus.cpp \
AudioDecoder.cpp \
FilesystemInstaller.cpp \
+ GameResource.cpp \
GUIDialogAddonInfo.cpp \
GUIDialogAddonSettings.cpp \
GUIViewStateAddonBrowser.cpp \
diff --git a/xbmc/addons/PluginSource.cpp b/xbmc/addons/PluginSource.cpp
index 14ca09e4ce..dd3c9250c7 100644
--- a/xbmc/addons/PluginSource.cpp
+++ b/xbmc/addons/PluginSource.cpp
@@ -77,6 +77,8 @@ CPluginSource::Content CPluginSource::Translate(const std::string &content)
return CPluginSource::EXECUTABLE;
else if (content == "video")
return CPluginSource::VIDEO;
+ else if (content == "game")
+ return CPluginSource::GAME;
else
return CPluginSource::UNKNOWN;
}
@@ -89,6 +91,8 @@ TYPE CPluginSource::FullType() const
return ADDON_AUDIO;
if (Provides(IMAGE))
return ADDON_IMAGE;
+ if (Provides(GAME))
+ return ADDON_GAME;
if (Provides(EXECUTABLE))
return ADDON_EXECUTABLE;
@@ -100,6 +104,7 @@ bool CPluginSource::IsType(TYPE type) const
return ((type == ADDON_VIDEO && Provides(VIDEO))
|| (type == ADDON_AUDIO && Provides(AUDIO))
|| (type == ADDON_IMAGE && Provides(IMAGE))
+ || (type == ADDON_GAME && Provides(GAME))
|| (type == ADDON_EXECUTABLE && Provides(EXECUTABLE)));
}
diff --git a/xbmc/addons/PluginSource.h b/xbmc/addons/PluginSource.h
index eafc12be79..bd059fd2fc 100644
--- a/xbmc/addons/PluginSource.h
+++ b/xbmc/addons/PluginSource.h
@@ -28,7 +28,7 @@ class CPluginSource : public CAddon
{
public:
- enum Content { UNKNOWN, AUDIO, IMAGE, EXECUTABLE, VIDEO };
+ enum Content { UNKNOWN, AUDIO, IMAGE, EXECUTABLE, VIDEO, GAME };
static std::unique_ptr<CPluginSource> FromExtension(AddonProps props, const cp_extension_t* ext);
diff --git a/xbmc/addons/addon-bindings.mk b/xbmc/addons/addon-bindings.mk
index 6bfed360d7..788b50eb0e 100644
--- a/xbmc/addons/addon-bindings.mk
+++ b/xbmc/addons/addon-bindings.mk
@@ -13,6 +13,9 @@ BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_audioenc_types.h
BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_audioengine_types.h
BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_codec_types.h
BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_epg_types.h
+BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_callbacks.h
+BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_dll.h
+BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h
BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_inputstream_dll.h
BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_inputstream_types.h
BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_callbacks.h
@@ -30,6 +33,7 @@ BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_vis_types.h
BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/libXBMC_addon.h
BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_audioengine.h
BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_adsp.h
+BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_game.h
BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_guilib.h
BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_inputstream.h
BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_peripheral.h
@@ -38,3 +42,4 @@ BINDINGS+=xbmc/addons/kodi-addon-dev-kit/include/kodi/libXBMC_codec.h
BINDINGS+=xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxPacket.h
BINDINGS+=xbmc/cores/AudioEngine/Utils/AEChannelData.h
BINDINGS+=xbmc/filesystem/IFileTypes.h
+BINDINGS+=xbmc/input/XBMC_vkeys.h
diff --git a/xbmc/addons/binary/interfaces/AddonInterfaces.cpp b/xbmc/addons/binary/interfaces/AddonInterfaces.cpp
index 5d517a4e01..2809cbb0a4 100644
--- a/xbmc/addons/binary/interfaces/AddonInterfaces.cpp
+++ b/xbmc/addons/binary/interfaces/AddonInterfaces.cpp
@@ -27,6 +27,7 @@
#include "addons/binary/interfaces/api1/AudioDSP/AddonCallbacksAudioDSP.h"
#include "addons/binary/interfaces/api1/AudioEngine/AddonCallbacksAudioEngine.h"
#include "addons/binary/interfaces/api1/Codec/AddonCallbacksCodec.h"
+#include "addons/binary/interfaces/api1/Game/AddonCallbacksGame.h"
#include "addons/binary/interfaces/api1/GUI/AddonCallbacksGUI.h"
#include "addons/binary/interfaces/api1/GUI/AddonGUIWindow.h"
#include "addons/binary/interfaces/api1/InputStream/AddonCallbacksInputStream.h"
@@ -51,7 +52,8 @@ CAddonInterfaces::CAddonInterfaces(CAddon* addon)
m_helperADSP(nullptr),
m_helperCODEC(nullptr),
m_helperInputStream(nullptr),
- m_helperPeripheral(nullptr)
+ m_helperPeripheral(nullptr),
+ m_helperGame(nullptr)
{
m_callbacks->libBasePath = strdup(CSpecialProtocol::TranslatePath("special://xbmcbinaddons").c_str());
m_callbacks->addonData = this;
@@ -72,6 +74,8 @@ CAddonInterfaces::CAddonInterfaces(CAddon* addon)
m_callbacks->INPUTSTREAMLib_UnRegisterMe = CAddonInterfaces::INPUTSTREAMLib_UnRegisterMe;
m_callbacks->PeripheralLib_RegisterMe = CAddonInterfaces::PeripheralLib_RegisterMe;
m_callbacks->PeripheralLib_UnRegisterMe = CAddonInterfaces::PeripheralLib_UnRegisterMe;
+ m_callbacks->GameLib_RegisterMe = CAddonInterfaces::GameLib_RegisterMe;
+ m_callbacks->GameLib_UnRegisterMe = CAddonInterfaces::GameLib_UnRegisterMe;
}
CAddonInterfaces::~CAddonInterfaces()
@@ -84,6 +88,7 @@ CAddonInterfaces::~CAddonInterfaces()
delete static_cast<V1::KodiAPI::Codec::CAddonCallbacksCodec*>(m_helperCODEC);
delete static_cast<V1::KodiAPI::InputStream::CAddonCallbacksInputStream*>(m_helperInputStream);
delete static_cast<V1::KodiAPI::Peripheral::CAddonCallbacksPeripheral*>(m_helperPeripheral);
+ delete static_cast<V1::KodiAPI::Game::CAddonCallbacksGame*>(m_helperGame);
free((char*)m_callbacks->libBasePath);
delete m_callbacks;
@@ -255,6 +260,33 @@ void CAddonInterfaces::CodecLib_UnRegisterMe(void *addonData, void *cbTable)
}
/*\_____________________________________________________________________________
\*/
+CB_GameLib* CAddonInterfaces::GameLib_RegisterMe(void *addonData)
+{
+ CAddonInterfaces* addon = static_cast<CAddonInterfaces*>(addonData);
+ if (addon == nullptr)
+ {
+ CLog::Log(LOGERROR, "CAddonInterfaces - %s - called with a null pointer", __FUNCTION__);
+ return nullptr;
+ }
+
+ addon->m_helperGame = new V1::KodiAPI::Game::CAddonCallbacksGame(addon->m_addon);
+ return static_cast<V1::KodiAPI::Game::CAddonCallbacksGame*>(addon->m_helperGame)->GetCallbacks();
+}
+
+void CAddonInterfaces::GameLib_UnRegisterMe(void *addonData, CB_GameLib *cbTable)
+{
+ CAddonInterfaces* addon = static_cast<CAddonInterfaces*>(addonData);
+ if (addon == nullptr)
+ {
+ CLog::Log(LOGERROR, "CAddonInterfaces - %s - called with a null pointer", __FUNCTION__);
+ return;
+ }
+
+ delete static_cast<V1::KodiAPI::Game::CAddonCallbacksGame*>(addon->m_helperGame);
+ addon->m_helperGame = nullptr;
+}
+/*\_____________________________________________________________________________
+\*/
void* CAddonInterfaces::INPUTSTREAMLib_RegisterMe(void *addonData)
{
CAddonInterfaces* addon = static_cast<CAddonInterfaces*>(addonData);
diff --git a/xbmc/addons/binary/interfaces/AddonInterfaces.h b/xbmc/addons/binary/interfaces/AddonInterfaces.h
index 05149bc3fc..3c7c56e5a4 100644
--- a/xbmc/addons/binary/interfaces/AddonInterfaces.h
+++ b/xbmc/addons/binary/interfaces/AddonInterfaces.h
@@ -22,6 +22,7 @@
#include "IAddonInterface.h"
#include "addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_callbacks.h"
+#include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_callbacks.h"
#include <stdint.h>
@@ -49,6 +50,8 @@ typedef void* (*KODIINPUTSTREAMLib_RegisterMe)(void *addonData);
typedef void (*KODIINPUTSTREAMLib_UnRegisterMe)(void *addonData, void *cbTable);
typedef CB_PeripheralLib* (*KODIPeripheralLib_RegisterMe)(void *addonData);
typedef void (*KODIPeripheralLib_UnRegisterMe)(void *addonData, CB_PeripheralLib *cbTable);
+typedef CB_GameLib* (*KODIGameLib_RegisterMe)(void *addonData);
+typedef void (*KODIGameLib_UnRegisterMe)(void *addonData, CB_GameLib *cbTable);
typedef struct AddonCB
{
@@ -70,6 +73,8 @@ typedef struct AddonCB
KODIINPUTSTREAMLib_UnRegisterMe INPUTSTREAMLib_UnRegisterMe;
KODIPeripheralLib_RegisterMe PeripheralLib_RegisterMe;
KODIPeripheralLib_UnRegisterMe PeripheralLib_UnRegisterMe;
+ KODIGameLib_RegisterMe GameLib_RegisterMe;
+ KODIGameLib_UnRegisterMe GameLib_UnRegisterMe;
} AddonCB;
@@ -127,6 +132,11 @@ namespace ADDON
static CB_PeripheralLib* PeripheralLib_RegisterMe (void *addonData);
static void PeripheralLib_UnRegisterMe (void *addonData, CB_PeripheralLib* cbTable);
void* GetHelperPeripheral() { return m_helperPeripheral; }
+ /*\_________________________________________________________________________
+ \*/
+ static CB_GameLib* GameLib_RegisterMe (void *addonData);
+ static void GameLib_UnRegisterMe (void *addonData, CB_GameLib* cbTable);
+ void* GetHelperGame() { return m_helperGame; }
/*
* API level independent functions for Kodi
*/
@@ -144,6 +154,7 @@ namespace ADDON
void* m_helperCODEC;
void* m_helperInputStream;
void* m_helperPeripheral;
+ void* m_helperGame;
};
} /* namespace ADDON */
diff --git a/xbmc/addons/binary/interfaces/api1/Game/AddonCallbacksGame.cpp b/xbmc/addons/binary/interfaces/api1/Game/AddonCallbacksGame.cpp
new file mode 100644
index 0000000000..bf21499102
--- /dev/null
+++ b/xbmc/addons/binary/interfaces/api1/Game/AddonCallbacksGame.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "AddonCallbacksGame.h"
+#include "cores/AudioEngine/Utils/AEChannelInfo.h"
+#include "games/addons/GameClient.h"
+#include "guilib/WindowIDs.h"
+#include "input/Key.h"
+#include "messaging/ApplicationMessenger.h"
+#include "utils/log.h"
+
+#include <string>
+
+using namespace ADDON;
+using namespace GAME;
+
+namespace V1
+{
+namespace KodiAPI
+{
+
+namespace Game
+{
+
+CAddonCallbacksGame::CAddonCallbacksGame(CAddon* addon) :
+ ADDON::IAddonInterface(addon, 1, GAME_API_VERSION),
+ m_callbacks(new CB_GameLib)
+{
+ /* write Kodi game specific add-on function addresses to callback table */
+ m_callbacks->CloseGame = CloseGame;
+ m_callbacks->OpenPixelStream = OpenPixelStream;
+ m_callbacks->OpenVideoStream = OpenVideoStream;
+ m_callbacks->OpenPCMStream = OpenPCMStream;
+ m_callbacks->OpenAudioStream = OpenAudioStream;
+ m_callbacks->AddStreamData = AddStreamData;
+ m_callbacks->CloseStream = CloseStream;
+ m_callbacks->EnableHardwareRendering = EnableHardwareRendering;
+ m_callbacks->HwGetCurrentFramebuffer = HwGetCurrentFramebuffer;
+ m_callbacks->HwGetProcAddress = HwGetProcAddress;
+ m_callbacks->RenderFrame = RenderFrame;
+ m_callbacks->OpenPort = OpenPort;
+ m_callbacks->ClosePort = ClosePort;
+ m_callbacks->InputEvent = InputEvent;
+}
+
+CAddonCallbacksGame::~CAddonCallbacksGame()
+{
+ /* delete the callback table */
+ delete m_callbacks;
+}
+
+CGameClient* CAddonCallbacksGame::GetGameClient(void* addonData, const char* strFunction)
+{
+ CAddonInterfaces* addon = static_cast<CAddonInterfaces*>(addonData);
+ if (!addon || !addon->GetHelperGame())
+ {
+ CLog::Log(LOGERROR, "GAME - %s - called with a null pointer", strFunction);
+ return NULL;
+ }
+
+ return dynamic_cast<CGameClient*>(static_cast<CAddonCallbacksGame*>(addon->GetHelperGame())->m_addon);
+}
+
+void CAddonCallbacksGame::CloseGame(void* addonData)
+{
+ using namespace KODI::MESSAGING;
+
+ CApplicationMessenger::GetInstance().PostMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_STOP)));
+}
+
+int CAddonCallbacksGame::OpenPixelStream(void* addonData, GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return -1;
+
+ return gameClient->OpenPixelStream(format, width, height, rotation) ? 0 : -1;
+}
+
+int CAddonCallbacksGame::OpenVideoStream(void* addonData, GAME_VIDEO_CODEC codec)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return -1;
+
+ return gameClient->OpenVideoStream(codec) ? 0 : -1;
+}
+
+int CAddonCallbacksGame::OpenPCMStream(void* addonData, GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channel_map)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return -1;
+
+ return gameClient->OpenPCMStream(format, channel_map) ? 0 : -1;
+}
+
+int CAddonCallbacksGame::OpenAudioStream(void* addonData, GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channel_map)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return -1;
+
+ return gameClient->OpenAudioStream(codec, channel_map) ? 0 : -1;
+}
+
+void CAddonCallbacksGame::AddStreamData(void* addonData, GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return;
+
+ gameClient->AddStreamData(stream, data, size);
+}
+
+void CAddonCallbacksGame::CloseStream(void* addonData, GAME_STREAM_TYPE stream)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return;
+
+ gameClient->CloseStream(stream);
+}
+
+void CAddonCallbacksGame::EnableHardwareRendering(void* addonData, const game_hw_info *hw_info)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return;
+
+ //! @todo
+}
+
+uintptr_t CAddonCallbacksGame::HwGetCurrentFramebuffer(void* addonData)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return 0;
+
+ //! @todo
+ return 0;
+}
+
+game_proc_address_t CAddonCallbacksGame::HwGetProcAddress(void* addonData, const char *sym)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return nullptr;
+
+ //! @todo
+ return nullptr;
+}
+
+void CAddonCallbacksGame::RenderFrame(void* addonData)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return;
+
+ //! @todo
+}
+
+bool CAddonCallbacksGame::OpenPort(void* addonData, unsigned int port)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return false;
+
+ return gameClient->OpenPort(port);
+}
+
+void CAddonCallbacksGame::ClosePort(void* addonData, unsigned int port)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return;
+
+ gameClient->ClosePort(port);
+}
+
+bool CAddonCallbacksGame::InputEvent(void* addonData, const game_input_event* event)
+{
+ CGameClient* gameClient = GetGameClient(addonData, __FUNCTION__);
+ if (!gameClient)
+ return false;
+
+ if (event == nullptr)
+ return false;
+
+ return gameClient->ReceiveInputEvent(*event);
+}
+
+} /* namespace Game */
+
+} /* namespace KodiAPI */
+} /* namespace V1 */
diff --git a/xbmc/addons/binary/interfaces/api1/Game/AddonCallbacksGame.h b/xbmc/addons/binary/interfaces/api1/Game/AddonCallbacksGame.h
new file mode 100644
index 0000000000..07136afc6b
--- /dev/null
+++ b/xbmc/addons/binary/interfaces/api1/Game/AddonCallbacksGame.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "addons/binary/interfaces/AddonInterfaces.h"
+
+namespace GAME { class CGameClient; }
+
+namespace V1
+{
+namespace KodiAPI
+{
+
+namespace Game
+{
+
+/*!
+ * Callbacks for a game add-on to Kodi
+ */
+class CAddonCallbacksGame : public ADDON::IAddonInterface
+{
+public:
+ CAddonCallbacksGame(ADDON::CAddon* addon);
+ ~CAddonCallbacksGame(void);
+
+ /*!
+ * @return The callback table.
+ */
+ CB_GameLib* GetCallbacks() const { return m_callbacks; }
+
+ static void CloseGame(void* addonData);
+ static int OpenPixelStream(void* addonData, GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation);
+ static int OpenVideoStream(void* addonData, GAME_VIDEO_CODEC codec);
+ static int OpenPCMStream(void* addonData, GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channel_map);
+ static int OpenAudioStream(void* addonData, GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channel_map);
+ static void AddStreamData(void* addonData, GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size);
+ static void CloseStream(void* addonData, GAME_STREAM_TYPE stream);
+ static void EnableHardwareRendering(void* addonData, const game_hw_info* hw_info);
+ static uintptr_t HwGetCurrentFramebuffer(void* addonData);
+ static game_proc_address_t HwGetProcAddress(void* addonData, const char* sym);
+ static void RenderFrame(void* addonData);
+ static bool OpenPort(void* addonData, unsigned int port);
+ static void ClosePort(void* addonData, unsigned int port);
+ static bool InputEvent(void* addonData, const game_input_event* event);
+
+private:
+ static GAME::CGameClient* GetGameClient(void* addonData, const char* strFunction);
+
+ CB_GameLib* m_callbacks; /*!< callback addresses */
+};
+
+} /* namespace Game */
+
+} /* namespace KoidAPI */
+} /* namespace V1 */
diff --git a/xbmc/addons/binary/interfaces/api1/Game/CMakeLists.txt b/xbmc/addons/binary/interfaces/api1/Game/CMakeLists.txt
new file mode 100644
index 0000000000..48e3cfc1f3
--- /dev/null
+++ b/xbmc/addons/binary/interfaces/api1/Game/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(SOURCES AddonCallbacksGame.cpp)
+
+set(HEADERS AddonCallbacksGame.h)
+
+core_add_library(api1AddonCallbacks_Game)
+
+if(ENABLE_INTERNAL_FFMPEG)
+ add_dependencies(api1AddonCallbacks_Game ffmpeg)
+endif()
diff --git a/xbmc/addons/binary/interfaces/api1/Game/Makefile b/xbmc/addons/binary/interfaces/api1/Game/Makefile
new file mode 100644
index 0000000000..460425b058
--- /dev/null
+++ b/xbmc/addons/binary/interfaces/api1/Game/Makefile
@@ -0,0 +1,7 @@
+SRCS=AddonCallbacksGame.cpp \
+
+LIB=addon-callbacks-game.a
+
+include ../../../../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+
diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_callbacks.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_callbacks.h
new file mode 100644
index 0000000000..6057f4635f
--- /dev/null
+++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_callbacks.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef KODI_GAME_CALLBACKS_H_
+#define KODI_GAME_CALLBACKS_H_
+
+#include "kodi_game_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct CB_GameLib
+{
+ // --- Game callbacks --------------------------------------------------------
+
+ /*!
+ * \brief Requests the frontend to stop the current game
+ */
+ void (*CloseGame)(void* addonData);
+
+ /*!
+ * \brief Create a video stream for pixel data
+ *
+ * \param format The type of pixel data accepted by this stream
+ * \param width The frame width
+ * \param height The frame height
+ * \param rotation The rotation (counter-clockwise) of the video frames
+ *
+ * \return 0 on success or -1 if a video stream is already created
+ */
+ int (*OpenPixelStream)(void* addonData, GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation);
+
+ /*!
+ * \brief Create a video stream for encoded video data
+ *
+ * \param codec The video format accepted by this stream
+ *
+ * \return 0 on success or -1 if a video stream is already created
+ */
+ int (*OpenVideoStream)(void* addonData, GAME_VIDEO_CODEC codec);
+
+ /*!
+ * \brief Create an audio stream for PCM audio data
+ *
+ * \param format The type of audio data accepted by this stream
+ * \param channel_map The channel layout terminated by GAME_CH_NULL
+ *
+ * \return 0 on success or -1 if an audio stream is already created
+ */
+ int (*OpenPCMStream)(void* addonData, GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channel_map);
+
+ /*!
+ * \brief Create an audio stream for encoded audio data
+ *
+ * \param codec The audio format accepted by this stream
+ * \param channel_map The channel layout terminated by GAME_CH_NULL
+ *
+ * \return 0 on success or -1 if an audio stream is already created
+ */
+ int(*OpenAudioStream)(void* addonData, GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channel_map);
+
+ /*!
+ * \brief Add a data packet to an audio or video stream
+ *
+ * \param stream The target stream
+ * \param data The data packet
+ * \param size The size of the data
+ */
+ void (*AddStreamData)(void* addonData, GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size);
+
+ /*!
+ * \brief Free the specified stream
+ *
+ * \param stream The stream to close
+ */
+ void (*CloseStream)(void* addonData, GAME_STREAM_TYPE stream);
+
+ // -- Hardware rendering callbacks -------------------------------------------
+
+ /*!
+ * \brief Enable hardware rendering
+ *
+ * \param hw_info A struct of properties for the hardware rendering system
+ */
+ void (*EnableHardwareRendering)(void* addonData, const game_hw_info* hw_info);
+
+ /*!
+ * \brief Get the framebuffer for rendering
+ *
+ * \return The framebuffer
+ */
+ uintptr_t (*HwGetCurrentFramebuffer)(void* addonData);
+
+ /*!
+ * \brief Get a symbol from the hardware context
+ *
+ * \param symbol The symbol's name
+ *
+ * \return A function pointer for the specified symbol
+ */
+ game_proc_address_t (*HwGetProcAddress)(void* addonData, const char* symbol);
+
+ /*!
+ * \brief Called when a frame is being rendered
+ */
+ void (*RenderFrame)(void* addonData);
+
+ // --- Input callbacks -------------------------------------------------------
+
+ /*!
+ * \brief Begin reporting events for the specified joystick port
+ *
+ * \param port The zero-indexed port number
+ *
+ * \return true if the port was opened, false otherwise
+ */
+ bool (*OpenPort)(void* addonData, unsigned int port);
+
+ /*!
+ * \brief End reporting events for the specified port
+ *
+ * \param port The port number passed to OpenPort()
+ */
+ void (*ClosePort)(void* addonData, unsigned int port);
+
+ /*!
+ * \brief Notify the port of an input event
+ *
+ * \param event The input event
+ *
+ * Input events can arrive for the following sources:
+ * - GAME_INPUT_EVENT_MOTOR
+ *
+ * \return true if the event was handled, false otherwise
+ */
+ bool (*InputEvent)(void* addonData, const game_input_event* event);
+
+} CB_GameLib;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // KODI_GAME_CALLBACKS_H_
diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_dll.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_dll.h
new file mode 100644
index 0000000000..247f24ba27
--- /dev/null
+++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_dll.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2014-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef KODI_GAME_DLL_H_
+#define KODI_GAME_DLL_H_
+
+#include "kodi_game_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// --- Game API operations -----------------------------------------------------
+
+/*!
+ * \brief Return GAME_API_VERSION_STRING
+ *
+ * The add-on is backwards compatible with the frontend if this API version is
+ * is at least the frontend's minimum API version.
+ *
+ * \return Must be GAME_API_VERSION_STRING
+ */
+const char* GetGameAPIVersion(void);
+
+/*!
+ * \brief Return GAME_MIN_API_VERSION_STRING
+ *
+ * The add-on is forwards compatible with the frontend if this minimum version
+ * is no more than the frontend's API version.
+ *
+ * \return Must be GAME_MIN_API_VERSION_STRING
+ */
+const char* GetMininumGameAPIVersion(void);
+
+// --- Game operations ---------------------------------------------------------
+
+/*!
+ * \brief Load a game
+ *
+ * \param url The URL to load
+ *
+ * return the error, or GAME_ERROR_NO_ERROR if the game was loaded
+ */
+GAME_ERROR LoadGame(const char* url);
+
+/*!
+ * \brief Load a game that requires multiple files
+ *
+ * \param type The game stype
+ * \param urls An array of urls
+ * \param urlCount The number of urls in the array
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if the game was loaded
+ */
+GAME_ERROR LoadGameSpecial(SPECIAL_GAME_TYPE type, const char** urls, size_t urlCount);
+
+/*!
+ * \brief Begin playing without a game file
+ *
+ * If the add-on supports standalone mode, it must add the <supports_standalone>
+ * tag to the extension point in addon.xml:
+ *
+ * <supports_no_game>false</supports_no_game>
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if the game add-on was loaded
+ */
+GAME_ERROR LoadStandalone(void);
+
+/*!
+ * \brief Unload the current game
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if the game was unloaded
+ */
+/*! Unloads a currently loaded game */
+GAME_ERROR UnloadGame(void);
+
+/*!
+ * \brief Get information about the loaded game
+ *
+ * \param info The info structure to fill
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if info was filled
+ */
+GAME_ERROR GetGameInfo(game_system_av_info* info);
+
+/*!
+ * \brief Get region of the loaded game
+ *
+ * \return the region, or GAME_REGION_UNKNOWN if unknown or no game is loaded
+ */
+GAME_REGION GetRegion(void);
+
+/*!
+ * \brief Return true if the client requires the frontend to provide a game loop
+ *
+ * The game loop is a thread that calls RunFrame() in a loop at a rate
+ * determined by the playback speed and the client's FPS.
+ *
+ * \return true if the frontend should provide a game loop, false otherwise
+ */
+bool RequiresGameLoop(void);
+
+/*!
+ * \brief Run a single frame for add-ons that use a game loop
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if there was no error
+ */
+GAME_ERROR RunFrame(void);
+
+/*!
+ * \brief Reset the current game
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if the game was reset
+ */
+GAME_ERROR Reset(void);
+
+// --- Hardware rendering operations -------------------------------------------
+
+/*!
+ * \brief Invalidates the current HW context and reinitializes GPU resources
+ *
+ * Any GL state is lost, and must not be deinitialized explicitly.
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if the HW context was reset
+ */
+GAME_ERROR HwContextReset(void);
+
+/*!
+ * \brief Called before the context is destroyed
+ *
+ * Resources can be deinitialized at this step.
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if the HW context was destroyed
+ */
+GAME_ERROR HwContextDestroy(void);
+
+// --- Input operations --------------------------------------------------------
+
+/*!
+ * \brief Notify the add-on of a status change on an open port
+ *
+ * Ports can be opened using the OpenPort() callback
+ *
+ * \param port Non-negative for a joystick port, or GAME_INPUT_PORT value otherwise
+ * \param collected True if a controller was connected, false if disconnected
+ * \param controller The connected controller
+ */
+void UpdatePort(int port, bool connected, const game_controller* controller);
+
+/*!
+ * \brief Check if input is accepted for a feature on the controller
+ *
+ * If only a subset of the controller profile is used, this can return false
+ * for unsupported features to not absorb their input.
+ *
+ * If the entire controller profile is used, this should always return true.
+ *
+ * \param controller_id The ID of the controller profile
+ * \param feature_name The name of a feature in that profile
+ * \return true if input is accepted for the feature, false otherwise
+ */
+bool HasFeature(const char* controller_id, const char* feature_name);
+
+/*!
+ * \brief Notify the add-on of an input event
+ *
+ * \param event The input event
+ *
+ * \return true if the event was handled, false otherwise
+ */
+bool InputEvent(const game_input_event* event);
+
+// --- Serialization operations ------------------------------------------------
+
+/*!
+ * \brief Get the number of bytes required to serialize the game
+ *
+ * \return the number of bytes, or 0 if serialization is not supported
+ */
+size_t SerializeSize(void);
+
+/*!
+ * \brief Serialize the state of the game
+ *
+ * \param data The buffer receiving the serialized game data
+ * \param size The size of the buffer
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if the game was serialized into the buffer
+ */
+GAME_ERROR Serialize(uint8_t* data, size_t size);
+
+/*!
+ * \brief Deserialize the game from the given state
+ *
+ * \param data A buffer containing the game's new state
+ * \param size The size of the buffer
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if the game deserialized
+ */
+GAME_ERROR Deserialize(const uint8_t* data, size_t size);
+
+// --- Cheat operations --------------------------------------------------------
+
+/*!
+ * \brief Reset the cheat system
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if the cheat system was reset
+ */
+GAME_ERROR CheatReset(void);
+
+/*!
+ * \brief Get a region of memory
+ *
+ * \param type The type of memory to retrieve
+ * \param data Set to the region of memory; must remain valid until UnloadGame() is called
+ * \param size Set to the size of the region of memory
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if data was set to a valid buffer
+ */
+GAME_ERROR GetMemory(GAME_MEMORY type, const uint8_t** data, size_t* size);
+
+/*!
+ * \brief Set a cheat code
+ *
+ * \param index
+ * \param enabled
+ * \param code
+ *
+ * \return the error, or GAME_ERROR_NO_ERROR if the cheat was set
+ */
+GAME_ERROR SetCheat(unsigned int index, bool enabled, const char* code);
+
+// --- Add-on helper implementation --------------------------------------------
+
+/*!
+ * \brief Called by Kodi to assign the function pointers of this add-on to pClient
+ *
+ * Note that get_addon() is defined here, so it will be available in all
+ * compiled game clients.
+ */
+void __declspec(dllexport) get_addon(GameClient* pClient)
+{
+ pClient->GetGameAPIVersion = GetGameAPIVersion;
+ pClient->GetMininumGameAPIVersion = GetMininumGameAPIVersion;
+ pClient->LoadGame = LoadGame;
+ pClient->LoadGameSpecial = LoadGameSpecial;
+ pClient->LoadStandalone = LoadStandalone;
+ pClient->UnloadGame = UnloadGame;
+ pClient->GetGameInfo = GetGameInfo;
+ pClient->GetRegion = GetRegion;
+ pClient->RequiresGameLoop = RequiresGameLoop;
+ pClient->RunFrame = RunFrame;
+ pClient->Reset = Reset;
+ pClient->HwContextReset = HwContextReset;
+ pClient->HwContextDestroy = HwContextDestroy;
+ pClient->UpdatePort = UpdatePort;
+ pClient->HasFeature = HasFeature;
+ pClient->InputEvent = InputEvent;
+ pClient->SerializeSize = SerializeSize;
+ pClient->Serialize = Serialize;
+ pClient->Deserialize = Deserialize;
+ pClient->CheatReset = CheatReset;
+ pClient->GetMemory = GetMemory;
+ pClient->SetCheat = SetCheat;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // KODI_GAME_DLL_H_
diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h
new file mode 100644
index 0000000000..fa2762d87d
--- /dev/null
+++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2014-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef KODI_GAME_TYPES_H_
+#define KODI_GAME_TYPES_H_
+
+/* current game API version */
+#define GAME_API_VERSION "1.0.28"
+
+/* min. game API version */
+#define GAME_MIN_API_VERSION "1.0.28"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef TARGET_WINDOWS
+ #include <windows.h>
+#else
+ #ifndef __cdecl
+ #define __cdecl
+ #endif
+ #ifndef __declspec
+ #define __declspec(X)
+ #endif
+#endif
+
+#undef ATTRIBUTE_PACKED
+#undef PRAGMA_PACK_BEGIN
+#undef PRAGMA_PACK_END
+
+#if defined(__GNUC__)
+ #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+ #define ATTRIBUTE_PACKED __attribute__ ((packed))
+ #define PRAGMA_PACK 0
+ #endif
+#endif
+
+#if !defined(ATTRIBUTE_PACKED)
+ #define ATTRIBUTE_PACKED
+ #define PRAGMA_PACK 1
+#endif
+
+#ifdef BUILD_KODI_ADDON
+#include "XBMC_vkeys.h"
+#else
+#include "input/XBMC_vkeys.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! Game add-on error codes */
+typedef enum GAME_ERROR
+{
+ GAME_ERROR_NO_ERROR, // no error occurred
+ GAME_ERROR_UNKNOWN, // an unknown error occurred
+ GAME_ERROR_NOT_IMPLEMENTED, // the method that the frontend called is not implemented
+ GAME_ERROR_REJECTED, // the command was rejected by the game client
+ GAME_ERROR_INVALID_PARAMETERS, // the parameters of the method that was called are invalid for this operation
+ GAME_ERROR_FAILED, // the command failed
+ GAME_ERROR_NOT_LOADED, // no game is loaded
+ GAME_ERROR_RESTRICTED, // game requires restricted resources
+} GAME_ERROR;
+
+typedef enum GAME_STREAM_TYPE
+{
+ GAME_STREAM_UNKNOWN,
+ GAME_STREAM_AUDIO,
+ GAME_STREAM_VIDEO,
+} GAME_STREAM_TYPE;
+
+typedef enum GAME_PIXEL_FORMAT
+{
+ GAME_PIXEL_FORMAT_UNKNOWN,
+ GAME_PIXEL_FORMAT_YUV420P,
+ GAME_PIXEL_FORMAT_0RGB8888,
+ GAME_PIXEL_FORMAT_RGB565,
+ GAME_PIXEL_FORMAT_0RGB1555,
+} GAME_PIXEL_FORMAT;
+
+typedef enum GAME_VIDEO_CODEC
+{
+ GAME_VIDEO_CODEC_UNKNOWN,
+ GAME_VIDEO_CODEC_H264,
+ GAME_VIDEO_CODEC_THEORA,
+} GAME_VIDEO_CODEC;
+
+typedef enum GAME_VIDEO_ROTATION // Counter-clockwise
+{
+ GAME_VIDEO_ROTATION_0,
+ GAME_VIDEO_ROTATION_90,
+ GAME_VIDEO_ROTATION_180,
+ GAME_VIDEO_ROTATION_270,
+} GAME_VIDEO_ROTATION;
+
+typedef enum GAME_PCM_FORMAT
+{
+ GAME_PCM_FORMAT_UNKNOWN,
+ GAME_PCM_FORMAT_S16NE,
+} GAME_PCM_FORMAT;
+
+typedef enum GAME_AUDIO_CODEC
+{
+ GAME_AUDIO_CODEC_UNKNOWN,
+ GAME_AUDIO_CODEC_OPUS,
+} GAME_AUDIO_CODEC;
+
+typedef enum GAME_AUDIO_CHANNEL
+{
+ GAME_CH_NULL, // Channel list terminator
+ GAME_CH_FL,
+ GAME_CH_FR,
+ GAME_CH_FC,
+ GAME_CH_LFE,
+ GAME_CH_BL,
+ GAME_CH_BR,
+ GAME_CH_FLOC,
+ GAME_CH_FROC,
+ GAME_CH_BC,
+ GAME_CH_SL,
+ GAME_CH_SR,
+ GAME_CH_TFL,
+ GAME_CH_TFR,
+ GAME_CH_TFC,
+ GAME_CH_TC,
+ GAME_CH_TBL,
+ GAME_CH_TBR,
+ GAME_CH_TBC,
+ GAME_CH_BLOC,
+ GAME_CH_BROC,
+} GAME_AUDIO_CHANNEL;
+
+// TODO
+typedef enum GAME_HW_FRAME_BUFFER
+{
+ GAME_HW_FRAME_BUFFER_VALID, // Pass this to game_video_refresh if rendering to hardware
+ GAME_HW_FRAME_BUFFER_DUPLICATE, // Passing NULL to game_video_refresh is still a frame dupe as normal
+ GAME_HW_FRAME_BUFFER_RENDER,
+} GAME_HW_FRAME_BUFFER;
+
+typedef enum GAME_HW_CONTEXT_TYPE
+{
+ GAME_HW_CONTEXT_NONE,
+ GAME_HW_CONTEXT_OPENGL, // OpenGL 2.x. Latest version available before 3.x+. Driver can choose to use latest compatibility context
+ GAME_HW_CONTEXT_OPENGLES2, // GLES 2.0
+ GAME_HW_CONTEXT_OPENGL_CORE, // Modern desktop core GL context. Use major/minor fields to set GL version
+ GAME_HW_CONTEXT_OPENGLES3, // GLES 3.0
+} GAME_HW_CONTEXT_TYPE;
+
+typedef enum GAME_INPUT_PORT
+{
+ GAME_INPUT_PORT_JOYSTICK_START = 0, // Non-negative values are for joystick ports
+ GAME_INPUT_PORT_KEYBOARD = -1,
+ GAME_INPUT_PORT_MOUSE = -2,
+} GAME_INPUT_PORT;
+
+typedef enum GAME_INPUT_EVENT_SOURCE
+{
+ GAME_INPUT_EVENT_DIGITAL_BUTTON,
+ GAME_INPUT_EVENT_ANALOG_BUTTON,
+ GAME_INPUT_EVENT_ANALOG_STICK,
+ GAME_INPUT_EVENT_ACCELEROMETER,
+ GAME_INPUT_EVENT_KEY,
+ GAME_INPUT_EVENT_RELATIVE_POINTER,
+ GAME_INPUT_EVENT_ABSOLUTE_POINTER,
+ GAME_INPUT_EVENT_MOTOR,
+} GAME_INPUT_EVENT_SOURCE;
+
+typedef enum GAME_KEY_MOD
+{
+ GAME_KEY_MOD_NONE = 0x00,
+
+ GAME_KEY_MOD_SHIFT = 0x01,
+ GAME_KEY_MOD_CTRL = 0x02,
+ GAME_KEY_MOD_ALT = 0x04,
+ GAME_KEY_MOD_RALT = 0x08,
+ GAME_KEY_MOD_META = 0x10,
+
+ GAME_KEY_MOD_NUMLOCK = 0x20,
+ GAME_KEY_MOD_CAPSLOCK = 0x40,
+ GAME_KEY_MOD_SCROLLOCK = 0x80,
+} GAME_KEY_MOD;
+
+/*! Returned from game_get_region() */
+typedef enum GAME_REGION
+{
+ GAME_REGION_UNKNOWN,
+ GAME_REGION_NTSC,
+ GAME_REGION_PAL,
+} GAME_REGION;
+
+/*!
+* Special game types passed into game_load_game_special(). Only used when
+* multiple ROMs are required.
+*/
+typedef enum SPECIAL_GAME_TYPE
+{
+ SPECIAL_GAME_TYPE_BSX,
+ SPECIAL_GAME_TYPE_BSX_SLOTTED,
+ SPECIAL_GAME_TYPE_SUFAMI_TURBO,
+ SPECIAL_GAME_TYPE_SUPER_GAME_BOY,
+} SPECIAL_GAME_TYPE;
+
+typedef enum GAME_MEMORY
+{
+ /*!
+ * Passed to game_get_memory_data/size(). If the memory type doesn't apply
+ * to the implementation NULL/0 can be returned.
+ */
+ GAME_MEMORY_MASK = 0xff,
+
+ /*!
+ * Regular save ram. This ram is usually found on a game cartridge, backed
+ * up by a battery. If save game data is too complex for a single memory
+ * buffer, the SYSTEM_DIRECTORY environment callback can be used.
+ */
+ GAME_MEMORY_SAVE_RAM = 0,
+
+ /*!
+ * Some games have a built-in clock to keep track of time. This memory is
+ * usually just a couple of bytes to keep track of time.
+ */
+ GAME_MEMORY_RTC = 1,
+
+ /*! System ram lets a frontend peek into a game systems main RAM */
+ GAME_MEMORY_SYSTEM_RAM = 2,
+
+ /*! Video ram lets a frontend peek into a game systems video RAM (VRAM) */
+ GAME_MEMORY_VIDEO_RAM = 3,
+
+ /*! Special memory types */
+ GAME_MEMORY_SNES_BSX_RAM = ((1 << 8) | GAME_MEMORY_SAVE_RAM),
+ GAME_MEMORY_SNES_BSX_PRAM = ((2 << 8) | GAME_MEMORY_SAVE_RAM),
+ GAME_MEMORY_SNES_SUFAMI_TURBO_A_RAM= ((3 << 8) | GAME_MEMORY_SAVE_RAM),
+ GAME_MEMORY_SNES_SUFAMI_TURBO_B_RAM= ((4 << 8) | GAME_MEMORY_SAVE_RAM),
+ GAME_MEMORY_SNES_GAME_BOY_RAM = ((5 << 8) | GAME_MEMORY_SAVE_RAM),
+ GAME_MEMORY_SNES_GAME_BOY_RTC = ((6 << 8) | GAME_MEMORY_RTC),
+} GAME_MEMORY;
+
+/*! ID values for SIMD CPU features */
+typedef enum GAME_SIMD
+{
+ GAME_SIMD_SSE = (1 << 0),
+ GAME_SIMD_SSE2 = (1 << 1),
+ GAME_SIMD_VMX = (1 << 2),
+ GAME_SIMD_VMX128 = (1 << 3),
+ GAME_SIMD_AVX = (1 << 4),
+ GAME_SIMD_NEON = (1 << 5),
+ GAME_SIMD_SSE3 = (1 << 6),
+ GAME_SIMD_SSSE3 = (1 << 7),
+ GAME_SIMD_MMX = (1 << 8),
+ GAME_SIMD_MMXEXT = (1 << 9),
+ GAME_SIMD_SSE4 = (1 << 10),
+ GAME_SIMD_SSE42 = (1 << 11),
+ GAME_SIMD_AVX2 = (1 << 12),
+ GAME_SIMD_VFPU = (1 << 13),
+} GAME_SIMD;
+
+typedef enum GAME_ROTATION
+{
+ GAME_ROTATION_0_CW,
+ GAME_ROTATION_90_CW,
+ GAME_ROTATION_180_CW,
+ GAME_ROTATION_270_CW,
+} GAME_ROTATION;
+
+typedef struct game_controller
+{
+ const char* controller_id;
+ unsigned int digital_button_count;
+ unsigned int analog_button_count;
+ unsigned int analog_stick_count;
+ unsigned int accelerometer_count;
+ unsigned int key_count;
+ unsigned int rel_pointer_count;
+ unsigned int abs_pointer_count;
+ unsigned int motor_count;
+} ATTRIBUTE_PACKED game_controller;
+
+typedef struct game_digital_button_event
+{
+ bool pressed;
+} ATTRIBUTE_PACKED game_digital_button_event;
+
+typedef struct game_analog_button_event
+{
+ float magnitude;
+} ATTRIBUTE_PACKED game_analog_button_event;
+
+typedef struct game_analog_stick_event
+{
+ float x;
+ float y;
+} ATTRIBUTE_PACKED game_analog_stick_event;
+
+typedef struct game_accelerometer_event
+{
+ float x;
+ float y;
+ float z;
+} ATTRIBUTE_PACKED game_accelerometer_event;
+
+typedef struct game_key_event
+{
+ bool pressed;
+ XBMCVKey character;
+ GAME_KEY_MOD modifiers;
+} ATTRIBUTE_PACKED game_key_event;
+
+typedef struct game_rel_pointer_event
+{
+ int x;
+ int y;
+} ATTRIBUTE_PACKED game_rel_pointer_event;
+
+typedef struct game_abs_pointer_event
+{
+ bool pressed;
+ float x;
+ float y;
+} ATTRIBUTE_PACKED game_abs_pointer_event;
+
+typedef struct game_motor_event
+{
+ float magnitude;
+} ATTRIBUTE_PACKED game_motor_event;
+
+typedef struct game_input_event
+{
+ GAME_INPUT_EVENT_SOURCE type;
+ int port;
+ const char* controller_id;
+ const char* feature_name;
+ union
+ {
+ struct game_digital_button_event digital_button;
+ struct game_analog_button_event analog_button;
+ struct game_analog_stick_event analog_stick;
+ struct game_accelerometer_event accelerometer;
+ struct game_key_event key;
+ struct game_rel_pointer_event rel_pointer;
+ struct game_abs_pointer_event abs_pointer;
+ struct game_motor_event motor;
+ };
+} ATTRIBUTE_PACKED game_input_event;
+
+struct game_geometry
+{
+ unsigned base_width; // Nominal video width of game
+ unsigned base_height; // Nominal video height of game
+ unsigned max_width; // Maximum possible width of game
+ unsigned max_height; // Maximum possible height of game
+ float aspect_ratio; // Nominal aspect ratio of game. If aspect_ratio is <= 0.0,
+ // an aspect ratio of base_width / base_height is assumed.
+ // A frontend could override this setting if desired.
+};
+
+struct game_system_timing
+{
+ double fps; // FPS of video content.
+ double sample_rate; // Sampling rate of audio.
+};
+
+struct game_system_av_info
+{
+ struct game_geometry geometry;
+ struct game_system_timing timing;
+};
+
+typedef void (*game_proc_address_t)(void);
+
+struct game_hw_info
+{
+ GAME_HW_CONTEXT_TYPE context_type; // Which API to use. Set by game client
+ bool depth; // Set if render buffers should have depth component attached
+ bool stencil; // Set if stencil buffers should be attached
+ // If depth and stencil are true, a packed 24/8 buffer will be added. Only attaching stencil is invalid and will be ignored
+ bool bottom_left_origin; // Use conventional bottom-left origin convention. Is false, standard top-left origin semantics are used
+ unsigned version_major; // Major version number for core GL context
+ unsigned version_minor; // Minor version number for core GL context
+ bool cache_context; // If this is true, the frontend will go very far to avoid resetting context in scenarios like toggling fullscreen, etc.
+ // The reset callback might still be called in extreme situations such as if the context is lost beyond recovery
+ // For optimal stability, set this to false, and allow context to be reset at any time
+ bool debug_context; // Creates a debug context
+};
+
+/*! Properties passed to the ADDON_Create() method of a game client */
+typedef struct game_client_properties
+{
+ /*!
+ * The path of the game client being loaded.
+ */
+ const char* game_client_dll_path;
+
+ /*!
+ * Paths to proxy DLLs used to load the game client.
+ */
+ const char** proxy_dll_paths;
+
+ /*!
+ * Number of proxy DLL paths provided.
+ */
+ unsigned int proxy_dll_count;
+
+ /*!
+ * The "system" directories of the frontend. These directories can be used to
+ * store system-specific ROMs such as BIOSes, configuration data, etc.
+ */
+ const char** resource_directories;
+
+ /*!
+ * Number of resource directories provided
+ */
+ unsigned int resource_directory_count;
+
+ /*!
+ * The writable directory of the frontend. This directory can be used to store
+ * SRAM, memory cards, high scores, etc, if the game client cannot use the
+ * regular memory interface, GetMemoryData().
+ */
+ const char* profile_directory;
+
+ /*!
+ * The value of the <supports_vfs> property from addon.xml
+ */
+ bool supports_vfs;
+
+ /*!
+ * The extensions in the <extensions> property from addon.xml
+ */
+ const char** extensions;
+
+ /*!
+ * Number of extensions provided
+ */
+ unsigned int extension_count;
+} game_client_properties;
+
+/*! Structure to transfer the methods from kodi_game_dll.h to Kodi */
+typedef struct GameClient
+{
+ const char* (__cdecl* GetGameAPIVersion)(void);
+ const char* (__cdecl* GetMininumGameAPIVersion)(void);
+ GAME_ERROR (__cdecl* LoadGame)(const char*);
+ GAME_ERROR (__cdecl* LoadGameSpecial)(SPECIAL_GAME_TYPE, const char**, size_t);
+ GAME_ERROR (__cdecl* LoadStandalone)(void);
+ GAME_ERROR (__cdecl* UnloadGame)(void);
+ GAME_ERROR (__cdecl* GetGameInfo)(game_system_av_info*);
+ GAME_REGION (__cdecl* GetRegion)(void);
+ bool (__cdecl* RequiresGameLoop)(void);
+ GAME_ERROR (__cdecl* RunFrame)(void);
+ GAME_ERROR (__cdecl* Reset)(void);
+ GAME_ERROR (__cdecl* HwContextReset)(void);
+ GAME_ERROR (__cdecl* HwContextDestroy)(void);
+ void (__cdecl* UpdatePort)(int, bool, const game_controller*);
+ bool (__cdecl* HasFeature)(const char* controller_id, const char* feature_name);
+ bool (__cdecl* InputEvent)(const game_input_event*);
+ size_t (__cdecl* SerializeSize)(void);
+ GAME_ERROR (__cdecl* Serialize)(uint8_t*, size_t);
+ GAME_ERROR (__cdecl* Deserialize)(const uint8_t*, size_t);
+ GAME_ERROR (__cdecl* CheatReset)(void);
+ GAME_ERROR (__cdecl* GetMemory)(GAME_MEMORY, const uint8_t**, size_t*);
+ GAME_ERROR (__cdecl* SetCheat)(unsigned int, bool, const char*);
+} GameClient;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // KODI_GAME_TYPES_H_
diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_game.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_game.h
new file mode 100644
index 0000000000..b5a46dd223
--- /dev/null
+++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_game.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2014-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "libXBMC_addon.h"
+#include "kodi_game_callbacks.h"
+
+#include <string>
+#include <stdio.h>
+
+#if defined(ANDROID)
+ #include <sys/stat.h>
+#endif
+
+#ifdef _WIN32
+ #define GAME_HELPER_DLL "\\library.kodi.game\\libKODI_game" ADDON_HELPER_EXT
+#else
+ #define GAME_HELPER_DLL_NAME "libKODI_game-" ADDON_HELPER_ARCH ADDON_HELPER_EXT
+ #define GAME_HELPER_DLL "/library.kodi.game/" GAME_HELPER_DLL_NAME
+#endif
+
+#define GAME_REGISTER_SYMBOL(dll, functionPtr) \
+ CHelper_libKODI_game::RegisterSymbol(dll, functionPtr, #functionPtr)
+
+class CHelper_libKODI_game
+{
+public:
+ CHelper_libKODI_game(void) :
+ GAME_register_me(nullptr),
+ GAME_unregister_me(nullptr),
+ GAME_close_game(nullptr),
+ GAME_open_pixel_stream(nullptr),
+ GAME_open_video_stream(nullptr),
+ GAME_open_pcm_stream(nullptr),
+ GAME_open_audio_stream(nullptr),
+ GAME_add_stream_data(nullptr),
+ GAME_close_stream(nullptr),
+ GAME_enable_hardware_rendering(nullptr),
+ GAME_hw_get_current_framebuffer(nullptr),
+ GAME_hw_get_proc_address(nullptr),
+ GAME_render_frame(nullptr),
+ GAME_open_port(nullptr),
+ GAME_close_port(nullptr),
+ GAME_input_event(nullptr),
+ m_handle(nullptr),
+ m_callbacks(nullptr),
+ m_libKODI_game(nullptr)
+ {
+ }
+
+ ~CHelper_libKODI_game(void)
+ {
+ if (m_libKODI_game)
+ {
+ GAME_unregister_me(m_handle, m_callbacks);
+ dlclose(m_libKODI_game);
+ }
+ }
+
+ template <typename T>
+ static bool RegisterSymbol(void* dll, T& functionPtr, const char* strFunctionPtr)
+ {
+ return (functionPtr = (T)dlsym(dll, strFunctionPtr)) != NULL;
+ }
+
+ /*!
+ * @brief Resolve all callback methods
+ * @param handle Pointer to the add-on
+ * @return True when all methods were resolved, false otherwise.
+ */
+ bool RegisterMe(void* handle)
+ {
+ m_handle = handle;
+
+ std::string libBasePath;
+ libBasePath = ((cb_array*)m_handle)->libBasePath;
+ libBasePath += GAME_HELPER_DLL;
+
+#if defined(ANDROID)
+ struct stat st;
+ if (stat(libBasePath.c_str(),&st) != 0)
+ {
+ std::string tempbin = getenv("XBMC_ANDROID_LIBS");
+ libBasePath = tempbin + "/" + GAME_HELPER_DLL_NAME;
+ }
+#endif
+
+ m_libKODI_game = dlopen(libBasePath.c_str(), RTLD_LAZY);
+ if (m_libKODI_game == NULL)
+ {
+ fprintf(stderr, "Unable to load %s\n", dlerror());
+ return false;
+ }
+
+ try
+ {
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_register_me)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_unregister_me)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_close_game)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_open_pixel_stream)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_open_video_stream)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_open_pcm_stream)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_open_audio_stream)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_add_stream_data)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_close_stream)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_enable_hardware_rendering)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_hw_get_current_framebuffer)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_hw_get_proc_address)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_render_frame)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_open_port)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_close_port)) throw false;
+ if (!GAME_REGISTER_SYMBOL(m_libKODI_game, GAME_input_event)) throw false;
+ }
+ catch (const bool& bSuccess)
+ {
+ fprintf(stderr, "ERROR: Unable to assign function %s\n", dlerror());
+ return bSuccess;
+ }
+
+ m_callbacks = GAME_register_me(m_handle);
+ return m_callbacks != NULL;
+ }
+
+ void CloseGame(void)
+ {
+ return GAME_close_game(m_handle, m_callbacks);
+ }
+
+ bool OpenPixelStream(GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation)
+ {
+ return GAME_open_pixel_stream(m_handle, m_callbacks, format, width, height, rotation) == 0;
+ }
+
+ bool OpenVideoStream(GAME_VIDEO_CODEC codec)
+ {
+ return GAME_open_video_stream(m_handle, m_callbacks, codec) == 0;
+ }
+
+ bool OpenPCMStream(GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channel_map)
+ {
+ return GAME_open_pcm_stream(m_handle, m_callbacks, format, channel_map) == 0;
+ }
+
+ bool OpenAudioStream(GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channel_map)
+ {
+ return GAME_open_audio_stream(m_handle, m_callbacks, codec, channel_map) == 0;
+ }
+
+ void AddStreamData(GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size)
+ {
+ GAME_add_stream_data(m_handle, m_callbacks, stream, data, size);
+ }
+
+ void CloseStream(GAME_STREAM_TYPE stream)
+ {
+ GAME_close_stream(m_handle, m_callbacks, stream);
+ }
+
+ void EnableHardwareRendering(const struct game_hw_info* hw_info)
+ {
+ return GAME_enable_hardware_rendering(m_handle, m_callbacks, hw_info);
+ }
+
+ uintptr_t HwGetCurrentFramebuffer(void)
+ {
+ return GAME_hw_get_current_framebuffer(m_handle, m_callbacks);
+ }
+
+ game_proc_address_t HwGetProcAddress(const char* sym)
+ {
+ return GAME_hw_get_proc_address(m_handle, m_callbacks, sym);
+ }
+
+ void RenderFrame()
+ {
+ return GAME_render_frame(m_handle, m_callbacks);
+ }
+
+ bool OpenPort(unsigned int port)
+ {
+ return GAME_open_port(m_handle, m_callbacks, port);
+ }
+
+ void ClosePort(unsigned int port)
+ {
+ return GAME_close_port(m_handle, m_callbacks, port);
+ }
+
+ bool InputEvent(const game_input_event& event)
+ {
+ return GAME_input_event(m_handle, m_callbacks, &event);
+ }
+
+protected:
+ CB_GameLib* (*GAME_register_me)(void* handle);
+ void (*GAME_unregister_me)(void* handle, CB_GameLib* cb);
+ void (*GAME_close_game)(void* handle, CB_GameLib* cb);
+ int (*GAME_open_pixel_stream)(void* handle, CB_GameLib* cb, GAME_PIXEL_FORMAT, unsigned int, unsigned int, GAME_VIDEO_ROTATION);
+ int (*GAME_open_video_stream)(void* handle, CB_GameLib* cb, GAME_VIDEO_CODEC);
+ int (*GAME_open_pcm_stream)(void* handle, CB_GameLib* cb, GAME_PCM_FORMAT, const GAME_AUDIO_CHANNEL*);
+ int (*GAME_open_audio_stream)(void* handle, CB_GameLib* cb, GAME_AUDIO_CODEC, const GAME_AUDIO_CHANNEL*);
+ int (*GAME_add_stream_data)(void* handle, CB_GameLib* cb, GAME_STREAM_TYPE, const uint8_t*, unsigned int);
+ int (*GAME_close_stream)(void* handle, CB_GameLib* cb, GAME_STREAM_TYPE);
+ void (*GAME_enable_hardware_rendering)(void* handle, CB_GameLib* cb, const struct game_hw_info*);
+ uintptr_t (*GAME_hw_get_current_framebuffer)(void* handle, CB_GameLib* cb);
+ game_proc_address_t (*GAME_hw_get_proc_address)(void* handle, CB_GameLib* cb, const char*);
+ void (*GAME_render_frame)(void* handle, CB_GameLib* cb);
+ bool (*GAME_open_port)(void* handle, CB_GameLib* cb, unsigned int);
+ void (*GAME_close_port)(void* handle, CB_GameLib* cb, unsigned int);
+ bool (*GAME_input_event)(void* handle, CB_GameLib* cb, const game_input_event* event);
+
+private:
+ void* m_handle;
+ CB_GameLib* m_callbacks;
+ void* m_libKODI_game;
+
+ struct cb_array
+ {
+ const char* libBasePath;
+ };
+};
diff --git a/xbmc/filesystem/AddonsDirectory.cpp b/xbmc/filesystem/AddonsDirectory.cpp
index a89cc89dd3..d150935f64 100644
--- a/xbmc/filesystem/AddonsDirectory.cpp
+++ b/xbmc/filesystem/AddonsDirectory.cpp
@@ -28,11 +28,15 @@
#include "interfaces/generic/ScriptInvocationManager.h"
#include "FileItem.h"
#include "addons/AddonInstaller.h"
+#include "addons/BinaryAddonCache.h"
#include "addons/PluginSource.h"
#include "addons/RepositoryUpdater.h"
#include "dialogs/GUIDialogOK.h"
+#include "games/addons/GameClient.h"
+#include "games/GameUtils.h"
#include "guilib/TextureManager.h"
#include "File.h"
+#include "ServiceBroker.h"
#include "settings/Settings.h"
#include "SpecialProtocol.h"
#include "utils/URIUtils.h"
@@ -51,6 +55,11 @@ CAddonsDirectory::~CAddonsDirectory(void) {}
const auto CATEGORY_INFO_PROVIDERS = "category.infoproviders";
const auto CATEGORY_LOOK_AND_FEEL = "category.lookandfeel";
const auto CATEGORY_GAME_ADDONS = "category.gameaddons";
+const auto CATEGORY_EMULATORS = "category.emulators";
+const auto CATEGORY_STANDALONE_GAMES = "category.standalonegames";
+const auto CATEGORY_GAME_PROVIDERS = "category.gameproviders";
+const auto CATEGORY_GAME_RESOURCES = "category.gameresources";
+const auto CATEGORY_GAME_SUPPORT_ADDONS = "category.gamesupport";
const std::set<TYPE> dependencyTypes = {
ADDON_SCRAPER_LIBRARY,
@@ -77,6 +86,9 @@ const std::set<TYPE> lookAndFeelTypes = {
const std::set<TYPE> gameTypes = {
ADDON_GAME_CONTROLLER,
+ ADDON_GAMEDLL,
+ ADDON_GAME,
+ ADDON_RESOURCE_GAMES,
};
static bool IsInfoProviderType(TYPE type)
@@ -104,9 +116,40 @@ static bool IsGameType(TYPE type)
return gameTypes.find(type) != gameTypes.end();
}
+static bool IsStandaloneGame(const AddonPtr& addon)
+{
+ return GAME::CGameUtils::IsStandaloneGame(addon);
+}
+
+static bool IsEmulator(const AddonPtr& addon)
+{
+ return addon->Type() == ADDON_GAMEDLL && std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsPath();
+}
+
+static bool IsGameProvider(const AddonPtr& addon)
+{
+ return addon->Type() == ADDON_PLUGIN && addon->IsType(ADDON_GAME);
+}
+
+static bool IsGameResource(const AddonPtr& addon)
+{
+ return addon->Type() == ADDON_RESOURCE_GAMES;
+}
+
+static bool IsGameSupportAddon(const AddonPtr& addon)
+{
+ return addon->Type() == ADDON_GAMEDLL &&
+ !std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsPath() &&
+ !std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsStandalone();
+}
+
static bool IsGameAddon(const AddonPtr& addon)
{
- return IsGameType(addon->Type());
+ return IsGameType(addon->Type()) ||
+ IsStandaloneGame(addon) ||
+ IsGameProvider(addon) ||
+ IsGameResource(addon) ||
+ IsGameSupportAddon(addon);
}
static bool IsDependecyType(TYPE type)
@@ -161,6 +204,113 @@ static void GenerateTypeListing(const CURL& path, const std::set<TYPE>& types,
}
}
+// Creates categories for game add-ons, if we have any game add-ons
+static void GenerateGameListing(const CURL& path, const VECADDONS& addons, CFileItemList& items)
+{
+ // Game controllers
+ for (const auto& addon : addons)
+ {
+ if (addon->Type() == ADDON_GAME_CONTROLLER)
+ {
+ CFileItemPtr item(new CFileItem(TranslateType(ADDON_GAME_CONTROLLER, true)));
+ CURL itemPath = path;
+ itemPath.SetFileName(TranslateType(ADDON_GAME_CONTROLLER, false));
+ item->SetPath(itemPath.Get());
+ item->m_bIsFolder = true;
+ std::string thumb = GetIcon(ADDON_GAME_CONTROLLER);
+ if (!thumb.empty() && g_TextureManager.HasTexture(thumb))
+ item->SetArt("thumb", thumb);
+ items.Add(item);
+ break;
+ }
+ }
+ // Emulators
+ for (const auto& addon : addons)
+ {
+ if (IsEmulator(addon))
+ {
+ CFileItemPtr item(new CFileItem(g_localizeStrings.Get(35207))); // Emulators
+ CURL itemPath = path;
+ itemPath.SetFileName(CATEGORY_EMULATORS);
+ item->SetPath(itemPath.Get());
+ item->m_bIsFolder = true;
+ std::string thumb = GetIcon(ADDON_GAMEDLL);
+ if (!thumb.empty() && g_TextureManager.HasTexture(thumb))
+ item->SetArt("thumb", thumb);
+ items.Add(item);
+ break;
+ }
+ }
+ // Standalone games
+ for (const auto& addon : addons)
+ {
+ if (IsStandaloneGame(addon))
+ {
+ CFileItemPtr item(new CFileItem(g_localizeStrings.Get(35208))); // Standalone games
+ CURL itemPath = path;
+ itemPath.SetFileName(CATEGORY_STANDALONE_GAMES);
+ item->SetPath(itemPath.Get());
+ item->m_bIsFolder = true;
+ std::string thumb = GetIcon(ADDON_GAMEDLL);
+ if (!thumb.empty() && g_TextureManager.HasTexture(thumb))
+ item->SetArt("thumb", thumb);
+ items.Add(item);
+ break;
+ }
+ }
+ // Game providers
+ for (const auto& addon : addons)
+ {
+ if (IsGameProvider(addon))
+ {
+ CFileItemPtr item(new CFileItem(g_localizeStrings.Get(35220))); // Game providers
+ CURL itemPath = path;
+ itemPath.SetFileName(CATEGORY_GAME_PROVIDERS);
+ item->SetPath(itemPath.Get());
+ item->m_bIsFolder = true;
+ std::string thumb = GetIcon(ADDON_GAMEDLL);
+ if (!thumb.empty() && g_TextureManager.HasTexture(thumb))
+ item->SetArt("thumb", thumb);
+ items.Add(item);
+ break;
+ }
+ }
+ // Game resources
+ for (const auto& addon : addons)
+ {
+ if (IsGameResource(addon))
+ {
+ CFileItemPtr item(new CFileItem(g_localizeStrings.Get(35209))); // Game resources
+ CURL itemPath = path;
+ itemPath.SetFileName(CATEGORY_GAME_RESOURCES);
+ item->SetPath(itemPath.Get());
+ item->m_bIsFolder = true;
+ std::string thumb = GetIcon(ADDON_GAMEDLL);
+ if (!thumb.empty() && g_TextureManager.HasTexture(thumb))
+ item->SetArt("thumb", thumb);
+ items.Add(item);
+ break;
+ }
+ }
+ // Game support add-ons
+ for (const auto& addon : addons)
+ {
+ if (IsGameSupportAddon(addon))
+ {
+ CFileItemPtr item(new CFileItem(g_localizeStrings.Get(35216))); // Support add-ons
+ CURL itemPath = path;
+ itemPath.SetFileName(CATEGORY_GAME_SUPPORT_ADDONS);
+ item->SetPath(itemPath.Get());
+ item->m_bIsFolder = true;
+ std::string thumb = GetIcon(ADDON_GAMEDLL);
+ if (!thumb.empty() && g_TextureManager.HasTexture(thumb))
+ item->SetArt("thumb", thumb);
+ items.Add(item);
+ break;
+ }
+ }
+}
+
//Creates the top-level category list
static void GenerateMainCategoryListing(const CURL& path, const VECADDONS& addons,
CFileItemList& items)
@@ -187,10 +337,10 @@ static void GenerateMainCategoryListing(const CURL& path, const VECADDONS& addon
}
if (std::any_of(addons.begin(), addons.end(), IsGameAddon))
{
- CFileItemPtr item(new CFileItem(g_localizeStrings.Get(35049))); // Game add-ons
+ CFileItemPtr item(new CFileItem(TranslateType(ADDON_GAME, true)));
item->SetPath(URIUtils::AddFileToFolder(path.Get(), CATEGORY_GAME_ADDONS));
item->m_bIsFolder = true;
- const std::string thumb = "DefaultGameAddons.png";
+ const std::string thumb = GetIcon(ADDON_GAME);
if (g_TextureManager.HasTexture(thumb))
item->SetArt("thumb", thumb);
items.Add(item);
@@ -200,7 +350,7 @@ static void GenerateMainCategoryListing(const CURL& path, const VECADDONS& addon
for (unsigned int i = ADDON_UNKNOWN + 1; i < ADDON_MAX - 1; ++i)
{
const TYPE type = (TYPE)i;
- if (!IsInfoProviderType(type) && !IsLookAndFeelType(type) && !IsDependecyType(type))
+ if (!IsInfoProviderType(type) && !IsLookAndFeelType(type) && !IsDependecyType(type) && !IsGameType(type))
uncategorized.insert(static_cast<TYPE>(i));
}
GenerateTypeListing(path, uncategorized, addons, items);
@@ -225,9 +375,44 @@ static void GenerateCategoryListing(const CURL& path, VECADDONS& addons,
}
else if (category == CATEGORY_GAME_ADDONS)
{
- items.SetProperty("addoncategory", g_localizeStrings.Get(35049)); // Game add-ons
- items.SetLabel(g_localizeStrings.Get(35049)); // Game add-ons
- GenerateTypeListing(path, gameTypes, addons, items);
+ items.SetProperty("addoncategory", TranslateType(ADDON_GAME, true));
+ items.SetLabel(TranslateType(ADDON_GAME, true));
+ GenerateGameListing(path, addons, items);
+ }
+ else if (category == CATEGORY_EMULATORS)
+ {
+ items.SetProperty("addoncategory", g_localizeStrings.Get(35207)); // Emulators
+ addons.erase(std::remove_if(addons.begin(), addons.end(),
+ [](const AddonPtr& addon){ return !IsEmulator(addon); }), addons.end());
+ CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(35207)); // Emulators
+ }
+ else if (category == CATEGORY_STANDALONE_GAMES)
+ {
+ items.SetProperty("addoncategory", g_localizeStrings.Get(35208)); // Standalone games
+ addons.erase(std::remove_if(addons.begin(), addons.end(),
+ [](const AddonPtr& addon){ return !IsStandaloneGame(addon); }), addons.end());
+ CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(35208)); // Standalone games
+ }
+ else if (category == CATEGORY_GAME_PROVIDERS)
+ {
+ items.SetProperty("addoncategory", g_localizeStrings.Get(35220)); // Game providers
+ addons.erase(std::remove_if(addons.begin(), addons.end(),
+ [](const AddonPtr& addon){ return !IsGameProvider(addon); }), addons.end());
+ CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(35220)); // Game providers
+ }
+ else if (category == CATEGORY_GAME_RESOURCES)
+ {
+ items.SetProperty("addoncategory", g_localizeStrings.Get(35209)); // Game resources
+ addons.erase(std::remove_if(addons.begin(), addons.end(),
+ [](const AddonPtr& addon){ return !IsGameResource(addon); }), addons.end());
+ CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(35209)); // Game resources
+ }
+ else if (category == CATEGORY_GAME_SUPPORT_ADDONS)
+ {
+ items.SetProperty("addoncategory", g_localizeStrings.Get(35216)); // Support add-ons
+ addons.erase(std::remove_if(addons.begin(), addons.end(),
+ [](const AddonPtr& addon) { return !IsGameSupportAddon(addon); }), addons.end());
+ CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(35216)); // Support add-ons
}
else
{ // fallback to addon type
@@ -572,6 +757,8 @@ bool CAddonsDirectory::GetDirectory(const CURL& url, CFileItemList &items)
std::string type = path.GetFileName();
if (type == "video" || type == "audio" || type == "image" || type == "executable")
return Browse(CURL("addons://all/xbmc.addon." + type), items);
+ else if (type == "game")
+ return Browse(CURL("addons://all/category.gameaddons"), items);
return false;
}
else
@@ -681,6 +868,18 @@ bool CAddonsDirectory::GetScriptsAndPlugins(const std::string &content, VECADDON
if (plugin && plugin->Provides(type))
addons.push_back(tempAddons[i]);
}
+ tempAddons.clear();
+
+ if (type == CPluginSource::GAME)
+ {
+ CAddonMgr::GetInstance().GetAddons(tempAddons, ADDON_GAMEDLL);
+ for (auto& addon : tempAddons)
+ {
+ if (IsStandaloneGame(addon))
+ addons.push_back(addon);
+ }
+ }
+
return true;
}
@@ -715,6 +914,12 @@ bool CAddonsDirectory::GetScriptsAndPlugins(const std::string &content, CFileIte
path = "script://" + addon->ID();
break;
}
+ case ADDON_GAMEDLL:
+ {
+ // Kodi fails to launch games with empty path from home screen
+ path = "game://" + addon->ID();
+ break;
+ }
default:
break;
}
diff --git a/xbmc/games/CMakeLists.txt b/xbmc/games/CMakeLists.txt
index b65d7f905e..759f0987b0 100644
--- a/xbmc/games/CMakeLists.txt
+++ b/xbmc/games/CMakeLists.txt
@@ -1,5 +1,8 @@
-set(SOURCES GameSettings.cpp)
+set(SOURCES GameSettings.cpp
+ GameUtils.cpp)
-set(HEADERS GameSettings.h)
+set(HEADERS GameSettings.h
+ GameTypes.h
+ GameUtils.h)
core_add_library(games)
diff --git a/xbmc/games/GameSettings.cpp b/xbmc/games/GameSettings.cpp
index 0396870f27..a72878249f 100644
--- a/xbmc/games/GameSettings.cpp
+++ b/xbmc/games/GameSettings.cpp
@@ -48,6 +48,12 @@ void CGameSettings::OnSettingChanged(const CSetting *setting)
{
PERIPHERALS::g_peripherals.TriggerDeviceScan(PERIPHERALS::PERIPHERAL_BUS_APPLICATION);
}
+ else if (settingId == CSettings::SETTING_GAMES_ENABLEREWIND ||
+ settingId == CSettings::SETTING_GAMES_REWINDTIME)
+ {
+ SetChanged();
+ NotifyObservers(ObservableMessageSettingsChanged);
+ }
}
void CGameSettings::OnSettingAction(const CSetting *setting)
diff --git a/xbmc/games/GameSettings.h b/xbmc/games/GameSettings.h
index f805006010..753b8f1f13 100644
--- a/xbmc/games/GameSettings.h
+++ b/xbmc/games/GameSettings.h
@@ -20,13 +20,15 @@
#pragma once
#include "settings/lib/ISettingCallback.h"
+#include "utils/Observer.h"
class CSetting;
namespace GAME
{
-class CGameSettings : public ISettingCallback
+class CGameSettings : public ISettingCallback,
+ public Observable
{
public:
static CGameSettings& GetInstance();
diff --git a/xbmc/games/GameTypes.h b/xbmc/games/GameTypes.h
new file mode 100644
index 0000000000..0421908260
--- /dev/null
+++ b/xbmc/games/GameTypes.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include <memory>
+#include <vector>
+
+namespace GAME
+{
+
+ class CGameClient;
+ typedef std::shared_ptr<CGameClient> GameClientPtr;
+ typedef std::vector<GameClientPtr> GameClientVector;
+
+}
diff --git a/xbmc/games/GameUtils.cpp b/xbmc/games/GameUtils.cpp
new file mode 100644
index 0000000000..2226336b0c
--- /dev/null
+++ b/xbmc/games/GameUtils.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GameUtils.h"
+#include "addons/Addon.h"
+#include "addons/AddonManager.h"
+#include "addons/BinaryAddonCache.h"
+#include "dialogs/GUIDialogOK.h"
+#include "games/addons/GameClient.h"
+#include "games/dialogs/GUIDialogSelectGameClient.h"
+#include "games/tags/GameInfoTag.h"
+#include "filesystem/SpecialProtocol.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+
+#include <algorithm>
+
+using namespace GAME;
+
+GameClientPtr CGameUtils::OpenGameClient(const CFileItem& file)
+{
+ using namespace ADDON;
+
+ GameClientPtr gameClient;
+
+ // Get the game client ID from the game info tag
+ std::string gameClientId;
+ if (file.HasGameInfoTag())
+ gameClientId = file.GetGameInfoTag()->GetGameClient();
+
+ // If the fileitem is an add-on, fall back to that
+ if (gameClientId.empty())
+ {
+ if (file.HasAddonInfo() && file.GetAddonInfo()->Type() == ADDON::ADDON_GAMEDLL)
+ gameClientId = file.GetAddonInfo()->ID();
+ }
+
+ // Get game client by ID
+ if (!gameClientId.empty())
+ {
+ CBinaryAddonCache& addonCache = CServiceBroker::GetBinaryAddonCache();
+ AddonPtr addon = addonCache.GetAddonInstance(gameClientId, ADDON_GAMEDLL);
+ gameClient = std::static_pointer_cast<GAME::CGameClient>(addon);
+ }
+
+ // Need to prompt the user if no game client was found
+ if (!gameClient)
+ {
+ GameClientVector candidates;
+ GameClientVector installable;
+ bool bHasVfsGameClient;
+ GetGameClients(file, candidates, installable, bHasVfsGameClient);
+
+ if (candidates.empty() && installable.empty())
+ {
+ int errorTextId = bHasVfsGameClient ?
+ 35214 : // "This game can only be played directly from a hard drive or partition. Compressed files must be extracted."
+ 35212; // "This game isn't compatible with any available emulators."
+
+ // "Failed to play game"
+ CGUIDialogOK::ShowAndGetInput(CVariant{ 35210 }, CVariant{ errorTextId });
+ }
+ else if (candidates.size() == 1 && installable.empty())
+ {
+ // Only 1 option, avoid prompting the user
+ gameClient = candidates[0];
+ }
+ else
+ {
+ CGUIDialogSelectGameClient::ShowAndGetGameClient(candidates, installable, gameClient);
+ }
+ }
+
+ return gameClient;
+}
+
+void CGameUtils::GetGameClients(const CFileItem& file, GameClientVector& candidates, GameClientVector& installable, bool& bHasVfsGameClient)
+{
+ using namespace ADDON;
+
+ bHasVfsGameClient = false;
+
+ // Try to resolve path to a local file, as not all game clients support VFS
+ CURL translatedUrl(CSpecialProtocol::TranslatePath(file.GetPath()));
+
+ // Get local candidates
+ VECADDONS localAddons;
+ CBinaryAddonCache& addonCache = CServiceBroker::GetBinaryAddonCache();
+ addonCache.GetAddons(localAddons, ADDON_GAMEDLL);
+
+ bool bVfs = false;
+ GetGameClients(localAddons, translatedUrl, candidates, bVfs);
+ bHasVfsGameClient |= bVfs;
+
+ // Get remote candidates
+ VECADDONS remoteAddons;
+ if (CAddonMgr::GetInstance().GetInstallableAddons(remoteAddons, ADDON_GAMEDLL))
+ {
+ GetGameClients(remoteAddons, translatedUrl, installable, bVfs);
+ bHasVfsGameClient |= bVfs;
+ }
+
+ // Sort by name
+ //! @todo Move to presentation code
+ auto SortByName = [](const GameClientPtr& lhs, const GameClientPtr& rhs)
+ {
+ std::string lhsName = lhs->Name();
+ std::string rhsName = rhs->Name();
+
+ StringUtils::ToLower(lhsName);
+ StringUtils::ToLower(rhsName);
+
+ return lhsName < rhsName;
+ };
+
+ std::sort(candidates.begin(), candidates.end(), SortByName);
+ std::sort(installable.begin(), installable.end(), SortByName);
+}
+
+void CGameUtils::GetGameClients(const ADDON::VECADDONS& addons, const CURL& translatedUrl, GameClientVector& candidates, bool& bHasVfsGameClient)
+{
+ bHasVfsGameClient = false;
+
+ const std::string extension = URIUtils::GetExtension(translatedUrl.Get());
+
+ const bool bIsLocalFile = (translatedUrl.GetProtocol() == "file" ||
+ translatedUrl.GetProtocol().empty());
+
+ for (auto& addon : addons)
+ {
+ GameClientPtr gameClient = std::static_pointer_cast<CGameClient>(addon);
+
+ // Filter by extension
+ if (!gameClient->IsExtensionValid(extension))
+ continue;
+
+ // Filter by VFS
+ if (!bIsLocalFile && !gameClient->SupportsVFS())
+ {
+ bHasVfsGameClient = true;
+ continue;
+ }
+
+ candidates.push_back(gameClient);
+ }
+}
+
+bool CGameUtils::HasGameExtension(const std::string &path)
+{
+ using namespace ADDON;
+
+ // Get filename from CURL so that top-level zip directories will become
+ // normal paths:
+ //
+ // zip://%2Fpath_to_zip_file.zip/ -> /path_to_zip_file.zip
+ //
+ std::string filename = CURL(path).GetFileNameWithoutPath();
+
+ // Get the file extension
+ std::string extension = URIUtils::GetExtension(filename);
+ if (extension.empty())
+ return false;
+
+ StringUtils::ToLower(extension);
+
+ // Look for a game client that supports this extension
+ VECADDONS gameClients;
+ CBinaryAddonCache& addonCache = CServiceBroker::GetBinaryAddonCache();
+ addonCache.GetAddons(gameClients, ADDON_GAMEDLL);
+ for (auto& gameClient : gameClients)
+ {
+ GameClientPtr gc(std::static_pointer_cast<CGameClient>(gameClient));
+ if (gc->IsExtensionValid(extension))
+ return true;
+ }
+
+ // Check remote add-ons
+ gameClients.clear();
+ if (CAddonMgr::GetInstance().GetInstallableAddons(gameClients, ADDON_GAMEDLL))
+ {
+ for (auto& gameClient : gameClients)
+ {
+ GameClientPtr gc(std::static_pointer_cast<CGameClient>(gameClient));
+ if (gc->IsExtensionValid(extension))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::set<std::string> CGameUtils::GetGameExtensions()
+{
+ using namespace ADDON;
+
+ std::set<std::string> extensions;
+
+ VECADDONS gameClients;
+ CBinaryAddonCache& addonCache = CServiceBroker::GetBinaryAddonCache();
+ addonCache.GetAddons(gameClients, ADDON_GAMEDLL);
+ for (auto& gameClient : gameClients)
+ {
+ GameClientPtr gc(std::static_pointer_cast<CGameClient>(gameClient));
+ extensions.insert(gc->GetExtensions().begin(), gc->GetExtensions().end());
+ }
+
+ // Check remote add-ons
+ gameClients.clear();
+ if (CAddonMgr::GetInstance().GetInstallableAddons(gameClients, ADDON_GAMEDLL))
+ {
+ for (auto& gameClient : gameClients)
+ {
+ GameClientPtr gc(std::static_pointer_cast<CGameClient>(gameClient));
+ extensions.insert(gc->GetExtensions().begin(), gc->GetExtensions().end());
+ }
+ }
+
+ return extensions;
+}
+
+bool CGameUtils::IsStandaloneGame(const ADDON::AddonPtr& addon)
+{
+ using namespace ADDON;
+
+ switch (addon->Type())
+ {
+ case ADDON_GAMEDLL:
+ {
+ return std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsStandalone();
+ }
+ case ADDON_SCRIPT:
+ {
+ return addon->IsType(ADDON_GAME);
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
diff --git a/xbmc/games/GameUtils.h b/xbmc/games/GameUtils.h
new file mode 100644
index 0000000000..7c3ac9cd92
--- /dev/null
+++ b/xbmc/games/GameUtils.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "GameTypes.h"
+#include "addons/Addon.h"
+#include "addons/IAddon.h"
+
+#include <set>
+#include <string>
+
+class CFileItem;
+class CURL;
+
+namespace GAME
+{
+ /*!
+ * \ingroup games
+ * \brief Game related utilities.
+ */
+ class CGameUtils
+ {
+ public:
+ /*!
+ * \brief Select a game client, possibly via prompt, for the given game
+ *
+ * \param file The game being played
+ *
+ * \return A game client ready to be initialized for playback
+ */
+ static GameClientPtr OpenGameClient(const CFileItem& file);
+
+ /*!
+ * \brief Check if the file extension is supported by an add-on in
+ * a local or remote repository
+ *
+ * \param path The path of the game file
+ *
+ * \return true if the path's extension is supported by a known game client
+ */
+ static bool HasGameExtension(const std::string& path);
+
+ static std::set<std::string> GetGameExtensions();
+
+ /*!
+ * \brief Check if game script or game add-on can be launched directly
+ *
+ * \return true if the add-on can be launched, false otherwise
+ */
+ static bool IsStandaloneGame(const ADDON::AddonPtr& addon);
+
+ private:
+ static void GetGameClients(const CFileItem& file, GameClientVector& candidates, GameClientVector& installable, bool& bHasVfsGameClient);
+ static void GetGameClients(const ADDON::VECADDONS& addons, const CURL& translatedUrl, GameClientVector& candidates, bool& bHasVfsGameClient);
+ };
+} // namespace GAME
diff --git a/xbmc/games/Makefile b/xbmc/games/Makefile
index 14bb7c1a32..2aad479598 100644
--- a/xbmc/games/Makefile
+++ b/xbmc/games/Makefile
@@ -1,4 +1,5 @@
SRCS=GameSettings.cpp \
+ GameUtils.cpp \
LIB=games.a
diff --git a/xbmc/games/addons/CMakeLists.txt b/xbmc/games/addons/CMakeLists.txt
new file mode 100644
index 0000000000..8a89ec63c6
--- /dev/null
+++ b/xbmc/games/addons/CMakeLists.txt
@@ -0,0 +1,18 @@
+set(SOURCES GameClient.cpp
+ GameClientInput.cpp
+ GameClientKeyboard.cpp
+ GameClientMouse.cpp
+ GameClientProperties.cpp
+ GameClientTiming.cpp
+ GameClientTranslator.cpp)
+
+set(HEADERS GameClient.h
+ GameClientCallbacks.h
+ GameClientInput.h
+ GameClientKeyboard.h
+ GameClientMouse.h
+ GameClientProperties.h
+ GameClientTiming.h
+ GameClientTranslator.h)
+
+core_add_library(gameaddons)
diff --git a/xbmc/games/addons/GameClient.cpp b/xbmc/games/addons/GameClient.cpp
new file mode 100644
index 0000000000..0c6f04e42d
--- /dev/null
+++ b/xbmc/games/addons/GameClient.cpp
@@ -0,0 +1,918 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GameClient.h"
+#include "GameClientCallbacks.h"
+#include "GameClientInput.h"
+#include "GameClientKeyboard.h"
+#include "GameClientMouse.h"
+#include "GameClientTranslator.h"
+#include "addons/AddonManager.h"
+#include "addons/BinaryAddonCache.h"
+#include "cores/AudioEngine/Utils/AEChannelInfo.h"
+#include "dialogs/GUIDialogOK.h"
+#include "filesystem/Directory.h"
+#include "filesystem/SpecialProtocol.h"
+#include "games/addons/playback/GameClientRealtimePlayback.h"
+#include "games/addons/playback/GameClientReversiblePlayback.h"
+#include "games/controllers/Controller.h"
+#include "games/ports/PortManager.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/WindowIDs.h"
+#include "input/joysticks/DefaultJoystick.h" // for DEFAULT_CONTROLLER_ID
+#include "input/joysticks/JoystickTypes.h"
+#include "peripherals/Peripherals.h"
+#include "profiles/ProfilesManager.h"
+#include "settings/Settings.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "Application.h"
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+
+#include <algorithm>
+#include <cstring>
+#include <iterator>
+#include <utility>
+
+using namespace GAME;
+
+#define EXTENSION_SEPARATOR "|"
+#define EXTENSION_WILDCARD "*"
+
+#define GAME_PROPERTY_EXTENSIONS "extensions"
+#define GAME_PROPERTY_SUPPORTS_VFS "supports_vfs"
+#define GAME_PROPERTY_SUPPORTS_STANDALONE "supports_standalone"
+#define GAME_PROPERTY_SUPPORTS_KEYBOARD "supports_keyboard"
+#define GAME_PROPERTY_SUPPORTS_MOUSE "supports_mouse"
+
+#define INPUT_SCAN_RATE 125 // Hz
+
+// --- NormalizeExtension ------------------------------------------------------
+
+namespace
+{
+ /*
+ * \brief Convert to lower case and canonicalize with a leading "."
+ */
+ std::string NormalizeExtension(const std::string& strExtension)
+ {
+ std::string ext = strExtension;
+
+ if (!ext.empty() && ext != EXTENSION_WILDCARD)
+ {
+ StringUtils::ToLower(ext);
+
+ if (ext[0] != '.')
+ ext.insert(0, ".");
+ }
+
+ return ext;
+ }
+}
+
+// --- CGameClient -------------------------------------------------------------
+
+std::unique_ptr<CGameClient> CGameClient::FromExtension(ADDON::AddonProps props, const cp_extension_t* ext)
+{
+ using namespace ADDON;
+
+ static const std::vector<std::string> properties = {
+ GAME_PROPERTY_EXTENSIONS,
+ GAME_PROPERTY_SUPPORTS_VFS,
+ GAME_PROPERTY_SUPPORTS_STANDALONE,
+ GAME_PROPERTY_SUPPORTS_KEYBOARD,
+ GAME_PROPERTY_SUPPORTS_MOUSE,
+ };
+
+ for (const auto& property : properties)
+ {
+ std::string strProperty = CAddonMgr::GetInstance().GetExtValue(ext->configuration, property.c_str());
+ if (!strProperty.empty())
+ props.extrainfo[property] = strProperty;
+ }
+
+ return std::unique_ptr<CGameClient>(new CGameClient(std::move(props)));
+}
+
+CGameClient::CGameClient(ADDON::AddonProps props) :
+ CAddonDll<DllGameClient, GameClient, game_client_properties>(std::move(props)),
+ m_apiVersion("0.0.0"),
+ m_libraryProps(this, m_pInfo),
+ m_bSupportsVFS(false),
+ m_bSupportsStandalone(false),
+ m_bSupportsKeyboard(false),
+ m_bSupportsMouse(false),
+ m_bSupportsAllExtensions(false),
+ m_bIsPlaying(false),
+ m_serializeSize(0),
+ m_audio(nullptr),
+ m_video(nullptr),
+ m_region(GAME_REGION_UNKNOWN)
+{
+ const ADDON::InfoMap& extraInfo = m_props.extrainfo;
+ ADDON::InfoMap::const_iterator it;
+
+ it = extraInfo.find(GAME_PROPERTY_EXTENSIONS);
+ if (it != extraInfo.end())
+ {
+ std::vector<std::string> extensions = StringUtils::Split(it->second, EXTENSION_SEPARATOR);
+ std::transform(extensions.begin(), extensions.end(),
+ std::inserter(m_extensions, m_extensions.begin()), NormalizeExtension);
+
+ // Check for wildcard extension
+ if (m_extensions.find(EXTENSION_WILDCARD) != m_extensions.end())
+ {
+ m_bSupportsAllExtensions = true;
+ m_extensions.clear();
+ }
+ }
+
+ it = extraInfo.find(GAME_PROPERTY_SUPPORTS_VFS);
+ if (it != extraInfo.end())
+ m_bSupportsVFS = (it->second == "true");
+
+ it = extraInfo.find(GAME_PROPERTY_SUPPORTS_STANDALONE);
+ if (it != extraInfo.end())
+ m_bSupportsStandalone = (it->second == "true");
+
+ it = extraInfo.find(GAME_PROPERTY_SUPPORTS_KEYBOARD);
+ if (it != extraInfo.end())
+ m_bSupportsKeyboard = (it->second == "true");
+
+ it = extraInfo.find(GAME_PROPERTY_SUPPORTS_MOUSE);
+ if (it != extraInfo.end())
+ m_bSupportsMouse = (it->second == "true");
+
+ ResetPlayback();
+}
+
+CGameClient::~CGameClient(void)
+{
+}
+
+std::string CGameClient::LibPath() const
+{
+ // If the game client requires a proxy, load its DLL instead
+ if (m_pInfo->proxy_dll_count > 0)
+ return m_pInfo->proxy_dll_paths[0];
+
+ return CAddon::LibPath();
+}
+
+ADDON::AddonPtr CGameClient::GetRunningInstance() const
+{
+ using namespace ADDON;
+
+ CBinaryAddonCache& addonCache = CServiceBroker::GetBinaryAddonCache();
+ return addonCache.GetAddonInstance(ID(), Type());
+}
+
+bool CGameClient::SupportsPath() const
+{
+ return !m_extensions.empty() || m_bSupportsAllExtensions;
+}
+
+bool CGameClient::IsExtensionValid(const std::string& strExtension) const
+{
+ if (strExtension.empty())
+ return false;
+
+ if (SupportsAllExtensions())
+ return true;
+
+ return m_extensions.find(NormalizeExtension(strExtension)) != m_extensions.end();
+}
+
+bool CGameClient::Initialize(void)
+{
+ using namespace XFILE;
+
+ // Ensure user profile directory exists for add-on
+ if (!CDirectory::Exists(Profile()))
+ CDirectory::Create(Profile());
+
+ // Ensure directory exists for savestates
+ std::string savestatesDir = URIUtils::AddFileToFolder(CProfilesManager::GetInstance().GetSavestatesFolder(), ID());
+ if (!CDirectory::Exists(savestatesDir))
+ CDirectory::Create(savestatesDir);
+
+ m_libraryProps.InitializeProperties();
+
+ if (Create() == ADDON_STATUS_OK)
+ {
+ LogAddonProperties();
+ return true;
+ }
+
+ return false;
+}
+
+void CGameClient::Unload()
+{
+ Destroy();
+}
+
+bool CGameClient::OpenFile(const CFileItem& file, IGameAudioCallback* audio, IGameVideoCallback* video)
+{
+ if (audio == nullptr || video == nullptr)
+ return false;
+
+ // Check if we should open in standalone mode
+ if (file.GetPath().empty())
+ return OpenStandalone(audio, video);
+
+ // Resolve special:// URLs
+ CURL translatedUrl(CSpecialProtocol::TranslatePath(file.GetPath()));
+
+ // Remove file:// from URLs if add-on doesn't support VFS
+ if (!m_bSupportsVFS)
+ {
+ if (translatedUrl.GetProtocol() == "file")
+ translatedUrl.SetProtocol("");
+ }
+
+ std::string path = translatedUrl.Get();
+ CLog::Log(LOGDEBUG, "GameClient: Loading %s", CURL::GetRedacted(path).c_str());
+
+ CSingleLock lock(m_critSection);
+
+ if (!Initialized())
+ return false;
+
+ CloseFile();
+
+ GAME_ERROR error = GAME_ERROR_FAILED;
+
+ try { LogError(error = m_pStruct->LoadGame(path.c_str()), "LoadGame()"); }
+ catch (...) { LogException("LoadGame()"); }
+
+ if (error != GAME_ERROR_NO_ERROR)
+ {
+ NotifyError(error);
+ return false;
+ }
+
+ if (!InitializeGameplay(file.GetPath(), audio, video))
+ return false;
+
+ return true;
+}
+
+bool CGameClient::OpenStandalone(IGameAudioCallback* audio, IGameVideoCallback* video)
+{
+ CLog::Log(LOGDEBUG, "GameClient: Loading %s in standalone mode", ID().c_str());
+
+ CSingleLock lock(m_critSection);
+
+ if (!Initialized())
+ return false;
+
+ CloseFile();
+
+ GAME_ERROR error = GAME_ERROR_FAILED;
+
+ try { LogError(error = m_pStruct->LoadStandalone(), "LoadStandalone()"); }
+ catch (...) { LogException("LoadStandalone()"); }
+
+ if (error != GAME_ERROR_NO_ERROR)
+ {
+ NotifyError(error);
+ return false;
+ }
+
+ if (!InitializeGameplay(ID(), audio, video))
+ return false;
+
+ return true;
+}
+
+bool CGameClient::InitializeGameplay(const std::string& gamePath, IGameAudioCallback* audio, IGameVideoCallback* video)
+{
+ if (LoadGameInfo() && NormalizeAudio(audio))
+ {
+ m_bIsPlaying = true;
+ m_gamePath = gamePath;
+ m_serializeSize = GetSerializeSize();
+ m_audio = audio;
+ m_video = video;
+ m_inputRateHandle = PERIPHERALS::g_peripherals.SetEventScanRate(INPUT_SCAN_RATE);
+
+ if (m_bSupportsKeyboard)
+ OpenKeyboard();
+
+ if (m_bSupportsMouse)
+ OpenMouse();
+
+ // Start playback
+ CreatePlayback();
+
+ return true;
+ }
+
+ return false;
+}
+
+bool CGameClient::NormalizeAudio(IGameAudioCallback* audioCallback)
+{
+ unsigned int originalSampleRate = m_timing.GetSampleRate();
+
+ if (m_timing.NormalizeAudio(audioCallback))
+ {
+ const bool bChanged = (originalSampleRate != m_timing.GetSampleRate());
+ if (bChanged)
+ {
+ CLog::Log(LOGDEBUG, "GAME: Correcting audio and video by %f to avoid resampling", m_timing.GetCorrectionFactor());
+ CLog::Log(LOGDEBUG, "GAME: Audio sample rate normalized to %u", m_timing.GetSampleRate());
+ CLog::Log(LOGDEBUG, "GAME: Video frame rate scaled to %f", m_timing.GetFrameRate());
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "GAME: Audio sample rate is supported, no scaling or resampling needed");
+ }
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "GAME: Failed to normalize audio sample rate: exceeds %u%% difference", CGameClientTiming::MAX_CORRECTION_FACTOR_PERCENT);
+ return false;
+ }
+
+ return true;
+}
+
+bool CGameClient::LoadGameInfo()
+{
+ // Get information about system audio/video timings and geometry
+ // Can be called only after retro_load_game()
+ game_system_av_info av_info = { };
+
+ bool bSuccess = false;
+ try { bSuccess = LogError(m_pStruct->GetGameInfo(&av_info), "GetGameInfo()"); }
+ catch (...) { LogException("GetGameInfo()"); }
+
+ if (!bSuccess)
+ return false;
+
+ GAME_REGION region;
+ try { region = m_pStruct->GetRegion(); }
+ catch (...) { LogException("GetRegion()"); return false; }
+
+ CLog::Log(LOGINFO, "GAME: ---------------------------------------");
+ CLog::Log(LOGINFO, "GAME: Base Width: %u", av_info.geometry.base_width);
+ CLog::Log(LOGINFO, "GAME: Base Height: %u", av_info.geometry.base_height);
+ CLog::Log(LOGINFO, "GAME: Max Width: %u", av_info.geometry.max_width);
+ CLog::Log(LOGINFO, "GAME: Max Height: %u", av_info.geometry.max_height);
+ CLog::Log(LOGINFO, "GAME: Aspect Ratio: %f", av_info.geometry.aspect_ratio);
+ CLog::Log(LOGINFO, "GAME: FPS: %f", av_info.timing.fps);
+ CLog::Log(LOGINFO, "GAME: Sample Rate: %f", av_info.timing.sample_rate);
+ CLog::Log(LOGINFO, "GAME: Region: %s", CGameClientTranslator::TranslateRegion(region));
+ CLog::Log(LOGINFO, "GAME: ---------------------------------------");
+
+ m_timing.SetFrameRate(av_info.timing.fps);
+ m_timing.SetSampleRate(av_info.timing.sample_rate);
+ m_region = region;
+
+ return true;
+}
+
+void CGameClient::NotifyError(GAME_ERROR error)
+{
+ std::string missingResource;
+
+ if (error == GAME_ERROR_RESTRICTED)
+ missingResource = GetMissingResource();
+
+ if (!missingResource.empty())
+ {
+ // Failed to play game
+ // This game requires the following add-on: %s
+ CGUIDialogOK::ShowAndGetInput(CVariant{ 35210 }, StringUtils::Format(g_localizeStrings.Get(35211).c_str(), missingResource.c_str()));
+ }
+ else
+ {
+ // Failed to play game
+ // The emulator "%s" had an internal error.
+ CGUIDialogOK::ShowAndGetInput(CVariant{ 35210 }, StringUtils::Format(g_localizeStrings.Get(35213).c_str(), Name().c_str()));
+ }
+}
+
+std::string CGameClient::GetMissingResource()
+{
+ using namespace ADDON;
+
+ std::string strAddonId;
+
+ const ADDONDEPS& dependencies = GetDeps();
+ for (ADDONDEPS::const_iterator it = dependencies.begin(); it != dependencies.end(); ++it)
+ {
+ const std::string& strDependencyId = it->first;
+ if (StringUtils::StartsWith(strDependencyId, "resource.games"))
+ {
+ AddonPtr addon;
+ const bool bInstalled = CAddonMgr::GetInstance().GetAddon(strDependencyId, addon);
+ if (!bInstalled)
+ {
+ strAddonId = strDependencyId;
+ break;
+ }
+ }
+ }
+
+ return strAddonId;
+}
+
+void CGameClient::CreatePlayback()
+{
+ bool bRequiresGameLoop = false;
+
+ try { bRequiresGameLoop = m_pStruct->RequiresGameLoop(); }
+ catch (...) { LogException("RequiresGameLoop()"); }
+
+ if (bRequiresGameLoop)
+ {
+ m_playback.reset(new CGameClientReversiblePlayback(this, m_timing.GetFrameRate(), m_serializeSize));
+ }
+ else
+ {
+ ResetPlayback();
+ }
+}
+
+void CGameClient::ResetPlayback()
+{
+ m_playback.reset(new CGameClientRealtimePlayback);
+}
+
+void CGameClient::Reset()
+{
+ ResetPlayback();
+
+ CSingleLock lock(m_critSection);
+
+ if (m_bIsPlaying)
+ {
+ try { LogError(m_pStruct->Reset(), "Reset()"); }
+ catch (...) { LogException("Reset()"); }
+
+ CreatePlayback();
+ }
+}
+
+void CGameClient::CloseFile()
+{
+ ResetPlayback();
+
+ CSingleLock lock(m_critSection);
+
+ if (m_bIsPlaying)
+ {
+ try { LogError(m_pStruct->UnloadGame(), "UnloadGame()"); }
+ catch (...) { LogException("UnloadGame()"); }
+ }
+
+ ClearPorts();
+
+ if (m_bSupportsKeyboard)
+ CloseKeyboard();
+
+ if (m_bSupportsMouse)
+ CloseMouse();
+
+ m_bIsPlaying = false;
+ m_gamePath.clear();
+ m_serializeSize = 0;
+ if (m_inputRateHandle)
+ {
+ m_inputRateHandle->Release();
+ m_inputRateHandle.reset();
+ }
+
+ m_audio = nullptr;
+ m_video = nullptr;
+ m_timing.Reset();
+}
+
+void CGameClient::RunFrame()
+{
+ CSingleLock lock(m_critSection);
+
+ if (m_bIsPlaying)
+ {
+ try { LogError(m_pStruct->RunFrame(), "RunFrame()"); }
+ catch (...) { LogException("RunFrame()"); }
+ }
+}
+
+bool CGameClient::OpenPixelStream(GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation)
+{
+ if (!m_video)
+ return false;
+
+ AVPixelFormat pixelFormat = CGameClientTranslator::TranslatePixelFormat(format);
+ if (pixelFormat == AV_PIX_FMT_NONE)
+ {
+ CLog::Log(LOGERROR, "GAME: Unknown pixel format: %d", format);
+ return false;
+ }
+
+ unsigned int orientation = 0;
+ switch (rotation)
+ {
+ case GAME_VIDEO_ROTATION_90:
+ orientation = 360 - 90;
+ break;
+ case GAME_VIDEO_ROTATION_180:
+ orientation = 360 - 180;
+ break;
+ case GAME_VIDEO_ROTATION_270:
+ orientation = 360 - 270;
+ break;
+ default:
+ break;
+ }
+
+ return m_video->OpenPixelStream(pixelFormat, width, height, m_timing.GetFrameRate(), orientation);
+}
+
+bool CGameClient::OpenVideoStream(GAME_VIDEO_CODEC codec)
+{
+ if (!m_video)
+ return false;
+
+ AVCodecID videoCodec = CGameClientTranslator::TranslateVideoCodec(codec);
+ if (videoCodec == AV_CODEC_ID_NONE)
+ {
+ CLog::Log(LOGERROR, "GAME: Unknown video format: %d", codec);
+ return false;
+ }
+
+ return m_video->OpenEncodedStream(videoCodec);
+}
+
+bool CGameClient::OpenPCMStream(GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channelMap)
+{
+ if (!m_audio || channelMap == nullptr)
+ return false;
+
+ AEDataFormat pcmFormat = CGameClientTranslator::TranslatePCMFormat(format);
+ if (pcmFormat == AE_FMT_INVALID)
+ {
+ CLog::Log(LOGERROR, "GAME: Unknown PCM format: %d", format);
+ return false;
+ }
+
+ CAEChannelInfo channelLayout;
+ for (const GAME_AUDIO_CHANNEL* channelPtr = channelMap; *channelPtr != GAME_CH_NULL; channelPtr++)
+ {
+ AEChannel channel = CGameClientTranslator::TranslateAudioChannel(*channelPtr);
+ if (channel == AE_CH_NULL)
+ {
+ CLog::Log(LOGERROR, "GAME: Unknown channel ID: %d", *channelPtr);
+ return false;
+ }
+ channelLayout += channel;
+ }
+
+ return m_audio->OpenPCMStream(pcmFormat, m_timing.GetSampleRate(), channelLayout);
+}
+
+bool CGameClient::OpenAudioStream(GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channelMap)
+{
+ if (!m_audio)
+ return false;
+
+ AVCodecID audioCodec = CGameClientTranslator::TranslateAudioCodec(codec);
+ if (audioCodec == AV_CODEC_ID_NONE)
+ {
+ CLog::Log(LOGERROR, "GAME: Unknown audio codec: %d", codec);
+ return false;
+ }
+
+ CAEChannelInfo channelLayout;
+ for (const GAME_AUDIO_CHANNEL* channelPtr = channelMap; *channelPtr != GAME_CH_NULL; channelPtr++)
+ {
+ AEChannel channel = CGameClientTranslator::TranslateAudioChannel(*channelPtr);
+ if (channel == AE_CH_NULL)
+ {
+ CLog::Log(LOGERROR, "GAME: Unknown channel ID: %d", *channelPtr);
+ return false;
+ }
+ channelLayout += channel;
+ }
+
+ return m_audio->OpenEncodedStream(audioCodec, m_timing.GetSampleRate(), channelLayout);
+}
+
+void CGameClient::AddStreamData(GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size)
+{
+ switch (stream)
+ {
+ case GAME_STREAM_AUDIO:
+ {
+ if (m_audio)
+ m_audio->AddData(data, size);
+ break;
+ }
+ case GAME_STREAM_VIDEO:
+ {
+ if (m_video)
+ m_video->AddData(data, size);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void CGameClient::CloseStream(GAME_STREAM_TYPE stream)
+{
+ switch (stream)
+ {
+ case GAME_STREAM_AUDIO:
+ {
+ if (m_audio)
+ m_audio->CloseStream();
+ break;
+ }
+ case GAME_STREAM_VIDEO:
+ {
+ if (m_video)
+ m_video->CloseStream();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+size_t CGameClient::GetSerializeSize()
+{
+ CSingleLock lock(m_critSection);
+
+ size_t serializeSize = 0;
+ if (m_bIsPlaying)
+ {
+ try { serializeSize = m_pStruct->SerializeSize(); }
+ catch (...) { LogException("SerializeSize()"); }
+ }
+
+ return serializeSize;
+}
+
+bool CGameClient::Serialize(uint8_t* data, size_t size)
+{
+ if (data == nullptr || size == 0)
+ return false;
+
+ CSingleLock lock(m_critSection);
+
+ bool bSuccess = false;
+ if (m_bIsPlaying)
+ {
+ try { bSuccess = LogError(m_pStruct->Serialize(data, size), "Serialize()"); }
+ catch (...) { LogException("Serialize()"); }
+ }
+
+ return bSuccess;
+}
+
+bool CGameClient::Deserialize(const uint8_t* data, size_t size)
+{
+ if (data == nullptr || size == 0)
+ return false;
+
+ CSingleLock lock(m_critSection);
+
+ bool bSuccess = false;
+ if (m_bIsPlaying)
+ {
+ try { bSuccess = LogError(m_pStruct->Deserialize(data, size), "Deserialize()"); }
+ catch (...) { LogException("Deserialize()"); }
+ }
+
+ return bSuccess;
+}
+
+bool CGameClient::OpenPort(unsigned int port)
+{
+ // Fail if port is already open
+ if (m_ports.find(port) != m_ports.end())
+ return false;
+
+ ControllerVector controllers = GetControllers();
+ if (!controllers.empty())
+ {
+ //! @todo Choose controller
+ ControllerPtr& controller = controllers[0];
+
+ if (controller->LoadLayout())
+ {
+ m_ports[port].reset(new CGameClientInput(this, port, controller, m_pStruct));
+
+ // If keyboard input is being captured by this add-on, force the port type to PERIPHERAL_JOYSTICK
+ PERIPHERALS::PeripheralType device = PERIPHERALS::PERIPHERAL_UNKNOWN;
+ if (m_bSupportsKeyboard)
+ device = PERIPHERALS::PERIPHERAL_JOYSTICK;
+
+ CPortManager::GetInstance().OpenPort(m_ports[port].get(), port, device);
+
+ UpdatePort(port, controller);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void CGameClient::ClosePort(unsigned int port)
+{
+ // Can't close port if it doesn't exist
+ if (m_ports.find(port) == m_ports.end())
+ return;
+
+ CPortManager::GetInstance().ClosePort(m_ports[port].get());
+
+ m_ports.erase(port);
+
+ UpdatePort(port, CController::EmptyPtr);
+}
+
+void CGameClient::UpdatePort(unsigned int port, const ControllerPtr& controller)
+{
+ using namespace JOYSTICK;
+
+ if (controller != CController::EmptyPtr)
+ {
+ std::string strId = controller->ID();
+
+ game_controller controllerStruct;
+
+ controllerStruct.controller_id = strId.c_str();
+ controllerStruct.digital_button_count = controller->Layout().FeatureCount(FEATURE_TYPE::SCALAR, INPUT_TYPE::DIGITAL);
+ controllerStruct.analog_button_count = controller->Layout().FeatureCount(FEATURE_TYPE::SCALAR, INPUT_TYPE::ANALOG);
+ controllerStruct.analog_stick_count = controller->Layout().FeatureCount(FEATURE_TYPE::ANALOG_STICK);
+ controllerStruct.accelerometer_count = controller->Layout().FeatureCount(FEATURE_TYPE::ACCELEROMETER);
+ controllerStruct.key_count = 0; //! @todo
+ controllerStruct.rel_pointer_count = controller->Layout().FeatureCount(FEATURE_TYPE::RELPOINTER);
+ controllerStruct.abs_pointer_count = 0; //! @todo
+ controllerStruct.motor_count = controller->Layout().FeatureCount(FEATURE_TYPE::MOTOR);
+
+ try { m_pStruct->UpdatePort(port, true, &controllerStruct); }
+ catch (...) { LogException("UpdatePort()"); }
+ }
+ else
+ {
+ try { m_pStruct->UpdatePort(port, false, nullptr); }
+ catch (...) { LogException("UpdatePort()"); }
+ }
+}
+
+bool CGameClient::AcceptsInput(void) const
+{
+ return g_application.IsAppFocused() &&
+ g_windowManager.GetActiveWindowID() == WINDOW_FULLSCREEN_GAME;
+}
+
+void CGameClient::ClearPorts(void)
+{
+ while (!m_ports.empty())
+ ClosePort(m_ports.begin()->first);
+}
+
+ControllerVector CGameClient::GetControllers(void) const
+{
+ using namespace ADDON;
+
+ ControllerVector controllers;
+
+ const ADDONDEPS& dependencies = GetDeps();
+ for (ADDONDEPS::const_iterator it = dependencies.begin(); it != dependencies.end(); ++it)
+ {
+ AddonPtr addon;
+ if (CAddonMgr::GetInstance().GetAddon(it->first, addon, ADDON_GAME_CONTROLLER))
+ {
+ ControllerPtr controller = std::dynamic_pointer_cast<CController>(addon);
+ if (controller)
+ controllers.push_back(controller);
+ }
+ }
+
+ if (controllers.empty())
+ {
+ // Use the default controller
+ AddonPtr addon;
+ if (CAddonMgr::GetInstance().GetAddon(DEFAULT_CONTROLLER_ID, addon, ADDON_GAME_CONTROLLER))
+ controllers.push_back(std::static_pointer_cast<CController>(addon));
+ }
+
+ return controllers;
+}
+
+bool CGameClient::ReceiveInputEvent(const game_input_event& event)
+{
+ bool bHandled = false;
+
+ switch (event.type)
+ {
+ case GAME_INPUT_EVENT_MOTOR:
+ if (event.feature_name)
+ bHandled = SetRumble(event.port, event.feature_name, event.motor.magnitude);
+ break;
+ default:
+ break;
+ }
+
+ return bHandled;
+}
+
+bool CGameClient::SetRumble(unsigned int port, const std::string& feature, float magnitude)
+{
+ bool bHandled = false;
+
+ if (m_ports.find(port) != m_ports.end())
+ bHandled = m_ports[port]->SetRumble(feature, magnitude);
+
+ return bHandled;
+}
+
+void CGameClient::OpenKeyboard(void)
+{
+ m_keyboard.reset(new CGameClientKeyboard(this, m_pStruct));
+}
+
+void CGameClient::CloseKeyboard(void)
+{
+ m_keyboard.reset();
+}
+
+void CGameClient::OpenMouse(void)
+{
+ m_mouse.reset(new CGameClientMouse(this, m_pStruct));
+
+ std::string strId = m_mouse->ControllerID();
+
+ game_controller controllerStruct = { strId.c_str() };
+
+ try { m_pStruct->UpdatePort(GAME_INPUT_PORT_MOUSE, true, &controllerStruct); }
+ catch (...) { LogException("UpdatePort()"); }
+}
+
+void CGameClient::CloseMouse(void)
+{
+ try { m_pStruct->UpdatePort(GAME_INPUT_PORT_MOUSE, false, nullptr); }
+ catch (...) { LogException("UpdatePort()"); }
+
+ m_mouse.reset();
+}
+
+void CGameClient::LogAddonProperties(void) const
+{
+ CLog::Log(LOGINFO, "GAME: ------------------------------------");
+ CLog::Log(LOGINFO, "GAME: Loaded DLL for %s", ID().c_str());
+ CLog::Log(LOGINFO, "GAME: Client: %s at version %s", Name().c_str(), Version().asString().c_str());
+ CLog::Log(LOGINFO, "GAME: Valid extensions: %s", StringUtils::Join(m_extensions, " ").c_str());
+ CLog::Log(LOGINFO, "GAME: Supports VFS: %s", m_bSupportsVFS ? "yes" : "no");
+ CLog::Log(LOGINFO, "GAME: Supports standalone execution: %s", m_bSupportsStandalone ? "yes" : "no");
+ CLog::Log(LOGINFO, "GAME: Supports keyboard: %s", m_bSupportsKeyboard ? "yes" : "no");
+ CLog::Log(LOGINFO, "GAME: Supports mouse: %s", m_bSupportsMouse ? "yes" : "no");
+ CLog::Log(LOGINFO, "GAME: ------------------------------------");
+}
+
+bool CGameClient::LogError(GAME_ERROR error, const char* strMethod) const
+{
+ if (error != GAME_ERROR_NO_ERROR)
+ {
+ CLog::Log(LOGERROR, "GAME - %s - addon '%s' returned an error: %s",
+ strMethod, ID().c_str(), CGameClientTranslator::ToString(error));
+ return false;
+ }
+ return true;
+}
+
+void CGameClient::LogException(const char* strFunctionName) const
+{
+ CLog::Log(LOGERROR, "GAME: exception caught while trying to call '%s' on add-on %s",
+ strFunctionName, ID().c_str());
+ CLog::Log(LOGERROR, "Please contact the developer of this add-on: %s", Author().c_str());
+}
diff --git a/xbmc/games/addons/GameClient.h b/xbmc/games/addons/GameClient.h
new file mode 100644
index 0000000000..a88b644030
--- /dev/null
+++ b/xbmc/games/addons/GameClient.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "GameClientProperties.h"
+#include "GameClientTiming.h"
+#include "addons/AddonDll.h"
+#include "addons/DllGameClient.h"
+#include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h"
+#include "games/controllers/ControllerTypes.h"
+#include "games/GameTypes.h"
+#include "peripherals/EventScanRate.h"
+#include "threads/CriticalSection.h"
+
+#include <atomic>
+#include <set>
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+class CFileItem;
+
+namespace GAME
+{
+
+class CGameClientInput;
+class CGameClientKeyboard;
+class CGameClientMouse;
+class IGameAudioCallback;
+class IGameClientPlayback;
+class IGameVideoCallback;
+
+// --- CGameClient -------------------------------------------------------------
+
+/*!
+ * \ingroup games
+ * \brief Interface between Kodi and Game add-ons.
+ */
+class CGameClient : public ADDON::CAddonDll<DllGameClient, GameClient, game_client_properties>
+{
+public:
+ static std::unique_ptr<CGameClient> FromExtension(ADDON::AddonProps props, const cp_extension_t* ext);
+
+ CGameClient(ADDON::AddonProps props);
+
+ virtual ~CGameClient(void);
+
+ // Implementation of IAddon via CAddonDll
+ virtual std::string LibPath() const override;
+ virtual ADDON::AddonPtr GetRunningInstance() const override;
+
+ // Query properties of the game client
+ bool SupportsStandalone() const { return m_bSupportsStandalone; }
+ bool SupportsPath() const;
+ bool SupportsVFS() const { return m_bSupportsVFS; }
+ const std::set<std::string>& GetExtensions() const { return m_extensions; }
+ bool SupportsAllExtensions() const { return m_bSupportsAllExtensions; }
+ bool IsExtensionValid(const std::string& strExtension) const;
+
+ // Start/stop gameplay
+ bool Initialize(void);
+ void Unload();
+ bool OpenFile(const CFileItem& file, IGameAudioCallback* audio, IGameVideoCallback* video);
+ void Reset();
+ void CloseFile();
+ const std::string& GetGamePath() const { return m_gamePath; }
+
+ // Playback control
+ bool IsPlaying() const { return m_bIsPlaying; }
+ IGameClientPlayback* GetPlayback() { return m_playback.get(); }
+ const CGameClientTiming& Timing() const { return m_timing; }
+ void RunFrame();
+
+ // Audio/video callbacks
+ bool OpenPixelStream(GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation);
+ bool OpenVideoStream(GAME_VIDEO_CODEC codec);
+ bool OpenPCMStream(GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channelMap);
+ bool OpenAudioStream(GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channelMap);
+ void AddStreamData(GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size);
+ void CloseStream(GAME_STREAM_TYPE stream);
+
+ // Access memory
+ size_t SerializeSize() const { return m_serializeSize; }
+ bool Serialize(uint8_t* data, size_t size);
+ bool Deserialize(const uint8_t* data, size_t size);
+
+ // Input callbacks
+ bool OpenPort(unsigned int port);
+ void ClosePort(unsigned int port);
+ bool ReceiveInputEvent(const game_input_event& eventStruct);
+
+ // Input functions
+ bool AcceptsInput(void) const;
+
+private:
+ // Private gameplay functions
+ bool OpenStandalone(IGameAudioCallback* audio, IGameVideoCallback* video);
+ bool InitializeGameplay(const std::string& gamePath, IGameAudioCallback* audio, IGameVideoCallback* video);
+ bool LoadGameInfo();
+ bool NormalizeAudio(IGameAudioCallback* audioCallback);
+ void NotifyError(GAME_ERROR error);
+ std::string GetMissingResource();
+ void CreatePlayback();
+ void ResetPlayback();
+
+ // Private input functions
+ void UpdatePort(unsigned int port, const ControllerPtr& controller);
+ void ClearPorts(void);
+ bool SetRumble(unsigned int port, const std::string& feature, float magnitude);
+ void OpenKeyboard(void);
+ void CloseKeyboard(void);
+ void OpenMouse(void);
+ void CloseMouse(void);
+ ControllerVector GetControllers(void) const;
+
+ // Private memory stream functions
+ size_t GetSerializeSize();
+
+ // Helper functions
+ void LogAddonProperties(void) const;
+ bool LogError(GAME_ERROR error, const char* strMethod) const;
+ void LogException(const char* strFunctionName) const;
+
+ // Add-on properties
+ ADDON::AddonVersion m_apiVersion;
+ CGameClientProperties m_libraryProps; // Properties to pass to the DLL
+
+ // Game API xml parameters
+ bool m_bSupportsVFS;
+ bool m_bSupportsStandalone;
+ bool m_bSupportsKeyboard;
+ bool m_bSupportsMouse;
+ std::set<std::string> m_extensions;
+ bool m_bSupportsAllExtensions;
+ //GamePlatforms m_platforms;
+
+ // Properties of the current playing file
+ std::atomic_bool m_bIsPlaying; // True between OpenFile() and CloseFile()
+ std::string m_gamePath;
+ size_t m_serializeSize;
+ IGameAudioCallback* m_audio; // The audio callback passed to OpenFile()
+ IGameVideoCallback* m_video; // The video callback passed to OpenFile()
+ CGameClientTiming m_timing; // Class to scale playback to avoid resampling audio
+ PERIPHERALS::EventRateHandle m_inputRateHandle; // Handle while keeping the input sampling rate at the frame rate
+ std::unique_ptr<IGameClientPlayback> m_playback; // Interface to control playback
+ GAME_REGION m_region; // Region of the loaded game
+
+ // Input
+ std::map<int, std::unique_ptr<CGameClientInput>> m_ports;
+ std::unique_ptr<CGameClientKeyboard> m_keyboard;
+ std::unique_ptr<CGameClientMouse> m_mouse;
+
+ CCriticalSection m_critSection;
+};
+
+} // namespace GAME
diff --git a/xbmc/games/addons/GameClientCallbacks.h b/xbmc/games/addons/GameClientCallbacks.h
new file mode 100644
index 0000000000..316a460082
--- /dev/null
+++ b/xbmc/games/addons/GameClientCallbacks.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "cores/AudioEngine/Utils/AEChannelData.h"
+
+#include "libavcodec/avcodec.h"
+#include "libavutil/pixfmt.h"
+
+#include <stdint.h>
+
+class CAEChannelInfo;
+
+namespace GAME
+{
+ class IGameAudioCallback
+ {
+ public:
+ virtual ~IGameAudioCallback() { }
+
+ virtual unsigned int NormalizeSamplerate(unsigned int samplerate) const = 0;
+ virtual bool OpenPCMStream(AEDataFormat format, unsigned int samplerate, const CAEChannelInfo& channelLayout) = 0;
+ virtual bool OpenEncodedStream(AVCodecID codec, unsigned int samplerate, const CAEChannelInfo& channelLayout) = 0;
+ virtual void AddData(const uint8_t* data, unsigned int size) = 0;
+ virtual void CloseStream() = 0;
+ };
+
+ class IGameVideoCallback
+ {
+ public:
+ virtual ~IGameVideoCallback() { }
+
+ virtual bool OpenPixelStream(AVPixelFormat pixfmt, unsigned int width, unsigned int height, double framerate, unsigned int orientationDeg) = 0;
+ virtual bool OpenEncodedStream(AVCodecID codec) = 0;
+ virtual void AddData(const uint8_t* data, unsigned int size) = 0;
+ virtual void CloseStream() = 0;
+ };
+}
diff --git a/xbmc/games/addons/GameClientInput.cpp b/xbmc/games/addons/GameClientInput.cpp
new file mode 100644
index 0000000000..7c7b44ebe1
--- /dev/null
+++ b/xbmc/games/addons/GameClientInput.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2015-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GameClientInput.h"
+#include "GameClient.h"
+#include "games/controllers/Controller.h"
+#include "input/joysticks/IInputReceiver.h"
+
+#include <algorithm>
+#include <assert.h>
+
+using namespace GAME;
+
+CGameClientInput::CGameClientInput(CGameClient* gameClient, int port, const ControllerPtr& controller, const GameClient *dllStruct) :
+ m_gameClient(gameClient),
+ m_port(port),
+ m_controller(controller),
+ m_dllStruct(dllStruct)
+{
+ assert(m_gameClient != NULL);
+ assert(m_controller.get() != NULL);
+}
+
+std::string CGameClientInput::ControllerID(void) const
+{
+ return m_controller->ID();
+}
+
+bool CGameClientInput::HasFeature(const std::string& feature) const
+{
+ try
+ {
+ return m_dllStruct->HasFeature(m_controller->ID().c_str(), feature.c_str());
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "GAME: %s: exception caught in HasFeature()", m_gameClient->ID().c_str());
+ }
+
+ return false;
+}
+
+bool CGameClientInput::AcceptsInput(void)
+{
+ return m_gameClient->AcceptsInput();
+}
+
+JOYSTICK::INPUT_TYPE CGameClientInput::GetInputType(const std::string& feature) const
+{
+ const std::vector<CControllerFeature>& features = m_controller->Layout().Features();
+
+ for (std::vector<CControllerFeature>::const_iterator it = features.begin(); it != features.end(); ++it)
+ {
+ if (feature == it->Name())
+ return it->InputType();
+ }
+
+ return JOYSTICK::INPUT_TYPE::UNKNOWN;
+}
+
+bool CGameClientInput::OnButtonPress(const std::string& feature, bool bPressed)
+{
+ bool bHandled = false;
+
+ game_input_event event;
+
+ std::string controllerId = m_controller->ID();
+
+ event.type = GAME_INPUT_EVENT_DIGITAL_BUTTON;
+ event.port = m_port;
+ event.controller_id = controllerId.c_str();
+ event.feature_name = feature.c_str();
+ event.digital_button.pressed = bPressed;
+
+ try
+ {
+ bHandled = m_dllStruct->InputEvent(&event);
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "GAME: %s: exception caught in InputEvent()", m_gameClient->ID().c_str());
+ }
+
+ return bHandled;
+}
+
+bool CGameClientInput::OnButtonMotion(const std::string& feature, float magnitude)
+{
+ bool bHandled = false;
+
+ game_input_event event;
+
+ std::string controllerId = m_controller->ID();
+
+ event.type = GAME_INPUT_EVENT_ANALOG_BUTTON;
+ event.port = m_port;
+ event.controller_id = controllerId.c_str();
+ event.feature_name = feature.c_str();
+ event.analog_button.magnitude = magnitude;
+
+ try
+ {
+ bHandled = m_dllStruct->InputEvent(&event);
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "GAME: %s: exception caught in InputEvent()", m_gameClient->ID().c_str());
+ }
+
+ return bHandled;
+}
+
+bool CGameClientInput::OnAnalogStickMotion(const std::string& feature, float x, float y, unsigned int motionTimeMs /* = 0 */)
+{
+ bool bHandled = false;
+
+ game_input_event event;
+
+ std::string controllerId = m_controller->ID();
+
+ event.type = GAME_INPUT_EVENT_ANALOG_STICK;
+ event.port = m_port;
+ event.controller_id = controllerId.c_str();
+ event.feature_name = feature.c_str();
+ event.analog_stick.x = x;
+ event.analog_stick.y = y;
+
+ try
+ {
+ bHandled = m_dllStruct->InputEvent(&event);
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "GAME: %s: exception caught in InputEvent()", m_gameClient->ID().c_str());
+ }
+
+ return bHandled;
+}
+
+bool CGameClientInput::OnAccelerometerMotion(const std::string& feature, float x, float y, float z)
+{
+ bool bHandled = false;
+
+ game_input_event event;
+
+ std::string controllerId = m_controller->ID();
+
+ event.type = GAME_INPUT_EVENT_ACCELEROMETER;
+ event.port = m_port;
+ event.controller_id = controllerId.c_str();
+ event.feature_name = feature.c_str();
+ event.accelerometer.x = x;
+ event.accelerometer.y = y;
+ event.accelerometer.z = z;
+
+ try
+ {
+ bHandled = m_dllStruct->InputEvent(&event);
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "GAME: %s: exception caught in InputEvent()", m_gameClient->ID().c_str());
+ }
+
+ return bHandled;
+}
+
+bool CGameClientInput::SetRumble(const std::string& feature, float magnitude)
+{
+ bool bHandled = false;
+
+ if (InputReceiver())
+ bHandled = InputReceiver()->SetRumbleState(feature, magnitude);
+
+ return bHandled;
+}
diff --git a/xbmc/games/addons/GameClientInput.h b/xbmc/games/addons/GameClientInput.h
new file mode 100644
index 0000000000..75bfd5668b
--- /dev/null
+++ b/xbmc/games/addons/GameClientInput.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "games/controllers/ControllerTypes.h"
+#include "input/joysticks/IInputHandler.h"
+
+struct GameClient;
+
+namespace GAME
+{
+ class CGameClient;
+
+ /*!
+ * \ingroup games
+ * \brief Handles game controller events for games.
+ *
+ * Listens to game controller events and forwards them to the games (as game_input_event).
+ */
+ class CGameClientInput : public JOYSTICK::IInputHandler
+ {
+ public:
+ /*!
+ * \brief Constructor.
+ * \param addon The game client implementation.
+ * \param port The port this game controller is associated with.
+ * \param controller The game controller which is used (for controller mapping).
+ * \param dllStruct The emulator or game to which the events are sent.
+ */
+ CGameClientInput(CGameClient* addon, int port, const ControllerPtr& controller, const GameClient* dllStruct);
+
+ // Implementation of IInputHandler
+ virtual std::string ControllerID(void) const override;
+ virtual bool HasFeature(const std::string& feature) const override;
+ virtual bool AcceptsInput(void) override;
+ virtual JOYSTICK::INPUT_TYPE GetInputType(const std::string& feature) const override;
+ virtual bool OnButtonPress(const std::string& feature, bool bPressed) override;
+ virtual void OnButtonHold(const std::string& feature, unsigned int holdTimeMs) override { }
+ virtual bool OnButtonMotion(const std::string& feature, float magnitude) override;
+ virtual bool OnAnalogStickMotion(const std::string& feature, float x, float y, unsigned int motionTimeMs = 0) override;
+ virtual bool OnAccelerometerMotion(const std::string& feature, float x, float y, float z) override;
+
+ bool SetRumble(const std::string& feature, float magnitude);
+
+ private:
+ const CGameClient* const m_gameClient;
+ const int m_port;
+ const ControllerPtr m_controller;
+ const GameClient* const m_dllStruct;
+ };
+}
diff --git a/xbmc/games/addons/GameClientKeyboard.cpp b/xbmc/games/addons/GameClientKeyboard.cpp
new file mode 100644
index 0000000000..157e202183
--- /dev/null
+++ b/xbmc/games/addons/GameClientKeyboard.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GameClientKeyboard.h"
+#include "GameClient.h"
+#include "GameClientTranslator.h"
+#include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h"
+#include "input/InputManager.h"
+#include "input/Key.h"
+#include "utils/log.h"
+
+using namespace GAME;
+
+#define BUTTON_INDEX_MASK 0x01ff
+
+CGameClientKeyboard::CGameClientKeyboard(const CGameClient* gameClient, const GameClient* dllStruct) :
+ m_gameClient(gameClient),
+ m_dllStruct(dllStruct)
+{
+ CInputManager::GetInstance().RegisterKeyboardHandler(this);
+}
+
+CGameClientKeyboard::~CGameClientKeyboard()
+{
+ CInputManager::GetInstance().UnregisterKeyboardHandler(this);
+}
+
+bool CGameClientKeyboard::OnKeyPress(const CKey& key)
+{
+ // Only allow activated input in fullscreen game
+ if (!m_gameClient->AcceptsInput())
+ {
+ CLog::Log(LOGDEBUG, "GAME: key press ignored, not in fullscreen game");
+ return false;
+ }
+
+ bool bHandled = false;
+
+ game_input_event event;
+
+ event.type = GAME_INPUT_EVENT_KEY;
+ event.port = GAME_INPUT_PORT_KEYBOARD;
+ event.controller_id = ""; //! @todo
+ event.feature_name = ""; //! @todo
+ event.key.pressed = true;
+ event.key.character = static_cast<XBMCVKey>(key.GetButtonCode() & BUTTON_INDEX_MASK);
+ event.key.modifiers = CGameClientTranslator::GetModifiers(static_cast<CKey::Modifier>(key.GetModifiers()));
+
+ if (event.key.character != 0)
+ {
+ try
+ {
+ bHandled = m_dllStruct->InputEvent(&event);
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "GAME: %s: exception caught in InputEvent()", m_gameClient->ID().c_str());
+ }
+ }
+
+ return bHandled;
+}
+
+void CGameClientKeyboard::OnKeyRelease(const CKey& key)
+{
+ game_input_event event;
+
+ event.type = GAME_INPUT_EVENT_KEY;
+ event.port = GAME_INPUT_PORT_KEYBOARD;
+ event.controller_id = ""; //! @todo
+ event.feature_name = ""; //! @todo
+ event.key.pressed = false;
+ event.key.character = static_cast<XBMCVKey>(key.GetButtonCode() & BUTTON_INDEX_MASK);
+ event.key.modifiers = CGameClientTranslator::GetModifiers(static_cast<CKey::Modifier>(key.GetModifiers()));
+
+ if (event.key.character != 0)
+ {
+ try
+ {
+ m_dllStruct->InputEvent(&event);
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "GAME: %s: exception caught in InputEvent()", m_gameClient->ID().c_str());
+ }
+ }
+}
diff --git a/xbmc/games/addons/GameClientKeyboard.h b/xbmc/games/addons/GameClientKeyboard.h
new file mode 100644
index 0000000000..d565c1e7fd
--- /dev/null
+++ b/xbmc/games/addons/GameClientKeyboard.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "input/keyboard/IKeyboardHandler.h"
+
+struct GameClient;
+
+namespace GAME
+{
+ class CGameClient;
+
+ /*!
+ * \ingroup games
+ * \brief Handles keyboard events for games.
+ *
+ * Listens to keyboard events and forwards them to the games (as game_input_event).
+ */
+ class CGameClientKeyboard : public KEYBOARD::IKeyboardHandler
+ {
+ public:
+ /*!
+ * \brief Constructor registers for keyboard events at CInputManager.
+ * \param gameClient The game client implementation.
+ * \param dllStruct The emulator or game to which the events are sent.
+ */
+ CGameClientKeyboard(const CGameClient* gameClient, const GameClient* dllStruct);
+
+ /*!
+ * \brief Destructor unregisters from keyboard events from CInputManager.
+ */
+ ~CGameClientKeyboard();
+
+ // implementation of IKeyboardHandler
+ virtual bool OnKeyPress(const CKey& key) override;
+ virtual void OnKeyRelease(const CKey& key) override;
+
+ private:
+ // Construction parameters
+ const CGameClient* const m_gameClient;
+ const GameClient* const m_dllStruct;
+ };
+}
diff --git a/xbmc/games/addons/GameClientMouse.cpp b/xbmc/games/addons/GameClientMouse.cpp
new file mode 100644
index 0000000000..20f7dd9fbe
--- /dev/null
+++ b/xbmc/games/addons/GameClientMouse.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GameClientMouse.h"
+#include "GameClient.h"
+#include "GameClientTranslator.h"
+#include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h"
+#include "input/InputManager.h"
+#include "input/Key.h"
+#include "utils/log.h"
+
+using namespace GAME;
+
+CGameClientMouse::CGameClientMouse(const CGameClient* gameClient, const GameClient* dllStruct) :
+ m_gameClient(gameClient),
+ m_dllStruct(dllStruct),
+ m_controllerId(CInputManager::GetInstance().RegisterMouseHandler(this))
+{
+}
+
+CGameClientMouse::~CGameClientMouse()
+{
+ CInputManager::GetInstance().UnregisterMouseHandler(this);
+}
+
+std::string CGameClientMouse::ControllerID(void) const
+{
+ return m_controllerId;
+}
+
+bool CGameClientMouse::OnMotion(const std::string& relpointer, int dx, int dy)
+{
+ // Only allow activated input in fullscreen game
+ if (!m_gameClient->AcceptsInput())
+ {
+ return false;
+ }
+
+ bool bHandled = false;
+
+ game_input_event event;
+
+ event.type = GAME_INPUT_EVENT_RELATIVE_POINTER;
+ event.port = GAME_INPUT_PORT_MOUSE;
+ event.controller_id = m_controllerId.c_str();
+ event.feature_name = relpointer.c_str();
+ event.rel_pointer.x = dx;
+ event.rel_pointer.y = dy;
+
+ try
+ {
+ bHandled = m_dllStruct->InputEvent(&event);
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "GAME: %s: exception caught in InputEvent()", m_gameClient->ID().c_str());
+ }
+
+ return bHandled;
+}
+
+bool CGameClientMouse::OnButtonPress(const std::string& button)
+{
+ // Only allow activated input in fullscreen game
+ if (!m_gameClient->AcceptsInput())
+ {
+ return false;
+ }
+
+ bool bHandled = false;
+
+ game_input_event event;
+
+ event.type = GAME_INPUT_EVENT_DIGITAL_BUTTON;
+ event.port = GAME_INPUT_PORT_MOUSE;
+ event.controller_id = m_controllerId.c_str();
+ event.feature_name = button.c_str();
+ event.digital_button.pressed = true;
+
+ try
+ {
+ bHandled = m_dllStruct->InputEvent(&event);
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "GAME: %s: exception caught in InputEvent()", m_gameClient->ID().c_str());
+ }
+
+ return bHandled;
+}
+
+void CGameClientMouse::OnButtonRelease(const std::string& button)
+{
+ game_input_event event;
+
+ event.type = GAME_INPUT_EVENT_DIGITAL_BUTTON;
+ event.port = GAME_INPUT_PORT_MOUSE;
+ event.controller_id = m_controllerId.c_str();
+ event.feature_name = button.c_str();
+ event.digital_button.pressed = false;
+
+ try
+ {
+ m_dllStruct->InputEvent(&event);
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "GAME: %s: exception caught in InputEvent()", m_gameClient->ID().c_str());
+ }
+}
diff --git a/xbmc/games/addons/GameClientMouse.h b/xbmc/games/addons/GameClientMouse.h
new file mode 100644
index 0000000000..41d0c4efa5
--- /dev/null
+++ b/xbmc/games/addons/GameClientMouse.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "input/mouse/IMouseInputHandler.h"
+
+struct GameClient;
+
+namespace GAME
+{
+ class CGameClient;
+
+ /*!
+ * \ingroup games
+ * \brief Handles mouse events for games.
+ *
+ * Listens to mouse events and forwards them to the games (as game_input_event).
+ */
+ class CGameClientMouse : public MOUSE::IMouseInputHandler
+ {
+ public:
+ /*!
+ * \brief Constructor registers for mouse events at CInputManager.
+ * \param gameClient The game client implementation.
+ * \param dllStruct The emulator or game to which the events are sent.
+ */
+ CGameClientMouse(const CGameClient* gameClient, const GameClient* dllStruct);
+
+ /*!
+ * \brief Destructor unregisters from mouse events from CInputManager.
+ */
+ ~CGameClientMouse();
+
+ // implementation of IMouseInputHandler
+ virtual std::string ControllerID(void) const override;
+ virtual bool OnMotion(const std::string& relpointer, int dx, int dy) override;
+ virtual bool OnButtonPress(const std::string& button) override;
+ virtual void OnButtonRelease(const std::string& button) override;
+
+ private:
+ // Construction parameters
+ const CGameClient* const m_gameClient;
+ const GameClient* const m_dllStruct;
+ const std::string m_controllerId;
+ };
+}
diff --git a/xbmc/games/addons/GameClientProperties.cpp b/xbmc/games/addons/GameClientProperties.cpp
new file mode 100644
index 0000000000..c2a8f63753
--- /dev/null
+++ b/xbmc/games/addons/GameClientProperties.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GameClientProperties.h"
+#include "GameClient.h"
+#include "addons/IAddon.h"
+#include "addons/AddonManager.h"
+#include "addons/GameResource.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "filesystem/Directory.h"
+#include "filesystem/SpecialProtocol.h"
+#include "settings/Settings.h"
+#include "utils/log.h"
+#include "utils/Variant.h"
+
+#include <cstring>
+
+using namespace ADDON;
+using namespace GAME;
+using namespace XFILE;
+
+#define GAME_CLIENT_RESOURCES_DIRECTORY "resources"
+
+CGameClientProperties::CGameClientProperties(const CGameClient* parent, game_client_properties*& props)
+ : m_parent(parent),
+ m_properties()
+{
+ // Allow the caller to access the property structure directly
+ props = &m_properties;
+}
+
+void CGameClientProperties::ReleaseResources(void)
+{
+ for (std::vector<char*>::const_iterator it = m_proxyDllPaths.begin(); it != m_proxyDllPaths.end(); ++it)
+ delete[] *it;
+ m_proxyDllPaths.clear();
+
+ for (std::vector<char*>::const_iterator it = m_resourceDirectories.begin(); it != m_resourceDirectories.end(); ++it)
+ delete[] *it;
+ m_resourceDirectories.clear();
+
+ for (std::vector<char*>::const_iterator it = m_extensions.begin(); it != m_extensions.end(); ++it)
+ delete[] *it;
+ m_extensions.clear();
+}
+
+void CGameClientProperties::InitializeProperties(void)
+{
+ ReleaseResources();
+
+ m_properties.game_client_dll_path = GetLibraryPath();
+ m_properties.proxy_dll_paths = GetProxyDllPaths();
+ m_properties.proxy_dll_count = GetProxyDllCount();
+ m_properties.resource_directories = GetResourceDirectories();
+ m_properties.resource_directory_count = GetResourceDirectoryCount();
+ m_properties.profile_directory = GetProfileDirectory();
+ m_properties.supports_vfs = m_parent->SupportsVFS();
+ m_properties.extensions = GetExtensions();
+ m_properties.extension_count = GetExtensionCount();
+}
+
+const char* CGameClientProperties::GetLibraryPath(void)
+{
+ if (m_strLibraryPath.empty())
+ {
+ // Get the parent add-on's real path
+ std::string strLibPath = m_parent->CAddon::LibPath();
+ m_strLibraryPath = CSpecialProtocol::TranslatePath(strLibPath);
+ }
+ return m_strLibraryPath.c_str();
+}
+
+const char** CGameClientProperties::GetProxyDllPaths(void)
+{
+ if (m_proxyDllPaths.empty())
+ {
+ // Add all game client dependencies
+ //! @todo Compare helper version with required dependency
+ const ADDONDEPS& dependencies = m_parent->GetDeps();
+ for (ADDONDEPS::const_iterator it = dependencies.begin(); it != dependencies.end(); ++it)
+ {
+ const std::string& strAddonId = it->first;
+ AddonPtr addon;
+ if (CAddonMgr::GetInstance().GetAddon(strAddonId, addon, ADDON_GAMEDLL, false))
+ {
+ // If add-on is disabled, ask the user to enable it
+ if (CAddonMgr::GetInstance().IsAddonDisabled(addon->ID()))
+ {
+ // Failed to play game
+ // This game depends on a disabled add-on. Would you like to enable it?
+ if (CGUIDialogYesNo::ShowAndGetInput(CVariant{ 35210 }, CVariant{ 35215 }))
+ CAddonMgr::GetInstance().EnableAddon(addon->ID());
+ else
+ addon.reset();
+ }
+ }
+
+ if (addon)
+ AddProxyDll(std::static_pointer_cast<CGameClient>(addon));
+ }
+ }
+
+ if (!m_proxyDllPaths.empty())
+ return const_cast<const char**>(m_proxyDllPaths.data());
+
+ return nullptr;
+}
+
+const char** CGameClientProperties::GetResourceDirectories(void)
+{
+ if (m_resourceDirectories.empty())
+ {
+ // Add all other game resources
+ const ADDONDEPS& dependencies = m_parent->GetDeps();
+ for (ADDONDEPS::const_iterator it = dependencies.begin(); it != dependencies.end(); ++it)
+ {
+ const std::string& strAddonId = it->first;
+ AddonPtr addon;
+ if (CAddonMgr::GetInstance().GetAddon(strAddonId, addon, ADDON_RESOURCE_GAMES))
+ {
+ std::shared_ptr<CGameResource> resource = std::static_pointer_cast<CGameResource>(addon);
+
+ std::string resourcePath = resource->GetFullPath("");
+
+ char* resourceDir = new char[resourcePath.length() + 1];
+ std::strcpy(resourceDir, resourcePath.c_str());
+ m_resourceDirectories.push_back(resourceDir);
+ }
+ }
+
+ // Add resource directories for profile and path
+ std::string addonProfile = CSpecialProtocol::TranslatePath(m_parent->Profile());
+ std::string addonPath = m_parent->Path();
+
+ addonProfile = URIUtils::AddFileToFolder(addonProfile, GAME_CLIENT_RESOURCES_DIRECTORY);
+ addonPath = URIUtils::AddFileToFolder(addonPath, GAME_CLIENT_RESOURCES_DIRECTORY);
+
+ if (!CDirectory::Exists(addonProfile))
+ {
+ CLog::Log(LOGDEBUG, "Creating resource directory: %s", addonProfile.c_str());
+ CDirectory::Create(addonProfile);
+ }
+
+ char* addonProfileDir = new char[addonProfile.length() + 1];
+ std::strcpy(addonProfileDir, addonProfile.c_str());
+ m_resourceDirectories.push_back(addonProfileDir);
+
+ char* addonPathDir = new char[addonPath.length() + 1];
+ std::strcpy(addonPathDir, addonPath.c_str());
+ m_resourceDirectories.push_back(addonPathDir);
+ }
+
+ if (!m_resourceDirectories.empty())
+ return const_cast<const char**>(m_resourceDirectories.data());
+
+ return nullptr;
+}
+
+const char* CGameClientProperties::GetProfileDirectory(void)
+{
+ if (m_strProfileDirectory.empty())
+ m_strProfileDirectory = CSpecialProtocol::TranslatePath(m_parent->Profile());
+
+ return m_strProfileDirectory.c_str();
+}
+
+const char** CGameClientProperties::GetExtensions(void)
+{
+ for (auto& extension : m_parent->GetExtensions())
+ {
+ char* ext = new char[extension.length() + 1];
+ std::strcpy(ext, extension.c_str());
+ m_extensions.push_back(ext);
+ }
+
+ return !m_extensions.empty() ? const_cast<const char**>(m_extensions.data()) : nullptr;
+}
+
+void CGameClientProperties::AddProxyDll(const GameClientPtr& gameClient)
+{
+ // Get the add-on's real path
+ std::string strLibPath = gameClient->CAddon::LibPath();
+
+ // Ignore add-on if it is already added
+ if (!HasProxyDll(strLibPath))
+ {
+ char* libPath = new char[strLibPath.length() + 1];
+ std::strcpy(libPath, strLibPath.c_str());
+ m_proxyDllPaths.push_back(libPath);
+ }
+}
+
+bool CGameClientProperties::HasProxyDll(const std::string& strLibPath) const
+{
+ for (std::vector<char*>::const_iterator it = m_proxyDllPaths.begin(); it != m_proxyDllPaths.end(); ++it)
+ {
+ if (strLibPath == *it)
+ return true;
+ }
+ return false;
+}
diff --git a/xbmc/games/addons/GameClientProperties.h b/xbmc/games/addons/GameClientProperties.h
new file mode 100644
index 0000000000..26a8aa5dd0
--- /dev/null
+++ b/xbmc/games/addons/GameClientProperties.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h"
+#include "games/GameTypes.h"
+
+#include <string>
+#include <vector>
+
+struct game_client_properties;
+
+namespace GAME
+{
+
+class CGameClient;
+
+/**
+ * \ingroup games
+ * \brief C++ wrapper for game client properties declared in kodi_game_types.h
+ */
+class CGameClientProperties
+{
+public:
+ CGameClientProperties(const CGameClient* parent, game_client_properties*& props);
+ ~CGameClientProperties(void) { ReleaseResources(); }
+
+ void InitializeProperties(void);
+
+private:
+ // Release mutable resources
+ void ReleaseResources(void);
+
+ // Equal to parent's real library path
+ const char* GetLibraryPath(void);
+
+ // List of proxy DLLs needed to load the game client
+ const char** GetProxyDllPaths(void);
+
+ // Number of proxy DLLs needed to load the game client
+ unsigned int GetProxyDllCount(void) const { return m_proxyDllPaths.size(); }
+
+ // Paths to game resources
+ const char** GetResourceDirectories(void);
+
+ // Number of resource directories
+ unsigned int GetResourceDirectoryCount(void) const { return m_resourceDirectories.size(); }
+
+ // Equal to special://profile/addon_data/<parent's id>
+ const char* GetProfileDirectory(void);
+
+ // List of extensions from addon.xml
+ const char** GetExtensions(void);
+
+ // Number of extensions
+ unsigned int GetExtensionCount(void) const { return m_extensions.size(); }
+
+ // Helper functions
+ void AddProxyDll(const GameClientPtr& gameClient);
+ bool HasProxyDll(const std::string& strLibPath) const;
+
+ const CGameClient* const m_parent;
+ game_client_properties m_properties;
+
+ // Buffers to hold the strings
+ std::string m_strLibraryPath;
+ std::vector<char*> m_proxyDllPaths;
+ std::vector<char*> m_resourceDirectories;
+ std::string m_strProfileDirectory;
+ std::vector<char*> m_extensions;
+};
+
+} // namespace GAME
diff --git a/xbmc/games/addons/GameClientTiming.cpp b/xbmc/games/addons/GameClientTiming.cpp
new file mode 100644
index 0000000000..ccd1533dc8
--- /dev/null
+++ b/xbmc/games/addons/GameClientTiming.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GameClientTiming.h"
+#include "GameClientCallbacks.h"
+#include "utils/MathUtils.h"
+
+#include <cmath>
+
+using namespace GAME;
+
+void CGameClientTiming::Reset()
+{
+ m_framerate = 0.0;
+ m_samplerate = 0.0;
+ m_audioCorrectionFactor = 1.0;
+}
+
+bool CGameClientTiming::NormalizeAudio(IGameAudioCallback* audio)
+{
+ m_audioCorrectionFactor = audio->NormalizeSamplerate(static_cast<unsigned int>(m_samplerate)) / m_samplerate;
+
+ const double correctionPercent = std::abs(m_audioCorrectionFactor - 1.0) * 100;
+
+ return correctionPercent < MAX_CORRECTION_FACTOR_PERCENT;
+}
+
+double CGameClientTiming::GetFrameRate() const
+{
+ return m_framerate * m_audioCorrectionFactor;
+}
+
+unsigned int CGameClientTiming::GetSampleRate() const
+{
+ return MathUtils::round_int(m_samplerate * m_audioCorrectionFactor);
+}
diff --git a/xbmc/games/addons/GameClientTiming.h b/xbmc/games/addons/GameClientTiming.h
new file mode 100644
index 0000000000..65d05fd0c9
--- /dev/null
+++ b/xbmc/games/addons/GameClientTiming.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+namespace GAME
+{
+ class IGameAudioCallback;
+
+ /*!
+ * \ingroup games
+ * \brief Class to normalize audio and video timing to avoid audio resampling
+ *
+ * For example, assume the audio callback supports two sample rates:
+ * 32,000 Hz and 44,100 Hz.
+ *
+ * If the game client reports an audio sample rate of 32040.5, the audio
+ * callback will normalize this to 32,000 Hz. The correction factor is then
+ * set to (32000 / 32040.5) = 0.9987.
+ *
+ * After normalization, GetSampleRate() will report (32040.5 * 0.9987) = 32000.
+ *
+ * If the game client's frame rate is 60.1 fps, after normalization
+ * GetFrameRate() will report (60.1 * 0.9987) = 60.024 fps. The game
+ * client's frame rate has been slowed slightly to avoid resampling audio.
+ *
+ * To avoid excessive scaling, normalization will fail if the correction
+ * factor exceeds MAX_CORRECTION_FACTOR_PERCENT.
+ */
+ class CGameClientTiming
+ {
+ public:
+ static const unsigned int MAX_CORRECTION_FACTOR_PERCENT = 7;
+
+ CGameClientTiming() { Reset(); }
+
+ void Reset();
+
+ /*!
+ * \brief Calculate normalization factor to avoid audio resampling
+ *
+ * \param audio Callback capable of normalizing sample rate to one of the
+ * discrete values supported by the audio system
+ *
+ * \return false If the correction factor exceeds a pre-defined value, true otherwise
+ */
+ bool NormalizeAudio(IGameAudioCallback* audio);
+
+ // Set frame rate and sample rate reported by the game client
+ void SetFrameRate(double framerate) { m_framerate = framerate; }
+ void SetSampleRate(double samplerate) { m_samplerate = samplerate; }
+
+ // Get frame rate and sample rate multiplied by the correction factor
+ double GetFrameRate() const;
+ unsigned int GetSampleRate() const;
+ double GetCorrectionFactor() const { return m_audioCorrectionFactor; }
+
+ private:
+ double m_framerate; // Video frame rate (fps)
+ double m_samplerate; // Audio sample rate (Hz)
+ double m_audioCorrectionFactor; // Factor that audio is normalized by to avoid resampling
+ };
+}
diff --git a/xbmc/games/addons/GameClientTranslator.cpp b/xbmc/games/addons/GameClientTranslator.cpp
new file mode 100644
index 0000000000..dda424fc03
--- /dev/null
+++ b/xbmc/games/addons/GameClientTranslator.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GameClientTranslator.h"
+
+using namespace GAME;
+
+const char* CGameClientTranslator::ToString(GAME_ERROR error)
+{
+ switch (error)
+ {
+ case GAME_ERROR_NO_ERROR: return "no error";
+ case GAME_ERROR_NOT_IMPLEMENTED: return "not implemented";
+ case GAME_ERROR_REJECTED: return "rejected by the client";
+ case GAME_ERROR_INVALID_PARAMETERS: return "invalid parameters for this method";
+ case GAME_ERROR_FAILED: return "the command failed";
+ case GAME_ERROR_NOT_LOADED: return "no game is loaded";
+ case GAME_ERROR_RESTRICTED: return "the required resources are restricted";
+ default:
+ break;
+ }
+ return "unknown error";
+}
+
+AVPixelFormat CGameClientTranslator::TranslatePixelFormat(GAME_PIXEL_FORMAT format)
+{
+ switch (format)
+ {
+ case GAME_PIXEL_FORMAT_YUV420P: return AV_PIX_FMT_YUV420P;
+ case GAME_PIXEL_FORMAT_0RGB8888: return AV_PIX_FMT_0RGB32;
+ case GAME_PIXEL_FORMAT_RGB565: return AV_PIX_FMT_RGB565;
+ case GAME_PIXEL_FORMAT_0RGB1555: return AV_PIX_FMT_RGB555;
+ default:
+ break;
+ }
+ return AV_PIX_FMT_NONE;
+}
+
+AVCodecID CGameClientTranslator::TranslateVideoCodec(GAME_VIDEO_CODEC codec)
+{
+ switch (codec)
+ {
+ case GAME_VIDEO_CODEC_H264: return AV_CODEC_ID_H264;
+ default:
+ break;
+ }
+ return AV_CODEC_ID_NONE;
+}
+
+AEDataFormat CGameClientTranslator::TranslatePCMFormat(GAME_PCM_FORMAT format)
+{
+ switch (format)
+ {
+ case GAME_PCM_FORMAT_S16NE: return AE_FMT_S16NE;
+ default:
+ break;
+ }
+ return AE_FMT_INVALID;
+}
+
+AEChannel CGameClientTranslator::TranslateAudioChannel(GAME_AUDIO_CHANNEL channel)
+{
+ switch (channel)
+ {
+ case GAME_CH_FL: return AE_CH_FL;
+ case GAME_CH_FR: return AE_CH_FR;
+ case GAME_CH_FC: return AE_CH_FC;
+ case GAME_CH_LFE: return AE_CH_LFE;
+ case GAME_CH_BL: return AE_CH_BL;
+ case GAME_CH_BR: return AE_CH_BR;
+ case GAME_CH_FLOC: return AE_CH_FLOC;
+ case GAME_CH_FROC: return AE_CH_FROC;
+ case GAME_CH_BC: return AE_CH_BC;
+ case GAME_CH_SL: return AE_CH_SL;
+ case GAME_CH_SR: return AE_CH_SR;
+ case GAME_CH_TFL: return AE_CH_TFL;
+ case GAME_CH_TFR: return AE_CH_TFR;
+ case GAME_CH_TFC: return AE_CH_TFC;
+ case GAME_CH_TC: return AE_CH_TC;
+ case GAME_CH_TBL: return AE_CH_TBL;
+ case GAME_CH_TBR: return AE_CH_TBR;
+ case GAME_CH_TBC: return AE_CH_TBC;
+ case GAME_CH_BLOC: return AE_CH_BLOC;
+ case GAME_CH_BROC: return AE_CH_BROC;
+ default:
+ break;
+ }
+ return AE_CH_NULL;
+}
+
+AVCodecID CGameClientTranslator::TranslateAudioCodec(GAME_AUDIO_CODEC codec)
+{
+ switch (codec)
+ {
+ case GAME_AUDIO_CODEC_OPUS: return AV_CODEC_ID_OPUS;
+ default:
+ break;
+ }
+ return AV_CODEC_ID_NONE;
+}
+
+GAME_KEY_MOD CGameClientTranslator::GetModifiers(CKey::Modifier modifier)
+{
+ unsigned int mods = GAME_KEY_MOD_NONE;
+
+ if (modifier & CKey::MODIFIER_CTRL) mods |= GAME_KEY_MOD_CTRL;
+ if (modifier & CKey::MODIFIER_SHIFT) mods |= GAME_KEY_MOD_SHIFT;
+ if (modifier & CKey::MODIFIER_ALT) mods |= GAME_KEY_MOD_ALT;
+ if (modifier & CKey::MODIFIER_RALT) mods |= GAME_KEY_MOD_RALT;
+ if (modifier & CKey::MODIFIER_META) mods |= GAME_KEY_MOD_META;
+
+ return static_cast<GAME_KEY_MOD>(mods);
+}
+
+const char* CGameClientTranslator::TranslateRegion(GAME_REGION region)
+{
+ switch (region)
+ {
+ case GAME_REGION_NTSC: return "NTSC";
+ case GAME_REGION_PAL: return "PAL";
+ default:
+ break;
+ }
+ return "Unknown";
+}
diff --git a/xbmc/games/addons/GameClientTranslator.h b/xbmc/games/addons/GameClientTranslator.h
new file mode 100644
index 0000000000..55a4c94893
--- /dev/null
+++ b/xbmc/games/addons/GameClientTranslator.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h"
+#include "cores/AudioEngine/Utils/AEChannelData.h"
+#include "input/Key.h"
+
+#include "libavcodec/avcodec.h"
+#include "libavutil/pixfmt.h"
+
+namespace GAME
+{
+ /*!
+ * \ingroup games
+ * \brief Translates data types from Game API to the corresponding format in Kodi.
+ *
+ * This class is stateless.
+ */
+ class CGameClientTranslator
+ {
+ CGameClientTranslator() = delete;
+
+ public:
+ /*!
+ * \brief Translates game errors to string representation (e.g. for logging).
+ * \param error The error to translate.
+ * \return Translated error.
+ */
+ static const char* ToString(GAME_ERROR error);
+
+ /*!
+ * \brief Translate pixel format (Game API to FFMPEG).
+ * \param format The pixel format to translate.
+ * \return Translated pixel format.
+ */
+ static AVPixelFormat TranslatePixelFormat(GAME_PIXEL_FORMAT format);
+
+ /*!
+ * \brief Translate video codec (Game API to FFMPEG).
+ * \param format The video codec to translate.
+ * \return Translated video codec format.
+ */
+ static AVCodecID TranslateVideoCodec(GAME_VIDEO_CODEC codec);
+
+ /*!
+ * \brief Translate audio PCM format (Game API to AudioEngine).
+ * \param format The audio PCM format to translate.
+ * \return Translated audio PCM format.
+ */
+ static AEDataFormat TranslatePCMFormat(GAME_PCM_FORMAT format);
+
+ /*!
+ * \brief Translate audio channels (Game API to AudioEngine).
+ * \param format The audio channels to translate.
+ * \return Translated audio channels.
+ */
+ static AEChannel TranslateAudioChannel(GAME_AUDIO_CHANNEL channel);
+
+ /*!
+ * \brief Translate audio codec (Game API to FFMPEG).
+ * \param format The audio codec to translate.
+ * \return Translated audio codec format.
+ */
+ static AVCodecID TranslateAudioCodec(GAME_AUDIO_CODEC codec);
+
+ /*!
+ * \brief Translate key modifiers (Kodi to Game API).
+ * \param modifiers The key modifiers to translate (e.g. Shift, Ctrl).
+ * \return Translated key modifiers.
+ */
+ static GAME_KEY_MOD GetModifiers(CKey::Modifier modifier);
+
+ /*!
+ * \brief Translate region to string representation (e.g. for logging).
+ * \param error The region to translate (e.g. PAL, NTSC).
+ * \return Translated region.
+ */
+ static const char* TranslateRegion(GAME_REGION region);
+ };
+}
diff --git a/xbmc/games/addons/Makefile b/xbmc/games/addons/Makefile
new file mode 100644
index 0000000000..6dd4b0333e
--- /dev/null
+++ b/xbmc/games/addons/Makefile
@@ -0,0 +1,12 @@
+SRCS=GameClient.cpp \
+ GameClientInput.cpp \
+ GameClientKeyboard.cpp \
+ GameClientMouse.cpp \
+ GameClientProperties.cpp \
+ GameClientTiming.cpp \
+ GameClientTranslator.cpp \
+
+LIB=gameaddons.a
+
+include ../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/games/addons/playback/CMakeLists.txt b/xbmc/games/addons/playback/CMakeLists.txt
new file mode 100644
index 0000000000..c4a7ab4662
--- /dev/null
+++ b/xbmc/games/addons/playback/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(SOURCES GameClientReversiblePlayback.cpp
+ GameLoop.cpp)
+
+set(HEADERS GameClientRealtimePlayback.h
+ GameClientReversiblePlayback.h
+ GameLoop.h
+ IGameClientPlayback.h)
+
+core_add_library(gameplayback)
diff --git a/xbmc/games/addons/playback/GameClientRealtimePlayback.h b/xbmc/games/addons/playback/GameClientRealtimePlayback.h
new file mode 100644
index 0000000000..2f7c7b4b34
--- /dev/null
+++ b/xbmc/games/addons/playback/GameClientRealtimePlayback.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "IGameClientPlayback.h"
+
+namespace GAME
+{
+ class CGameClientRealtimePlayback : public IGameClientPlayback
+ {
+ public:
+ virtual ~CGameClientRealtimePlayback() = default;
+
+ // implementation of IGameClientPlayback
+ virtual bool CanPause() const override { return false; }
+ virtual bool CanSeek() const override { return false; }
+ virtual void PauseUnpause() override { }
+ virtual unsigned int GetTimeMs() const override { return 0; }
+ virtual unsigned int GetTotalTimeMs() const override { return 0; }
+ virtual unsigned int GetCacheTimeMs() const override { return 0; }
+ virtual void SeekTimeMs(unsigned int timeMs) override { }
+ virtual double GetSpeed() const override { return 1.0; }
+ virtual void SetSpeed(double speedFactor) override { }
+ virtual std::string CreateManualSavestate() override { return ""; }
+ virtual bool LoadSavestate(const std::string& path) override { return false; }
+ };
+}
diff --git a/xbmc/games/addons/playback/GameClientReversiblePlayback.cpp b/xbmc/games/addons/playback/GameClientReversiblePlayback.cpp
new file mode 100644
index 0000000000..a75a9c3124
--- /dev/null
+++ b/xbmc/games/addons/playback/GameClientReversiblePlayback.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GameClientReversiblePlayback.h"
+#include "games/addons/GameClient.h"
+#include "games/addons/savestates/BasicMemoryStream.h"
+#include "games/addons/savestates/DeltaPairMemoryStream.h"
+#include "games/addons/savestates/Savestate.h"
+#include "games/addons/savestates/SavestateReader.h"
+#include "games/addons/savestates/SavestateWriter.h"
+#include "games/GameSettings.h"
+#include "settings/Settings.h"
+#include "threads/SingleLock.h"
+#include "utils/MathUtils.h"
+
+#include <algorithm>
+
+using namespace GAME;
+
+#define REWIND_FACTOR 0.25 // Rewind at 25% of gameplay speed
+
+CGameClientReversiblePlayback::CGameClientReversiblePlayback(CGameClient* gameClient, double fps, size_t serializeSize) :
+ m_gameClient(gameClient),
+ m_gameLoop(this, fps),
+ m_savestateWriter(new CSavestateWriter),
+ m_savestateReader(new CSavestateReader),
+ m_totalFrameCount(0),
+ m_pastFrameCount(0),
+ m_futureFrameCount(0),
+ m_playTimeMs(0),
+ m_totalTimeMs(0),
+ m_cacheTimeMs(0)
+{
+ UpdateMemoryStream();
+
+ CGameSettings::GetInstance().RegisterObserver(this);
+
+ m_gameLoop.Start();
+}
+
+CGameClientReversiblePlayback::~CGameClientReversiblePlayback()
+{
+ CGameSettings::GetInstance().UnregisterObserver(this);
+
+ m_gameLoop.Stop();
+}
+
+void CGameClientReversiblePlayback::PauseUnpause()
+{
+ if (GetSpeed() == 0.0)
+ m_gameLoop.SetSpeed(1.0);
+ else
+ m_gameLoop.SetSpeed(0.0);
+}
+
+void CGameClientReversiblePlayback::SeekTimeMs(unsigned int timeMs)
+{
+ const int offsetTimeMs = timeMs - GetTimeMs();
+ const int offsetFrames = MathUtils::round_int(offsetTimeMs / 1000.0 * m_gameLoop.FPS());
+
+ if (offsetFrames > 0)
+ {
+ const unsigned int frames = std::min(static_cast<unsigned int>(offsetFrames), m_futureFrameCount);
+ if (frames > 0)
+ {
+ m_gameLoop.SetSpeed(0.0);
+ AdvanceFrames(frames);
+ m_gameLoop.SetSpeed(1.0);
+ }
+ }
+ else if (offsetFrames < 0)
+ {
+ const unsigned int frames = std::min(static_cast<unsigned int>(-offsetFrames), m_pastFrameCount);
+ if (frames > 0)
+ {
+ m_gameLoop.SetSpeed(0.0);
+ RewindFrames(frames);
+ m_gameLoop.SetSpeed(1.0);
+ }
+ }
+}
+
+double CGameClientReversiblePlayback::GetSpeed() const
+{
+ return m_gameLoop.GetSpeed();
+}
+
+void CGameClientReversiblePlayback::SetSpeed(double speedFactor)
+{
+ if (speedFactor >= 0.0)
+ m_gameLoop.SetSpeed(speedFactor);
+ else
+ m_gameLoop.SetSpeed(speedFactor * REWIND_FACTOR);
+}
+
+std::string CGameClientReversiblePlayback::CreateManualSavestate()
+{
+ std::string empty;
+
+ // Game client must support serialization
+ if (m_gameClient->SerializeSize() == 0)
+ return empty;
+
+ if (!m_savestateWriter->Initialize(m_gameClient, m_totalFrameCount))
+ return empty;
+
+ std::unique_ptr<IMemoryStream> memoryStream;
+ bool bHasMemoryStream = false;
+
+ {
+ CSingleLock lock(m_mutex);
+ if (m_memoryStream)
+ {
+ memoryStream = std::move(m_memoryStream);
+ bHasMemoryStream = true;
+ }
+ else
+ {
+ lock.Leave();
+ memoryStream.reset(new CBasicMemoryStream);
+ memoryStream->Init(m_gameClient->SerializeSize(), 1);
+ }
+ }
+
+ // If memory stream is empty, ask the game client for a frame
+ if (memoryStream->CurrentFrame() == nullptr)
+ {
+ if (m_gameClient->Serialize(memoryStream->BeginFrame(), memoryStream->FrameSize()))
+ memoryStream->SubmitFrame();
+ }
+
+ bool bSuccess = false;
+
+ if (m_savestateWriter->WriteSave(memoryStream.get()))
+ {
+ m_savestateWriter->WriteThumb();
+
+ if (m_savestateWriter->CommitToDatabase())
+ bSuccess = true;
+ else
+ m_savestateWriter->CleanUpTransaction();
+ }
+
+ if (bHasMemoryStream)
+ {
+ CSingleLock lock(m_mutex);
+ m_memoryStream = std::move(memoryStream);
+ }
+
+ return bSuccess ? m_savestateWriter->GetPath() : "";
+}
+
+bool CGameClientReversiblePlayback::LoadSavestate(const std::string& path)
+{
+ // Game client must support serialization
+ if (m_gameClient->SerializeSize() == 0)
+ return false;
+
+ if (!m_savestateReader->Initialize(path, m_gameClient))
+ return false;
+
+ std::unique_ptr<IMemoryStream> memoryStream;
+ bool bHasMemoryStream = false;
+
+ {
+ CSingleLock lock(m_mutex);
+ if (m_memoryStream)
+ {
+ memoryStream = std::move(m_memoryStream);
+ bHasMemoryStream = true;
+ }
+ else
+ {
+ lock.Leave();
+ memoryStream.reset(new CBasicMemoryStream);
+ memoryStream->Init(m_gameClient->SerializeSize(), 1);
+ }
+ }
+
+ bool bSuccess = false;
+
+ if (m_savestateReader->ReadSave(memoryStream.get()))
+ {
+ m_gameClient->Deserialize(memoryStream->CurrentFrame(), memoryStream->FrameSize());
+ m_totalFrameCount = m_savestateReader->GetFrameCount();
+ bSuccess = true;
+ }
+
+ if (bHasMemoryStream)
+ {
+ CSingleLock lock(m_mutex);
+ m_memoryStream = std::move(memoryStream);
+ }
+
+ return bSuccess;
+}
+
+void CGameClientReversiblePlayback::FrameEvent()
+{
+ m_gameClient->RunFrame();
+
+ AddFrame();
+}
+
+void CGameClientReversiblePlayback::RewindEvent()
+{
+ RewindFrames(1);
+
+ m_gameClient->RunFrame();
+}
+
+void CGameClientReversiblePlayback::AddFrame()
+{
+ CSingleLock lock(m_mutex);
+
+ if (m_memoryStream)
+ {
+ if (m_gameClient->Serialize(m_memoryStream->BeginFrame(), m_memoryStream->FrameSize()))
+ {
+ m_memoryStream->SubmitFrame();
+ UpdatePlaybackStats();
+ }
+ }
+
+ m_totalFrameCount++;
+}
+
+void CGameClientReversiblePlayback::RewindFrames(unsigned int frames)
+{
+ CSingleLock lock(m_mutex);
+
+ if (m_memoryStream)
+ {
+ m_memoryStream->RewindFrames(frames);
+ m_gameClient->Deserialize(m_memoryStream->CurrentFrame(), m_memoryStream->FrameSize());
+ UpdatePlaybackStats();
+ }
+
+ m_totalFrameCount -= std::min(m_totalFrameCount, static_cast<uint64_t>(frames));
+}
+
+void CGameClientReversiblePlayback::AdvanceFrames(unsigned int frames)
+{
+ CSingleLock lock(m_mutex);
+
+ if (m_memoryStream)
+ {
+ m_memoryStream->AdvanceFrames(frames);
+ m_gameClient->Deserialize(m_memoryStream->CurrentFrame(), m_memoryStream->FrameSize());
+ UpdatePlaybackStats();
+ }
+
+ m_totalFrameCount += frames;
+}
+
+void CGameClientReversiblePlayback::UpdatePlaybackStats()
+{
+ m_pastFrameCount = m_memoryStream->PastFramesAvailable();
+ m_futureFrameCount = m_memoryStream->FutureFramesAvailable();
+
+ const unsigned int played = m_pastFrameCount + (m_memoryStream->CurrentFrame() ? 1 : 0);
+ const unsigned int total = m_memoryStream->MaxFrameCount();
+ const unsigned int cached = m_futureFrameCount;
+
+ m_playTimeMs = MathUtils::round_int(1000.0 * played / m_gameLoop.FPS());
+ m_totalTimeMs = MathUtils::round_int(1000.0 * total / m_gameLoop.FPS());
+ m_cacheTimeMs = MathUtils::round_int(1000.0 * cached / m_gameLoop.FPS());
+}
+
+void CGameClientReversiblePlayback::Notify(const Observable &obs, const ObservableMessage msg)
+{
+ switch (msg)
+ {
+ case ObservableMessageSettingsChanged:
+ UpdateMemoryStream();
+ break;
+ default:
+ break;
+ }
+}
+
+void CGameClientReversiblePlayback::UpdateMemoryStream()
+{
+ CSingleLock lock(m_mutex);
+
+ bool bRewindEnabled = false;
+
+ if (m_gameClient->SerializeSize() > 0)
+ bRewindEnabled = CSettings::GetInstance().GetBool(CSettings::SETTING_GAMES_ENABLEREWIND);
+
+ if (bRewindEnabled)
+ {
+ unsigned int rewindBufferSec = CSettings::GetInstance().GetInt(CSettings::SETTING_GAMES_REWINDTIME);
+ if (rewindBufferSec < 10)
+ rewindBufferSec = 10; // Sanity check
+
+ unsigned int frameCount = MathUtils::round_int(rewindBufferSec * m_gameLoop.FPS());
+
+ if (!m_memoryStream)
+ {
+ m_memoryStream.reset(new CDeltaPairMemoryStream);
+ m_memoryStream->Init(m_gameClient->SerializeSize(), frameCount);
+ }
+
+ if (m_memoryStream->MaxFrameCount() != frameCount)
+ {
+ m_memoryStream->SetMaxFrameCount(frameCount);
+ }
+ }
+ else
+ {
+ m_memoryStream.reset();
+
+ // Reset playback stats
+ m_pastFrameCount = 0;
+ m_futureFrameCount = 0;
+ m_playTimeMs = 0;
+ m_totalTimeMs = 0;
+ m_cacheTimeMs = 0;
+ }
+}
diff --git a/xbmc/games/addons/playback/GameClientReversiblePlayback.h b/xbmc/games/addons/playback/GameClientReversiblePlayback.h
new file mode 100644
index 0000000000..aea47cbb13
--- /dev/null
+++ b/xbmc/games/addons/playback/GameClientReversiblePlayback.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "IGameClientPlayback.h"
+#include "GameLoop.h"
+#include "threads/CriticalSection.h"
+#include "utils/Observer.h"
+
+#include <memory>
+#include <stddef.h>
+#include <stdint.h>
+
+namespace GAME
+{
+ class CGameClient;
+ class CSavestateReader;
+ class CSavestateWriter;
+ class IMemoryStream;
+
+ class CGameClientReversiblePlayback : public IGameClientPlayback,
+ public IGameLoopCallback,
+ public Observer
+ {
+ public:
+ CGameClientReversiblePlayback(CGameClient* gameClient, double fps, size_t serializeSize);
+
+ virtual ~CGameClientReversiblePlayback();
+
+ // implementation of IGameClientPlayback
+ virtual bool CanPause() const override { return true; }
+ virtual bool CanSeek() const override { return true; }
+ virtual void PauseUnpause() override;
+ virtual unsigned int GetTimeMs() const override { return m_playTimeMs; }
+ virtual unsigned int GetTotalTimeMs() const override { return m_totalTimeMs; }
+ virtual unsigned int GetCacheTimeMs() const override { return m_cacheTimeMs; }
+ virtual void SeekTimeMs(unsigned int timeMs) override;
+ virtual double GetSpeed() const override;
+ virtual void SetSpeed(double speedFactor) override;
+ virtual std::string CreateManualSavestate() override;
+ virtual bool LoadSavestate(const std::string& path) override;
+
+ // implementation of IGameLoopCallback
+ virtual void FrameEvent() override;
+ virtual void RewindEvent() override;
+
+ // implementation of Observer
+ virtual void Notify(const Observable &obs, const ObservableMessage msg) override;
+
+ private:
+ void AddFrame();
+ void RewindFrames(unsigned int frames);
+ void AdvanceFrames(unsigned int frames);
+ void UpdatePlaybackStats();
+ void UpdateMemoryStream();
+
+ // Construction parameter
+ CGameClient* const m_gameClient;
+
+ // Gameplay functionality
+ CGameLoop m_gameLoop;
+ std::unique_ptr<IMemoryStream> m_memoryStream;
+ CCriticalSection m_mutex;
+
+ // Savestate functionality
+ std::unique_ptr<CSavestateWriter> m_savestateWriter;
+ std::unique_ptr<CSavestateReader> m_savestateReader;
+
+ // Playback stats
+ uint64_t m_totalFrameCount;
+ unsigned int m_pastFrameCount;
+ unsigned int m_futureFrameCount;
+ unsigned int m_playTimeMs;
+ unsigned int m_totalTimeMs;
+ unsigned int m_cacheTimeMs;
+ };
+}
diff --git a/xbmc/games/addons/playback/GameLoop.cpp b/xbmc/games/addons/playback/GameLoop.cpp
new file mode 100644
index 0000000000..f091b32da1
--- /dev/null
+++ b/xbmc/games/addons/playback/GameLoop.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GameLoop.h"
+#include "games/addons/GameClient.h"
+#include "threads/SingleLock.h"
+#include "threads/SystemClock.h"
+
+#include <cmath>
+
+using namespace GAME;
+
+#define DEFAULT_FPS 60 // In case fps is 0 (shouldn't happen)
+#define FOREVER_MS (7 * 24 * 60 * 60 * 1000) // 1 week is large enough
+
+CGameLoop::CGameLoop(IGameLoopCallback* callback, double fps) :
+ CThread("GameLoop"),
+ m_callback(callback),
+ m_fps(fps ? fps : DEFAULT_FPS),
+ m_speedFactor(0.0),
+ m_lastFrameMs(0.0)
+{
+}
+
+void CGameLoop::Start()
+{
+ Create();
+}
+
+void CGameLoop::Stop()
+{
+ StopThread(false);
+ m_sleepEvent.Set();
+ StopThread(true);
+}
+
+void CGameLoop::SetSpeed(double speedFactor)
+{
+ {
+ CSingleLock lock(m_mutex);
+ m_speedFactor = speedFactor;
+ }
+ m_sleepEvent.Set();
+}
+
+void CGameLoop::Process(void)
+{
+ double nextFrameMs = NowMs();
+
+ CSingleLock lock(m_mutex);
+
+ while (!m_bStop)
+ {
+ double speedFactor = m_speedFactor;
+
+ {
+ CSingleExit exit(m_mutex);
+ if (speedFactor > 0.0)
+ m_callback->FrameEvent();
+ else if (speedFactor < 0.0)
+ m_callback->RewindEvent();
+ }
+
+ // Record frame time
+ m_lastFrameMs = nextFrameMs;
+
+ // Calculate sleep time
+ double nowMs = NowMs();
+ double sleepTimeMs = SleepTimeMs(nowMs);
+
+ // Sleep at least 1 ms to avoid sleeping forever
+ while (sleepTimeMs > 1.0)
+ {
+ {
+ CSingleExit exit(m_mutex);
+ m_sleepEvent.WaitMSec(static_cast<unsigned int>(sleepTimeMs));
+ }
+
+ if (m_bStop)
+ break;
+
+ // Speed may have changed, update sleep time
+ nowMs = NowMs();
+ sleepTimeMs = SleepTimeMs(nowMs);
+ }
+
+ // Calculate next frame time
+ nextFrameMs += FrameTimeMs();
+
+ // If sleep time goes negative, we fell behind, so fast-forward to now
+ if (sleepTimeMs < 0.0)
+ nextFrameMs = nowMs;
+ }
+}
+
+double CGameLoop::FrameTimeMs() const
+{
+ if (m_speedFactor != 0.0)
+ return 1000.0 / m_fps / std::abs(m_speedFactor);
+ else
+ return FOREVER_MS;
+}
+
+double CGameLoop::SleepTimeMs(double nowMs) const
+{
+ // Calculate next frame time
+ const double nextFrameMs = m_lastFrameMs + FrameTimeMs();
+
+ // Calculate sleep time
+ const double sleepTimeMs = nextFrameMs - nowMs;
+
+ return sleepTimeMs;
+}
+
+double CGameLoop::NowMs() const
+{
+ return static_cast<double>(XbmcThreads::SystemClockMillis());
+}
diff --git a/xbmc/games/addons/playback/GameLoop.h b/xbmc/games/addons/playback/GameLoop.h
new file mode 100644
index 0000000000..70ca8b6419
--- /dev/null
+++ b/xbmc/games/addons/playback/GameLoop.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "threads/CriticalSection.h"
+#include "threads/Event.h"
+#include "threads/Thread.h"
+
+namespace GAME
+{
+ class IGameLoopCallback
+ {
+ public:
+ virtual ~IGameLoopCallback() = default;
+
+ /*!
+ * \brief The next frame is being shown
+ */
+ virtual void FrameEvent() = 0;
+
+ /*!
+ * \brief The prior frame is being shown
+ */
+ virtual void RewindEvent() = 0;
+ };
+
+ class CGameLoop : protected CThread
+ {
+ public:
+ CGameLoop(IGameLoopCallback* callback, double fps);
+
+ void Start();
+ void Stop();
+
+ double FPS() const { return m_fps; }
+
+ double GetSpeed() const { return m_speedFactor; }
+ void SetSpeed(double speedFactor);
+
+ protected:
+ // implementation of CThread
+ virtual void Process() override;
+
+ private:
+ double FrameTimeMs() const;
+ double SleepTimeMs(double nowMs) const;
+ double NowMs() const;
+
+ IGameLoopCallback* const m_callback;
+ const double m_fps;
+ double m_speedFactor;
+ double m_lastFrameMs;
+ CEvent m_sleepEvent;
+ CCriticalSection m_mutex;
+ };
+}
diff --git a/xbmc/games/addons/playback/IGameClientPlayback.h b/xbmc/games/addons/playback/IGameClientPlayback.h
new file mode 100644
index 0000000000..4e9b09e6bc
--- /dev/null
+++ b/xbmc/games/addons/playback/IGameClientPlayback.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include <stdint.h>
+#include <string>
+
+namespace GAME
+{
+ class IGameClientPlayback
+ {
+ public:
+ virtual ~IGameClientPlayback() = default;
+
+ // Playback capabilities
+ virtual bool CanPause() const = 0;
+ virtual bool CanSeek() const = 0;
+
+ // Control playback
+ virtual void PauseUnpause() = 0;
+ virtual unsigned int GetTimeMs() const = 0;
+ virtual unsigned int GetTotalTimeMs() const = 0;
+ virtual unsigned int GetCacheTimeMs() const = 0;
+ virtual void SeekTimeMs(unsigned int timeMs) = 0;
+ virtual double GetSpeed() const = 0;
+ virtual void SetSpeed(double speedFactor) = 0;
+
+ // Savestates
+ virtual std::string CreateManualSavestate() = 0; // Returns the path of savestate on success
+ virtual bool LoadSavestate(const std::string& path) = 0;
+ };
+}
diff --git a/xbmc/games/addons/playback/Makefile b/xbmc/games/addons/playback/Makefile
new file mode 100644
index 0000000000..3a92133b31
--- /dev/null
+++ b/xbmc/games/addons/playback/Makefile
@@ -0,0 +1,7 @@
+SRCS=GameClientReversiblePlayback.cpp \
+ GameLoop.cpp \
+
+LIB=gameaddonplayback.a
+
+include ../../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/games/addons/savestates/BasicMemoryStream.cpp b/xbmc/games/addons/savestates/BasicMemoryStream.cpp
new file mode 100644
index 0000000000..057050dad6
--- /dev/null
+++ b/xbmc/games/addons/savestates/BasicMemoryStream.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "BasicMemoryStream.h"
+
+using namespace GAME;
+
+CBasicMemoryStream::CBasicMemoryStream()
+{
+ Reset();
+}
+
+void CBasicMemoryStream::Init(size_t frameSize, size_t maxFrameCount)
+{
+ Reset();
+
+ m_frameSize = frameSize;
+}
+
+void CBasicMemoryStream::Reset()
+{
+ m_frameSize = 0;
+ m_frameBuffer.reset();
+ m_bHasFrame = false;
+}
+
+uint8_t* CBasicMemoryStream::BeginFrame()
+{
+ if (m_frameSize == 0)
+ return nullptr;
+
+ if (!m_frameBuffer)
+ m_frameBuffer.reset(new uint8_t[m_frameSize]);
+
+ m_bHasFrame = false;
+
+ return m_frameBuffer.get();
+}
+
+void CBasicMemoryStream::SubmitFrame()
+{
+ if (m_frameBuffer)
+ m_bHasFrame = true;
+}
+
+const uint8_t* CBasicMemoryStream::CurrentFrame() const
+{
+ return m_bHasFrame ? m_frameBuffer.get() : nullptr;
+}
diff --git a/xbmc/games/addons/savestates/BasicMemoryStream.h b/xbmc/games/addons/savestates/BasicMemoryStream.h
new file mode 100644
index 0000000000..2191bb791b
--- /dev/null
+++ b/xbmc/games/addons/savestates/BasicMemoryStream.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "IMemoryStream.h"
+
+#include <memory>
+
+namespace GAME
+{
+ class CBasicMemoryStream : public IMemoryStream
+ {
+ public:
+ CBasicMemoryStream();
+
+ virtual ~CBasicMemoryStream() = default;
+
+ // implementation of IMemoryStream
+ virtual void Init(size_t frameSize, size_t maxFrameCount) override;
+ virtual void Reset() override;
+ virtual size_t FrameSize() const override { return m_frameSize; }
+ virtual unsigned int MaxFrameCount() const override { return 1; }
+ virtual void SetMaxFrameCount(size_t maxFrameCount) override { }
+ virtual uint8_t* BeginFrame() override;
+ virtual void SubmitFrame() override;
+ virtual const uint8_t* CurrentFrame() const override;
+ virtual unsigned int FutureFramesAvailable() const override { return 0; }
+ virtual unsigned int AdvanceFrames(unsigned int frameCount) override { return 0; }
+ virtual unsigned int PastFramesAvailable() const override { return 0; }
+ virtual unsigned int RewindFrames(unsigned int frameCount) override { return 0; }
+ virtual uint64_t GetFrameCounter() const override { return 0; }
+ virtual void SetFrameCounter(uint64_t frameCount) override { };
+
+ private:
+ size_t m_frameSize;
+ std::unique_ptr<uint8_t[]> m_frameBuffer;
+ bool m_bHasFrame;
+ };
+}
diff --git a/xbmc/games/addons/savestates/CMakeLists.txt b/xbmc/games/addons/savestates/CMakeLists.txt
new file mode 100644
index 0000000000..984bf423ac
--- /dev/null
+++ b/xbmc/games/addons/savestates/CMakeLists.txt
@@ -0,0 +1,23 @@
+set(SOURCES BasicMemoryStream.cpp
+ DeltaPairMemoryStream.cpp
+ LinearMemoryStream.cpp
+ Savestate.cpp
+ SavestateDatabase.cpp
+ SavestateReader.cpp
+ SavestateTranslator.cpp
+ SavestateUtils.cpp
+ SavestateWriter.cpp)
+
+set(HEADERS BasicMemoryStream.h
+ DeltaPairMemoryStream.h
+ IMemoryStream.h
+ LinearMemoryStream.h
+ Savestate.h
+ SavestateDatabase.h
+ SavestateDefines.h
+ SavestateReader.h
+ SavestateTranslator.h
+ SavestateUtils.h
+ SavestateWriter.h)
+
+core_add_library(gamesavestates)
diff --git a/xbmc/games/addons/savestates/DeltaPairMemoryStream.cpp b/xbmc/games/addons/savestates/DeltaPairMemoryStream.cpp
new file mode 100644
index 0000000000..4cc303f3e2
--- /dev/null
+++ b/xbmc/games/addons/savestates/DeltaPairMemoryStream.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "DeltaPairMemoryStream.h"
+#include "utils/log.h"
+
+using namespace GAME;
+
+void CDeltaPairMemoryStream::Reset()
+{
+ CLinearMemoryStream::Reset();
+
+ m_rewindBuffer.clear();
+}
+
+void CDeltaPairMemoryStream::SubmitFrameInternal()
+{
+ m_rewindBuffer.push_back(MemoryFrame());
+ MemoryFrame& frame = m_rewindBuffer.back();
+
+ // Record frame history
+ frame.frameHistoryCount = m_currentFrameHistory++;
+
+ uint32_t* currentFrame = m_currentFrame.get();
+ uint32_t* nextFrame = m_nextFrame.get();
+
+ for (size_t i = 0; i < m_paddedFrameSize; i++)
+ {
+ uint32_t xor_val = currentFrame[i] ^ nextFrame[i];
+ if (xor_val)
+ {
+ DeltaPair pair = { i, xor_val };
+ frame.buffer.push_back(pair);
+ }
+ }
+
+ // Delta is generated, bring the new frame forward (m_nextFrame is now disposable)
+ std::swap(m_currentFrame, m_nextFrame);
+
+ m_bHasNextFrame = false;
+
+ if (PastFramesAvailable() + 1 > MaxFrameCount())
+ CullPastFrames(1);
+}
+
+unsigned int CDeltaPairMemoryStream::PastFramesAvailable() const
+{
+ return static_cast<unsigned int>(m_rewindBuffer.size());
+}
+
+unsigned int CDeltaPairMemoryStream::RewindFrames(unsigned int frameCount)
+{
+ unsigned int rewound;
+
+ for (rewound = 0; rewound < frameCount; rewound++)
+ {
+ if (m_rewindBuffer.empty())
+ break;
+
+ const MemoryFrame& frame = m_rewindBuffer.back();
+ const DeltaPair* buffer = frame.buffer.data();
+
+ size_t bufferSize = frame.buffer.size();
+
+ // buffer pointer redirection violates data-dependency requirements...
+ // no vectorization for us :(
+ for (size_t i = 0; i < bufferSize; i++)
+ m_currentFrame[buffer[i].pos] ^= buffer[i].delta;
+
+ // Restore frame history
+ m_currentFrameHistory = frame.frameHistoryCount;
+
+ m_rewindBuffer.pop_back();
+ }
+
+ return rewound;
+}
+
+void CDeltaPairMemoryStream::CullPastFrames(unsigned int frameCount)
+{
+ for (unsigned int removedCount = 0; removedCount < frameCount; removedCount++)
+ {
+ if (m_rewindBuffer.empty())
+ {
+ CLog::Log(LOGDEBUG, "CDeltaPairMemoryStream: Tried to cull %d frames too many. Check your math!", frameCount - removedCount);
+ break;
+ }
+ m_rewindBuffer.pop_front();
+ }
+}
diff --git a/xbmc/games/addons/savestates/DeltaPairMemoryStream.h b/xbmc/games/addons/savestates/DeltaPairMemoryStream.h
new file mode 100644
index 0000000000..b4ce01426c
--- /dev/null
+++ b/xbmc/games/addons/savestates/DeltaPairMemoryStream.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "LinearMemoryStream.h"
+
+#include <deque>
+#include <vector>
+
+namespace GAME
+{
+ /*!
+ * \brief Implementation of a linear memory stream using XOR deltas
+ */
+ class CDeltaPairMemoryStream : public CLinearMemoryStream
+ {
+ public:
+ CDeltaPairMemoryStream() = default;
+
+ virtual ~CDeltaPairMemoryStream() = default;
+
+ // implementation of IMemoryStream via CLinearMemoryStream
+ virtual void Reset() override;
+ virtual unsigned int PastFramesAvailable() const override;
+ virtual unsigned int RewindFrames(unsigned int frameCount) override;
+
+ protected:
+ // implementation of CLinearMemoryStream
+ virtual void SubmitFrameInternal() override;
+ virtual void CullPastFrames(unsigned int frameCount) override;
+
+ /*!
+ * Rewinding is implemented by applying XOR deltas on the specific parts of
+ * the save state buffer which have changed. In practice, this is very fast
+ * and simple (linear scan) and allows deltas to be compressed down to 1-3%
+ * of original save state size depending on the system. The algorithm runs
+ * on 32 bits at a time for speed.
+ *
+ * Use std::deque here to achieve amortized O(1) on pop/push to front and
+ * back.
+ */
+ struct DeltaPair
+ {
+ size_t pos;
+ uint32_t delta;
+ };
+
+ typedef std::vector<DeltaPair> DeltaPairVector;
+
+ struct MemoryFrame
+ {
+ DeltaPairVector buffer;
+ uint64_t frameHistoryCount;
+ };
+
+ std::deque<MemoryFrame> m_rewindBuffer;
+ };
+}
diff --git a/xbmc/games/addons/savestates/IMemoryStream.h b/xbmc/games/addons/savestates/IMemoryStream.h
new file mode 100644
index 0000000000..b5523ea93c
--- /dev/null
+++ b/xbmc/games/addons/savestates/IMemoryStream.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace GAME
+{
+ /*!
+ * \brief Stream of serialized states from game clients
+ *
+ * A memory stream is composed of "frames" of memory representing serialized
+ * states of the game client. For each video frame run by the game loop, the
+ * game client's state is serialized into a buffer provided by this interface.
+ *
+ * Implementation of three types of memory streams are provided:
+ *
+ * - Basic memory stream: has only a current frame, and supports neither
+ * rewind nor forward seeking.
+ *
+ * \sa CBasicMemoryStream
+ *
+ * - Linear memory stream: can grow in one direction. It is possible to
+ * rewind, but not fast-forward.
+ *
+ * \sa CLinearMemoryStream
+ *
+ * - Nonlinear memory stream: can have frames both ahead of and behind
+ * the current frame. If a stream is rewound, it is possible to
+ * recover these frames by seeking forward again.
+ *
+ * \sa CNonlinearMemoryStream (TODO)
+ */
+ class IMemoryStream
+ {
+ public:
+ virtual ~IMemoryStream() = default;
+
+ /*!
+ * \brief Initialize memory stream
+ *
+ * \param frameSize The size of the serialized memory state
+ * \param maxFrameCount The maximum number of frames this steam can hold
+ */
+ virtual void Init(size_t frameSize, size_t maxFrameCount) = 0;
+
+ /*!
+ * \brief Free any resources used by this stream
+ */
+ virtual void Reset() = 0;
+
+ /*!
+ * \brief Return the frame size passed to Init()
+ */
+ virtual size_t FrameSize() const = 0;
+
+ /*!
+ * \brief Return the current max frame count
+ */
+ virtual unsigned int MaxFrameCount() const = 0;
+
+ /*!
+ * \brief Update the max frame count
+ *
+ * Old frames may be deleted if the max frame count is reduced.
+ */
+ virtual void SetMaxFrameCount(size_t maxFrameCount) = 0;
+
+ /*!
+ * \ brief Get a pointer to which FrameSize() bytes can be written
+ *
+ * The buffer exposed by this function is passed to the game client, which
+ * fills it with a serialization of its current state.
+ */
+ virtual uint8_t* BeginFrame() = 0;
+
+ /*!
+ * \brief Indicate that a frame of size FrameSize() has been written to the
+ * location returned from BeginFrame()
+ */
+ virtual void SubmitFrame() = 0;
+
+ /*!
+ * \brief Get a pointer to the current frame
+ *
+ * This function must have no side effects. The pointer is valid until the
+ * stream is modified.
+ *
+ * \return A buffer of size FrameSize(), or nullptr if the stream is empty
+ */
+ virtual const uint8_t* CurrentFrame() const = 0;
+
+ /*!
+ * \brief Return the number of frames ahead of the current frame
+ *
+ * If the stream supports forward seeking, frames that are passed over
+ * during a "rewind" operation can be recovered again.
+ */
+ virtual unsigned int FutureFramesAvailable() const = 0;
+
+ /*!
+ * \brief Seek ahead the specified number of frames
+ *
+ * \return The number of frames advanced
+ */
+ virtual unsigned int AdvanceFrames(unsigned int frameCount) = 0;
+
+ /*!
+ * \brief Return the number of frames behind the current frame
+ */
+ virtual unsigned int PastFramesAvailable() const = 0;
+
+ /*!
+ * \brief Seek backwards the specified number of frames
+ *
+ * \return The number of frames rewound
+ */
+ virtual unsigned int RewindFrames(unsigned int frameCount) = 0;
+
+ /*!
+ * \brief Get the total number of frames played until the current frame
+ *
+ * \return The history of the current frame, or 0 for unknown
+ */
+ virtual uint64_t GetFrameCounter() const = 0;
+
+ /*!
+ * \brief Set the total number of frames played until the current frame
+ *
+ * \param frameCount The history of the current frame
+ */
+ virtual void SetFrameCounter(uint64_t frameCount) = 0;
+ };
+}
diff --git a/xbmc/games/addons/savestates/LinearMemoryStream.cpp b/xbmc/games/addons/savestates/LinearMemoryStream.cpp
new file mode 100644
index 0000000000..2e54859112
--- /dev/null
+++ b/xbmc/games/addons/savestates/LinearMemoryStream.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "LinearMemoryStream.h"
+
+using namespace GAME;
+
+// Pad forward to nearest boundary of bytes
+#define PAD_TO_CEIL(x, bytes) (((x) + (bytes) - 1) / (bytes))
+
+CLinearMemoryStream::CLinearMemoryStream()
+{
+ Reset();
+}
+
+void CLinearMemoryStream::Init(size_t frameSize, size_t maxFrameCount)
+{
+ Reset();
+
+ m_frameSize = frameSize;
+ m_paddedFrameSize = PAD_TO_CEIL(m_frameSize, sizeof(uint32_t));
+ m_maxFrames = maxFrameCount;
+}
+
+void CLinearMemoryStream::Reset()
+{
+ m_frameSize = 0;
+ m_paddedFrameSize = 0;
+ m_maxFrames = 0;
+ m_currentFrame.reset();
+ m_nextFrame.reset();
+ m_bHasCurrentFrame = false;
+ m_bHasNextFrame = false;
+ m_currentFrameHistory = 0;
+}
+
+void CLinearMemoryStream::SetMaxFrameCount(size_t maxFrameCount)
+{
+ if (maxFrameCount == 0)
+ {
+ Reset();
+ }
+ else
+ {
+ const unsigned int frameCount = BufferSize();
+ if (maxFrameCount < frameCount)
+ CullPastFrames(frameCount - maxFrameCount);
+ }
+
+ m_maxFrames = maxFrameCount;
+}
+
+uint8_t* CLinearMemoryStream::BeginFrame()
+{
+ if (m_paddedFrameSize == 0)
+ return nullptr;
+
+ if (!m_bHasCurrentFrame)
+ {
+ if (!m_currentFrame)
+ m_currentFrame.reset(new uint32_t[m_paddedFrameSize]);
+ return reinterpret_cast<uint8_t*>(m_currentFrame.get());
+ }
+
+ if (!m_nextFrame)
+ m_nextFrame.reset(new uint32_t[m_paddedFrameSize]);
+ return reinterpret_cast<uint8_t*>(m_nextFrame.get());
+}
+
+const uint8_t* CLinearMemoryStream::CurrentFrame() const
+{
+ if (m_bHasCurrentFrame)
+ return reinterpret_cast<const uint8_t*>(m_currentFrame.get());
+
+ return nullptr;
+}
+
+void CLinearMemoryStream::SubmitFrame()
+{
+ if (!m_bHasCurrentFrame)
+ {
+ m_bHasCurrentFrame = true;
+ }
+ else if (!m_bHasNextFrame)
+ {
+ m_bHasNextFrame = true;
+ }
+
+ if (m_bHasNextFrame)
+ {
+ SubmitFrameInternal();
+ }
+}
+
+unsigned int CLinearMemoryStream::BufferSize() const
+{
+ return PastFramesAvailable() + (m_bHasCurrentFrame ? 1 : 0);
+}
diff --git a/xbmc/games/addons/savestates/LinearMemoryStream.h b/xbmc/games/addons/savestates/LinearMemoryStream.h
new file mode 100644
index 0000000000..f5c5578667
--- /dev/null
+++ b/xbmc/games/addons/savestates/LinearMemoryStream.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "IMemoryStream.h"
+
+#include <memory>
+
+namespace GAME
+{
+ class CLinearMemoryStream : public IMemoryStream
+ {
+ public:
+ CLinearMemoryStream();
+
+ virtual ~CLinearMemoryStream() = default;
+
+ // partial implementation of IMemoryStream
+ virtual void Init(size_t frameSize, size_t maxFrameCount) override;
+ virtual void Reset() override;
+ virtual size_t FrameSize() const override { return m_frameSize; }
+ virtual unsigned int MaxFrameCount() const override { return m_maxFrames; }
+ virtual void SetMaxFrameCount(size_t maxFrameCount) override;
+ virtual uint8_t* BeginFrame() override;
+ virtual void SubmitFrame() override;
+ virtual const uint8_t* CurrentFrame() const override;
+ virtual unsigned int FutureFramesAvailable() const override { return 0; }
+ virtual unsigned int AdvanceFrames(unsigned int frameCount) override { return 0; }
+ virtual unsigned int PastFramesAvailable() const override = 0;
+ virtual unsigned int RewindFrames(unsigned int frameCount) override = 0;
+ virtual uint64_t GetFrameCounter() const override { return m_currentFrameHistory; }
+ virtual void SetFrameCounter(uint64_t frameCount) override { m_currentFrameHistory = frameCount; }
+
+ protected:
+ virtual void SubmitFrameInternal() = 0;
+ virtual void CullPastFrames(unsigned int frameCount) = 0;
+
+ // Helper function
+ unsigned int BufferSize() const;
+
+ size_t m_paddedFrameSize;
+ unsigned int m_maxFrames;
+
+ /**
+ * Simple double-buffering. After XORing the two states, the next becomes
+ * the current, and the current becomes a buffer for the next call to
+ * CGameClient::Serialize().
+ */
+ std::unique_ptr<uint32_t[]> m_currentFrame;
+ std::unique_ptr<uint32_t[]> m_nextFrame;
+ bool m_bHasCurrentFrame;
+ bool m_bHasNextFrame;
+
+ uint64_t m_currentFrameHistory;
+
+ private:
+ size_t m_frameSize;
+ };
+}
diff --git a/xbmc/games/addons/savestates/Makefile b/xbmc/games/addons/savestates/Makefile
new file mode 100644
index 0000000000..178e6db8a4
--- /dev/null
+++ b/xbmc/games/addons/savestates/Makefile
@@ -0,0 +1,14 @@
+SRCS=BasicMemoryStream.cpp \
+ DeltaPairMemoryStream.cpp \
+ LinearMemoryStream.cpp \
+ Savestate.cpp \
+ SavestateDatabase.cpp \
+ SavestateReader.cpp \
+ SavestateTranslator.cpp \
+ SavestateUtils.cpp \
+ SavestateWriter.cpp \
+
+LIB=gamesavestates.a
+
+include ../../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/games/addons/savestates/Savestate.cpp b/xbmc/games/addons/savestates/Savestate.cpp
new file mode 100644
index 0000000000..763382ea71
--- /dev/null
+++ b/xbmc/games/addons/savestates/Savestate.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Savestate.h"
+#include "SavestateDefines.h"
+#include "SavestateTranslator.h"
+#include "utils/log.h"
+#include "utils/Variant.h"
+#include "utils/XMLUtils.h"
+
+#include <tinyxml.h>
+
+using namespace GAME;
+
+void CSavestate::Reset()
+{
+ m_path.clear();
+ m_type = SAVETYPE::UNKNOWN;
+ m_slot = -1;
+ m_label.clear();
+ m_size = 0;
+ m_gameClient.clear();
+ m_databaseId = -1;
+ m_gamePath.clear();
+ m_gameCRC.clear();
+ m_playtimeFrames = 0;
+ m_playtimeWallClock = 0.0;
+ m_timestamp.Reset();
+ m_thumbnail.clear();
+}
+
+void CSavestate::Serialize(CVariant& value) const
+{
+ value[SAVESTATE_FIELD_PATH] = m_path;
+ value[SAVESTATE_FIELD_TYPE] = static_cast<unsigned int>(m_type);
+ value[SAVESTATE_FIELD_SLOT] = m_slot;
+ value[SAVESTATE_FIELD_LABEL] = m_label;
+ value[SAVESTATE_FIELD_SIZE] = static_cast<unsigned int>(m_size);
+ value[SAVESTATE_FIELD_GAMECLIENT] = m_gameClient;
+ value[SAVESTATE_FIELD_DB_ID] = m_databaseId;
+ value[SAVESTATE_FIELD_GAME_PATH] = m_gamePath;
+ value[SAVESTATE_FIELD_GAME_CRC] = m_gameCRC;
+ value[SAVESTATE_FIELD_FRAMES] = m_playtimeFrames;
+ value[SAVESTATE_FIELD_WALLCLOCK] = m_playtimeWallClock;
+ value[SAVESTATE_FIELD_TIMESTAMP] = m_timestamp.GetAsDBDateTime();
+ value[SAVESTATE_FIELD_THUMBNAIL] = m_thumbnail;
+}
+
+void CSavestate::Deserialize(const CVariant& value)
+{
+ m_path = value[SAVESTATE_FIELD_PATH].asString();
+ m_type = static_cast<SAVETYPE>(value[SAVESTATE_FIELD_TYPE].asInteger());
+ m_slot = static_cast<int>(value[SAVESTATE_FIELD_SLOT].asInteger());
+ m_label = value[SAVESTATE_FIELD_LABEL].asString();
+ m_size = static_cast<size_t>(value[SAVESTATE_FIELD_SIZE].asUnsignedInteger());
+ m_gameClient = value[SAVESTATE_FIELD_GAMECLIENT].asString();
+ m_databaseId = static_cast<int>(value[SAVESTATE_FIELD_DB_ID].asInteger());
+ m_gamePath = value[SAVESTATE_FIELD_GAME_PATH].asString();
+ m_gameCRC = value[SAVESTATE_FIELD_GAME_CRC].asString();
+ m_playtimeFrames = value[SAVESTATE_FIELD_FRAMES].asUnsignedInteger();
+ m_playtimeWallClock = value[SAVESTATE_FIELD_WALLCLOCK].asDouble();
+ m_timestamp.SetFromDBDateTime(value[SAVESTATE_FIELD_TIMESTAMP].asString());
+ m_thumbnail = value[SAVESTATE_FIELD_THUMBNAIL].asString();
+}
+
+bool CSavestate::Serialize(const std::string& path) const
+{
+ if (m_type == SAVETYPE::UNKNOWN)
+ {
+ CLog::Log(LOGERROR, "Failed to serialize savestate (unknown type)");
+ return false;
+ }
+
+ TiXmlDocument xmlFile;
+
+ TiXmlDeclaration* decl = new TiXmlDeclaration("1.0", "", "");
+ xmlFile.LinkEndChild(decl);
+
+ TiXmlElement rootElement(SAVESTATE_XML_ROOT);
+ TiXmlNode* root = xmlFile.InsertEndChild(rootElement);
+ if (root == NULL)
+ return false;
+
+ TiXmlElement* pElement = root->ToElement();
+ if (!pElement)
+ return false;
+
+ XMLUtils::SetString(pElement, SAVESTATE_FIELD_PATH, m_path);
+ XMLUtils::SetString(pElement, SAVESTATE_FIELD_TYPE, CSavestateTranslator::TranslateType(m_type));
+ if (m_type == SAVETYPE::SLOT)
+ XMLUtils::SetInt(pElement, SAVESTATE_FIELD_SLOT, m_slot);
+ XMLUtils::SetString(pElement, SAVESTATE_FIELD_LABEL, m_label);
+ XMLUtils::SetLong(pElement, SAVESTATE_FIELD_SIZE, m_size);
+ XMLUtils::SetString(pElement, SAVESTATE_FIELD_GAMECLIENT, m_gameClient);
+ XMLUtils::SetString(pElement, SAVESTATE_FIELD_GAME_PATH, m_gamePath);
+ XMLUtils::SetString(pElement, SAVESTATE_FIELD_GAME_CRC, m_gameCRC);
+ XMLUtils::SetLong(pElement, SAVESTATE_FIELD_FRAMES, static_cast<long>(m_playtimeFrames));
+ XMLUtils::SetFloat(pElement, SAVESTATE_FIELD_WALLCLOCK, static_cast<float>(m_playtimeWallClock));
+ XMLUtils::SetString(pElement, SAVESTATE_FIELD_TIMESTAMP, m_timestamp.GetAsDBDateTime());
+ XMLUtils::SetString(pElement, SAVESTATE_FIELD_THUMBNAIL, m_thumbnail);
+
+ if (!xmlFile.SaveFile(path))
+ {
+ CLog::Log(LOGERROR, "Failed to serialize savestate to %s: %s", path.c_str(), xmlFile.ErrorDesc());
+ return false;
+ }
+
+ return true;
+}
+
+bool CSavestate::Deserialize(const std::string& path)
+{
+ Reset();
+
+ TiXmlDocument xmlFile;
+ if (!xmlFile.LoadFile(path))
+ {
+ CLog::Log(LOGERROR, "Failed to open %s: %s", path.c_str(), xmlFile.ErrorDesc());
+ return false;
+ }
+
+ TiXmlElement* pElement = xmlFile.RootElement();
+ if (!pElement || pElement->NoChildren() || pElement->ValueStr() != SAVESTATE_XML_ROOT)
+ {
+ CLog::Log(LOGERROR, "Can't find root <%s> tag", SAVESTATE_XML_ROOT);
+ return false;
+ }
+
+ // Path
+ if (!XMLUtils::GetString(pElement, SAVESTATE_FIELD_PATH, m_path))
+ {
+ CLog::Log(LOGERROR, "Savestate has no <%s> element", SAVESTATE_FIELD_PATH);
+ return false;
+ }
+
+ // Type
+ std::string type;
+ if (!XMLUtils::GetString(pElement, SAVESTATE_FIELD_TYPE, type))
+ {
+ CLog::Log(LOGERROR, "Savestate has no <%s> element", SAVESTATE_FIELD_TYPE);
+ return false;
+ }
+ m_type = CSavestateTranslator::TranslateType(type);
+ if (m_type == SAVETYPE::UNKNOWN)
+ {
+ CLog::Log(LOGERROR, "Invalid savestate type: %s", type.c_str());
+ return false;
+ }
+
+ // Slot
+ if (m_type == SAVETYPE::SLOT)
+ {
+ if (!XMLUtils::GetInt(pElement, SAVESTATE_FIELD_SLOT, m_slot))
+ {
+ CLog::Log(LOGERROR, "Savestate has type \"%s\" but no <%s> element!", type.c_str(), SAVESTATE_FIELD_TYPE);
+ return false;
+ }
+ if (m_slot < 0)
+ {
+ CLog::Log(LOGERROR, "Invalid savestate slot: %d", m_slot);
+ return false;
+ }
+ }
+
+ // Label (optional)
+ XMLUtils::GetString(pElement, SAVESTATE_FIELD_LABEL, m_label);
+
+ // Size
+ long size;
+ if (!XMLUtils::GetLong(pElement, SAVESTATE_FIELD_SIZE, size))
+ {
+ CLog::Log(LOGERROR, "Savestate has no <%s> element", SAVESTATE_FIELD_SIZE);
+ return false;
+ }
+ if (size < 0)
+ {
+ CLog::Log(LOGERROR, "Invalid savestate size: %ld", size);
+ return false;
+ }
+ m_size = size;
+
+ // Game client
+ if (!XMLUtils::GetString(pElement, SAVESTATE_FIELD_GAMECLIENT, m_gameClient))
+ {
+ CLog::Log(LOGERROR, "Savestate has no <%s> element", SAVESTATE_FIELD_GAMECLIENT);
+ return false;
+ }
+
+ // Game path
+ if (!XMLUtils::GetString(pElement, SAVESTATE_FIELD_GAME_PATH, m_gamePath))
+ {
+ CLog::Log(LOGERROR, "Savestate has no <%s> element", SAVESTATE_FIELD_GAME_PATH);
+ return false;
+ }
+
+ // Game CRC
+ if (!XMLUtils::GetString(pElement, SAVESTATE_FIELD_GAME_CRC, m_gameCRC))
+ {
+ CLog::Log(LOGERROR, "Savestate has no <%s> element", SAVESTATE_FIELD_GAME_CRC);
+ return false;
+ }
+
+ // Playtime (frames)
+ long playtimeFrames;
+ if (!XMLUtils::GetLong(pElement, SAVESTATE_FIELD_FRAMES, playtimeFrames))
+ {
+ CLog::Log(LOGERROR, "Savestate has no <%s> element", SAVESTATE_FIELD_FRAMES);
+ return false;
+ }
+ if (playtimeFrames < 0)
+ {
+ CLog::Log(LOGERROR, "Invalid savestate frame count: %ld", playtimeFrames);
+ return false;
+ }
+ m_size = playtimeFrames;
+
+ // Playtime (wall clock)
+ float playtimeWallClock;
+ if (!XMLUtils::GetFloat(pElement, SAVESTATE_FIELD_WALLCLOCK, playtimeWallClock))
+ {
+ CLog::Log(LOGERROR, "Savestate has no <%s> element", SAVESTATE_FIELD_WALLCLOCK);
+ return false;
+ }
+ m_playtimeWallClock = playtimeWallClock;
+
+ // Timestamp
+ std::string timestamp;
+ if (!XMLUtils::GetString(pElement, SAVESTATE_FIELD_TIMESTAMP, timestamp))
+ {
+ CLog::Log(LOGERROR, "Savestate has no <%s> element", SAVESTATE_FIELD_TIMESTAMP);
+ return false;
+ }
+ if (!m_timestamp.SetFromDBDateTime(timestamp))
+ {
+ CLog::Log(LOGERROR, "Invalid savestate timestamp: %s", timestamp.c_str());
+ return false;
+ }
+
+ // Thumbnail
+ if (!XMLUtils::GetString(pElement, SAVESTATE_FIELD_THUMBNAIL, m_thumbnail))
+ {
+ CLog::Log(LOGERROR, "Savestate has no <%s> element", SAVESTATE_FIELD_THUMBNAIL);
+ return false;
+ }
+
+ return true;
+}
diff --git a/xbmc/games/addons/savestates/Savestate.h b/xbmc/games/addons/savestates/Savestate.h
new file mode 100644
index 0000000000..7180a56df3
--- /dev/null
+++ b/xbmc/games/addons/savestates/Savestate.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "XBDateTime.h"
+
+#include <stdint.h>
+#include <string>
+
+class CVariant;
+
+namespace GAME
+{
+ enum class SAVETYPE
+ {
+ UNKNOWN = 0,
+ AUTO = 1,
+ SLOT = 2,
+ MANUAL = 3,
+ };
+
+ class CSavestate
+ {
+ public:
+ CSavestate() { Reset(); }
+
+ virtual ~CSavestate() = default;
+
+ void Reset();
+
+ const std::string& Path() const { return m_path; }
+ SAVETYPE Type() const { return m_type; }
+ int Slot() const { return m_slot; }
+ const std::string& Label() const { return m_label; }
+ size_t Size() const { return m_size; }
+ const std::string& GameClient() const { return m_gameClient; }
+ int DatabaseId() const { return m_databaseId; }
+ bool IsDatabaseObject() const { return m_databaseId != -1; }
+ const std::string& GamePath() const { return m_gamePath; }
+ const std::string& GameCRC() const { return m_gameCRC; }
+ uint64_t PlaytimeFrames() const { return m_playtimeFrames; }
+ double PlaytimeWallClock() const { return m_playtimeWallClock; }
+ const CDateTime& Timestamp() const { return m_timestamp; }
+ const std::string& Thumbnail() const { return m_thumbnail; }
+
+ void SetPath(const std::string& path) { m_path = path; }
+ void SetType(SAVETYPE type) { m_type = type; }
+ void SetSlot(int slot) { m_slot = slot; }
+ void SetLabel(const std::string& label) { m_label = label; }
+ void SetSize(size_t size) { m_size = size; }
+ void SetGameClient(const std::string& gameClient) { m_gameClient = gameClient; }
+ void SetDatabaseId(int id) { m_databaseId = id; }
+ void SetGamePath(const std::string& gamePath) { m_gamePath = gamePath; }
+ void SetGameCRC(const std::string& crc) { m_gameCRC = crc; }
+ void SetPlaytimeFrames(uint64_t frames) { m_playtimeFrames = frames; }
+ void SetPlaytimeWallClock(double playtime) { m_playtimeWallClock = playtime; }
+ void SetTimestamp(const CDateTime& timestamp) { m_timestamp = timestamp; }
+ void SetThumbnail(const std::string& thumbnail) { m_thumbnail = thumbnail; }
+
+ void Serialize(CVariant& value) const;
+ void Deserialize(const CVariant& value);
+
+ bool Serialize(const std::string& path) const;
+ bool Deserialize(const std::string& path);
+
+ private:
+ // Savestate properties
+ std::string m_path;
+ SAVETYPE m_type;
+ int m_slot; // -1 for no slot
+ std::string m_label;
+ size_t m_size;
+ std::string m_gameClient;
+
+ // Database properties
+ int m_databaseId;
+
+ // Gameplay properties
+ std::string m_gamePath;
+ std::string m_gameCRC;
+ uint64_t m_playtimeFrames;
+ double m_playtimeWallClock; // seconds
+ CDateTime m_timestamp;
+ std::string m_thumbnail;
+ };
+}
diff --git a/xbmc/games/addons/savestates/SavestateDatabase.cpp b/xbmc/games/addons/savestates/SavestateDatabase.cpp
new file mode 100644
index 0000000000..faebda2b87
--- /dev/null
+++ b/xbmc/games/addons/savestates/SavestateDatabase.cpp
@@ -0,0 +1,110 @@
+ /*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "SavestateDatabase.h"
+#include "Savestate.h"
+#include "SavestateDefines.h"
+#include "SavestateUtils.h"
+#include "addons/AddonManager.h"
+#include "dbwrappers/dataset.h"
+#include "filesystem/File.h"
+#include "games/GameTypes.h"
+#include "games/tags/GameInfoTag.h"
+#include "settings/AdvancedSettings.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "FileItem.h"
+
+#include "utils/log.h"
+
+using namespace GAME;
+
+#define SAVESTATE_OBJECT "savestate"
+
+CSavestateDatabase::CSavestateDatabase()
+{
+}
+
+bool CSavestateDatabase::AddSavestate(const CSavestate& save)
+{
+ //! @todo
+ return false;
+}
+
+bool CSavestateDatabase::GetSavestate(const std::string& path, CSavestate& save)
+{
+ //! @todo
+ return false;
+}
+
+bool CSavestateDatabase::GetSavestatesNav(CFileItemList& items, const std::string& gamePath, const std::string& gameClient /* = "" */)
+{
+ //! @todo
+ return false;
+}
+
+bool CSavestateDatabase::RenameSavestate(const std::string& path, const std::string& label)
+{
+ //! @todo
+ return false;
+}
+
+bool CSavestateDatabase::DeleteSavestate(const std::string& path)
+{
+ //! @todo
+ return false;
+}
+
+bool CSavestateDatabase::ClearSavestatesOfGame(const std::string& gamePath, const std::string& gameClient /* = "" */)
+{
+ //! @todo
+ return false;
+}
+
+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;
+}
diff --git a/xbmc/games/addons/savestates/SavestateDatabase.h b/xbmc/games/addons/savestates/SavestateDatabase.h
new file mode 100644
index 0000000000..24234611ca
--- /dev/null
+++ b/xbmc/games/addons/savestates/SavestateDatabase.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include <string>
+
+#define SAVESTATES_DATABASE_NAME "Savestates"
+
+class CFileItem;
+class CFileItemList;
+class CVariant;
+
+namespace GAME
+{
+ class CSavestate;
+
+ class CSavestateDatabase
+ {
+ public:
+ CSavestateDatabase();
+ virtual ~CSavestateDatabase() = default;
+
+ bool AddSavestate(const CSavestate& save);
+
+ bool GetSavestate(const std::string& path, CSavestate& save);
+
+ bool GetSavestatesNav(CFileItemList& items, const std::string& gamePath, const std::string& gameClient = "");
+
+ bool RenameSavestate(const std::string& path, const std::string& label);
+
+ bool DeleteSavestate(const std::string& path);
+
+ bool ClearSavestatesOfGame(const std::string& gamePath, const std::string& gameClient = "");
+
+ private:
+ CFileItem* CreateFileItem(const CVariant& object) const;
+ };
+}
diff --git a/xbmc/games/addons/savestates/SavestateDefines.h b/xbmc/games/addons/savestates/SavestateDefines.h
new file mode 100644
index 0000000000..3f739db221
--- /dev/null
+++ b/xbmc/games/addons/savestates/SavestateDefines.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#define SAVESTATE_XML_ROOT "savestate"
+
+#define SAVESTATE_FIELD_PATH "path"
+#define SAVESTATE_FIELD_TYPE "type"
+#define SAVESTATE_FIELD_SLOT "slot"
+#define SAVESTATE_FIELD_LABEL "label"
+#define SAVESTATE_FIELD_SIZE "size"
+#define SAVESTATE_FIELD_GAMECLIENT "gameclient"
+#define SAVESTATE_FIELD_DB_ID "databaseid"
+#define SAVESTATE_FIELD_GAME_PATH "gamepath"
+#define SAVESTATE_FIELD_GAME_CRC "gamecrc"
+#define SAVESTATE_FIELD_FRAMES "frames"
+#define SAVESTATE_FIELD_WALLCLOCK "wallclock"
+#define SAVESTATE_FIELD_TIMESTAMP "timestamp"
+#define SAVESTATE_FIELD_THUMBNAIL "thumbnail"
+
+#define SAVESTATE_TYPE_UNKNOWN "unknown"
+#define SAVESTATE_TYPE_AUTO "auto"
+#define SAVESTATE_TYPE_SLOT "slot"
+#define SAVESTATE_TYPE_MANUAL "manual"
+
+#define FILEITEM_PROPERTY_SAVESTATE_DURATION "duration"
diff --git a/xbmc/games/addons/savestates/SavestateReader.cpp b/xbmc/games/addons/savestates/SavestateReader.cpp
new file mode 100644
index 0000000000..32cdaffea1
--- /dev/null
+++ b/xbmc/games/addons/savestates/SavestateReader.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "SavestateReader.h"
+#include "filesystem/File.h"
+#include "games/addons/GameClient.h"
+#include "IMemoryStream.h"
+
+using namespace GAME;
+
+CSavestateReader::CSavestateReader() :
+ m_frameCount(0)
+{
+}
+
+CSavestateReader::~CSavestateReader()
+{
+}
+
+bool CSavestateReader::Initialize(const std::string& path, const CGameClient* gameClient)
+{
+ bool bSuccess = false;
+
+ CLog::Log(LOGDEBUG, "Loading savestate from %s", path.c_str());
+
+ if (m_db.GetSavestate(path, m_savestate))
+ {
+ // Sanity checks
+ if (m_savestate.GameClient() == gameClient->ID())
+ bSuccess = true;
+ else
+ CLog::Log(LOGDEBUG, "Savestate game client %s doesn't match active %s", m_savestate.GameClient().c_str(), gameClient->ID().c_str());
+ }
+ else
+ CLog::Log(LOGERROR, "Failed to query savestate %s", path.c_str());
+
+ return bSuccess;
+}
+
+bool CSavestateReader::ReadSave(IMemoryStream* memoryStream)
+{
+ using namespace XFILE;
+
+ bool bSuccess = false;
+
+ CFile file;
+ if (file.Open(m_savestate.Path()))
+ {
+ ssize_t read = file.Read(memoryStream->BeginFrame(), memoryStream->FrameSize());
+ if (read == static_cast<ssize_t>(memoryStream->FrameSize()))
+ {
+ memoryStream->SubmitFrame();
+ m_frameCount = m_savestate.PlaytimeFrames();
+ bSuccess = true;
+ }
+ }
+
+ if (!bSuccess)
+ CLog::Log(LOGERROR, "Failed to read savestate %s", m_savestate.Path().c_str());
+
+ return bSuccess;
+}
diff --git a/xbmc/games/addons/savestates/SavestateReader.h b/xbmc/games/addons/savestates/SavestateReader.h
new file mode 100644
index 0000000000..5494c803ca
--- /dev/null
+++ b/xbmc/games/addons/savestates/SavestateReader.h
@@ -0,0 +1,48 @@
+/*
+* Copyright (C) 2016 Team Kodi
+* http://kodi.tv
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this Program; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+#pragma once
+
+#include "Savestate.h"
+#include "SavestateDatabase.h"
+
+#include <stdint.h>
+#include <string>
+
+namespace GAME
+{
+ class CGameClient;
+ class IMemoryStream;
+
+ class CSavestateReader
+ {
+ public:
+ CSavestateReader();
+ ~CSavestateReader();
+
+ bool Initialize(const std::string& path, const CGameClient* gameClient);
+ bool ReadSave(IMemoryStream* memoryStream);
+ uint64_t GetFrameCount(void) const { return m_frameCount; }
+
+ private:
+ CSavestate m_savestate;
+ CSavestateDatabase m_db;
+ uint64_t m_frameCount;
+ };
+}
diff --git a/xbmc/games/addons/savestates/SavestateTranslator.cpp b/xbmc/games/addons/savestates/SavestateTranslator.cpp
new file mode 100644
index 0000000000..f1dba1bebe
--- /dev/null
+++ b/xbmc/games/addons/savestates/SavestateTranslator.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "SavestateTranslator.h"
+#include "SavestateDefines.h"
+
+using namespace GAME;
+
+SAVETYPE CSavestateTranslator::TranslateType(const std::string& type)
+{
+ if (type == SAVESTATE_TYPE_AUTO) return SAVETYPE::AUTO;
+ else if (type == SAVESTATE_TYPE_SLOT) return SAVETYPE::SLOT;
+ else if (type == SAVESTATE_TYPE_MANUAL) return SAVETYPE::MANUAL;
+
+ return SAVETYPE::UNKNOWN;
+}
+
+std::string CSavestateTranslator::TranslateType(const SAVETYPE& type)
+{
+ switch (type)
+ {
+ case SAVETYPE::AUTO: return SAVESTATE_TYPE_AUTO;
+ case SAVETYPE::SLOT: return SAVESTATE_TYPE_SLOT;
+ case SAVETYPE::MANUAL: return SAVESTATE_TYPE_MANUAL;
+ default:
+ break;
+ }
+ return SAVESTATE_TYPE_UNKNOWN;
+}
diff --git a/xbmc/games/addons/savestates/SavestateTranslator.h b/xbmc/games/addons/savestates/SavestateTranslator.h
new file mode 100644
index 0000000000..67f8314f14
--- /dev/null
+++ b/xbmc/games/addons/savestates/SavestateTranslator.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "Savestate.h"
+
+#include <string>
+
+namespace GAME
+{
+ class CSavestateTranslator
+ {
+ public:
+ static SAVETYPE TranslateType(const std::string& type);
+ static std::string TranslateType(const SAVETYPE& type);
+ };
+}
diff --git a/xbmc/games/addons/savestates/SavestateUtils.cpp b/xbmc/games/addons/savestates/SavestateUtils.cpp
new file mode 100644
index 0000000000..4067692bae
--- /dev/null
+++ b/xbmc/games/addons/savestates/SavestateUtils.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "SavestateUtils.h"
+#include "Savestate.h"
+#include "filesystem/Directory.h"
+#include "filesystem/File.h"
+#include "profiles/ProfilesManager.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+
+#include <algorithm>
+#include <cctype>
+#include <cstring>
+#include <sstream>
+
+#define SAVESTATE_EXTENSION ".sav"
+#define SAVESTATE_AUTO_PREFIX "auto_"
+#define SAVESTATE_SLOT_PREFIX "slot%d_"
+#define SAVESTATE_MANUAL_PREFIX "save_"
+
+using namespace GAME;
+
+namespace
+{
+ void make_safe_path(std::string& path)
+ {
+ // Replace unsafe characters with "_"
+ std::transform(path.begin(), path.end(), path.begin(),
+ [](char c)
+ {
+ if (std::isalnum(c))
+ return c;
+
+ switch (c)
+ {
+ case '-':
+ case '.':
+ case '_':
+ case '[':
+ case ']':
+ case '(':
+ case ')':
+ case '!':
+ return c;
+ default:
+ break;
+ }
+
+ return '_';
+ });
+
+ // Combine successive runs of underscores
+ path.erase(std::unique(path.begin(), path.end(),
+ [](char a, char b)
+ {
+ return a == '_' && b == '_';
+ }), path.end());
+
+ // Limit folderName to a sane number of characters
+ if (path.length() > 40)
+ path.erase(path.begin() + 40, path.end());
+
+ // Trim trailing underscores
+ StringUtils::TrimRight(path, "_");
+ }
+}
+
+std::string CSavestateUtils::MakePath(const CSavestate& save)
+{
+ using namespace XFILE;
+
+ if (save.GameClient().empty())
+ return "";
+
+ // Build path
+ std::string savePath = CProfilesManager::GetInstance().GetSavestatesFolder();
+
+ // Append game client
+ savePath = URIUtils::AddFileToFolder(savePath, save.GameClient());
+
+ // Generate a folder name based on game path and CRC
+ std::string folderName = URIUtils::GetFileName(save.GamePath()) + "_" + save.GameCRC();
+
+ make_safe_path(folderName);
+
+ savePath = URIUtils::AddFileToFolder(savePath, folderName);
+
+ if (!CDirectory::Exists(savePath))
+ CDirectory::Create(savePath);
+
+ // Generate a filename based on type
+ std::string filename;
+ switch (save.Type())
+ {
+ case SAVETYPE::AUTO:
+ filename = SAVESTATE_AUTO_PREFIX + save.Timestamp().GetAsDBDateTime();
+ break;
+ case SAVETYPE::SLOT:
+ filename = StringUtils::Format(SAVESTATE_SLOT_PREFIX, save.Slot()) + save.Timestamp().GetAsDBDateTime();
+ break;
+ case SAVETYPE::MANUAL:
+ filename = SAVESTATE_MANUAL_PREFIX + save.Timestamp().GetAsDBDateTime();
+ break;
+ default:
+ return ""; // Invalid savestate, bail out
+ }
+
+ make_safe_path(filename);
+
+ filename += SAVESTATE_EXTENSION;
+
+ savePath = URIUtils::AddFileToFolder(savePath, filename);
+
+ for (unsigned int i = 1; i <= 9; i++)
+ {
+ if (!CFile::Exists(savePath))
+ break;
+
+ if (i != 1)
+ savePath.erase(savePath.end() - 1, savePath.end());
+
+ savePath += StringUtils::Format("%u", i);
+ }
+
+ return savePath;
+}
+
+std::string CSavestateUtils::MakeThumbPath(const std::string& savePath)
+{
+ return URIUtils::ReplaceExtension(savePath, ".jpg");
+}
diff --git a/xbmc/games/addons/savestates/SavestateUtils.h b/xbmc/games/addons/savestates/SavestateUtils.h
new file mode 100644
index 0000000000..e3f705e980
--- /dev/null
+++ b/xbmc/games/addons/savestates/SavestateUtils.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include <string>
+
+namespace GAME
+{
+ class CSavestate;
+
+ class CSavestateUtils
+ {
+ public:
+ /*!
+ * \brief Calculate a path for the specified savestate
+ *
+ * Path to savestate is derived from game client and game CRC. Returns empty
+ * if either of these is unknown. Format is
+ *
+ * Autosave (hex is game CRC):
+ * special://savegames/gameclient.id/feba62c2.sav
+ *
+ * Save type slot (digit after the underscore is slot 1-9):
+ * special://savegames/gameclient.id/feba62c2_1.sav
+ *
+ * Save type label (hex after the underscore is CRC of the label):
+ * special://savegames/gameclient.id/feba62c2_8dc22669.sav
+ */
+ static std::string MakePath(const CSavestate& save);
+
+ /*!
+ * \brief Calculate the thumbnail path for the specified savestate
+ *
+ * This is the savestate path with a different extension
+ */
+ static std::string MakeThumbPath(const std::string& savePath);
+ };
+}
diff --git a/xbmc/games/addons/savestates/SavestateWriter.cpp b/xbmc/games/addons/savestates/SavestateWriter.cpp
new file mode 100644
index 0000000000..8d90027a7e
--- /dev/null
+++ b/xbmc/games/addons/savestates/SavestateWriter.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "SavestateWriter.h"
+#include "filesystem/File.h"
+#include "games/addons/GameClient.h"
+#include "IMemoryStream.h"
+#include "games/addons/savestates/SavestateUtils.h"
+#include "pictures/Picture.h"
+#include "settings/AdvancedSettings.h"
+#include "utils/Crc32.h"
+#include "utils/StringUtils.h"
+#include "Application.h"
+#include "XBDateTime.h"
+
+using namespace GAME;
+
+CSavestateWriter::CSavestateWriter() :
+ m_fps(0.0)
+{
+}
+
+CSavestateWriter::~CSavestateWriter()
+{
+}
+
+bool CSavestateWriter::Initialize(const CGameClient* gameClient, uint64_t frameHistoryCount)
+{
+ m_savestate.Reset();
+ m_fps = 0.0;
+
+ m_fps = gameClient->Timing().GetFrameRate();
+
+ CDateTime now = CDateTime::GetCurrentDateTime();
+ std::string label = now.GetAsLocalizedDateTime();
+
+ m_savestate.SetType(SAVETYPE::MANUAL);
+ m_savestate.SetLabel(label);
+ m_savestate.SetGameClient(gameClient->ID());
+ m_savestate.SetGamePath(gameClient->GetGamePath());
+ m_savestate.SetTimestamp(now);
+ m_savestate.SetPlaytimeFrames(frameHistoryCount);
+ m_savestate.SetPlaytimeWallClock(frameHistoryCount / m_fps); //! @todo Accumulate playtime instead of deriving it
+
+ //! @todo Get CRC from game data instead of filename
+ Crc32 crc;
+ crc.Compute(gameClient->GetGamePath());
+ m_savestate.SetGameCRC(StringUtils::Format("%08x", (unsigned __int32)crc));
+
+ m_savestate.SetPath(CSavestateUtils::MakePath(m_savestate));
+ if (m_savestate.Path().empty())
+ CLog::Log(LOGDEBUG, "Failed to calculate savestate path");
+
+ if (m_fps == 0.0)
+ return false; // Sanity check
+
+ return !m_savestate.Path().empty();
+}
+
+bool CSavestateWriter::WriteSave(IMemoryStream* memoryStream)
+{
+ using namespace XFILE;
+
+ if (memoryStream->CurrentFrame() == nullptr)
+ return false;
+
+ m_savestate.SetSize(memoryStream->FrameSize());
+
+ CLog::Log(LOGDEBUG, "Saving savestate to %s", m_savestate.Path().c_str());
+
+ bool bSuccess = false;
+
+ CFile file;
+ if (file.OpenForWrite(m_savestate.Path()))
+ {
+ ssize_t written = file.Write(memoryStream->CurrentFrame(), memoryStream->FrameSize());
+ bSuccess = (written == static_cast<ssize_t>(memoryStream->FrameSize()));
+ }
+
+ if (!bSuccess)
+ CLog::Log(LOGERROR, "Failed to write savestate to %s", m_savestate.Path().c_str());
+
+ return bSuccess;
+}
+
+void CSavestateWriter::WriteThumb()
+{
+ if (g_advancedSettings.m_imageRes == 0)
+ return; // Sanity check
+
+ std::string thumbPath = CSavestateUtils::MakeThumbPath(m_savestate.Path());
+ CLog::Log(LOGDEBUG, "Saving savestate thumbnail to %s", thumbPath.c_str());
+
+ // Calculate width and height
+ float aspectRatio = g_application.m_pPlayer->GetRenderAspectRatio();
+ if (aspectRatio <= 0.0f)
+ aspectRatio = 1.0f;
+
+ unsigned int width;
+ unsigned int height;
+ if (aspectRatio >= 1.0f)
+ {
+ width = g_advancedSettings.m_imageRes;
+ height = static_cast<unsigned int>(g_advancedSettings.m_imageRes / aspectRatio);
+ }
+ else
+ {
+ width = static_cast<unsigned int>(g_advancedSettings.m_imageRes * aspectRatio);
+ height = g_advancedSettings.m_imageRes;
+ }
+
+ // Allocate pixels
+ uint8_t* pixels = new uint8_t[height * width * 4];
+
+ // Capture picture
+ unsigned int captureId = g_application.m_pPlayer->RenderCaptureAlloc();
+ g_application.m_pPlayer->RenderCapture(captureId, width, height, CAPTUREFLAG_IMMEDIATELY);
+ bool hasImage = g_application.m_pPlayer->RenderCaptureGetPixels(captureId, 1000, pixels, height * width * 4);
+
+ // Save picture
+ if (hasImage)
+ {
+ if (CPicture::CreateThumbnailFromSurface(pixels, width, height, width * 4, thumbPath))
+ m_savestate.SetThumbnail(thumbPath);
+ else
+ CLog::Log(LOGERROR, "Failed to save thumbnail to %s", thumbPath.c_str());
+
+ g_application.m_pPlayer->RenderCaptureRelease(captureId);
+ }
+ else
+ CLog::Log(LOGERROR, "Failed to capture thumbnail");
+
+ // Free pixels
+ delete[] pixels;
+}
+
+bool CSavestateWriter::CommitToDatabase()
+{
+ bool bSuccess = m_db.AddSavestate(m_savestate);
+
+ if (!bSuccess)
+ CLog::Log(LOGERROR, "Failed to write savestate to database: %s", m_savestate.Path().c_str());
+
+ return bSuccess;
+}
+
+void CSavestateWriter::CleanUpTransaction()
+{
+ using namespace XFILE;
+
+ CFile::Delete(m_savestate.Path());
+ if (CFile::Exists(m_savestate.Thumbnail()))
+ CFile::Delete(m_savestate.Thumbnail());
+}
diff --git a/xbmc/games/addons/savestates/SavestateWriter.h b/xbmc/games/addons/savestates/SavestateWriter.h
new file mode 100644
index 0000000000..c60a74d7c8
--- /dev/null
+++ b/xbmc/games/addons/savestates/SavestateWriter.h
@@ -0,0 +1,51 @@
+/*
+* Copyright (C) 2016 Team Kodi
+* http://kodi.tv
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this Program; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+#pragma once
+
+#include "Savestate.h"
+#include "SavestateDatabase.h"
+
+#include <stdint.h>
+#include <string>
+
+namespace GAME
+{
+ class CGameClient;
+ class IMemoryStream;
+
+ class CSavestateWriter
+ {
+ public:
+ CSavestateWriter();
+ ~CSavestateWriter();
+
+ bool Initialize(const CGameClient* gameClient, uint64_t frameHistoryCount);
+ bool WriteSave(IMemoryStream* memoryStream);
+ void WriteThumb();
+ bool CommitToDatabase();
+ void CleanUpTransaction();
+ const std::string& GetPath() const { return m_savestate.Path(); }
+
+ private:
+ CSavestate m_savestate;
+ double m_fps; //! @todo
+ CSavestateDatabase m_db;
+ };
+}
diff --git a/xbmc/games/controllers/ControllerDefinitions.h b/xbmc/games/controllers/ControllerDefinitions.h
index 8ade354737..bcb1639e43 100644
--- a/xbmc/games/controllers/ControllerDefinitions.h
+++ b/xbmc/games/controllers/ControllerDefinitions.h
@@ -27,6 +27,7 @@
#define LAYOUT_XML_ELM_ANALOG_STICK "analogstick"
#define LAYOUT_XML_ELM_ACCELEROMETER "accelerometer"
#define LAYOUT_XML_ELM_MOTOR "motor"
+#define LAYOUT_XML_ELM_RELPOINTER "relpointer"
#define LAYOUT_XML_ATTR_LAYOUT_LABEL "label"
#define LAYOUT_XML_ATTR_LAYOUT_IMAGE "image"
diff --git a/xbmc/games/controllers/ControllerTranslator.cpp b/xbmc/games/controllers/ControllerTranslator.cpp
index 692960e720..0b0867c1a2 100644
--- a/xbmc/games/controllers/ControllerTranslator.cpp
+++ b/xbmc/games/controllers/ControllerTranslator.cpp
@@ -32,6 +32,7 @@ const char* CControllerTranslator::TranslateFeatureType(FEATURE_TYPE type)
case FEATURE_TYPE::ANALOG_STICK: return LAYOUT_XML_ELM_ANALOG_STICK;
case FEATURE_TYPE::ACCELEROMETER: return LAYOUT_XML_ELM_ACCELEROMETER;
case FEATURE_TYPE::MOTOR: return LAYOUT_XML_ELM_MOTOR;
+ case FEATURE_TYPE::RELPOINTER: return LAYOUT_XML_ELM_RELPOINTER;
default:
break;
}
@@ -44,6 +45,7 @@ FEATURE_TYPE CControllerTranslator::TranslateFeatureType(const std::string& strT
if (strType == LAYOUT_XML_ELM_ANALOG_STICK) return FEATURE_TYPE::ANALOG_STICK;
if (strType == LAYOUT_XML_ELM_ACCELEROMETER) return FEATURE_TYPE::ACCELEROMETER;
if (strType == LAYOUT_XML_ELM_MOTOR) return FEATURE_TYPE::MOTOR;
+ if (strType == LAYOUT_XML_ELM_RELPOINTER) return FEATURE_TYPE::RELPOINTER;
return FEATURE_TYPE::UNKNOWN;
}
diff --git a/xbmc/games/controllers/windows/GUIConfigurationWizard.cpp b/xbmc/games/controllers/windows/GUIConfigurationWizard.cpp
index f559db7675..525f83ca63 100644
--- a/xbmc/games/controllers/windows/GUIConfigurationWizard.cpp
+++ b/xbmc/games/controllers/windows/GUIConfigurationWizard.cpp
@@ -278,6 +278,11 @@ bool CGUIConfigurationWizard::OnKeyPress(const CKey& key)
return Abort(false);
}
+bool CGUIConfigurationWizard::OnButtonPress(const std::string& button)
+{
+ return Abort(false);
+}
+
void CGUIConfigurationWizard::InstallHooks(void)
{
using namespace PERIPHERALS;
@@ -288,12 +293,16 @@ void CGUIConfigurationWizard::InstallHooks(void)
// If we're not using emulation, allow keyboard input to abort prompt
if (!m_bEmulation)
CInputManager::GetInstance().RegisterKeyboardHandler(this);
+
+ CInputManager::GetInstance().RegisterMouseHandler(this);
}
void CGUIConfigurationWizard::RemoveHooks(void)
{
using namespace PERIPHERALS;
+ CInputManager::GetInstance().UnregisterMouseHandler(this);
+
if (!m_bEmulation)
CInputManager::GetInstance().UnregisterKeyboardHandler(this);
diff --git a/xbmc/games/controllers/windows/GUIConfigurationWizard.h b/xbmc/games/controllers/windows/GUIConfigurationWizard.h
index 112d41735f..b37aa1a018 100644
--- a/xbmc/games/controllers/windows/GUIConfigurationWizard.h
+++ b/xbmc/games/controllers/windows/GUIConfigurationWizard.h
@@ -23,6 +23,7 @@
#include "input/joysticks/DriverPrimitive.h"
#include "input/joysticks/IButtonMapper.h"
#include "input/keyboard/IKeyboardHandler.h"
+#include "input/mouse/IMouseInputHandler.h"
#include "threads/CriticalSection.h"
#include "threads/Event.h"
#include "threads/Thread.h"
@@ -37,6 +38,7 @@ namespace GAME
class CGUIConfigurationWizard : public IConfigurationWizard,
public JOYSTICK::IButtonMapper,
public KEYBOARD::IKeyboardHandler,
+ public MOUSE::IMouseInputHandler,
public Observer,
protected CThread
{
@@ -64,6 +66,11 @@ namespace GAME
virtual bool OnKeyPress(const CKey& key) override;
virtual void OnKeyRelease(const CKey& key) override { }
+ // implementation of IMouseInputHandler
+ virtual bool OnMotion(const std::string& relpointer, int dx, int dy) override { return false; }
+ virtual bool OnButtonPress(const std::string& button) override;
+ virtual void OnButtonRelease(const std::string& button) override { }
+
// implementation of Observer
virtual void Notify(const Observable& obs, const ObservableMessage msg) override;
diff --git a/xbmc/games/dialogs/CMakeLists.txt b/xbmc/games/dialogs/CMakeLists.txt
new file mode 100644
index 0000000000..c1f7b46c7a
--- /dev/null
+++ b/xbmc/games/dialogs/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(SOURCES GUIDialogSelectGameClient.cpp
+)
+
+set(HEADERS GUIDialogSelectGameClient.h
+)
+
+core_add_library(gamedialogs)
diff --git a/xbmc/games/dialogs/GUIDialogSelectGameClient.cpp b/xbmc/games/dialogs/GUIDialogSelectGameClient.cpp
new file mode 100644
index 0000000000..4a5b1302df
--- /dev/null
+++ b/xbmc/games/dialogs/GUIDialogSelectGameClient.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GUIDialogSelectGameClient.h"
+#include "addons/AddonInstaller.h"
+#include "addons/AddonManager.h"
+#include "addons/GUIWindowAddonBrowser.h"
+#include "dialogs/GUIDialogContextMenu.h"
+#include "games/addons/GameClient.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/WindowIDs.h"
+#include "utils/log.h"
+
+using namespace GAME;
+
+bool CGUIDialogSelectGameClient::ShowAndGetGameClient(const GameClientVector& candidates, const GameClientVector& installable, GameClientPtr& gameClient)
+{
+ CLog::Log(LOGDEBUG, "Select game client dialog: Found %lu candidates", candidates.size());
+ for (const auto& gameClient : candidates)
+ CLog::Log(LOGDEBUG, "Adding %s as a candidate", gameClient->ID().c_str());
+
+ if (!installable.empty())
+ {
+ CLog::Log(LOGDEBUG, "Select game client dialog: Found %lu installable clients", installable.size());
+ for (const auto& gameClient : installable)
+ CLog::Log(LOGDEBUG, "Adding %s as an installable client", gameClient->ID().c_str());
+ }
+
+ CContextButtons choiceButtons;
+
+ // Add emulators
+ int i = 0;
+ for (const GameClientPtr& gameClient : candidates)
+ choiceButtons.Add(i++, gameClient->Name());
+
+ // Add button to install emulators
+ const int iInstallEmulator = i++;
+ if (!installable.empty())
+ choiceButtons.Add(iInstallEmulator, 35253); // "Install emulator"
+
+ // Add button to manage emulators
+ const int iAddonMgr = i++;
+ choiceButtons.Add(iAddonMgr, 35254); // "Manage emulators"
+
+ // Do modal
+ int result = CGUIDialogContextMenu::ShowAndGetChoice(choiceButtons);
+
+ if (0 <= result && result < static_cast<int>(candidates.size()))
+ {
+ // Handle emulator
+ gameClient = candidates[result];
+ }
+ else if (result == iInstallEmulator)
+ {
+ // Install emulator
+ gameClient = InstallGameClient(installable);
+ }
+ else if (result == iAddonMgr)
+ {
+ // Go to add-on manager to manage emulators
+ ActivateAddonMgr();
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "Select game client dialog: User cancelled game client selection");
+ }
+
+ return gameClient.get() != nullptr;
+}
+
+GameClientPtr CGUIDialogSelectGameClient::InstallGameClient(const GameClientVector& installable)
+{
+ using namespace ADDON;
+
+ GameClientPtr gameClient;
+
+ //! @todo Switch to add-on browser when more emulators have icons
+ /*
+ std::string chosenClientId;
+ if (CGUIWindowAddonBrowser::SelectAddonID(ADDON_GAMEDLL, chosenClientId, false, true, false, true, false) >= 0 && !chosenClientId.empty())
+ {
+ CLog::Log(LOGDEBUG, "Select game client dialog: User installed %s", chosenClientId.c_str());
+ AddonPtr addon;
+ if (CAddonMgr::GetInstance().GetAddon(chosenClientId, addon, ADDON_GAMEDLL))
+ gameClient = std::dynamic_pointer_cast<CGameClient>(addon);
+
+ if (!gameClient)
+ CLog::Log(LOGERROR, "Select game client dialog: Failed to get addon %s", chosenClientId.c_str());
+ }
+ */
+
+ CContextButtons choiceButtons;
+
+ // Add emulators
+ int i = 0;
+ for (const GameClientPtr& gameClient : installable)
+ choiceButtons.Add(i++, gameClient->Name());
+
+ // Add button to browser all emulators
+ const int iAddonBrowser = i++;
+ choiceButtons.Add(iAddonBrowser, 35255); // "Browse all emulators"
+
+ // Do modal
+ int result = CGUIDialogContextMenu::ShowAndGetChoice(choiceButtons);
+
+ if (0 <= result && result < static_cast<int>(installable.size()))
+ {
+ std::string gameClientId = installable[result]->ID();
+ CLog::Log(LOGDEBUG, "Select game client dialog: Installing %s", gameClientId.c_str());
+ AddonPtr installedAddon;
+ if (CAddonInstaller::GetInstance().InstallModal(gameClientId, installedAddon, false))
+ {
+ CLog::Log(LOGDEBUG, "Select game client dialog: Successfully installed %s", installedAddon->ID().c_str());
+
+ // if the addon is disabled we need to enable it
+ if (CAddonMgr::GetInstance().IsAddonDisabled(installedAddon->ID()))
+ CAddonMgr::GetInstance().EnableAddon(installedAddon->ID());
+
+ gameClient = std::dynamic_pointer_cast<CGameClient>(installedAddon);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "Select game client dialog: Failed to install %s", gameClientId.c_str());
+ }
+ }
+ else if (result == iAddonBrowser)
+ {
+ ActivateAddonBrowser();
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "Select game client dialog: User cancelled game client installation");
+ }
+
+ return gameClient;
+}
+
+void CGUIDialogSelectGameClient::ActivateAddonMgr()
+{
+ CLog::Log(LOGDEBUG, "User chose to go to the add-on manager");
+ std::vector<std::string> params;
+ params.push_back("addons://user/category.emulators");
+ g_windowManager.ActivateWindow(WINDOW_ADDON_BROWSER, params);
+}
+
+void CGUIDialogSelectGameClient::ActivateAddonBrowser()
+{
+ CLog::Log(LOGDEBUG, "User chose to go to the add-on browser");
+ std::vector<std::string> params;
+ params.push_back("addons://all/category.emulators");
+ g_windowManager.ActivateWindow(WINDOW_ADDON_BROWSER, params);
+}
diff --git a/xbmc/games/dialogs/GUIDialogSelectGameClient.h b/xbmc/games/dialogs/GUIDialogSelectGameClient.h
new file mode 100644
index 0000000000..acad17b319
--- /dev/null
+++ b/xbmc/games/dialogs/GUIDialogSelectGameClient.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "games/GameTypes.h"
+
+namespace GAME
+{
+ class CGUIDialogSelectGameClient
+ {
+ public:
+ static bool ShowAndGetGameClient(const GameClientVector& candidates, const GameClientVector& installable, GameClientPtr& gameClient);
+
+ private:
+ static GameClientPtr InstallGameClient(const GameClientVector& installable);
+
+ /*!
+ * \brief Utility function to load the add-on manager for installed emulators
+ */
+ static void ActivateAddonMgr();
+
+ /*!
+ * \brief Utility function to load the add-on manager for all emulators
+ */
+ static void ActivateAddonBrowser();
+ };
+}
diff --git a/xbmc/games/dialogs/Makefile b/xbmc/games/dialogs/Makefile
new file mode 100644
index 0000000000..b544a0ef56
--- /dev/null
+++ b/xbmc/games/dialogs/Makefile
@@ -0,0 +1,6 @@
+SRCS=GUIDialogSelectGameClient.cpp \
+
+LIB=gamedialogs.a
+
+include ../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/games/ports/CMakeLists.txt b/xbmc/games/ports/CMakeLists.txt
new file mode 100644
index 0000000000..16b3867674
--- /dev/null
+++ b/xbmc/games/ports/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(SOURCES PortManager.cpp
+ PortMapper.cpp)
+
+set(HEADERS PortManager.h
+ PortMapper.h)
+
+core_add_library(gameports)
diff --git a/xbmc/games/ports/Makefile b/xbmc/games/ports/Makefile
new file mode 100644
index 0000000000..4f1829ed69
--- /dev/null
+++ b/xbmc/games/ports/Makefile
@@ -0,0 +1,7 @@
+SRCS=PortManager.cpp \
+ PortMapper.cpp \
+
+LIB=gameports.a
+
+include ../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/games/ports/PortManager.cpp b/xbmc/games/ports/PortManager.cpp
new file mode 100644
index 0000000000..5eafc923f9
--- /dev/null
+++ b/xbmc/games/ports/PortManager.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2015-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PortManager.h"
+#include "peripherals/devices/Peripheral.h"
+#include "peripherals/devices/PeripheralJoystick.h"
+#include "peripherals/devices/PeripheralJoystickEmulation.h"
+#include "threads/SingleLock.h"
+
+#include <algorithm>
+
+using namespace GAME;
+using namespace JOYSTICK;
+using namespace PERIPHERALS;
+
+// --- GetRequestedPort() -----------------------------------------------------
+
+namespace GAME
+{
+ int GetRequestedPort(const PERIPHERALS::PeripheralPtr& device)
+ {
+ if (device->Type() == PERIPHERAL_JOYSTICK)
+ return std::static_pointer_cast<CPeripheralJoystick>(device)->RequestedPort();
+ return JOYSTICK_PORT_UNKNOWN;
+ }
+}
+
+// --- CPortManager -----------------------------------------------------------
+
+CPortManager& CPortManager::GetInstance()
+{
+ static CPortManager instance;
+ return instance;
+}
+
+void CPortManager::OpenPort(IInputHandler* handler,
+ unsigned int port,
+ PERIPHERALS::PeripheralType requiredType /* = PERIPHERALS::PERIPHERAL_UNKNOWN) */)
+{
+ CSingleLock lock(m_mutex);
+
+ SPort newPort = { };
+ newPort.handler = handler;
+ newPort.port = port;
+ newPort.requiredType = requiredType;
+ m_ports.push_back(newPort);
+
+ SetChanged();
+ NotifyObservers(ObservableMessagePortsChanged);
+}
+
+void CPortManager::ClosePort(IInputHandler* handler)
+{
+ CSingleLock lock(m_mutex);
+
+ m_ports.erase(std::remove_if(m_ports.begin(), m_ports.end(),
+ [handler](const SPort& port)
+ {
+ return port.handler == handler;
+ }), m_ports.end());
+
+ SetChanged();
+ NotifyObservers(ObservableMessagePortsChanged);
+}
+
+void CPortManager::MapDevices(const PeripheralVector& devices,
+ std::map<PeripheralPtr, IInputHandler*>& deviceToPortMap)
+{
+ CSingleLock lock(m_mutex);
+
+ if (m_ports.empty())
+ return; // Nothing to do
+
+ // Clear all ports
+ for (SPort& port : m_ports)
+ port.device = nullptr;
+
+ // Prioritize devices by several criteria
+ PeripheralVector devicesCopy = devices;
+ std::sort(devicesCopy.begin(), devicesCopy.end(),
+ [](const PeripheralPtr& lhs, const PeripheralPtr& rhs)
+ {
+ // Prioritize physical joysticks over emulated ones
+ if (lhs->Type() == PERIPHERAL_JOYSTICK && rhs->Type() != PERIPHERAL_JOYSTICK)
+ return true;
+ if (lhs->Type() != PERIPHERAL_JOYSTICK && rhs->Type() == PERIPHERAL_JOYSTICK)
+ return false;
+
+ if (lhs->Type() == PERIPHERAL_JOYSTICK && rhs->Type() == PERIPHERAL_JOYSTICK)
+ {
+ std::shared_ptr<CPeripheralJoystick> i = std::static_pointer_cast<CPeripheralJoystick>(lhs);
+ std::shared_ptr<CPeripheralJoystick> j = std::static_pointer_cast<CPeripheralJoystick>(rhs);
+
+ // Prioritize requested a port over no port requested
+ if (i->RequestedPort() != JOYSTICK_PORT_UNKNOWN && j->RequestedPort() == JOYSTICK_PORT_UNKNOWN)
+ return true;
+ if (i->RequestedPort() == JOYSTICK_PORT_UNKNOWN && j->RequestedPort() != JOYSTICK_PORT_UNKNOWN)
+ return false;
+
+ // Sort joystick by requested port
+ return i->RequestedPort() < j->RequestedPort();
+ }
+
+ if (lhs->Type() == PERIPHERAL_JOYSTICK_EMULATION && rhs->Type() == PERIPHERAL_JOYSTICK_EMULATION)
+ {
+ std::shared_ptr<CPeripheralJoystickEmulation> i = std::static_pointer_cast<CPeripheralJoystickEmulation>(lhs);
+ std::shared_ptr<CPeripheralJoystickEmulation> j = std::static_pointer_cast<CPeripheralJoystickEmulation>(rhs);
+
+ // Sort emulated joysticks by player number
+ return i->ControllerNumber() < j->ControllerNumber();
+ }
+
+ return false;
+ });
+
+ // Record mapped devices in output variable
+ for (auto& device : devicesCopy)
+ {
+ IInputHandler* handler = AssignToPort(device);
+ if (handler)
+ deviceToPortMap[device] = handler;
+ }
+}
+
+IInputHandler* CPortManager::AssignToPort(const PeripheralPtr& device, bool checkPortNumber /* = true */)
+{
+ const int requestedPort = GetRequestedPort(device);
+ const bool bPortRequested = (requestedPort != JOYSTICK_PORT_UNKNOWN);
+
+ for (SPort& port : m_ports)
+ {
+ // Skip occupied ports
+ if (port.device != nullptr)
+ continue;
+
+ // If specified, check port numbers
+ if (checkPortNumber)
+ {
+ if (bPortRequested && requestedPort != static_cast<int>(port.port))
+ continue;
+ }
+
+ // If required, filter by type
+ const bool bTypeRequired = (port.requiredType != PERIPHERAL_UNKNOWN);
+ if (bTypeRequired && port.requiredType != device->Type())
+ continue;
+
+ // Success
+ port.device = device.get();
+ return port.handler;
+ }
+
+ // If joystick requested a port but wasn't mapped, try again without checking port numbers
+ if (checkPortNumber && bPortRequested)
+ return AssignToPort(device, false);
+
+ return nullptr;
+}
diff --git a/xbmc/games/ports/PortManager.h b/xbmc/games/ports/PortManager.h
new file mode 100644
index 0000000000..289c9ee935
--- /dev/null
+++ b/xbmc/games/ports/PortManager.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "peripherals/PeripheralTypes.h"
+#include "threads/CriticalSection.h"
+#include "utils/Observer.h"
+
+#include <map>
+#include <vector>
+
+namespace JOYSTICK { class IInputHandler; }
+namespace PERIPHERALS { class CPeripheral; }
+
+namespace GAME
+{
+ /*!
+ * \brief Class to manage ports opened by game clients
+ */
+ class CPortManager : public Observable
+ {
+ private:
+ CPortManager(void) = default;
+
+ public:
+ static CPortManager& GetInstance();
+
+ /*!
+ * \brief Request a new port be opened with input on that port sent to the
+ * specified handler.
+ *
+ * \param handler The instance accepting all input delivered to the port
+ * \param port The port number belonging to the game client
+ * \param requiredType Used to restrict port to devices of only a certain type
+ */
+ void OpenPort(JOYSTICK::IInputHandler* handler,
+ unsigned int port,
+ PERIPHERALS::PeripheralType requiredType = PERIPHERALS::PERIPHERAL_UNKNOWN);
+
+ /*!
+ * \brief Close an opened port
+ *
+ * \param handler The handler used to open the port
+ */
+ void ClosePort(JOYSTICK::IInputHandler* handler);
+
+ /*!
+ * \brief Map a list of devices to the available ports
+ *
+ * \param devices The devices capable of providing input to the ports
+ * \param portMap The resulting map of devices to ports
+ *
+ * If there are more devices than open ports, multiple devices may be assigned
+ * to the same port. If a device requests a specific port, this function will
+ * attempt to honor that request.
+ */
+ void MapDevices(const PERIPHERALS::PeripheralVector& devices,
+ std::map<PERIPHERALS::PeripheralPtr, JOYSTICK::IInputHandler*>& deviceToPortMap);
+
+ private:
+ JOYSTICK::IInputHandler* AssignToPort(const PERIPHERALS::PeripheralPtr& device, bool checkPortNumber = true);
+
+ struct SPort
+ {
+ JOYSTICK::IInputHandler* handler; // Input handler for this port
+ unsigned int port; // Port number belonging to the game client
+ PERIPHERALS::PeripheralType requiredType;
+ void* device;
+ };
+
+ std::vector<SPort> m_ports;
+ CCriticalSection m_mutex;
+ };
+}
diff --git a/xbmc/games/ports/PortMapper.cpp b/xbmc/games/ports/PortMapper.cpp
new file mode 100644
index 0000000000..2471c39a2b
--- /dev/null
+++ b/xbmc/games/ports/PortMapper.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PortMapper.h"
+#include "PortManager.h"
+#include "peripherals/devices/Peripheral.h"
+#include "peripherals/Peripherals.h"
+
+using namespace GAME;
+using namespace JOYSTICK;
+using namespace PERIPHERALS;
+
+CPortMapper::CPortMapper()
+{
+ CPortManager::GetInstance().RegisterObserver(this);
+}
+
+CPortMapper::~CPortMapper()
+{
+ CPortManager::GetInstance().UnregisterObserver(this);
+}
+
+void CPortMapper::Notify(const Observable &obs, const ObservableMessage msg)
+{
+ switch (msg)
+ {
+ case ObservableMessagePeripheralsChanged:
+ case ObservableMessagePortsChanged:
+ ProcessPeripherals();
+ break;
+ default:
+ break;
+ }
+}
+
+void CPortMapper::ProcessPeripherals()
+{
+ auto& oldPortMap = m_portMap;
+
+ PeripheralVector devices;
+ g_peripherals.GetPeripheralsWithFeature(devices, FEATURE_JOYSTICK);
+
+ std::map<PeripheralPtr, IInputHandler*> newPortMap;
+ CPortManager::GetInstance().MapDevices(devices, newPortMap);
+
+ for (auto& device : devices)
+ {
+ std::map<PeripheralPtr, IInputHandler*>::const_iterator itOld = oldPortMap.find(device);
+ std::map<PeripheralPtr, IInputHandler*>::const_iterator itNew = newPortMap.find(device);
+
+ IInputHandler* oldHandler = itOld != oldPortMap.end() ? itOld->second : NULL;
+ IInputHandler* newHandler = itNew != newPortMap.end() ? itNew->second : NULL;
+
+ if (oldHandler != newHandler)
+ {
+ // Unregister old handler
+ if (oldHandler != NULL)
+ device->UnregisterJoystickInputHandler(oldHandler);
+
+ // Register new handler
+ if (newHandler != NULL)
+ device->RegisterJoystickInputHandler(newHandler);
+ }
+ }
+
+ oldPortMap.swap(newPortMap);
+}
diff --git a/xbmc/games/ports/PortMapper.h b/xbmc/games/ports/PortMapper.h
new file mode 100644
index 0000000000..60330039a2
--- /dev/null
+++ b/xbmc/games/ports/PortMapper.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015-2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "peripherals/PeripheralTypes.h"
+#include "utils/Observer.h"
+
+#include <map>
+
+namespace JOYSTICK { class IInputHandler; }
+namespace GAME
+{
+ class CPortMapper : public Observer
+ {
+ public:
+ CPortMapper();
+
+ virtual ~CPortMapper();
+
+ virtual void Notify(const Observable& obs, const ObservableMessage msg) override;
+
+ private:
+ void ProcessPeripherals();
+
+ std::map<PERIPHERALS::PeripheralPtr, JOYSTICK::IInputHandler*> m_portMap;
+ };
+}
diff --git a/xbmc/guilib/WindowIDs.h b/xbmc/guilib/WindowIDs.h
index 943448ec36..abdd6188a3 100644
--- a/xbmc/guilib/WindowIDs.h
+++ b/xbmc/guilib/WindowIDs.h
@@ -145,6 +145,7 @@
#define WINDOW_FULLSCREEN_RADIO 10801 // virtual window for PVR radio specific keymaps with fallback to WINDOW_VISUALISATION
#define WINDOW_DIALOG_GAME_CONTROLLERS 10820
+#define WINDOW_GAMES 10821
//#define WINDOW_VIRTUAL_KEYBOARD 11000
// WINDOW_ID's from 11100 to 11199 reserved for Skins
@@ -163,6 +164,8 @@
#define WINDOW_VIDEO_MENU 12902
#define WINDOW_VIDEO_TIME_SEEK 12905 // virtual window for time seeking during fullscreen video
+#define WINDOW_FULLSCREEN_GAME 12906
+
#define WINDOW_SPLASH 12997 // splash window
#define WINDOW_START 12998 // first window to load
#define WINDOW_STARTUP_ANIM 12999 // for startup animations
diff --git a/xbmc/input/InputManager.cpp b/xbmc/input/InputManager.cpp
index 80c89f0283..2ceebed27a 100644
--- a/xbmc/input/InputManager.cpp
+++ b/xbmc/input/InputManager.cpp
@@ -23,6 +23,10 @@
#include "Application.h"
#include "InputManager.h"
#include "input/keyboard/IKeyboardHandler.h"
+#include "input/mouse/generic/MouseInputHandling.h"
+#include "input/mouse/IMouseDriverHandler.h"
+#include "input/mouse/MouseWindowingButtonMap.h"
+#include "input/keyboard/KeyboardEasterEgg.h"
#include "input/Key.h"
#include "messaging/ApplicationMessenger.h"
#include "guilib/Geometry.h"
@@ -68,6 +72,18 @@ using EVENTSERVER::CEventServer;
using namespace KODI::MESSAGING;
using PERIPHERALS::CPeripherals;
+CInputManager::CInputManager() :
+ m_mouseButtonMap(new MOUSE::CMouseWindowingButtonMap),
+ m_keyboardEasterEgg(new KEYBOARD::CKeyboardEasterEgg)
+{
+ RegisterKeyboardHandler(m_keyboardEasterEgg.get());
+}
+
+CInputManager::~CInputManager()
+{
+ UnregisterKeyboardHandler(m_keyboardEasterEgg.get());
+}
+
CInputManager& CInputManager::GetInstance()
{
static CInputManager inputManager;
@@ -382,9 +398,40 @@ bool CInputManager::OnEvent(XBMC_Event& newEvent)
case XBMC_MOUSEBUTTONDOWN:
case XBMC_MOUSEBUTTONUP:
case XBMC_MOUSEMOTION:
- m_Mouse.HandleEvent(newEvent);
- ProcessMouse(g_windowManager.GetActiveWindowID());
+ {
+ bool handled = false;
+
+ for (auto it = m_mouseHandlers.begin(); it != m_mouseHandlers.end(); ++it)
+ {
+ if (newEvent.type == XBMC_MOUSEMOTION)
+ {
+ if (it->driverHandler->OnPosition(newEvent.motion.x, newEvent.motion.y))
+ handled = true;
+ }
+ else
+ {
+ if (newEvent.button.type == XBMC_MOUSEBUTTONDOWN)
+ {
+ if (it->driverHandler->OnButtonPress(newEvent.button.button))
+ handled = true;
+ }
+ else if (newEvent.button.type == XBMC_MOUSEBUTTONUP)
+ {
+ it->driverHandler->OnButtonRelease(newEvent.button.button);
+ }
+ }
+
+ if (handled)
+ break;
+ }
+
+ if (!handled)
+ {
+ m_Mouse.HandleEvent(newEvent);
+ ProcessMouse(g_windowManager.GetActiveWindowID());
+ }
break;
+ }
case XBMC_TOUCH:
{
if (newEvent.touch.action == ACTION_TOUCH_TAP)
@@ -787,3 +834,30 @@ void CInputManager::UnregisterKeyboardHandler(KEYBOARD::IKeyboardHandler* handle
{
m_keyboardHandlers.erase(std::remove(m_keyboardHandlers.begin(), m_keyboardHandlers.end(), handler), m_keyboardHandlers.end());
}
+
+std::string CInputManager::RegisterMouseHandler(MOUSE::IMouseInputHandler* handler)
+{
+ auto it = std::find_if(m_mouseHandlers.begin(), m_mouseHandlers.end(),
+ [handler](const MouseHandlerHandle& element)
+ {
+ return element.inputHandler == handler;
+ });
+
+ if (it == m_mouseHandlers.end())
+ {
+ std::unique_ptr<MOUSE::IMouseDriverHandler> driverHandler(new MOUSE::CMouseInputHandling(handler, m_mouseButtonMap.get()));
+ MouseHandlerHandle handle = { handler, std::move(driverHandler) };
+ m_mouseHandlers.insert(m_mouseHandlers.begin(), std::move(handle));
+ }
+
+ return m_mouseButtonMap->ControllerID();
+}
+
+void CInputManager::UnregisterMouseHandler(MOUSE::IMouseInputHandler* handler)
+{
+ m_mouseHandlers.erase(std::remove_if(m_mouseHandlers.begin(), m_mouseHandlers.end(),
+ [handler](const MouseHandlerHandle& handle)
+ {
+ return handle.inputHandler == handler;
+ }), m_mouseHandlers.end());
+}
diff --git a/xbmc/input/InputManager.h b/xbmc/input/InputManager.h
index 393565726c..d2767722a2 100644
--- a/xbmc/input/InputManager.h
+++ b/xbmc/input/InputManager.h
@@ -20,6 +20,7 @@
*/
#include <map>
+#include <memory>
#include <string>
#include <vector>
@@ -43,13 +44,33 @@ namespace KEYBOARD
class IKeyboardHandler;
}
+namespace MOUSE
+{
+ class IMouseButtonMap;
+ class IMouseDriverHandler;
+ class IMouseInputHandler;
+}
+
+/// \addtogroup input
+/// \{
+
+/*!
+ * \ingroup input keyboard mouse touch joystick
+ * \brief Main input processing class.
+ *
+ * This class consolidates all input generated from different sources such as
+ * mouse, keyboard, joystick or touch (in \ref OnEvent).
+ *
+ * \copydoc keyboard
+ * \copydoc mouse
+ */
class CInputManager : public ISettingCallback
{
private:
- CInputManager() { }
+ CInputManager();
CInputManager(const CInputManager&);
CInputManager const& operator=(CInputManager const&);
- virtual ~CInputManager() { };
+ virtual ~CInputManager();
public:
/*! \brief static method to get the current instance of the class. Creates a new instance the first time it's called.
@@ -219,9 +240,32 @@ public:
virtual void OnSettingChanged(const CSetting *setting) override;
+ /*! \brief Registers a handler to be called on keyboard input (e.g a game client).
+ *
+ * \param handler The handler to call on keyboard input.
+ */
void RegisterKeyboardHandler(KEYBOARD::IKeyboardHandler* handler);
+
+ /*! \brief Unregisters handler from keyboard input.
+ *
+ * \param[in] handler The handler to unregister from keyboard input.
+ */
void UnregisterKeyboardHandler(KEYBOARD::IKeyboardHandler* handler);
+ /*! \brief Registers a handler to be called on mouse input (e.g a game client).
+ *
+ * \param handler The handler to call on mouse input.
+ * \return[in] The controller ID that serves as a context for incoming events.
+ * \sa IMouseButtonMap
+ */
+ std::string RegisterMouseHandler(MOUSE::IMouseInputHandler* handler);
+
+ /*! \brief Unregisters handler from mouse input.
+ *
+ * \param[in] handler The handler to unregister from mouse input.
+ */
+ void UnregisterMouseHandler(MOUSE::IMouseInputHandler* handler);
+
private:
/*! \brief Process keyboard event and translate into an action
@@ -273,4 +317,17 @@ private:
CCriticalSection m_actionMutex;
std::vector<KEYBOARD::IKeyboardHandler*> m_keyboardHandlers;
+
+ struct MouseHandlerHandle
+ {
+ MOUSE::IMouseInputHandler* inputHandler;
+ std::unique_ptr<MOUSE::IMouseDriverHandler> driverHandler;
+ };
+
+ std::vector<MouseHandlerHandle> m_mouseHandlers;
+ std::unique_ptr<MOUSE::IMouseButtonMap> m_mouseButtonMap;
+
+ std::unique_ptr<KEYBOARD::IKeyboardHandler> m_keyboardEasterEgg;
};
+
+/// \}
diff --git a/xbmc/input/joysticks/CMakeLists.txt b/xbmc/input/joysticks/CMakeLists.txt
index ac5450df91..2e1ec43705 100644
--- a/xbmc/input/joysticks/CMakeLists.txt
+++ b/xbmc/input/joysticks/CMakeLists.txt
@@ -1,6 +1,7 @@
set(SOURCES DeadzoneFilter.cpp
DefaultJoystick.cpp
DriverPrimitive.cpp
+ JoystickEasterEgg.cpp
JoystickMonitor.cpp
JoystickTranslator.cpp
KeymapHandler.cpp
@@ -13,11 +14,13 @@ set(HEADERS DeadzoneFilter.h
IButtonMap.h
IButtonMapCallback.h
IButtonMapper.h
+ IButtonSequence.h
IDriverHandler.h
IDriverReceiver.h
IInputHandler.h
IInputReceiver.h
IKeymapHandler.h
+ JoystickEasterEgg.h
JoystickMonitor.h
JoystickTranslator.h
JoystickTypes.h
diff --git a/xbmc/input/joysticks/DefaultJoystick.cpp b/xbmc/input/joysticks/DefaultJoystick.cpp
index 99c7c61f94..d474023850 100644
--- a/xbmc/input/joysticks/DefaultJoystick.cpp
+++ b/xbmc/input/joysticks/DefaultJoystick.cpp
@@ -20,6 +20,7 @@
#include "DefaultJoystick.h"
#include "KeymapHandler.h"
+#include "JoystickEasterEgg.h"
#include "JoystickTranslator.h"
#include "input/Key.h"
#include "Application.h"
@@ -34,7 +35,8 @@ using namespace JOYSTICK;
CDefaultJoystick::CDefaultJoystick(void) :
m_handler(new CKeymapHandler),
- m_rumbleGenerator(ControllerID())
+ m_rumbleGenerator(ControllerID()),
+ m_easterEgg(new CJoystickEasterEgg)
{
}
@@ -75,6 +77,9 @@ INPUT_TYPE CDefaultJoystick::GetInputType(const FeatureName& feature) const
bool CDefaultJoystick::OnButtonPress(const FeatureName& feature, bool bPressed)
{
+ if (bPressed && m_easterEgg->OnButtonPress(feature))
+ return true;
+
const unsigned int keyId = GetKeyID(feature);
if (m_handler->GetInputType(keyId) == INPUT_TYPE::DIGITAL)
diff --git a/xbmc/input/joysticks/DefaultJoystick.h b/xbmc/input/joysticks/DefaultJoystick.h
index 548b9e116e..0c22e14a2c 100644
--- a/xbmc/input/joysticks/DefaultJoystick.h
+++ b/xbmc/input/joysticks/DefaultJoystick.h
@@ -25,6 +25,7 @@
#include "RumbleGenerator.h"
#include <map>
+#include <memory>
#include <vector>
#define DEFAULT_CONTROLLER_ID "game.controller.default"
@@ -36,6 +37,7 @@
namespace JOYSTICK
{
class IKeymapHandler;
+ class IButtonSequence;
/*!
* \ingroup joystick
@@ -99,5 +101,7 @@ namespace JOYSTICK
// Rumble functionality
CRumbleGenerator m_rumbleGenerator;
+
+ std::unique_ptr<IButtonSequence> m_easterEgg;
};
}
diff --git a/xbmc/input/joysticks/IButtonSequence.h b/xbmc/input/joysticks/IButtonSequence.h
new file mode 100644
index 0000000000..d2646582e4
--- /dev/null
+++ b/xbmc/input/joysticks/IButtonSequence.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "JoystickTypes.h"
+
+namespace JOYSTICK
+{
+ class IButtonSequence
+ {
+ public:
+ virtual ~IButtonSequence() = default;
+
+ virtual bool OnButtonPress(const FeatureName& feature) = 0;
+ };
+}
diff --git a/xbmc/input/joysticks/JoystickEasterEgg.cpp b/xbmc/input/joysticks/JoystickEasterEgg.cpp
new file mode 100644
index 0000000000..b163faf710
--- /dev/null
+++ b/xbmc/input/joysticks/JoystickEasterEgg.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "JoystickEasterEgg.h"
+#include "guilib/GUIAudioManager.h"
+#include "guilib/WindowIDs.h"
+#include "settings/Settings.h"
+
+using namespace JOYSTICK;
+
+std::vector<FeatureName> CJoystickEasterEgg::m_sequence = {
+ "up",
+ "up",
+ "down",
+ "down",
+ "left",
+ "right",
+ "left",
+ "right",
+ "b",
+ "a",
+};
+
+CJoystickEasterEgg::CJoystickEasterEgg(void) :
+ m_state(0)
+{
+}
+
+bool CJoystickEasterEgg::OnButtonPress(const FeatureName& feature)
+{
+ bool bHandled = false;
+
+ // Update state
+ if (feature == m_sequence[m_state])
+ m_state++;
+ else
+ m_state = 0;
+
+ // Capture input when finished with arrows (2 x up/down/left/right)
+ if (m_state > 8)
+ {
+ bHandled = true;
+
+ if (m_state >= m_sequence.size())
+ {
+ OnFinish();
+ m_state = 0;
+ }
+ }
+
+ return bHandled;
+}
+
+void CJoystickEasterEgg::OnFinish(void)
+{
+ CSettings::GetInstance().ToggleBool(CSettings::SETTING_GAMES_ENABLE);
+
+ WINDOW_SOUND sound = CSettings::GetInstance().GetBool(CSettings::SETTING_GAMES_ENABLE) ? SOUND_INIT : SOUND_DEINIT;
+ g_audioManager.PlayWindowSound(WINDOW_DIALOG_KAI_TOAST, sound);
+
+ //! @todo Shake screen
+}
diff --git a/xbmc/input/joysticks/JoystickEasterEgg.h b/xbmc/input/joysticks/JoystickEasterEgg.h
new file mode 100644
index 0000000000..fe239fcec7
--- /dev/null
+++ b/xbmc/input/joysticks/JoystickEasterEgg.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "IButtonSequence.h"
+
+#include <vector>
+
+namespace JOYSTICK
+{
+ /*!
+ * \brief Hush!!!
+ */
+ class CJoystickEasterEgg : public IButtonSequence
+ {
+ public:
+ CJoystickEasterEgg(void);
+ virtual ~CJoystickEasterEgg() = default;
+
+ // implementation of IButtonSequence
+ virtual bool OnButtonPress(const FeatureName& feature) override;
+
+ static void OnFinish(void);
+
+ private:
+ static std::vector<FeatureName> m_sequence;
+
+ unsigned int m_state;
+ };
+}
diff --git a/xbmc/input/joysticks/JoystickTypes.h b/xbmc/input/joysticks/JoystickTypes.h
index 6e774a94fe..f30ddcb1fa 100644
--- a/xbmc/input/joysticks/JoystickTypes.h
+++ b/xbmc/input/joysticks/JoystickTypes.h
@@ -42,6 +42,7 @@ namespace JOYSTICK
* 2) analog stick
* 3) accelerometer
* 4) rumble motor
+ * 5) relative pointer
*
* [1] All three driver primitives (buttons, hats and axes) have a state that
* can be represented using a single scalar value. For this reason,
@@ -54,6 +55,7 @@ namespace JOYSTICK
ANALOG_STICK,
ACCELEROMETER,
MOTOR,
+ RELPOINTER,
};
/*!
diff --git a/xbmc/input/joysticks/Makefile b/xbmc/input/joysticks/Makefile
index cefbb9c1e9..c7e3a75c66 100644
--- a/xbmc/input/joysticks/Makefile
+++ b/xbmc/input/joysticks/Makefile
@@ -1,6 +1,7 @@
SRCS=DeadzoneFilter.cpp \
DefaultJoystick.cpp \
DriverPrimitive.cpp \
+ JoystickEasterEgg.cpp \
JoystickMonitor.cpp \
JoystickTranslator.cpp \
KeymapHandler.cpp \
diff --git a/xbmc/input/keyboard/CMakeLists.txt b/xbmc/input/keyboard/CMakeLists.txt
index 463c5d0f61..ed121ac30a 100644
--- a/xbmc/input/keyboard/CMakeLists.txt
+++ b/xbmc/input/keyboard/CMakeLists.txt
@@ -1 +1,6 @@
-set(HEADERS IKeyboardHandler.h)
+set(SOURCES KeyboardEasterEgg.cpp)
+
+set(HEADERS IKeyboardHandler.h
+ KeyboardEasterEgg.h)
+
+core_add_library(input_keyboard)
diff --git a/xbmc/input/keyboard/KeyboardEasterEgg.cpp b/xbmc/input/keyboard/KeyboardEasterEgg.cpp
new file mode 100644
index 0000000000..a2f089256c
--- /dev/null
+++ b/xbmc/input/keyboard/KeyboardEasterEgg.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "input/keyboard/KeyboardEasterEgg.h"
+#include "input/joysticks/JoystickEasterEgg.h"
+#include "input/Key.h"
+
+using namespace KEYBOARD;
+
+std::vector<XBMCVKey> CKeyboardEasterEgg::m_sequence = {
+ XBMCVK_UP,
+ XBMCVK_UP,
+ XBMCVK_DOWN,
+ XBMCVK_DOWN,
+ XBMCVK_LEFT,
+ XBMCVK_RIGHT,
+ XBMCVK_LEFT,
+ XBMCVK_RIGHT,
+ XBMCVK_B,
+ XBMCVK_A,
+};
+
+CKeyboardEasterEgg::CKeyboardEasterEgg(void) :
+ m_state(0)
+{
+}
+
+bool CKeyboardEasterEgg::OnKeyPress(const CKey& key)
+{
+ bool bHandled = false;
+
+ // Update state
+ if (key.GetVKey() == m_sequence[m_state])
+ m_state++;
+ else
+ m_state = 0;
+
+ // Capture input when finished with arrows (2 x up/down/left/right)
+ if (m_state > 8)
+ {
+ bHandled = true;
+
+ if (m_state >= m_sequence.size())
+ {
+ JOYSTICK::CJoystickEasterEgg::OnFinish();
+ m_state = 0;
+ }
+ }
+
+ return bHandled;
+}
diff --git a/xbmc/input/keyboard/KeyboardEasterEgg.h b/xbmc/input/keyboard/KeyboardEasterEgg.h
new file mode 100644
index 0000000000..7d3888644c
--- /dev/null
+++ b/xbmc/input/keyboard/KeyboardEasterEgg.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "IKeyboardHandler.h"
+#include "input/XBMC_vkeys.h"
+
+#include <vector>
+
+namespace KEYBOARD
+{
+ /*!
+ * \brief Hush!!!
+ */
+ class CKeyboardEasterEgg : public IKeyboardHandler
+ {
+ public:
+ CKeyboardEasterEgg(void);
+ virtual ~CKeyboardEasterEgg() = default;
+
+ // implementation of IKeyboardHandler
+ virtual bool OnKeyPress(const CKey& key);
+ virtual void OnKeyRelease(const CKey& key) { }
+
+ private:
+ static std::vector<XBMCVKey> m_sequence;
+
+ unsigned int m_state;
+ };
+}
diff --git a/xbmc/input/keyboard/Makefile b/xbmc/input/keyboard/Makefile
new file mode 100644
index 0000000000..939a34ed96
--- /dev/null
+++ b/xbmc/input/keyboard/Makefile
@@ -0,0 +1,6 @@
+SRCS=KeyboardEasterEgg.cpp \
+
+LIB=input_keyboard.a
+
+include ../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/input/mouse/CMakeLists.txt b/xbmc/input/mouse/CMakeLists.txt
new file mode 100644
index 0000000000..5a2f7b4560
--- /dev/null
+++ b/xbmc/input/mouse/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(SOURCES MouseWindowingButtonMap.cpp)
+
+set(HEADERS IMouseButtonMap.h
+ IMouseDriverHandler.h
+ IMouseInputHandler.h
+ MouseWindowingButtonMap.h)
+
+core_add_library(input_mouse)
diff --git a/xbmc/input/mouse/IMouseButtonMap.h b/xbmc/input/mouse/IMouseButtonMap.h
new file mode 100644
index 0000000000..80d812da4f
--- /dev/null
+++ b/xbmc/input/mouse/IMouseButtonMap.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include <string>
+
+namespace MOUSE
+{
+ /*!
+ * \ingroup mouse
+ * \brief Button map interface to translate between the mouse's driver data
+ * and its higher-level features.
+ */
+ class IMouseButtonMap
+ {
+ public:
+ virtual ~IMouseButtonMap(void) = default;
+
+ /*!
+ * \brief The ID of the controller profile associated with this button map
+ *
+ * The controller ID provided by the implementation serves as the context
+ * for the feature names below.
+ *
+ * \return The ID of this button map's controller profile
+ */
+ virtual std::string ControllerID(void) const = 0;
+
+ /*!
+ * \brief Get the name of a button by its index
+ *
+ * \param buttonIndex The index of the button
+ * \param[out] feature The name of the feature with the specified button index
+ *
+ * \return True if the button index is associated with a feature, false otherwise
+ */
+ virtual bool GetButton(unsigned int buttonIndex, std::string& feature) = 0;
+
+ /*!
+ * \brief Get the name of the mouse's relative pointer
+ *
+ * \param[out] feature The name of the relative pointer
+ *
+ * \return True if the mouse has a relative pointer, false otherwise
+ */
+ virtual bool GetRelativePointer(std::string& feature) = 0;
+
+ /*!
+ * \brief Get the button index for a button
+ *
+ * \param feature The name of the button
+ * \param buttonIndex The resolved button index
+ *
+ * \return True if the feature resolved to a button index, or false otherwise
+ */
+ virtual bool GetButtonIndex(const std::string& feature, unsigned int& buttonIndex) = 0;
+ };
+}
diff --git a/xbmc/input/mouse/IMouseDriverHandler.h b/xbmc/input/mouse/IMouseDriverHandler.h
new file mode 100644
index 0000000000..9da004a4a4
--- /dev/null
+++ b/xbmc/input/mouse/IMouseDriverHandler.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+namespace MOUSE
+{
+ /*!
+ * \ingroup mouse
+ * \brief Interface for handling mouse driver events
+ */
+ class IMouseDriverHandler
+ {
+ public:
+ virtual ~IMouseDriverHandler(void) = default;
+
+ /*!
+ * \brief Handle mouse position updates
+ *
+ * \param x The new x coordinate of the pointer
+ * \param y The new y coordinate of the pointer
+ *
+ * \return True if the event was handled, false otherwise
+ */
+ virtual bool OnPosition(int x, int y) = 0;
+
+ /*!
+ * \brief A mouse button has been pressed
+ *
+ * \param button The index of the pressed button
+ *
+ * \return True if the event was handled, otherwise false
+ */
+ virtual bool OnButtonPress(unsigned int button) = 0;
+
+ /*!
+ * \brief A mouse button has been released
+ *
+ * \param button The index of the released button
+ */
+ virtual void OnButtonRelease(unsigned int button) = 0;
+ };
+}
diff --git a/xbmc/input/mouse/IMouseInputHandler.h b/xbmc/input/mouse/IMouseInputHandler.h
new file mode 100644
index 0000000000..6eda79b954
--- /dev/null
+++ b/xbmc/input/mouse/IMouseInputHandler.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include <string>
+
+namespace MOUSE
+{
+ /*!
+ * \ingroup mouse
+ * \brief Interface for handling mouse events
+ */
+ class IMouseInputHandler
+ {
+ public:
+ virtual ~IMouseInputHandler(void) = default;
+
+ /*!
+ * \brief The controller profile for this mouse input handler
+ *
+ * \return The ID of the add-on extending kodi.game.controller
+ */
+ virtual std::string ControllerID(void) const = 0;
+
+ /*!
+ * \brief A relative pointer has moved
+ *
+ * \param relpointer The name of the relative pointer being moved
+ * \param dx The relative x coordinate of motion
+ * \param dy The relative y coordinate of motion
+ *
+ * \return True if the event was handled, otherwise false
+ */
+ virtual bool OnMotion(const std::string& relpointer, int dx, int dy) = 0;
+
+ /*!
+ * \brief A mouse button has been pressed
+ *
+ * \param button The name of the feature being pressed
+ *
+ * \return True if the event was handled, otherwise false
+ */
+ virtual bool OnButtonPress(const std::string& button) = 0;
+
+ /*!
+ * \brief A mouse button has been released
+ *
+ * \param button The name of the feature being released
+ */
+ virtual void OnButtonRelease(const std::string& button) = 0;
+ };
+}
diff --git a/xbmc/input/mouse/Makefile b/xbmc/input/mouse/Makefile
new file mode 100644
index 0000000000..62f682d704
--- /dev/null
+++ b/xbmc/input/mouse/Makefile
@@ -0,0 +1,6 @@
+SRCS=MouseWindowingButtonMap.cpp \
+
+LIB=input_mouse.a
+
+include ../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/input/mouse/MouseWindowingButtonMap.cpp b/xbmc/input/mouse/MouseWindowingButtonMap.cpp
new file mode 100644
index 0000000000..e2da4ae005
--- /dev/null
+++ b/xbmc/input/mouse/MouseWindowingButtonMap.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "MouseWindowingButtonMap.h"
+#include "input/MouseStat.h"
+
+#include <algorithm>
+
+using namespace MOUSE;
+
+#define CONTROLLER_PROFILE "game.controller.mouse"
+
+std::vector<std::pair<unsigned int, std::string>> CMouseWindowingButtonMap::m_buttonMap = {
+ { XBMC_BUTTON_LEFT, "left" },
+ { XBMC_BUTTON_MIDDLE, "middle" },
+ { XBMC_BUTTON_RIGHT, "right" },
+ { XBMC_BUTTON_WHEELUP, "wheelup" },
+ { XBMC_BUTTON_WHEELDOWN, "wheeldown" },
+};
+
+std::string CMouseWindowingButtonMap::m_pointerName = "pointer";
+
+std::string CMouseWindowingButtonMap::ControllerID(void) const
+{
+ return CONTROLLER_PROFILE;
+}
+
+bool CMouseWindowingButtonMap::GetButton(unsigned int buttonIndex, std::string& feature)
+{
+ auto it = std::find_if(m_buttonMap.begin(), m_buttonMap.end(),
+ [buttonIndex](const std::pair<unsigned int, std::string>& entry)
+ {
+ return entry.first == buttonIndex;
+ });
+
+ if (it != m_buttonMap.end())
+ {
+ feature = it->second;
+ return true;
+ }
+
+ return false;
+}
+
+bool CMouseWindowingButtonMap::GetRelativePointer(std::string& feature)
+{
+ feature = m_pointerName;
+ return true;
+}
+
+bool CMouseWindowingButtonMap::GetButtonIndex(const std::string& feature, unsigned int& buttonIndex)
+{
+ auto it = std::find_if(m_buttonMap.begin(), m_buttonMap.end(),
+ [&feature](const std::pair<unsigned int, std::string>& entry)
+ {
+ return entry.second == feature;
+ });
+
+ if (it != m_buttonMap.end())
+ {
+ buttonIndex = it->first;
+ return true;
+ }
+
+ return false;
+}
diff --git a/xbmc/input/mouse/MouseWindowingButtonMap.h b/xbmc/input/mouse/MouseWindowingButtonMap.h
new file mode 100644
index 0000000000..a728257178
--- /dev/null
+++ b/xbmc/input/mouse/MouseWindowingButtonMap.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "input/mouse/IMouseButtonMap.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace MOUSE
+{
+ /*!
+ * \ingroup mouse
+ * \brief Maps mouse windowing events to higher-level features understood by IMouseInputHandler implementations.
+ */
+ class CMouseWindowingButtonMap : public IMouseButtonMap
+ {
+ public:
+ virtual ~CMouseWindowingButtonMap(void) = default;
+
+ // implementation of IMouseButtonMap
+ virtual std::string ControllerID(void) const override;
+ virtual bool GetButton(unsigned int buttonIndex, std::string& feature) override;
+ virtual bool GetRelativePointer(std::string& feature) override;
+ virtual bool GetButtonIndex(const std::string& feature, unsigned int& buttonIndex) override;
+
+ private:
+ static std::vector<std::pair<unsigned int, std::string>> m_buttonMap;
+ static std::string m_pointerName;
+ };
+}
diff --git a/xbmc/input/mouse/generic/CMakeLists.txt b/xbmc/input/mouse/generic/CMakeLists.txt
new file mode 100644
index 0000000000..b5c28585b6
--- /dev/null
+++ b/xbmc/input/mouse/generic/CMakeLists.txt
@@ -0,0 +1,5 @@
+set(SOURCES MouseInputHandling.cpp)
+
+set(HEADERS MouseInputHandling.h)
+
+core_add_library(input_mouse_generic)
diff --git a/xbmc/input/mouse/generic/Makefile b/xbmc/input/mouse/generic/Makefile
new file mode 100644
index 0000000000..0170855539
--- /dev/null
+++ b/xbmc/input/mouse/generic/Makefile
@@ -0,0 +1,6 @@
+SRCS=MouseInputHandling.cpp \
+
+LIB=input_mouse_generic.a
+
+include ../../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/input/mouse/generic/MouseInputHandling.cpp b/xbmc/input/mouse/generic/MouseInputHandling.cpp
new file mode 100644
index 0000000000..641dd48ecb
--- /dev/null
+++ b/xbmc/input/mouse/generic/MouseInputHandling.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "MouseInputHandling.h"
+#include "input/mouse/IMouseButtonMap.h"
+#include "input/mouse/IMouseInputHandler.h"
+
+using namespace MOUSE;
+
+CMouseInputHandling::CMouseInputHandling(IMouseInputHandler* handler, IMouseButtonMap* buttonMap) :
+ m_handler(handler),
+ m_buttonMap(buttonMap),
+ m_x(0),
+ m_y(0)
+{
+}
+
+bool CMouseInputHandling::OnPosition(int x, int y)
+{
+ int dx = x - m_x;
+ int dy = y - m_y;
+
+ bool bHandled = false;
+
+ std::string featureName;
+ if (m_buttonMap->GetRelativePointer(featureName))
+ bHandled = m_handler->OnMotion(featureName, dx, dy);
+
+ m_x = x;
+ m_y = y;
+
+ return bHandled;
+}
+
+bool CMouseInputHandling::OnButtonPress(unsigned int button)
+{
+ bool bHandled = false;
+
+ std::string featureName;
+ if (m_buttonMap->GetButton(button, featureName))
+ bHandled = m_handler->OnButtonPress(featureName);
+
+ return bHandled;
+}
+
+void CMouseInputHandling::OnButtonRelease(unsigned int button)
+{
+ std::string featureName;
+ if (m_buttonMap->GetButton(button, featureName))
+ m_handler->OnButtonRelease(featureName);
+}
diff --git a/xbmc/input/mouse/generic/MouseInputHandling.h b/xbmc/input/mouse/generic/MouseInputHandling.h
new file mode 100644
index 0000000000..be1f7a94b6
--- /dev/null
+++ b/xbmc/input/mouse/generic/MouseInputHandling.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "input/mouse/IMouseDriverHandler.h"
+
+namespace MOUSE
+{
+ class IMouseInputHandler;
+ class IMouseButtonMap;
+ class CRelativePointer;
+
+ /*!
+ * \ingroup mouse
+ * \brief Class to translate input from driver info to higher-level features
+ */
+ class CMouseInputHandling : public IMouseDriverHandler
+ {
+ public:
+ CMouseInputHandling(IMouseInputHandler* handler, IMouseButtonMap* buttonMap);
+
+ virtual ~CMouseInputHandling(void) = default;
+
+ // implementation of IMouseDriverHandler
+ virtual bool OnPosition(int x, int y) override;
+ virtual bool OnButtonPress(unsigned int button) override;
+ virtual void OnButtonRelease(unsigned int button) override;
+
+ private:
+ // Construction parameters
+ IMouseInputHandler* const m_handler;
+ IMouseButtonMap* const m_buttonMap;
+
+ // Mouse parameters
+ int m_x;
+ int m_y;
+ };
+}
diff --git a/xbmc/interfaces/builtins/AddonBuiltins.cpp b/xbmc/interfaces/builtins/AddonBuiltins.cpp
index 5b5eb7ef8c..b4506abf38 100644
--- a/xbmc/interfaces/builtins/AddonBuiltins.cpp
+++ b/xbmc/interfaces/builtins/AddonBuiltins.cpp
@@ -31,12 +31,15 @@
#include "addons/RepositoryUpdater.h"
#include "FileItem.h"
#include "filesystem/PluginDirectory.h"
+#include "games/tags/GameInfoTag.h"
#include "guilib/GUIWindowManager.h"
#include "GUIUserMessages.h"
#include "interfaces/generic/ScriptInvocationManager.h"
#include "utils/log.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
+#include "Application.h"
+#include "PlayListPlayer.h"
#if defined(TARGET_DARWIN)
#include "filesystem/SpecialProtocol.h"
@@ -80,7 +83,7 @@ static int RunPlugin(const std::vector<std::string>& params)
return 0;
}
-/*! \brief Run a script or plugin add-on.
+/*! \brief Run a script, plugin or game add-on.
* \param params The parameters.
* \details params[0] = add-on id.
* params[1] is blank for no add-on parameters
@@ -140,6 +143,24 @@ static int RunAddon(const std::vector<std::string>& params)
// (params[1] ... params[x]) separated by a comma to RunScript
CBuiltins::GetInstance().Execute(StringUtils::Format("RunScript(%s)", StringUtils::Join(params, ",").c_str()));
}
+ else if (CAddonMgr::GetInstance().GetAddon(addonid, addon, ADDON_GAMEDLL))
+ {
+ CFileItem item;
+
+ if (params.size() >= 2)
+ {
+ item = CFileItem(params[1], false);
+ item.GetGameInfoTag()->SetGameClient(addonid);
+ }
+ else
+ item = CFileItem(addon);
+
+ if (!g_application.PlayMedia(item, "", PLAYLIST_NONE))
+ {
+ CLog::Log(LOGERROR, "RunAddon could not start %s", addonid.c_str());
+ return false;
+ }
+ }
else
CLog::Log(LOGERROR, "RunAddon: unknown add-on id '%s', or unexpected add-on type (not a script or plugin).", addonid.c_str());
}
diff --git a/xbmc/interfaces/json-rpc/AddonsOperations.cpp b/xbmc/interfaces/json-rpc/AddonsOperations.cpp
index 272620b20c..e0de3e0380 100644
--- a/xbmc/interfaces/json-rpc/AddonsOperations.cpp
+++ b/xbmc/interfaces/json-rpc/AddonsOperations.cpp
@@ -62,6 +62,9 @@ JSONRPC_STATUS CAddonsOperations::GetAddons(const std::string &method, ITranspor
case ADDON_IMAGE:
content = CPluginSource::IMAGE;
break;
+ case ADDON_GAME:
+ content = CPluginSource::GAME;
+ break;
case ADDON_EXECUTABLE:
content = CPluginSource::EXECUTABLE;
break;
diff --git a/xbmc/peripherals/Peripherals.cpp b/xbmc/peripherals/Peripherals.cpp
index d5ccff16be..0abf0ef0d4 100644
--- a/xbmc/peripherals/Peripherals.cpp
+++ b/xbmc/peripherals/Peripherals.cpp
@@ -77,12 +77,14 @@ using namespace XFILE;
CPeripherals::CPeripherals() :
m_eventScanner(this)
{
+ RegisterObserver(&m_portMapper);
Clear();
}
CPeripherals::~CPeripherals()
{
Clear();
+ UnregisterObserver(&m_portMapper);
}
CPeripherals &CPeripherals::GetInstance()
diff --git a/xbmc/peripherals/Peripherals.h b/xbmc/peripherals/Peripherals.h
index e7428df2e0..6495b5ecba 100644
--- a/xbmc/peripherals/Peripherals.h
+++ b/xbmc/peripherals/Peripherals.h
@@ -24,6 +24,7 @@
#include "EventScanner.h"
#include "bus/PeripheralBus.h"
#include "devices/Peripheral.h"
+#include "games/ports/PortMapper.h" //! @todo Find me a better place
#include "messaging/IMessageTarget.h"
#include "settings/lib/ISettingCallback.h"
#include "system.h"
@@ -306,6 +307,7 @@ namespace PERIPHERALS
std::vector<PeripheralBusPtr> m_busses;
std::vector<PeripheralDeviceMapping> m_mappings;
CEventScanner m_eventScanner;
+ GAME::CPortMapper m_portMapper; //! @todo Find me a better place
CCriticalSection m_critSection;
CCriticalSection m_critSectionBusses;
CCriticalSection m_critSectionMappings;
diff --git a/xbmc/profiles/ProfilesManager.cpp b/xbmc/profiles/ProfilesManager.cpp
index 54f6b8c438..c31579b973 100644
--- a/xbmc/profiles/ProfilesManager.cpp
+++ b/xbmc/profiles/ProfilesManager.cpp
@@ -354,6 +354,7 @@ void CProfilesManager::CreateProfileFolders()
CDirectory::Create(GetThumbnailsFolder());
CDirectory::Create(GetVideoThumbFolder());
CDirectory::Create(GetBookmarksThumbFolder());
+ CDirectory::Create(GetSavestatesFolder());
for (size_t hex = 0; hex < 16; hex++)
CDirectory::Create(URIUtils::AddFileToFolder(GetThumbnailsFolder(), StringUtils::Format("%lx", hex)));
@@ -505,6 +506,14 @@ std::string CProfilesManager::GetLibraryFolder() const
return URIUtils::AddFileToFolder(GetUserDataFolder(), "library");
}
+std::string CProfilesManager::GetSavestatesFolder() const
+{
+ if (GetCurrentProfile().hasDatabases())
+ return URIUtils::AddFileToFolder(GetProfileUserDataFolder(), "Savestates");
+
+ return URIUtils::AddFileToFolder(GetUserDataFolder(), "Savestates");
+}
+
std::string CProfilesManager::GetSettingsFile() const
{
std::string settings;
diff --git a/xbmc/profiles/ProfilesManager.h b/xbmc/profiles/ProfilesManager.h
index f0bcab87ed..3b50860b3c 100644
--- a/xbmc/profiles/ProfilesManager.h
+++ b/xbmc/profiles/ProfilesManager.h
@@ -171,6 +171,7 @@ public:
std::string GetVideoThumbFolder() const;
std::string GetBookmarksThumbFolder() const;
std::string GetLibraryFolder() const;
+ std::string GetSavestatesFolder() const;
std::string GetSettingsFile() const;
// uses HasSlashAtEnd to determine if a directory or file was meant
diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp
index cc37998f0c..6f9723e926 100644
--- a/xbmc/settings/AdvancedSettings.cpp
+++ b/xbmc/settings/AdvancedSettings.cpp
@@ -1159,6 +1159,23 @@ void CAdvancedSettings::ParseSettingsFile(const std::string &file)
XMLUtils::GetBoolean(pDatabase, "compression", m_databaseEpg.compression);
}
+ pDatabase = pRootElement->FirstChildElement("savestatedatabase");
+ if (pDatabase)
+ {
+ XMLUtils::GetString(pDatabase, "type", m_databaseSavestates.type);
+ XMLUtils::GetString(pDatabase, "host", m_databaseSavestates.host);
+ XMLUtils::GetString(pDatabase, "port", m_databaseSavestates.port);
+ XMLUtils::GetString(pDatabase, "user", m_databaseSavestates.user);
+ XMLUtils::GetString(pDatabase, "pass", m_databaseSavestates.pass);
+ XMLUtils::GetString(pDatabase, "name", m_databaseSavestates.name);
+ XMLUtils::GetString(pDatabase, "key", m_databaseSavestates.key);
+ XMLUtils::GetString(pDatabase, "cert", m_databaseSavestates.cert);
+ XMLUtils::GetString(pDatabase, "ca", m_databaseSavestates.ca);
+ XMLUtils::GetString(pDatabase, "capath", m_databaseSavestates.capath);
+ XMLUtils::GetString(pDatabase, "ciphers", m_databaseSavestates.ciphers);
+ XMLUtils::GetBoolean(pDatabase, "compression", m_databaseSavestates.compression);
+ }
+
pElement = pRootElement->FirstChildElement("enablemultimediakeys");
if (pElement)
{
diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h
index fc526d11c3..1b747edc0c 100644
--- a/xbmc/settings/AdvancedSettings.h
+++ b/xbmc/settings/AdvancedSettings.h
@@ -336,6 +336,7 @@ class CAdvancedSettings : public ISettingCallback, public ISettingsHandler
DatabaseSettings m_databaseTV; // advanced tv database setup
DatabaseSettings m_databaseEpg; /*!< advanced EPG database setup */
DatabaseSettings m_databaseADSP; /*!< advanced audio dsp database setup */
+ DatabaseSettings m_databaseSavestates; /*!< advanced savestate database setup */
bool m_guiVisualizeDirtyRegions;
int m_guiAlgorithmDirtyRegions;
diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp
index f7c86e6f81..13c5aaaa3e 100644
--- a/xbmc/settings/Settings.cpp
+++ b/xbmc/settings/Settings.cpp
@@ -431,6 +431,9 @@ const std::string CSettings::SETTING_GAMES_KEYBOARD_PLAYERCONFIG_5 = "gameskeybo
const std::string CSettings::SETTING_GAMES_KEYBOARD_PLAYERCONFIG_6 = "gameskeyboard.keyboardplayerconfig6";
const std::string CSettings::SETTING_GAMES_KEYBOARD_PLAYERCONFIG_7 = "gameskeyboard.keyboardplayerconfig7";
const std::string CSettings::SETTING_GAMES_KEYBOARD_PLAYERCONFIG_8 = "gameskeyboard.keyboardplayerconfig8";
+const std::string CSettings::SETTING_GAMES_ENABLE = "gamesgeneral.enable";
+const std::string CSettings::SETTING_GAMES_ENABLEREWIND = "gamesgeneral.enablerewind";
+const std::string CSettings::SETTING_GAMES_REWINDTIME = "gamesgeneral.rewindtime";
CSettings::CSettings()
: m_initialized(false)
@@ -1227,6 +1230,8 @@ void CSettings::InitializeISettingCallbacks()
settingSet.insert(CSettings::SETTING_GAMES_KEYBOARD_PLAYERCONFIG_6);
settingSet.insert(CSettings::SETTING_GAMES_KEYBOARD_PLAYERCONFIG_7);
settingSet.insert(CSettings::SETTING_GAMES_KEYBOARD_PLAYERCONFIG_8);
+ settingSet.insert(CSettings::SETTING_GAMES_ENABLEREWIND);
+ settingSet.insert(CSettings::SETTING_GAMES_REWINDTIME);
m_settingsManager->RegisterCallback(&GAME::CGameSettings::GetInstance(), settingSet);
}
diff --git a/xbmc/settings/Settings.h b/xbmc/settings/Settings.h
index 40e164cbf3..29b37283f8 100644
--- a/xbmc/settings/Settings.h
+++ b/xbmc/settings/Settings.h
@@ -387,6 +387,9 @@ public:
static const std::string SETTING_GAMES_KEYBOARD_PLAYERCONFIG_6;
static const std::string SETTING_GAMES_KEYBOARD_PLAYERCONFIG_7;
static const std::string SETTING_GAMES_KEYBOARD_PLAYERCONFIG_8;
+ static const std::string SETTING_GAMES_ENABLE;
+ static const std::string SETTING_GAMES_ENABLEREWIND;
+ static const std::string SETTING_GAMES_REWINDTIME;
/*!
\brief Creates a new settings wrapper around a new settings manager.
diff --git a/xbmc/utils/Observer.h b/xbmc/utils/Observer.h
index 3b597559e6..49eec5b8fc 100644
--- a/xbmc/utils/Observer.h
+++ b/xbmc/utils/Observer.h
@@ -43,7 +43,9 @@ typedef enum
ObservableMessageRecordings,
ObservableMessagePeripheralsChanged,
ObservableMessageChannelGroupsLoaded,
- ObservableMessageManagerStopped
+ ObservableMessageManagerStopped,
+ ObservableMessagePortsChanged,
+ ObservableMessageSettingsChanged,
} ObservableMessage;
class Observer