diff --git a/CMakeLists.txt b/CMakeLists.txt
index d0207912..3e4fe7ad 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,15 +4,19 @@ project(game.libretro)
 list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
 
 find_package(Kodi REQUIRED)
-find_package(p8-platform REQUIRED)
 find_package(TinyXML REQUIRED)
 
 include_directories(${KODI_INCLUDE_DIR}
-                    ${p8-platform_INCLUDE_DIRS}
                     ${PROJECT_SOURCE_DIR}/src
                     ${TINYXML_INCLUDE_DIRS})
 
-list(APPEND DEPLIBS ${p8-platform_LIBRARIES} ${TINYXML_LIBRARIES})
+list(APPEND DEPLIBS ${TINYXML_LIBRARIES})
+
+if(WIN32)
+  find_package(dlfcn-win32 REQUIRED)
+  list(APPEND DEPLIBS ${dlfcn-win32_LIBRARIES})
+  include_directories(${dlfcn-win32_INCLUDE_DIRS})
+endif()
 
 set(LIBRETRO_SOURCES src/client.cpp
                      src/audio/AudioStream.cpp
@@ -41,7 +45,6 @@ set(LIBRETRO_SOURCES src/client.cpp
                      src/settings/LibretroSettings.cpp
                      src/settings/Settings.cpp
                      src/settings/SettingsGenerator.cpp
-                     src/utils/PathUtils.cpp
                      src/utils/Timer.cpp
                      src/video/VideoGeometry.cpp
                      src/video/VideoStream.cpp)
@@ -80,7 +83,6 @@ set(LIBRETRO_HEADERS src/GameInfoLoader.h
                      src/settings/SettingsGenerator.h
                      src/settings/Settings.h
                      src/settings/SettingsTypes.h
-                     src/utils/PathUtils.h
                      src/utils/Timer.h
                      src/video/VideoGeometry.h
                      src/video/VideoStream.h)
diff --git a/Readme.md b/Readme.md
index 0aa7fa95..5b493b2f 100644
--- a/Readme.md
+++ b/Readme.md
@@ -80,29 +80,7 @@ When developing, compiling from a git repo is more convenient than repeatedly pu
 
 ### Developing on Linux
 
-The add-on requires several dependencies to build properly. Like Kodi's build system, you can perform a system install or a local one (demonstrated here).
-
-First, clone p8-platform and build per standard CMake:
-
-```shell
-git clone https://github.com/Pulse-Eight/platform.git
-cd platform
-mkdir build
-cd build
-cmake -DCMAKE_BUILD_TYPE=Debug \
-      -DCMAKE_INSTALL_PREFIX=$HOME/kodi \
-      ..
-make
-make install
-```
-
-The kodi-platform library was split from p8-platform. Do the same as above for this library:
-
-```
-git clone https://github.com/xbmc/kodi-platform.git
-cd kodi-platform
-...
-```
+The add-on requires several dependencies to build properly. Like Kodi's build system, you can perform a system install or a local one.
 
 With these dependencies in place, the add-on can be built:
 
diff --git a/depends/windows/dlfcn-win32/0001-dlopen_with_widechar.patch b/depends/windows/dlfcn-win32/0001-dlopen_with_widechar.patch
new file mode 100644
index 00000000..d292e187
--- /dev/null
+++ b/depends/windows/dlfcn-win32/0001-dlopen_with_widechar.patch
@@ -0,0 +1,26 @@
+diff --git a/dlfcn.c b/dlfcn.c
+index 69670d1..8d2dbc0 100644
+--- a/dlfcn.c
++++ b/dlfcn.c
+@@ -264,8 +264,19 @@ void *dlopen( const char *file, int mode )
+          * to UNIX's search paths (start with system folders instead of current
+          * folder).
+          */
+-        hModule = LoadLibraryExA(lpFileName, NULL, 
+-                                LOAD_WITH_ALTERED_SEARCH_PATH );
++        int wide_len = MultiByteToWideChar(CP_UTF8, 0, lpFileName, -1, 0, 0);
++        if (wide_len > 0)
++        {
++          wchar_t* lpFileNameW = (wchar_t*)malloc(wide_len * sizeof(wchar_t));
++          MultiByteToWideChar(CP_UTF8, 0, lpFileName, -1, lpFileNameW, wide_len);
++
++          hModule = LoadLibraryExW(lpFileNameW, NULL, 
++                                  LOAD_WITH_ALTERED_SEARCH_PATH );
++
++          free(lpFileNameW);
++        }
++        else
++          hModule = 0;
+ 
+         if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsAfter ) == 0 )
+             dwProcModsAfter = 0;
diff --git a/depends/windows/dlfcn-win32/dlfcn-win32.sha256 b/depends/windows/dlfcn-win32/dlfcn-win32.sha256
new file mode 100644
index 00000000..d906474c
--- /dev/null
+++ b/depends/windows/dlfcn-win32/dlfcn-win32.sha256
@@ -0,0 +1 @@
+f18a412e84d8b701e61a78252411fe8c72587f52417c1ef21ca93604de1b9c55
diff --git a/depends/windows/dlfcn-win32/dlfcn-win32.txt b/depends/windows/dlfcn-win32/dlfcn-win32.txt
new file mode 100644
index 00000000..6ef69af4
--- /dev/null
+++ b/depends/windows/dlfcn-win32/dlfcn-win32.txt
@@ -0,0 +1 @@
+dlfcn-win32 https://github.com/dlfcn-win32/dlfcn-win32/archive/v1.2.0.tar.gz
diff --git a/depends/windows/dlfcn-win32/flags.txt b/depends/windows/dlfcn-win32/flags.txt
new file mode 100644
index 00000000..3cb0a00e
--- /dev/null
+++ b/depends/windows/dlfcn-win32/flags.txt
@@ -0,0 +1 @@
+-DBUILD_SHARED_LIBS=OFF
diff --git a/depends/windowsstore/dlfcn-win32/0001-win10-fixed-uwp-build.patch b/depends/windowsstore/dlfcn-win32/0001-win10-fixed-uwp-build.patch
new file mode 100644
index 00000000..c8ac8dc6
--- /dev/null
+++ b/depends/windowsstore/dlfcn-win32/0001-win10-fixed-uwp-build.patch
@@ -0,0 +1,151 @@
+From f85366b1044fff7b4ea9162c3edcd8278c3e06ff Mon Sep 17 00:00:00 2001
+From: Alwin Esch <alwin.esch@web.de>
+Date: Thu, 22 Aug 2019 19:30:12 +0100
+Subject: [PATCH] [win10] fixed uwp build
+
+---
+ dlfcn.c | 43 ++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 38 insertions(+), 5 deletions(-)
+
+diff --git a/dlfcn.c b/dlfcn.c
+index 69670d1..2d77ca8 100644
+--- a/dlfcn.c
++++ b/dlfcn.c
+@@ -19,6 +19,7 @@
+  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+  */
+ 
++#define _CRT_SECURE_NO_WARNINGS
+ #ifdef _DEBUG
+ #define _CRTDBG_MAP_ALLOC
+ #include <stdlib.h>
+@@ -193,6 +194,7 @@ static void save_err_ptr_str( const void *ptr )
+ /* Load Psapi.dll at runtime, this avoids linking caveat */
+ static BOOL MyEnumProcessModules( HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded )
+ {
++#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY != WINAPI_FAMILY_APP)
+     static BOOL (WINAPI *EnumProcessModulesPtr)(HANDLE, HMODULE *, DWORD, LPDWORD);
+     HMODULE psapi;
+ 
+@@ -206,20 +208,26 @@ static BOOL MyEnumProcessModules( HANDLE hProcess, HMODULE *lphModule, DWORD cb,
+     }
+ 
+     return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded );
++#else
++  return 0;
++#endif
+ }
+ 
+ void *dlopen( const char *file, int mode )
+ {
+-    HMODULE hModule;
+-    UINT uMode;
++    HMODULE hModule = NULL;
++    UINT uMode = 0;
+ 
+     current_error = NULL;
+ 
+     /* Do not let Windows display the critical-error-handler message box */
++#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY != WINAPI_FAMILY_APP)
+     uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
++#endif
+ 
+     if( file == 0 )
+     {
++#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY != WINAPI_FAMILY_APP) // what is replacement of GMH on UWP?
+         /* POSIX says that if the value of file is 0, a handle on a global
+          * symbol object must be provided. That object must be able to access
+          * all symbols from the original program file, and any objects loaded
+@@ -234,6 +242,7 @@ void *dlopen( const char *file, int mode )
+ 
+         if( !hModule )
+             save_err_ptr_str( file );
++#endif
+     }
+     else
+     {
+@@ -264,11 +273,29 @@ void *dlopen( const char *file, int mode )
+          * to UNIX's search paths (start with system folders instead of current
+          * folder).
+          */
++#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
++        int result = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, lpFileName, strlen(lpFileName), NULL, 0);
++        if (result == 0)
++          return NULL;
++
++        wchar_t* newStr = (wchar_t*)malloc(result*sizeof(wchar_t));
++        result = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, lpFileName, strlen(lpFileName), newStr, result );
++        if (result == 0)
++        {
++          free( newStr );
++          return NULL;
++        }
++
++        hModule = LoadPackagedLibrary( newStr, 0 );
++        free( newStr );
++        dwProcModsAfter = 0;
++#else // WINAPI_PARTITION_DESKTOP
+         hModule = LoadLibraryExA(lpFileName, NULL, 
+                                 LOAD_WITH_ALTERED_SEARCH_PATH );
+ 
+         if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsAfter ) == 0 )
+             dwProcModsAfter = 0;
++#endif
+ 
+         /* If the object was loaded with RTLD_LOCAL, add it to list of local
+          * objects, so that its symbols cannot be retrieved even if the handle for
+@@ -288,7 +315,9 @@ void *dlopen( const char *file, int mode )
+     }
+ 
+     /* Return to previous state of the error-mode bit flags. */
++#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY != WINAPI_FAMILY_APP)
+     SetErrorMode( uMode );
++#endif
+ 
+     return (void *) hModule;
+ }
+@@ -321,12 +350,14 @@ void *dlsym( void *handle, const char *name )
+ {
+     FARPROC symbol;
+     HMODULE hCaller;
+-    HMODULE hModule;
+-    HANDLE hCurrentProc;
++    HMODULE hModule = 0;
++    HANDLE hCurrentProc = 0;
+ 
+     current_error = NULL;
+     symbol = NULL;
+     hCaller = NULL;
++
++#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY != WINAPI_FAMILY_APP) // what is replacement of GMH on UWP?
+     hModule = GetModuleHandle( NULL );
+     hCurrentProc = GetCurrentProcess( );
+ 
+@@ -358,6 +389,7 @@ void *dlsym( void *handle, const char *name )
+         if(!hCaller)
+             goto end;
+     }
++#endif
+ 
+     if( handle != RTLD_NEXT )
+     {
+@@ -370,7 +402,7 @@ void *dlsym( void *handle, const char *name )
+     /* If the handle for the original program file is passed, also search
+      * in all globally loaded objects.
+      */
+-
++#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY != WINAPI_FAMILY_APP)
+     if( hModule == handle || handle == RTLD_NEXT )
+     {
+         HMODULE *modules;
+@@ -410,6 +442,7 @@ void *dlsym( void *handle, const char *name )
+             }
+         }
+     }
++#endif
+ 
+ end:
+     if( symbol == NULL )
+-- 
+2.19.2.windows.1
+
diff --git a/depends/windowsstore/dlfcn-win32/dlfcn-win32.sha256 b/depends/windowsstore/dlfcn-win32/dlfcn-win32.sha256
new file mode 100644
index 00000000..d906474c
--- /dev/null
+++ b/depends/windowsstore/dlfcn-win32/dlfcn-win32.sha256
@@ -0,0 +1 @@
+f18a412e84d8b701e61a78252411fe8c72587f52417c1ef21ca93604de1b9c55
diff --git a/depends/windowsstore/dlfcn-win32/dlfcn-win32.txt b/depends/windowsstore/dlfcn-win32/dlfcn-win32.txt
new file mode 100644
index 00000000..6ef69af4
--- /dev/null
+++ b/depends/windowsstore/dlfcn-win32/dlfcn-win32.txt
@@ -0,0 +1 @@
+dlfcn-win32 https://github.com/dlfcn-win32/dlfcn-win32/archive/v1.2.0.tar.gz
diff --git a/depends/windowsstore/dlfcn-win32/flags.txt b/depends/windowsstore/dlfcn-win32/flags.txt
new file mode 100644
index 00000000..3cb0a00e
--- /dev/null
+++ b/depends/windowsstore/dlfcn-win32/flags.txt
@@ -0,0 +1 @@
+-DBUILD_SHARED_LIBS=OFF
diff --git a/game.libretro/addon.xml.in b/game.libretro/addon.xml.in
index 1a2eebf6..f71129fa 100644
--- a/game.libretro/addon.xml.in
+++ b/game.libretro/addon.xml.in
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <addon id="game.libretro"
        name="Libretro Compatibility"
-       version="1.1.0"
+       version="2.0.0"
        provider-name="Team Kodi">
   <backwards-compatibility abi="1.0.0"/>
   <requires>@ADDON_DEPENDS@</requires>
diff --git a/src/GameInfoLoader.cpp b/src/GameInfoLoader.cpp
index 7c56497e..45bb6e6a 100644
--- a/src/GameInfoLoader.cpp
+++ b/src/GameInfoLoader.cpp
@@ -21,19 +21,17 @@
 #include "GameInfoLoader.h"
 #include "log/Log.h"
 
-#include "libXBMC_addon.h"
+#include <kodi/Filesystem.h>
 
 #include <stdint.h>
 
-using namespace ADDON;
 using namespace LIBRETRO;
 
 #define READ_SIZE      (100 * 1024)         // Read from VFS 100KB at a time (if file size is unknown)
 #define MAX_READ_SIZE  (100 * 1024 * 1024)  // Read at most 100MB from VFS
 
-CGameInfoLoader::CGameInfoLoader(const char* path, CHelper_libXBMC_addon* XBMC, bool bSupportsVFS)
+CGameInfoLoader::CGameInfoLoader(const std::string& path, bool bSupportsVFS)
  : m_path(path),
-   m_xbmc(XBMC),
    m_bSupportsVfs(bSupportsVFS)
 {
 }
@@ -43,14 +41,13 @@ bool CGameInfoLoader::Load(void)
   if (!m_bSupportsVfs)
     return false;
 
-  struct __stat64 statStruct = { };
-
-  bool bExists = (m_xbmc->StatFile(m_path.c_str(), &statStruct) == 0);
+  STAT_STRUCTURE statStruct = {0};
+  bool bExists = kodi::vfs::StatFile(m_path, statStruct);
 
   // Not all VFS protocols necessarily support StatFile(), so also check if file exists
   if (!bExists)
   {
-    bExists = m_xbmc->FileExists(m_path.c_str(), true);
+    bExists = kodi::vfs::FileExists(m_path, true);
     if (bExists)
     {
       dsyslog("Failed to stat (but file exists): %s", m_path.c_str());
@@ -62,21 +59,21 @@ bool CGameInfoLoader::Load(void)
     }
   }
 
-  void* file = m_xbmc->OpenFile(m_path.c_str(), 0);
-  if (!file)
+  kodi::vfs::CFile file;
+  if (!file.OpenFile(m_path))
   {
     esyslog("Failed to open file: %s", m_path.c_str());
     return false;
   }
 
-  int64_t size = statStruct.st_size;
+  int64_t size = statStruct.size;
   if (size > 0)
   {
     // Size is known, read entire file at once (unless it is too big)
     if (size <= MAX_READ_SIZE)
     {
       m_dataBuffer.resize((size_t)size);
-      m_xbmc->ReadFile(file, m_dataBuffer.data(), size);
+      file.Read(m_dataBuffer.data(), size);
     }
     else
     {
@@ -90,7 +87,7 @@ bool CGameInfoLoader::Load(void)
     // Read file in chunks
     unsigned int bytesRead;
     uint8_t buffer[READ_SIZE];
-    while ((bytesRead = m_xbmc->ReadFile(file, buffer, sizeof(buffer))) > 0)
+    while ((bytesRead = file.Read(buffer, sizeof(buffer))) > 0)
     {
       m_dataBuffer.insert(m_dataBuffer.end(), buffer, buffer + bytesRead);
 
diff --git a/src/GameInfoLoader.h b/src/GameInfoLoader.h
index 41e9317b..180339bf 100644
--- a/src/GameInfoLoader.h
+++ b/src/GameInfoLoader.h
@@ -25,8 +25,6 @@
 #include <string>
 #include <vector>
 
-namespace ADDON { class CHelper_libXBMC_addon; }
-
 namespace LIBRETRO
 {
   /*!
@@ -39,7 +37,7 @@ namespace LIBRETRO
   class CGameInfoLoader
   {
   public:
-    CGameInfoLoader(const char* path, ADDON::CHelper_libXBMC_addon* XBMC, bool bSupportsVFS);
+    CGameInfoLoader(const std::string& path, bool bSupportsVFS);
 
     bool Load(void);
 
@@ -58,7 +56,6 @@ namespace LIBRETRO
 
   private:
     const std::string                   m_path;
-    ADDON::CHelper_libXBMC_addon* const m_xbmc;
     const bool                          m_bSupportsVfs;
     std::vector<uint8_t>                m_dataBuffer;
   };
diff --git a/src/audio/AudioStream.cpp b/src/audio/AudioStream.cpp
index 70be2cb1..5211969b 100644
--- a/src/audio/AudioStream.cpp
+++ b/src/audio/AudioStream.cpp
@@ -21,35 +21,30 @@
 #include "AudioStream.h"
 #include "libretro/LibretroEnvironment.h"
 
-#include "libKODI_game.h"
+#include "client.h"
 
 using namespace LIBRETRO;
 
 CAudioStream::CAudioStream() :
-  m_frontend(nullptr),
+  m_addon(nullptr),
   m_singleFrameAudio(this)
 {
 }
 
-void CAudioStream::Initialize(CHelper_libKODI_game* frontend)
+void CAudioStream::Initialize(CGameLibRetro* addon)
 {
-  m_frontend = frontend;
+  m_addon = addon;
 }
 
 void CAudioStream::Deinitialize()
 {
-  if (m_stream != nullptr)
-  {
-    m_frontend->CloseStream(m_stream);
-    m_stream = nullptr;
-  }
-
-  m_frontend = nullptr;
+  m_stream.Close();
+  m_addon = nullptr;
 }
 
 void CAudioStream::AddFrames_S16NE(const uint8_t* data, unsigned int size)
 {
-  if (m_frontend && m_stream == nullptr)
+  if (m_addon && !m_stream.IsOpen())
   {
     static const GAME_AUDIO_CHANNEL channelMap[] = { GAME_CH_FL, GAME_CH_FR, GAME_CH_NULL };
 
@@ -59,17 +54,15 @@ void CAudioStream::AddFrames_S16NE(const uint8_t* data, unsigned int size)
     properties.audio.format = GAME_PCM_FORMAT_S16NE;
     properties.audio.channel_map = channelMap;
 
-    m_stream = m_frontend->OpenStream(properties);
+    if (m_stream.Open(properties))
+      return;
   }
 
-  if (m_stream != nullptr)
-  {
-    game_stream_packet packet{};
+  game_stream_packet packet{};
 
-    packet.type = GAME_STREAM_AUDIO;
-    packet.audio.data = data;
-    packet.audio.size = size;
+  packet.type = GAME_STREAM_AUDIO;
+  packet.audio.data = data;
+  packet.audio.size = size;
 
-    m_frontend->AddStreamData(m_stream, packet);
-  }
+  m_stream.AddData(packet);
 }
diff --git a/src/audio/AudioStream.h b/src/audio/AudioStream.h
index dcfb7757..1bcc6970 100644
--- a/src/audio/AudioStream.h
+++ b/src/audio/AudioStream.h
@@ -21,11 +21,11 @@
 
 #include "SingleFrameAudio.h"
 
-#include "kodi_game_types.h"
+#include <kodi/addon-instance/Game.h>
 
 #include <stdint.h>
 
-class CHelper_libKODI_game;
+class CGameLibRetro;
 
 namespace LIBRETRO
 {
@@ -34,7 +34,7 @@ namespace LIBRETRO
   public:
     CAudioStream();
 
-    void Initialize(CHelper_libKODI_game* frontend);
+    void Initialize(CGameLibRetro* addon);
     void Deinitialize();
 
     void AddFrame_S16NE(int16_t left, int16_t right) { m_singleFrameAudio.AddFrame(left, right); }
@@ -42,9 +42,9 @@ namespace LIBRETRO
     void AddFrames_S16NE(const uint8_t* data, unsigned int size);
 
   private:
-    CHelper_libKODI_game* m_frontend;
+    CGameLibRetro*        m_addon;
     CSingleFrameAudio     m_singleFrameAudio;
 
-    void* m_stream = nullptr;
+    kodi::addon::CInstanceGame::CStream m_stream;
   };
 }
diff --git a/src/client.cpp b/src/client.cpp
index 669f9a06..7df91931 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -21,35 +21,24 @@
 #include "input/ButtonMapper.h"
 #include "input/ControllerTopology.h"
 #include "input/InputManager.h"
-#include "libretro/ClientBridge.h"
 #include "libretro/libretro.h"
-#include "libretro/LibretroDLL.h"
 #include "libretro/LibretroEnvironment.h"
 #include "log/Log.h"
 #include "log/LogAddon.h"
 #include "settings/Settings.h"
-#include "utils/Timer.h"
 #include "GameInfoLoader.h"
 
-#include "libXBMC_addon.h"
-#include "libKODI_game.h"
-#include "xbmc_addon_dll.h"
-#include "kodi_game_dll.h"
+#include "client.h"
 
 #include <set>
 #include <string>
 #include <vector>
 
-using namespace ADDON;
 using namespace LIBRETRO;
 
 #define GAME_CLIENT_NAME_UNKNOWN      "Unknown libretro core"
 #define GAME_CLIENT_VERSION_UNKNOWN   "0.0.0"
 
-#ifndef SAFE_DELETE
-#define SAFE_DELETE(x)  do { delete x; x = nullptr; } while (0)
-#endif
-
 void SAFE_DELETE_GAME_INFO(std::vector<CGameInfoLoader*>& vec)
 {
   for (std::vector<CGameInfoLoader*>::iterator it = vec.begin(); it != vec.end(); ++it)
@@ -57,51 +46,44 @@ void SAFE_DELETE_GAME_INFO(std::vector<CGameInfoLoader*>& vec)
   vec.clear();
 }
 
-namespace LIBRETRO
+CGameLibRetro::CGameLibRetro()
 {
-  CHelper_libXBMC_addon*        XBMC          = nullptr;
-  CHelper_libKODI_game*         FRONTEND      = nullptr;
-  CLibretroDLL*                 CLIENT        = nullptr;
-  CClientBridge*                CLIENT_BRIDGE = nullptr;
-  std::vector<CGameInfoLoader*> GAME_INFO;
-  bool                          SUPPORTS_VFS = false; // TODO
-  int64_t                       FRAME_TIME_LAST = 0;
-  Timer                         timer;
 }
 
-extern "C"
+CGameLibRetro::~CGameLibRetro()
 {
+  /* TODO
+  m_clientBridge.AudioEnable(false);
+  */
 
-ADDON_STATUS ADDON_Create(void* callbacks, void* props)
-{
-  try
-  {
-    if (!callbacks || !props)
-      throw ADDON_STATUS_UNKNOWN;
+  CInputManager::Get().ClosePorts();
 
-    AddonProps_Game* gameClientProps = static_cast<AddonProps_Game*>(props);
+  m_client.retro_deinit();
 
-    if (gameClientProps->game_client_dll_path == nullptr)
-      throw ADDON_STATUS_UNKNOWN;
+  CControllerTopology::GetInstance().Clear();
 
-    XBMC = new CHelper_libXBMC_addon;
-    if (!XBMC || !XBMC->RegisterMe(callbacks))
-      throw ADDON_STATUS_PERMANENT_FAILURE;
+  CLibretroEnvironment::Get().Deinitialize();
 
-    CLog::Get().SetPipe(new CLogAddon(XBMC));
+  CLog::Get().SetType(SYS_LOG_TYPE_CONSOLE);
 
-    FRONTEND = new CHelper_libKODI_game;
-    if (!FRONTEND || !FRONTEND->RegisterMe(callbacks))
-      throw ADDON_STATUS_PERMANENT_FAILURE;
+  SAFE_DELETE_GAME_INFO(m_gameInfo);
+}
 
-    CLIENT = new CLibretroDLL();
-    if (!CLIENT->Load(gameClientProps))
+ADDON_STATUS CGameLibRetro::Create()
+{
+  try
+  {
+    std::string dllPath = GameClientDllPath();
+    if (dllPath.empty())
+      throw ADDON_STATUS_UNKNOWN;
+
+    if (!m_client.Load(dllPath))
     {
-      esyslog("Failed to load %s", gameClientProps->game_client_dll_path);
+      esyslog("Failed to load %s", dllPath.c_str());
       throw ADDON_STATUS_PERMANENT_FAILURE;
     }
 
-    unsigned int version = CLIENT->retro_api_version();
+    unsigned int version = m_client.retro_api_version();
     if (version != 1)
     {
       esyslog("Expected libretro api v1, found version %u", version);
@@ -109,22 +91,21 @@ ADDON_STATUS ADDON_Create(void* callbacks, void* props)
     }
 
     // Environment must be initialized before calling retro_init()
-    CLIENT_BRIDGE = new CClientBridge;
-    CLibretroEnvironment::Get().Initialize(XBMC, FRONTEND, CLIENT, CLIENT_BRIDGE, gameClientProps);
+    CLibretroEnvironment::Get().Initialize(this, &m_client, &m_clientBridge);
 
     CButtonMapper::Get().LoadButtonMap();
     CControllerTopology::GetInstance().LoadTopology();
 
-    CLIENT->retro_init();
+    m_client.retro_init();
 
     // Log core info
     retro_system_info systemInfo = { };
-    CLIENT->retro_get_system_info(&systemInfo);
+    m_client.retro_get_system_info(&systemInfo);
 
     // VFS support is derived from need_fullpath. This property means that the
     // libretro cores requires a valid pathname. Conversely, if need_fullpath
     // is false, the core can load from memory.
-    SUPPORTS_VFS = !systemInfo.need_fullpath;
+    m_supportsVFS = !systemInfo.need_fullpath;
 
     std::string libraryName = systemInfo.library_name ? systemInfo.library_name : "";
     std::string libraryVersion = systemInfo.library_version ? systemInfo.library_version : "";
@@ -134,7 +115,7 @@ ADDON_STATUS ADDON_Create(void* callbacks, void* props)
     dsyslog("CORE: Library name:    %s", libraryName.c_str());
     dsyslog("CORE: Library version: %s", libraryVersion.c_str());
     dsyslog("CORE: Extensions:      %s", extensions.c_str());
-    dsyslog("CORE: Supports VFS:    %s", SUPPORTS_VFS ? "true" : "false");
+    dsyslog("CORE: Supports VFS:    %s", m_supportsVFS ? "true" : "false");
     dsyslog("CORE: ----------------------------------");
 
     // Reject invalid properties
@@ -148,191 +129,135 @@ ADDON_STATUS ADDON_Create(void* callbacks, void* props)
       throw ADDON_STATUS_PERMANENT_FAILURE;
     }
 
-    if (gameClientProps->supports_vfs != SUPPORTS_VFS)
+    if (SupportsVFS() != m_supportsVFS)
     {
-      esyslog("CORE: VFS support doesn't match addon.xml: %s", gameClientProps->supports_vfs ? "true" : "false");
+      esyslog("CORE: VFS support doesn't match addon.xml: %s", SupportsVFS() ? "true" : "false");
       throw ADDON_STATUS_PERMANENT_FAILURE;
     }
 
     /* TODO
     // Initialize libretro's extended audio interface
-    CLIENT_BRIDGE->AudioEnable(true);
+    m_clientBridge.AudioEnable(true);
     */
   }
   catch (const ADDON_STATUS& status)
   {
-    SAFE_DELETE(XBMC);
-    SAFE_DELETE(FRONTEND);
-    SAFE_DELETE(CLIENT);
-    SAFE_DELETE(CLIENT_BRIDGE);
     return status;
   }
 
-  return ADDON_GetStatus();
+  return GetStatus();
 }
 
-void ADDON_Destroy(void)
+ADDON_STATUS CGameLibRetro::GetStatus()
 {
-  /* TODO
-  if (CLIENT_BRIDGE)
-    CLIENT_BRIDGE->AudioEnable(false);
-  */
-
-  CInputManager::Get().ClosePorts();
-
-  if (CLIENT)
-    CLIENT->retro_deinit();
-
-  CControllerTopology::GetInstance().Clear();
-
-  CLibretroEnvironment::Get().Deinitialize();
-
-  CLog::Get().SetType(SYS_LOG_TYPE_CONSOLE);
-
-  SAFE_DELETE(XBMC);
-  SAFE_DELETE(FRONTEND);
-  SAFE_DELETE(CLIENT);
-  SAFE_DELETE(CLIENT_BRIDGE);
-  SAFE_DELETE_GAME_INFO(GAME_INFO);
-}
-
-ADDON_STATUS ADDON_GetStatus(void)
-{
-  if (!XBMC || !FRONTEND || !CLIENT || !CLIENT_BRIDGE)
-    return ADDON_STATUS_UNKNOWN;
-
   if (!CSettings::Get().IsInitialized())
     return ADDON_STATUS_NEED_SETTINGS;
 
   return ADDON_STATUS_OK;
 }
 
-ADDON_STATUS ADDON_SetSetting(const char* settingName, const void* settingValue)
+ADDON_STATUS CGameLibRetro::SetSetting(const std::string& settingName, const kodi::CSettingValue& settingValue)
 {
-  if (!settingName || !settingValue)
+  if (settingName == "" || settingValue.empty())
     return ADDON_STATUS_UNKNOWN;
 
   CSettings::Get().SetSetting(settingName, settingValue);
-  CLibretroEnvironment::Get().SetSetting(settingName, static_cast<const char*>(settingValue));
+  CLibretroEnvironment::Get().SetSetting(settingName, settingValue.GetString());
 
   return ADDON_STATUS_OK;
 }
 
-GAME_ERROR LoadGame(const char* url)
+GAME_ERROR CGameLibRetro::LoadGame(const std::string& url)
 {
-  if (!CLIENT)
-    return GAME_ERROR_FAILED;
-
-  if (url == nullptr)
-    return GAME_ERROR_INVALID_PARAMETERS;
-
   // Build info loader vector
-  SAFE_DELETE_GAME_INFO(GAME_INFO);
-  GAME_INFO.push_back(new CGameInfoLoader(url, XBMC, SUPPORTS_VFS));
+  SAFE_DELETE_GAME_INFO(m_gameInfo);
+  m_gameInfo.push_back(new CGameInfoLoader(url, m_supportsVFS));
 
   bool bResult = false;
 
   // Try to load via memory
   retro_game_info gameInfo;
-  if (GAME_INFO[0]->Load())
+  if (m_gameInfo[0]->Load())
   {
-    GAME_INFO[0]->GetMemoryStruct(gameInfo);
-    bResult = CLIENT->retro_load_game(&gameInfo);
+    m_gameInfo[0]->GetMemoryStruct(gameInfo);
+    bResult = m_client.retro_load_game(&gameInfo);
   }
 
   if (!bResult)
   {
     // Fall back to loading via path
-    GAME_INFO[0]->GetPathStruct(gameInfo);
-    bResult = CLIENT->retro_load_game(&gameInfo);
+    m_gameInfo[0]->GetPathStruct(gameInfo);
+    bResult = m_client.retro_load_game(&gameInfo);
   }
 
   return bResult ? GAME_ERROR_NO_ERROR : GAME_ERROR_FAILED;
 }
 
-GAME_ERROR LoadGameSpecial(SPECIAL_GAME_TYPE type, const char** urls, size_t urlCount)
+GAME_ERROR CGameLibRetro::LoadGameSpecial(SPECIAL_GAME_TYPE type, const std::vector<std::string>& urls)
 {
-  if (!CLIENT)
-    return GAME_ERROR_FAILED;
-
-  if (urls == nullptr || urlCount == 0)
-    return GAME_ERROR_INVALID_PARAMETERS;
-
   // TODO
   return GAME_ERROR_FAILED;
   /*
   retro_system_info info = { };
-  CLIENT->retro_get_system_info(&info);
+  m_client.retro_get_system_info(&info);
   const bool bSupportsVFS = !info.need_fullpath;
 
   // Build info loader vector
-  SAFE_DELETE_GAME_INFO(GAME_INFO);
-  for (unsigned int i = 0; i < urlCount; i++)
-    GAME_INFO.push_back(new CGameInfoLoader(urls[i], XBMC, bSupportsVFS));
+  SAFE_DELETE_GAME_INFO(m_gameInfo);
+  for (const auto& url : urls)
+    m_gameInfo.push_back(new CGameInfoLoader(url, bSupportsVFS));
 
   // Try to load via memory
   std::vector<retro_game_info> infoVec;
-  infoVec.resize(urlCount);
+  infoVec.resize(urls.size());
   bool bLoadFromMemory = true;
-  for (unsigned int i = 0; bLoadFromMemory && i < urlCount; i++)
-    bLoadFromMemory &= GAME_INFO[i]->GetMemoryStruct(infoVec[i]);
+  for (unsigned int i = 0; bLoadFromMemory && i < urls.size(); i++)
+    bLoadFromMemory &= m_gameInfo[i]->GetMemoryStruct(infoVec[i]);
   if (bLoadFromMemory)
   {
-    if (CLIENT->retro_load_game_special(type, infoVec.data(), urlCount))
+    if (m_client.retro_load_game_special(type, infoVec.data(), urls.size()))
       return GAME_ERROR_NO_ERROR;
   }
 
   // Fall back to loading by path
-  for (unsigned int i = 0; i < urlCount; i++)
-    GAME_INFO[i]->GetPathStruct(infoVec[i]);
-  bool result = CLIENT->retro_load_game_special(type, infoVec.data(), urlCount);
+  for (unsigned int i = 0; i < urls.size(); i++)
+    m_gameInfo[i]->GetPathStruct(infoVec[i]);
+  bool result = m_client.retro_load_game_special(type, infoVec.data(), urls.size());
 
   return result ? GAME_ERROR_NO_ERROR : GAME_ERROR_FAILED;
   */
 }
 
-GAME_ERROR LoadStandalone(void)
+GAME_ERROR CGameLibRetro::LoadStandalone()
 {
-  if (!CLIENT)
-    return GAME_ERROR_FAILED;
-
-  if (!CLIENT->retro_load_game(nullptr))
+  if (!m_client.retro_load_game(nullptr))
     return GAME_ERROR_FAILED;
 
   return GAME_ERROR_NO_ERROR;
 }
 
-GAME_ERROR UnloadGame(void)
+GAME_ERROR CGameLibRetro::UnloadGame()
 {
   GAME_ERROR error = GAME_ERROR_FAILED;
 
-  if (CLIENT)
-  {
-    CLIENT->retro_unload_game();
+  m_client.retro_unload_game();
 
-    CLibretroEnvironment::Get().CloseStreams();
+  CLibretroEnvironment::Get().CloseStreams();
 
-    error = GAME_ERROR_NO_ERROR;
-  }
+  error = GAME_ERROR_NO_ERROR;
 
-  SAFE_DELETE_GAME_INFO(GAME_INFO);
+  SAFE_DELETE_GAME_INFO(m_gameInfo);
 
   return error;
 }
 
-GAME_ERROR GetGameTiming(game_system_timing* timing_info)
+GAME_ERROR CGameLibRetro::GetGameTiming(game_system_timing& timing_info)
 {
-  if (!CLIENT)
-    return GAME_ERROR_FAILED;
-
-  if (timing_info == nullptr)
-    return GAME_ERROR_INVALID_PARAMETERS;
-
   retro_system_av_info retro_info = { };
-  CLIENT->retro_get_system_av_info(&retro_info);
+  m_client.retro_get_system_av_info(&retro_info);
 
-  timing_info->fps = retro_info.timing.fps;
-  timing_info->sample_rate = retro_info.timing.sample_rate;
+  timing_info.fps = retro_info.timing.fps;
+  timing_info.sample_rate = retro_info.timing.sample_rate;
 
   // Report info to CLibretroEnvironment
   CLibretroEnvironment::Get().UpdateVideoGeometry(retro_info.geometry);
@@ -340,50 +265,33 @@ GAME_ERROR GetGameTiming(game_system_timing* timing_info)
   return GAME_ERROR_NO_ERROR;
 }
 
-GAME_REGION GetRegion(void)
-{
-  if (!CLIENT)
-    return GAME_REGION_UNKNOWN;
-
-  return CLIENT->retro_get_region() == RETRO_REGION_NTSC ? GAME_REGION_NTSC : GAME_REGION_PAL;
-}
-
-bool RequiresGameLoop(void)
+GAME_REGION CGameLibRetro::GetRegion()
 {
-  if (!CLIENT)
-    return false;
-
-  return true;
+  return m_client.retro_get_region() == RETRO_REGION_NTSC ? GAME_REGION_NTSC : GAME_REGION_PAL;
 }
 
-GAME_ERROR RunFrame(void)
+GAME_ERROR CGameLibRetro::RunFrame()
 {
-  if (!CLIENT)
-    return GAME_ERROR_FAILED;
-
   // Trigger the frame time callback before running the core.
-  uint64_t current = timer.microseconds();
+  uint64_t current = m_timer.microseconds();
   int64_t delta = 0;
 
-  if (FRAME_TIME_LAST > 0)
-    delta = current - FRAME_TIME_LAST;
+  if (m_frameTimeLast > 0)
+    delta = current - m_frameTimeLast;
 
-  FRAME_TIME_LAST = current;
-  CLIENT_BRIDGE->FrameTime(delta);
+  m_frameTimeLast = current;
+  m_clientBridge.FrameTime(delta);
 
-  CLIENT->retro_run();
+  m_client.retro_run();
 
   CLibretroEnvironment::Get().OnFrameEnd();
 
   return GAME_ERROR_NO_ERROR;
 }
 
-GAME_ERROR Reset(void)
+GAME_ERROR CGameLibRetro::Reset()
 {
-  if (!CLIENT)
-    return GAME_ERROR_FAILED;
-
-  CLIENT->retro_reset();
+  m_client.retro_reset();
 
   return GAME_ERROR_NO_ERROR;
 }
@@ -404,68 +312,48 @@ GAME_ERROR Reset(void)
  * This function is not part of the Game API yet. It has been implemented
  * here in case a libretro core requires the extended audio interface.
  */
-GAME_ERROR AudioAvailable()
+GAME_ERROR CGameLibRetro::AudioAvailable()
 {
-  if (!CLIENT_BRIDGE)
-    return GAME_ERROR_FAILED;
-
-  return CLIENT_BRIDGE->AudioAvailable();
+  return m_clientBridge.AudioAvailable();
 }
 
-GAME_ERROR HwContextReset()
+GAME_ERROR CGameLibRetro::HwContextReset()
 {
-  if (!CLIENT_BRIDGE)
-    return GAME_ERROR_FAILED;
-
-  return CLIENT_BRIDGE->HwContextReset();
+  return m_clientBridge.HwContextReset();
 }
 
-GAME_ERROR HwContextDestroy()
+GAME_ERROR CGameLibRetro::HwContextDestroy()
 {
-  if (!CLIENT_BRIDGE)
-    return GAME_ERROR_FAILED;
-
-  return CLIENT_BRIDGE->HwContextDestroy();
+  return m_clientBridge.HwContextDestroy();
 }
 
-bool HasFeature(const char* controller_id, const char* feature_name)
+bool CGameLibRetro::HasFeature(const std::string& controller_id, const std::string& feature_name)
 {
-  if (controller_id == nullptr || feature_name == nullptr)
-    return false;
-
   return CButtonMapper::Get().GetLibretroIndex(controller_id, feature_name) >= 0;
 }
 
-game_input_topology* GetTopology()
+game_input_topology* CGameLibRetro::GetTopology()
 {
   return CControllerTopology::GetInstance().GetTopology();
 }
 
-void FreeTopology(game_input_topology* topology)
+void CGameLibRetro::FreeTopology(game_input_topology* topology)
 {
   CControllerTopology::FreeTopology(topology);
 }
 
-void SetControllerLayouts(const game_controller_layout* controllers, unsigned int controller_count)
+void CGameLibRetro::SetControllerLayouts(const std::vector<AddonGameControllerLayout>& controllers)
 {
-  if (controllers == nullptr)
-    return;
-
-  std::vector<game_controller_layout> controllerStructs;
-  for (unsigned int i = 0; i < controller_count; i++)
-    controllerStructs.emplace_back(controllers[i]);
-
-  CInputManager::Get().SetControllerLayouts(controllerStructs);
+  CInputManager::Get().SetControllerLayouts(controllers);
 }
 
-bool EnableKeyboard(bool enable, const char* controller_id)
+bool CGameLibRetro::EnableKeyboard(bool enable, const std::string& controller_id)
 {
   bool bSuccess = false;
 
   if (enable)
   {
-    if (controller_id != nullptr)
-      bSuccess = CInputManager::Get().EnableKeyboard(controller_id);
+    bSuccess = CInputManager::Get().EnableKeyboard(controller_id);
   }
   else
   {
@@ -476,14 +364,13 @@ bool EnableKeyboard(bool enable, const char* controller_id)
   return bSuccess;
 }
 
-bool EnableMouse(bool enable, const char* controller_id)
+bool CGameLibRetro::EnableMouse(bool enable, const std::string& controller_id)
 {
   bool bSuccess = false;
 
   if (enable)
   {
-    if (controller_id != nullptr)
-      bSuccess = CInputManager::Get().EnableMouse(controller_id);
+    bSuccess = CInputManager::Get().EnableMouse(controller_id);
   }
   else
   {
@@ -494,21 +381,13 @@ bool EnableMouse(bool enable, const char* controller_id)
   return bSuccess;
 }
 
-bool ConnectController(bool connect, const char *port_address, const char* controller_id)
+bool CGameLibRetro::ConnectController(bool connect, const std::string& port_address, const std::string& controller_id)
 {
-  if (port_address == nullptr)
-    return false;
-
   std::string strPortAddress(port_address);
   std::string strController;
 
   if (connect)
-  {
-    if (controller_id == nullptr)
-      return false;
-
     strController = controller_id;
-  }
 
   const int port = CInputManager::Get().GetPortIndex(strPortAddress);
   if (port < 0)
@@ -531,8 +410,7 @@ bool ConnectController(bool connect, const char *port_address, const char* contr
     dsyslog("Setting port \"%s\" (libretro port %d) to controller \"%s\" (libretro device ID %u)",
         strPortAddress.c_str(), port, strController.c_str(), device);
 
-    if (CLIENT)
-      CLIENT->retro_set_controller_port_device(port, device);
+    m_client.retro_set_controller_port_device(port, device);
 
     return true;
   }
@@ -540,80 +418,56 @@ bool ConnectController(bool connect, const char *port_address, const char* contr
   return false;
 }
 
-bool InputEvent(const game_input_event* event)
+bool CGameLibRetro::InputEvent(const game_input_event& event)
 {
-  if (!event)
-    return false;
-
-  return CInputManager::Get().InputEvent(*event);
+  return CInputManager::Get().InputEvent(event);
 }
 
-size_t SerializeSize(void)
+size_t CGameLibRetro::SerializeSize()
 {
-  if (!CLIENT)
-    return 0;
-
-  return CLIENT->retro_serialize_size();
+  return m_client.retro_serialize_size();
 }
 
-GAME_ERROR Serialize(uint8_t* data, size_t size)
+GAME_ERROR CGameLibRetro::Serialize(uint8_t* data, size_t size)
 {
-  if (!CLIENT)
-    return GAME_ERROR_FAILED;
-
   if (data == nullptr)
     return GAME_ERROR_INVALID_PARAMETERS;
 
-  bool result = CLIENT->retro_serialize(data, size);
+  bool result = m_client.retro_serialize(data, size);
 
   return result ? GAME_ERROR_NO_ERROR : GAME_ERROR_FAILED;
 }
 
-GAME_ERROR Deserialize(const uint8_t* data, size_t size)
+GAME_ERROR CGameLibRetro::Deserialize(const uint8_t* data, size_t size)
 {
-  if (!CLIENT)
-    return GAME_ERROR_FAILED;
-
   if (data == nullptr)
     return GAME_ERROR_INVALID_PARAMETERS;
 
-  bool result = CLIENT->retro_unserialize(data, size);
+  bool result = m_client.retro_unserialize(data, size);
 
   return result ? GAME_ERROR_NO_ERROR : GAME_ERROR_FAILED;
 }
 
-GAME_ERROR CheatReset(void)
+GAME_ERROR CGameLibRetro::CheatReset()
 {
-  if (!CLIENT)
-    return GAME_ERROR_FAILED;
-
-  CLIENT->retro_cheat_reset();
+  m_client.retro_cheat_reset();
 
   return GAME_ERROR_NO_ERROR;
 }
 
-GAME_ERROR GetMemory(GAME_MEMORY type, uint8_t** data, size_t* size)
+GAME_ERROR CGameLibRetro::GetMemory(GAME_MEMORY type, uint8_t*& data, size_t& size)
 {
-  if (!CLIENT)
-    return GAME_ERROR_FAILED;
-
-  if (data == nullptr || size == nullptr)
-    return GAME_ERROR_INVALID_PARAMETERS;
-
-  *data = static_cast<uint8_t*>(CLIENT->retro_get_memory_data(type));
-  *size = CLIENT->retro_get_memory_size(type);
+  data = static_cast<uint8_t*>(m_client.retro_get_memory_data(type));
+  size = m_client.retro_get_memory_size(type);
 
   return GAME_ERROR_NO_ERROR;
 }
 
-GAME_ERROR SetCheat(unsigned int index, bool enabled, const char* code)
+GAME_ERROR CGameLibRetro::SetCheat(unsigned int index, bool enabled, const std::string& code)
 {
-  if (!CLIENT)
-    return GAME_ERROR_FAILED;
-
-  CLIENT->retro_cheat_set(index, enabled, code);
+  m_client.retro_cheat_set(index, enabled, code.c_str());
 
   return GAME_ERROR_NO_ERROR;
 }
 
-} // extern "C"
+ADDONCREATOR(CGameLibRetro)
diff --git a/src/client.h b/src/client.h
new file mode 100644
index 00000000..f47d3761
--- /dev/null
+++ b/src/client.h
@@ -0,0 +1,95 @@
+/*
+ *      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 "libretro/ClientBridge.h"
+#include "libretro/LibretroDLL.h"
+#include "utils/Timer.h"
+
+#include <kodi/addon-instance/Game.h>
+
+namespace LIBRETRO
+{
+  class CGameInfoLoader;
+}
+
+class ATTRIBUTE_HIDDEN CGameLibRetro
+  : public kodi::addon::CAddonBase,
+    public kodi::addon::CInstanceGame
+{
+public:
+  CGameLibRetro();
+  ~CGameLibRetro() override;
+
+  ADDON_STATUS Create() override;
+  ADDON_STATUS GetStatus() override;
+  ADDON_STATUS SetSetting(const std::string& settingName, const kodi::CSettingValue& settingValue) override;
+
+  // --- Game operations ---------------------------------------------------------
+
+  GAME_ERROR LoadGame(const std::string& url) override;
+  GAME_ERROR LoadGameSpecial(SPECIAL_GAME_TYPE type, const std::vector<std::string>& urls) override;
+  GAME_ERROR LoadStandalone() override;
+  GAME_ERROR UnloadGame() override;
+  GAME_ERROR GetGameTiming(game_system_timing& timing_info) override;
+  GAME_REGION GetRegion() override;
+  bool RequiresGameLoop() override { return true; }
+  GAME_ERROR RunFrame() override;
+  GAME_ERROR Reset() override;
+
+  // --- Hardware rendering operations -------------------------------------------
+
+  GAME_ERROR HwContextReset() override;
+  GAME_ERROR HwContextDestroy() override;
+
+  // --- Input operations --------------------------------------------------------
+
+  bool HasFeature(const std::string& controller_id, const std::string& feature_name) override;
+  game_input_topology* GetTopology() override;
+  void FreeTopology(game_input_topology* topology) override;
+  void SetControllerLayouts(const std::vector<AddonGameControllerLayout>& controllers) override;
+  bool EnableKeyboard(bool enable, const std::string& controller_id) override;
+  bool EnableMouse(bool enable, const std::string& controller_id) override;
+  bool ConnectController(bool connect, const std::string& port_address, const std::string& controller_id) override;
+  bool InputEvent(const game_input_event& event) override;
+
+  // --- Serialization operations ------------------------------------------------
+
+  size_t SerializeSize() override;
+  GAME_ERROR Serialize(uint8_t* data, size_t size) override;
+  GAME_ERROR Deserialize(const uint8_t* data, size_t size) override;
+
+  // --- Cheat operations --------------------------------------------------------
+
+  GAME_ERROR CheatReset() override;
+  GAME_ERROR GetMemory(GAME_MEMORY type, uint8_t*& data, size_t& size) override;
+  GAME_ERROR SetCheat(unsigned int index, bool enabled, const std::string& code) override;
+
+private:
+  GAME_ERROR AudioAvailable();
+
+  LIBRETRO::Timer                         m_timer;
+  LIBRETRO::CLibretroDLL                  m_client;
+  LIBRETRO::CClientBridge                 m_clientBridge;
+  std::vector<LIBRETRO::CGameInfoLoader*> m_gameInfo;
+  bool                                    m_supportsVFS = false; // TODO
+  int64_t                                 m_frameTimeLast = 0;
+};
diff --git a/src/input/ControllerLayout.cpp b/src/input/ControllerLayout.cpp
index a4f10b37..735ad488 100644
--- a/src/input/ControllerLayout.cpp
+++ b/src/input/ControllerLayout.cpp
@@ -20,59 +20,9 @@
 
 #include "ControllerLayout.h"
 
-#include "kodi_game_types.h"
-
 using namespace LIBRETRO;
 
-CControllerLayout::CControllerLayout(const game_controller_layout &controller) :
-  m_controllerId(controller.controller_id != nullptr ? controller.controller_id : ""),
-  m_bProvidesInput(controller.provides_input)
+CControllerLayout::CControllerLayout(const AddonGameControllerLayout& controller) :
+  m_controller(controller)
 {
-  if (controller.digital_buttons != nullptr)
-  {
-    for (unsigned int i = 0; i < controller.digital_button_count; i++)
-      m_digitalButtons.emplace_back(controller.digital_buttons[i]);
-  }
-
-  if (controller.analog_buttons != nullptr)
-  {
-    for (unsigned int i = 0; i < controller.analog_button_count; i++)
-      m_analogButtons.emplace_back(controller.analog_buttons[i]);
-  }
-
-  if (controller.analog_sticks != nullptr)
-  {
-    for (unsigned int i = 0; i < controller.analog_stick_count; i++)
-      m_analogSticks.emplace_back(controller.analog_sticks[i]);
-  }
-
-  if (controller.accelerometers != nullptr)
-  {
-    for (unsigned int i = 0; i < controller.accelerometer_count; i++)
-      m_accelerometers.emplace_back(controller.accelerometers[i]);
-  }
-
-  if (controller.keys != nullptr)
-  {
-    for (unsigned int i = 0; i < controller.key_count; i++)
-      m_keys.emplace_back(controller.keys[i]);
-  }
-
-  if (controller.rel_pointers != nullptr)
-  {
-    for (unsigned int i = 0; i < controller.rel_pointer_count; i++)
-      m_relPointers.emplace_back(controller.rel_pointers[i]);
-  }
-
-  if (controller.abs_pointers != nullptr)
-  {
-    for (unsigned int i = 0; i < controller.abs_pointer_count; i++)
-      m_absPointers.emplace_back(controller.abs_pointers[i]);
-  }
-
-  if (controller.motors != nullptr)
-  {
-    for (unsigned int i = 0; i < controller.motor_count; i++)
-      m_motors.emplace_back(controller.motors[i]);
-  }
 }
diff --git a/src/input/ControllerLayout.h b/src/input/ControllerLayout.h
index 0f886287..6b1b51aa 100644
--- a/src/input/ControllerLayout.h
+++ b/src/input/ControllerLayout.h
@@ -19,6 +19,8 @@
  */
 #pragma once
 
+#include <kodi/addon-instance/Game.h>
+
 #include <string>
 #include <vector>
 
@@ -29,21 +31,12 @@ namespace LIBRETRO
   class CControllerLayout
   {
   public:
-    CControllerLayout(const game_controller_layout &controller);
+    CControllerLayout(const AddonGameControllerLayout& controller);
 
-    const std::string &ControllerID() const { return m_controllerId; }
-    bool ProvidesInput() const { return m_bProvidesInput; }
+    const std::string &ControllerID() const { return m_controller.controller_id; }
+    bool ProvidesInput() const { return m_controller.provides_input; }
 
   private:
-    std::string m_controllerId;
-    bool m_bProvidesInput;
-    std::vector<std::string> m_digitalButtons;
-    std::vector<std::string> m_analogButtons;
-    std::vector<std::string> m_analogSticks;
-    std::vector<std::string> m_accelerometers;
-    std::vector<std::string> m_keys;
-    std::vector<std::string> m_relPointers;
-    std::vector<std::string> m_absPointers;
-    std::vector<std::string> m_motors;
+    AddonGameControllerLayout m_controller;
   };
 }
diff --git a/src/input/ControllerTopology.cpp b/src/input/ControllerTopology.cpp
index a27341e5..7c9eac55 100644
--- a/src/input/ControllerTopology.cpp
+++ b/src/input/ControllerTopology.cpp
@@ -24,7 +24,6 @@
 #include "libretro/LibretroEnvironment.h"
 #include "log/Log.h"
 
-#include "kodi_game_types.h"
 #include <tinyxml.h>
 
 #include <algorithm>
diff --git a/src/input/ControllerTopology.h b/src/input/ControllerTopology.h
index bd0542fb..c9b0249f 100644
--- a/src/input/ControllerTopology.h
+++ b/src/input/ControllerTopology.h
@@ -19,7 +19,7 @@
  */
 #pragma once
 
-#include "kodi_game_types.h"
+#include <kodi/addon-instance/Game.h>
 
 #include <memory>
 #include <string>
diff --git a/src/input/InputManager.cpp b/src/input/InputManager.cpp
index 656489ac..cce674de 100644
--- a/src/input/InputManager.cpp
+++ b/src/input/InputManager.cpp
@@ -28,13 +28,10 @@
 #include "libretro/LibretroTranslator.h"
 #include "log/Log.h"
 
-#include "libKODI_game.h"
-
 #include <algorithm>
 #include <sstream>
 
 using namespace LIBRETRO;
-using namespace P8PLATFORM;
 
 #define PORT_MAX_COUNT  32 // Large enough
 
@@ -55,14 +52,13 @@ libretro_device_caps_t CInputManager::GetDeviceCaps(void) const
 }
 
 
-void CInputManager::SetControllerLayouts(const std::vector<game_controller_layout> &controllers)
+void CInputManager::SetControllerLayouts(const std::vector<AddonGameControllerLayout>& controllers)
 {
   m_controllerLayouts.clear();
 
   for (const auto &controller : controllers)
   {
-    if (controller.controller_id != nullptr)
-      m_controllerLayouts[controller.controller_id].reset(new CControllerLayout(controller));
+    m_controllerLayouts[controller.controller_id].reset(new CControllerLayout(controller));
   }
 }
 
diff --git a/src/input/InputManager.h b/src/input/InputManager.h
index d2d090c9..34774b75 100644
--- a/src/input/InputManager.h
+++ b/src/input/InputManager.h
@@ -23,8 +23,8 @@
 #include "ControllerLayout.h"
 #include "LibretroDevice.h"
 
-#include "kodi_game_types.h"
-#include "p8-platform/threads/mutex.h"
+#include <kodi/addon-instance/Game.h>
+#include <mutex>
 
 #include <map>
 #include <memory>
@@ -57,7 +57,7 @@ namespace LIBRETRO
     /*!
      * \brief
      */
-    void SetControllerLayouts(const std::vector<game_controller_layout> &controllers);
+    void SetControllerLayouts(const std::vector<AddonGameControllerLayout>& controllers);
 
     /*!
      * \brief Enable the keyboard
diff --git a/src/input/InputTranslator.h b/src/input/InputTranslator.h
index fb4bdcc6..d47afe9b 100644
--- a/src/input/InputTranslator.h
+++ b/src/input/InputTranslator.h
@@ -19,7 +19,7 @@
  */
 #pragma once
 
-#include <kodi/kodi_game_types.h>
+#include <kodi/addon-instance/Game.h>
 
 namespace LIBRETRO
 {
diff --git a/src/input/LibretroDevice.h b/src/input/LibretroDevice.h
index 2377fac2..9540baed 100644
--- a/src/input/LibretroDevice.h
+++ b/src/input/LibretroDevice.h
@@ -21,7 +21,7 @@
 
 #include "InputTypes.h"
 
-#include "kodi_game_types.h"
+#include <kodi/addon-instance/Game.h>
 
 #include <map>
 #include <memory>
diff --git a/src/input/LibretroDeviceInput.cpp b/src/input/LibretroDeviceInput.cpp
index c121eb31..89ce8273 100644
--- a/src/input/LibretroDeviceInput.cpp
+++ b/src/input/LibretroDeviceInput.cpp
@@ -28,7 +28,6 @@
 #include "log/Log.h"
 
 using namespace LIBRETRO;
-using namespace P8PLATFORM;
 
 #define LIBRETRO_JOYPAD_BUTTON_COUNT     16
 #define LIBRETRO_ANALOG_STICK_COUNT      2
@@ -136,7 +135,7 @@ int CLibretroDeviceInput::RelativePointerDeltaX(void)
 
   if (!m_relativePointers.empty())
   {
-    CLockObject lock(m_relativePtrMutex);
+    std::unique_lock<std::mutex> lock(m_relativePtrMutex);
 
     deltaX = m_relativePointers[0].x;
     m_relativePointers[0].x = 0;
@@ -151,7 +150,7 @@ int CLibretroDeviceInput::RelativePointerDeltaY(void)
 
   if (!m_relativePointers.empty())
   {
-    CLockObject lock(m_relativePtrMutex);
+    std::unique_lock<std::mutex> lock(m_relativePtrMutex);
 
     deltaY = m_relativePointers[0].y;
     m_relativePointers[0].y = 0;
@@ -285,7 +284,7 @@ bool CLibretroDeviceInput::InputEvent(const game_input_event& event)
       case GAME_INPUT_EVENT_RELATIVE_POINTER:
         if (index < (int)m_relativePointers.size())
         {
-          CLockObject lock(m_relativePtrMutex);
+          std::unique_lock<std::mutex> lock(m_relativePtrMutex);
 
           m_relativePointers[index].x += event.rel_pointer.x;
           m_relativePointers[index].y += event.rel_pointer.y;
diff --git a/src/input/LibretroDeviceInput.h b/src/input/LibretroDeviceInput.h
index c164407a..a79a9a7c 100644
--- a/src/input/LibretroDeviceInput.h
+++ b/src/input/LibretroDeviceInput.h
@@ -19,8 +19,8 @@
  */
 #pragma once
 
-#include "kodi_game_types.h"
-#include "p8-platform/threads/mutex.h"
+#include <kodi/addon-instance/Game.h>
+#include <mutex>
 
 #include <string>
 #include <vector>
@@ -59,6 +59,6 @@ namespace LIBRETRO
     std::vector<game_accelerometer_event>  m_accelerometers;
     std::vector<game_rel_pointer_event>    m_relativePointers;
     std::vector<game_abs_pointer_event>    m_absolutePointers;
-    P8PLATFORM::CMutex                     m_relativePtrMutex;
+    std::mutex                             m_relativePtrMutex;
   };
 }
diff --git a/src/libretro/ClientBridge.h b/src/libretro/ClientBridge.h
index 13269d38..4fd1c6a9 100644
--- a/src/libretro/ClientBridge.h
+++ b/src/libretro/ClientBridge.h
@@ -19,7 +19,7 @@
  */
 #pragma once
 
-#include "kodi_game_types.h"
+#include <kodi/addon-instance/Game.h>
 
 struct retro_game_info;
 
diff --git a/src/libretro/FrontendBridge.cpp b/src/libretro/FrontendBridge.cpp
index a2d70563..3fb1e841 100644
--- a/src/libretro/FrontendBridge.cpp
+++ b/src/libretro/FrontendBridge.cpp
@@ -23,14 +23,12 @@
 #include "LibretroTranslator.h"
 #include "input/ButtonMapper.h"
 #include "input/InputManager.h"
-
-#include "libXBMC_addon.h"
-#include "libKODI_game.h"
+#include "client.h"
 
 #include <algorithm>
 #include <assert.h>
+#include <kodi/General.h>
 
-using namespace ADDON;
 using namespace LIBRETRO;
 
 #define S16NE_FRAMESIZE  4 // int16 L + int16 R
@@ -44,17 +42,14 @@ using namespace LIBRETRO;
 
 void CFrontendBridge::LogFrontend(retro_log_level level, const char *fmt, ...)
 {
-  if (!CLibretroEnvironment::Get().GetXBMC())
-    return;
-
-  addon_log_t xbmcLevel;
+  AddonLog xbmcLevel;
   switch (level)
   {
-  case RETRO_LOG_DEBUG: xbmcLevel = LOG_DEBUG; break;
-  case RETRO_LOG_INFO:  xbmcLevel = LOG_INFO;  break;
-  case RETRO_LOG_WARN:  xbmcLevel = LOG_ERROR; break;
-  case RETRO_LOG_ERROR: xbmcLevel = LOG_ERROR; break;
-  default:              xbmcLevel = LOG_ERROR; break;
+  case RETRO_LOG_DEBUG: xbmcLevel = ADDON_LOG_DEBUG; break;
+  case RETRO_LOG_INFO:  xbmcLevel = ADDON_LOG_INFO;  break;
+  case RETRO_LOG_WARN:  xbmcLevel = ADDON_LOG_ERROR; break;
+  case RETRO_LOG_ERROR: xbmcLevel = ADDON_LOG_ERROR; break;
+  default:              xbmcLevel = ADDON_LOG_ERROR; break;
   }
 
   char buffer[16384];
@@ -63,7 +58,7 @@ void CFrontendBridge::LogFrontend(retro_log_level level, const char *fmt, ...)
   vsprintf(buffer, fmt, args);
   va_end(args);
 
-  CLibretroEnvironment::Get().GetXBMC()->Log(xbmcLevel, buffer);
+  kodi::Log(xbmcLevel, buffer);
 }
 
 void CFrontendBridge::VideoRefresh(const void* data, unsigned int width, unsigned int height, size_t pitch)
@@ -201,7 +196,7 @@ int16_t CFrontendBridge::InputState(unsigned int port, unsigned int device, unsi
 
 uintptr_t CFrontendBridge::HwGetCurrentFramebuffer(void)
 {
-  if (!CLibretroEnvironment::Get().GetFrontend())
+  if (!CLibretroEnvironment::Get().GetAddon())
     return 0;
 
   return CLibretroEnvironment::Get().Video().GetHwFramebuffer();
@@ -209,15 +204,15 @@ uintptr_t CFrontendBridge::HwGetCurrentFramebuffer(void)
 
 retro_proc_address_t CFrontendBridge::HwGetProcAddress(const char *sym)
 {
-  if (!CLibretroEnvironment::Get().GetFrontend())
+  if (!CLibretroEnvironment::Get().GetAddon())
     return nullptr;
 
-  return CLibretroEnvironment::Get().GetFrontend()->HwGetProcAddress(sym);
+  return CLibretroEnvironment::Get().GetAddon()->HwGetProcAddress(sym);
 }
 
 bool CFrontendBridge::RumbleSetState(unsigned int port, retro_rumble_effect effect, uint16_t strength)
 {
-  if (!CLibretroEnvironment::Get().GetFrontend())
+  if (!CLibretroEnvironment::Get().GetAddon())
     return false;
 
   std::string controllerId  = CInputManager::Get().ControllerID(port);
@@ -237,7 +232,7 @@ bool CFrontendBridge::RumbleSetState(unsigned int port, retro_rumble_effect effe
   eventStruct.feature_name    = featureName.c_str();
   eventStruct.motor.magnitude = CONSTRAIN(magnitude, 0.0f, 1.0f);
 
-  CLibretroEnvironment::Get().GetFrontend()->InputEvent(eventStruct);
+  CLibretroEnvironment::Get().GetAddon()->KodiInputEvent(eventStruct);
   return true;
 }
 
diff --git a/src/libretro/LibretroDLL.cpp b/src/libretro/LibretroDLL.cpp
index 9271f8d6..76167f7d 100644
--- a/src/libretro/LibretroDLL.cpp
+++ b/src/libretro/LibretroDLL.cpp
@@ -21,18 +21,12 @@
 #include "LibretroDLL.h"
 #include "log/Log.h"
 
-#include "libKODI_game.h"
-#include "kodi_game_types.h"
+#include <kodi/addon-instance/Game.h>
 
-#ifdef _WIN32
-  #include "dlfcn-win32.h" // TODO: Use file from kodi-platform
-#else
-  #include <dlfcn.h>
-#endif
+#include <dlfcn.h>
 
 #include <assert.h>
 
-using namespace ADDON;
 using namespace LIBRETRO;
 
 CLibretroDLL::CLibretroDLL(void) :
@@ -86,11 +80,12 @@ bool RegisterSymbol(void* dll, T& functionPtr, const char* strFunctionPtr)
   return (functionPtr = (T)dlsym(dll, strFunctionPtr)) != nullptr;
 }
 
-bool CLibretroDLL::Load(const AddonProps_Game* gameClientProps)
+bool CLibretroDLL::Load(const std::string& gameClientDllPath)
 {
   Unload();
 
-  m_libretroClient = dlopen(gameClientProps->game_client_dll_path, RTLD_LAZY);
+  m_strPath = gameClientDllPath;
+  m_libretroClient = dlopen(m_strPath.c_str(), RTLD_LAZY);
   if (m_libretroClient == nullptr)
   {
     esyslog("Unable to load: %s", dlerror());
@@ -131,7 +126,5 @@ bool CLibretroDLL::Load(const AddonProps_Game* gameClientProps)
     return bSuccess;
   }
 
-  m_strPath = gameClientProps->game_client_dll_path;
-
   return true;
 }
diff --git a/src/libretro/LibretroDLL.h b/src/libretro/LibretroDLL.h
index 743e5984..5b372aeb 100644
--- a/src/libretro/LibretroDLL.h
+++ b/src/libretro/LibretroDLL.h
@@ -36,7 +36,7 @@ namespace LIBRETRO
     ~CLibretroDLL(void) { Unload(); }
 
     void Unload(void);
-    bool Load(const AddonProps_Game* gameClientProps);
+    bool Load(const std::string& gameClientDllPath);
 
     const std::string& GetPath() const { return m_strPath; }
 
diff --git a/src/libretro/LibretroEnvironment.cpp b/src/libretro/LibretroEnvironment.cpp
index 9117dd41..c2c9eb7f 100644
--- a/src/libretro/LibretroEnvironment.cpp
+++ b/src/libretro/LibretroEnvironment.cpp
@@ -28,13 +28,11 @@
 #include "log/Log.h"
 #include "settings/Settings.h"
 #include "video/VideoGeometry.h"
+#include "client.h"
 
-#include "libKODI_game.h"
-#include "libXBMC_addon.h"
+#include <kodi/General.h>
 
-using namespace ADDON;
 using namespace LIBRETRO;
-using namespace P8PLATFORM;
 
 namespace LIBRETRO
 {
@@ -45,8 +43,7 @@ namespace LIBRETRO
 }
 
 CLibretroEnvironment::CLibretroEnvironment(void) :
-  m_xbmc(nullptr),
-  m_frontend(nullptr),
+  m_addon(nullptr),
   m_client(nullptr),
   m_clientBridge(nullptr),
   m_videoFormat(GAME_PIXEL_FORMAT_0RGB1555), // Default libretro format
@@ -60,22 +57,19 @@ CLibretroEnvironment& CLibretroEnvironment::Get(void)
   return _instance;
 }
 
-void CLibretroEnvironment::Initialize(ADDON::CHelper_libXBMC_addon* xbmc,
-                                      CHelper_libKODI_game*         frontend,
+void CLibretroEnvironment::Initialize(CGameLibRetro*                addon,
                                       CLibretroDLL*                 client,
-                                      CClientBridge*                clientBridge,
-                                      const AddonProps_Game*        gameClientProps)
+                                      CClientBridge*                clientBridge)
 {
-  m_xbmc         = xbmc;
-  m_frontend     = frontend;
+  m_addon        = addon;
   m_client       = client;
   m_clientBridge = clientBridge;
 
-  m_videoStream.Initialize(m_frontend);
-  m_audioStream.Initialize(m_frontend);
+  m_videoStream.Initialize(m_addon);
+  m_audioStream.Initialize(m_addon);
 
-  m_settings.Initialize(xbmc, gameClientProps);
-  m_resources.Initialize(xbmc, gameClientProps);
+  m_settings.Initialize(m_addon);
+  m_resources.Initialize(m_addon);
 
   // Install environment callback
   m_client->retro_set_environment(EnvCallback);
@@ -125,7 +119,7 @@ void CLibretroEnvironment::OnFrameEnd()
 
 bool CLibretroEnvironment::EnvironmentCallback(unsigned int cmd, void *data)
 {
-  if (!m_frontend || !m_clientBridge)
+  if (!m_addon || !m_clientBridge)
     return false;
 
   switch (cmd)
@@ -158,13 +152,13 @@ bool CLibretroEnvironment::EnvironmentCallback(unsigned int cmd, void *data)
       if (typedData)
       {
         const char* msg = typedData->msg;
-        m_xbmc->QueueNotification(QUEUE_INFO, msg);
+        kodi::QueueFormattedNotification(QUEUE_INFO, msg);
       }
       break;
     }
   case RETRO_ENVIRONMENT_SHUTDOWN:
     {
-      m_frontend->CloseGame();
+      m_addon->CloseGame();
       break;
     }
   case RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL:
@@ -290,7 +284,7 @@ bool CLibretroEnvironment::EnvironmentCallback(unsigned int cmd, void *data)
       {
         const bool bSupportsNoGame = *typedData;
         if (bSupportsNoGame)
-          m_xbmc->Log(LOG_DEBUG, "Libretro client supports loading with no game");
+          kodi::Log(ADDON_LOG_DEBUG, "Libretro client supports loading with no game");
       }
       break;
     }
diff --git a/src/libretro/LibretroEnvironment.h b/src/libretro/LibretroEnvironment.h
index 1e7347d4..de8f99e1 100644
--- a/src/libretro/LibretroEnvironment.h
+++ b/src/libretro/LibretroEnvironment.h
@@ -24,13 +24,12 @@
 #include "settings/LibretroSettings.h"
 #include "video/VideoStream.h"
 
-#include "kodi_game_types.h"
+#include <kodi/addon-instance/Game.h>
 
 #include <memory>
 #include <string>
 
-namespace ADDON { class CHelper_libXBMC_addon; }
-class CHelper_libKODI_game;
+class CGameLibRetro;
 
 struct retro_game_geometry;
 
@@ -44,16 +43,13 @@ namespace LIBRETRO
   public:
     static CLibretroEnvironment& Get(void);
 
-    void Initialize(ADDON::CHelper_libXBMC_addon* xbmc,
-                    CHelper_libKODI_game*         frontend,
+    void Initialize(CGameLibRetro*                addon,
                     CLibretroDLL*                 client,
-                    CClientBridge*                clientBridge,
-                    const AddonProps_Game*        gameClientProps);
+                    CClientBridge*                clientBridge);
 
     void Deinitialize(void);
 
-    ADDON::CHelper_libXBMC_addon* GetXBMC(void)         { return m_xbmc; }
-    CHelper_libKODI_game*         GetFrontend(void)     { return m_frontend; }
+    CGameLibRetro*                GetAddon(void)        { return m_addon; }
     CLibretroDLL*                 GetClient(void)       { return m_client; }
     CClientBridge*                GetClientBridge(void) { return m_clientBridge; }
 
@@ -90,8 +86,7 @@ namespace LIBRETRO
   private:
     CLibretroEnvironment(void);
 
-    ADDON::CHelper_libXBMC_addon* m_xbmc;
-    CHelper_libKODI_game*         m_frontend;
+    CGameLibRetro*                m_addon;
     CLibretroDLL*                 m_client;
     CClientBridge*                m_clientBridge;
     CVideoStream                  m_videoStream;
diff --git a/src/libretro/LibretroResources.cpp b/src/libretro/LibretroResources.cpp
index d852983d..430d26fe 100644
--- a/src/libretro/LibretroResources.cpp
+++ b/src/libretro/LibretroResources.cpp
@@ -21,11 +21,10 @@
 #include "LibretroResources.h"
 #include "LibretroDefines.h"
 #include "log/Log.h"
-#include "utils/PathUtils.h"
 
-#include "kodi_game_types.h"
-#include "libXBMC_addon.h"
+#include "client.h"
 
+#include <kodi/Filesystem.h>
 #include <assert.h>
 #include <utility>
 
@@ -36,52 +35,42 @@ CLibretroResources::CLibretroResources() :
 {
 }
 
-void CLibretroResources::Initialize(ADDON::CHelper_libXBMC_addon* addon, const AddonProps_Game* gameClientProps)
+void CLibretroResources::Initialize(CGameLibRetro* addon)
 {
   m_addon = addon;
 
   assert(m_addon != nullptr);
 
-  for (unsigned int i = 0; i < gameClientProps->resource_directory_count; i++)
+  std::vector<std::string> dirs;
+  m_addon->ResourceDirectories(dirs);
+  for (const auto& dir : dirs)
   {
-    if (gameClientProps->resource_directories[i] == nullptr)
-      break;
-
-    std::string resourcePath = gameClientProps->resource_directories[i];
-
-    PathUtils::RemoveSlashAtEnd(resourcePath);
-
-    if (resourcePath.empty())
+    if (dir.empty())
       continue;
 
     // Set system path to first resource path discovered
     if (m_systemDirectory.empty())
     {
-      m_systemDirectory = resourcePath + "/" LIBRETRO_SYSTEM_DIRECTORY_NAME;
+      m_systemDirectory = dir + "/" LIBRETRO_SYSTEM_DIRECTORY_NAME;
 
       // Ensure folder exists
-      if (!m_addon->DirectoryExists(m_systemDirectory.c_str()))
+      if (!kodi::vfs::DirectoryExists(m_systemDirectory))
       {
         dsyslog("Creating system directory: %s", m_systemDirectory.c_str());
-        m_addon->CreateDirectory(m_systemDirectory.c_str());
+        kodi::vfs::CreateDirectory(m_systemDirectory);
       }
     }
 
-    m_resourceDirectories.push_back(std::move(resourcePath));
-  }
+  m_resourceDirectories.push_back(std::move(dir));
+}
 
-  if (gameClientProps->profile_directory != nullptr)
-  {
-    m_saveDirectory = gameClientProps->profile_directory;
-    PathUtils::RemoveSlashAtEnd(m_saveDirectory);
-    m_saveDirectory += "/" LIBRETRO_SAVE_DIRECTORY_NAME;
+  m_saveDirectory = m_addon->ProfileDirectory() + "/" LIBRETRO_SAVE_DIRECTORY_NAME;
 
-    // Ensure folder exists
-    if (!m_addon->DirectoryExists(m_saveDirectory.c_str()))
-    {
-      dsyslog("Creating save directory: %s", m_saveDirectory.c_str());
-      m_addon->CreateDirectory(m_saveDirectory.c_str());
-    }
+  // Ensure folder exists
+  if (!kodi::vfs::DirectoryExists(m_saveDirectory))
+  {
+    dsyslog("Creating save directory: %s", m_saveDirectory.c_str());
+    kodi::vfs::CreateDirectory(m_saveDirectory);
   }
 }
 
@@ -101,7 +90,7 @@ const char* CLibretroResources::GetBasePath(const std::string& relPath)
       std::string resourcePath = dir + "/" + relPath;
 
       // Check for path existence
-      if (m_addon->FileExists(resourcePath.c_str(), true))
+      if (kodi::vfs::FileExists(resourcePath, true))
       {
         m_pathMap.insert(std::make_pair(relPath, std::move(dir)));
         it = m_pathMap.find(relPath);
diff --git a/src/libretro/LibretroResources.h b/src/libretro/LibretroResources.h
index a997e625..5e897920 100644
--- a/src/libretro/LibretroResources.h
+++ b/src/libretro/LibretroResources.h
@@ -23,9 +23,7 @@
 #include <string>
 #include <vector>
 
-namespace ADDON { class CHelper_libXBMC_addon; }
-
-struct AddonProps_Game;
+class CGameLibRetro;
 
 namespace LIBRETRO
 {
@@ -35,7 +33,7 @@ namespace LIBRETRO
     CLibretroResources();
     ~CLibretroResources() { Deinitialize(); }
 
-    void Initialize(ADDON::CHelper_libXBMC_addon* addon, const AddonProps_Game* gameClientProps);
+    void Initialize(CGameLibRetro* addon);
     void Deinitialize();
 
     const char* GetSystemDir() const { return m_systemDirectory.c_str(); }
@@ -50,8 +48,7 @@ namespace LIBRETRO
   private:
     const char* ApendSystemFolder(const std::string& path);
 
-    ADDON::CHelper_libXBMC_addon* m_addon;
-
+    CGameLibRetro*                     m_addon;
     std::vector<std::string>           m_resourceDirectories;
     std::map<std::string, std::string> m_pathMap;
     std::string                        m_systemDirectory;
diff --git a/src/libretro/LibretroTranslator.h b/src/libretro/LibretroTranslator.h
index 2802eca4..2ec60f52 100644
--- a/src/libretro/LibretroTranslator.h
+++ b/src/libretro/LibretroTranslator.h
@@ -22,7 +22,7 @@
 #include "input/LibretroDevice.h"
 #include "libretro.h"
 
-#include "kodi_game_types.h"
+#include <kodi/addon-instance/Game.h>
 
 #include <string>
 
diff --git a/src/log/Log.cpp b/src/log/Log.cpp
index f8796415..7e790ce5 100644
--- a/src/log/Log.cpp
+++ b/src/log/Log.cpp
@@ -26,7 +26,6 @@
 #include <stdio.h>
 
 using namespace LIBRETRO;
-using namespace P8PLATFORM;
 
 #define SYS_LOG_BUFFER_SIZE  256 // bytes
 
@@ -49,7 +48,7 @@ CLog::~CLog(void)
 
 bool CLog::SetType(SYS_LOG_TYPE type)
 {
-  P8PLATFORM::CLockObject lock(m_mutex);
+  std::unique_lock<std::mutex> lock(m_mutex);
   if (m_pipe && m_pipe->Type() == type)
     return true; // Already set
 
@@ -72,7 +71,7 @@ bool CLog::SetType(SYS_LOG_TYPE type)
 
 void CLog::SetPipe(ILog* pipe)
 {
-  P8PLATFORM::CLockObject lock(m_mutex);
+  std::unique_lock<std::mutex> lock(m_mutex);
 
   delete m_pipe;
   m_pipe = pipe;
@@ -80,7 +79,7 @@ void CLog::SetPipe(ILog* pipe)
 
 void CLog::SetLevel(SYS_LOG_LEVEL level)
 {
-  P8PLATFORM::CLockObject lock(m_mutex);
+  std::unique_lock<std::mutex> lock(m_mutex);
 
   m_level = level;
 }
@@ -108,7 +107,7 @@ void CLog::Log(SYS_LOG_LEVEL level, const char* format, ...)
   vsnprintf(buf, SYS_LOG_BUFFER_SIZE - 1, fmt, ap);
   va_end(ap);
 
-  P8PLATFORM::CLockObject lock(m_mutex);
+  std::unique_lock<std::mutex> lock(m_mutex);
 
   if (level > m_level)
     return;
diff --git a/src/log/Log.h b/src/log/Log.h
index 429700fb..80060136 100644
--- a/src/log/Log.h
+++ b/src/log/Log.h
@@ -21,7 +21,7 @@
 
 #include "ILog.h"
 
-#include "p8-platform/threads/mutex.h"
+#include <mutex>
 
 #include <string>
 
@@ -69,6 +69,6 @@ namespace LIBRETRO
     ILog*            m_pipe;
     SYS_LOG_LEVEL    m_level;
     std::string      m_strLogPrefix;
-    P8PLATFORM::CMutex m_mutex;
+    std::mutex       m_mutex;
   };
 }
diff --git a/src/log/LogAddon.cpp b/src/log/LogAddon.cpp
index 3f84f83a..03cbd008 100644
--- a/src/log/LogAddon.cpp
+++ b/src/log/LogAddon.cpp
@@ -20,7 +20,7 @@
 
 #include "LogAddon.h"
 
-#include "libXBMC_addon.h"
+#include <kodi/General.h>
 
 #include <assert.h>
 
@@ -28,29 +28,22 @@ using namespace LIBRETRO;
 
 // --- TranslateLogLevel() -----------------------------------------------------
 
-ADDON::addon_log_t TranslateLogLevel(SYS_LOG_LEVEL level)
+AddonLog TranslateLogLevel(SYS_LOG_LEVEL level)
 {
   switch (level)
   {
-    case SYS_LOG_ERROR: return ADDON::LOG_ERROR;
-    case SYS_LOG_INFO:  return ADDON::LOG_INFO;
-    case SYS_LOG_DEBUG: return ADDON::LOG_DEBUG;
+    case SYS_LOG_ERROR: return ADDON_LOG_ERROR;
+    case SYS_LOG_INFO:  return ADDON_LOG_INFO;
+    case SYS_LOG_DEBUG: return ADDON_LOG_DEBUG;
     default:
       break;
   }
-  return ADDON::LOG_INFO;
+  return ADDON_LOG_INFO;
 }
 
 // -- CLogAddon ----------------------------------------------------------------
 
-CLogAddon::CLogAddon(ADDON::CHelper_libXBMC_addon* frontend) :
-  m_frontend(frontend)
-{
-  assert(m_frontend);
-}
-
 void CLogAddon::Log(SYS_LOG_LEVEL level, const char* logline)
 {
-  if (m_frontend)
-    m_frontend->Log(TranslateLogLevel(level), "%s", logline);
+  kodi::Log(TranslateLogLevel(level), "%s", logline);
 }
diff --git a/src/log/LogAddon.h b/src/log/LogAddon.h
index 22957390..d16b46bb 100644
--- a/src/log/LogAddon.h
+++ b/src/log/LogAddon.h
@@ -21,24 +21,16 @@
 
 #include "ILog.h"
 
-namespace ADDON
-{
-  class CHelper_libXBMC_addon;
-}
-
 namespace LIBRETRO
 {
   class CLogAddon : public ILog
   {
   public:
-    CLogAddon(ADDON::CHelper_libXBMC_addon* frontend);
+    CLogAddon() = default;
     virtual ~CLogAddon(void) { }
 
     // implementation of ILog
     virtual void Log(SYS_LOG_LEVEL level, const char* logline);
     virtual SYS_LOG_TYPE Type(void) const { return SYS_LOG_TYPE_ADDON; }
-
-  private:
-    ADDON::CHelper_libXBMC_addon* const m_frontend;
   };
 }
diff --git a/src/log/LogConsole.cpp b/src/log/LogConsole.cpp
index 8a8977d0..3e3f364b 100644
--- a/src/log/LogConsole.cpp
+++ b/src/log/LogConsole.cpp
@@ -23,11 +23,10 @@
 #include <iostream>
 
 using namespace LIBRETRO;
-using namespace P8PLATFORM;
 
 void CLogConsole::Log(SYS_LOG_LEVEL level, const char* logline)
 {
-  CLockObject lock(m_mutex);
+  std::unique_lock<std::mutex> lock(m_mutex);
 
   // TODO: Prepend current date
   std::cout << logline << std::endl;
diff --git a/src/log/LogConsole.h b/src/log/LogConsole.h
index e58a7d9f..d0fbbe31 100644
--- a/src/log/LogConsole.h
+++ b/src/log/LogConsole.h
@@ -21,7 +21,7 @@
 
 #include "ILog.h"
 
-#include "p8-platform/threads/mutex.h"
+#include <mutex>
 
 namespace LIBRETRO
 {
@@ -35,6 +35,6 @@ namespace LIBRETRO
     virtual SYS_LOG_TYPE Type(void) const { return SYS_LOG_TYPE_CONSOLE; }
 
   private:
-    P8PLATFORM::CMutex m_mutex;
+    std::mutex m_mutex;
   };
 }
diff --git a/src/settings/LibretroSettings.cpp b/src/settings/LibretroSettings.cpp
index 75258d03..426a110b 100644
--- a/src/settings/LibretroSettings.cpp
+++ b/src/settings/LibretroSettings.cpp
@@ -23,17 +23,15 @@
 #include "SettingsGenerator.h"
 #include "libretro/libretro.h"
 #include "log/Log.h"
-#include "utils/PathUtils.h"
+#include "client.h"
 
-#include "kodi_game_types.h"
-#include "libXBMC_addon.h"
+#include <kodi/Filesystem.h>
 
 #include <algorithm>
 #include <assert.h>
 #include <utility>
 
 using namespace LIBRETRO;
-using namespace P8PLATFORM;
 
 CLibretroSettings::CLibretroSettings() :
   m_addon(nullptr),
@@ -42,14 +40,12 @@ CLibretroSettings::CLibretroSettings() :
 {
 }
 
-void CLibretroSettings::Initialize(ADDON::CHelper_libXBMC_addon* addon, const AddonProps_Game* props)
+void CLibretroSettings::Initialize(CGameLibRetro* addon)
 {
   m_addon = addon;
-
-  if (props->profile_directory != nullptr)
-    m_profileDirectory = props->profile_directory;
-
   assert(m_addon != nullptr);
+
+  m_profileDirectory = m_addon->ProfileDirectory();
 }
 
 void CLibretroSettings::Deinitialize()
@@ -59,13 +55,13 @@ void CLibretroSettings::Deinitialize()
 
 bool CLibretroSettings::Changed()
 {
-  CLockObject lock(m_mutex);
+  std::unique_lock<std::mutex> lock(m_mutex);
   return m_bChanged;
 }
 
 void CLibretroSettings::SetUnchanged()
 {
-  CLockObject lock(m_mutex);
+  std::unique_lock<std::mutex> lock(m_mutex);
   m_bChanged = false;
 }
 
@@ -74,7 +70,7 @@ void CLibretroSettings::SetAllSettings(const retro_variable* libretroVariables)
   // Keep track of whether Kodi has the correct settings
   bool bValid = true;
 
-  CLockObject lock(m_mutex);
+  std::unique_lock<std::mutex> lock(m_mutex);
 
   if (m_settings.empty())
   {
@@ -89,8 +85,8 @@ void CLibretroSettings::SetAllSettings(const retro_variable* libretroVariables)
       }
 
       // Query current value for setting from the frontend
-      char valueBuf[1024] = { };
-      if (m_addon->GetSetting(variable->key, valueBuf))
+      std::string valueBuf;
+      if (kodi::CheckSettingString(variable->key, valueBuf))
       {
         if (std::find(setting.Values().begin(), setting.Values().end(), valueBuf) != setting.Values().end())
         {
@@ -121,7 +117,7 @@ void CLibretroSettings::SetAllSettings(const retro_variable* libretroVariables)
 
 const char* CLibretroSettings::GetCurrentValue(const std::string& settingName)
 {
-  CLockObject lock(m_mutex);
+  std::unique_lock<std::mutex> lock(m_mutex);
 
   auto it = m_settings.find(settingName);
   if (it == m_settings.end())
@@ -135,7 +131,7 @@ const char* CLibretroSettings::GetCurrentValue(const std::string& settingName)
 
 void CLibretroSettings::SetCurrentValue(const std::string& name, const std::string& value)
 {
-  CLockObject lock(m_mutex);
+  std::unique_lock<std::mutex> lock(m_mutex);
 
   if (m_settings.empty())
   {
@@ -173,17 +169,15 @@ void CLibretroSettings::GenerateSettings()
 
     std::string generatedPath = m_profileDirectory;
 
-    PathUtils::RemoveSlashAtEnd(generatedPath);
-
-    std::string addonId = PathUtils::GetBasename(generatedPath);
+    std::string addonId = kodi::vfs::GetFileName(generatedPath);
 
     generatedPath += "/" SETTINGS_GENERATED_DIRECTORY_NAME;
 
     // Ensure folder exists
-    if (!m_addon->DirectoryExists(generatedPath.c_str()))
+    if (!kodi::vfs::DirectoryExists(generatedPath))
     {
       dsyslog("Creating directory for settings and language files: %s", generatedPath.c_str());
-      m_addon->CreateDirectory(generatedPath.c_str());
+      kodi::vfs::CreateDirectory(generatedPath);
     }
 
     bool bSuccess = false;
@@ -197,19 +191,19 @@ void CLibretroSettings::GenerateSettings()
     generatedPath += "/" SETTINGS_GENERATED_LANGUAGE_SUBDIR;
 
     // Ensure language folder exists
-    if (!m_addon->DirectoryExists(generatedPath.c_str()))
+    if (!kodi::vfs::DirectoryExists(generatedPath))
     {
       dsyslog("Creating directory for settings and language files: %s", generatedPath.c_str());
-      m_addon->CreateDirectory(generatedPath.c_str());
+      kodi::vfs::CreateDirectory(generatedPath);
     }
 
     generatedPath += "/" SETTINGS_GENERATED_LANGUAGE_ENGLISH_SUBDIR;
 
     // Ensure English folder exists
-    if (!m_addon->DirectoryExists(generatedPath.c_str()))
+    if (!kodi::vfs::DirectoryExists(generatedPath))
     {
       dsyslog("Creating directory for settings and language files: %s", generatedPath.c_str());
-      m_addon->CreateDirectory(generatedPath.c_str());
+      kodi::vfs::CreateDirectory(generatedPath);
     }
 
     CLanguageGenerator languageGen(addonId, generatedPath);
diff --git a/src/settings/LibretroSettings.h b/src/settings/LibretroSettings.h
index f0fdd205..c7751454 100644
--- a/src/settings/LibretroSettings.h
+++ b/src/settings/LibretroSettings.h
@@ -21,14 +21,12 @@
 
 #include "SettingsTypes.h"
 
-#include "p8-platform/threads/mutex.h"
+#include <mutex>
 
 #include <map>
 #include <string>
 
-namespace ADDON { class CHelper_libXBMC_addon; }
-
-struct AddonProps_Game;
+class CGameLibRetro;
 struct retro_variable;
 
 namespace LIBRETRO
@@ -38,7 +36,7 @@ namespace LIBRETRO
   public:
     CLibretroSettings();
 
-    void Initialize(ADDON::CHelper_libXBMC_addon* addon, const AddonProps_Game* props);
+    void Initialize(CGameLibRetro* addon);
     void Deinitialize();
 
     bool Changed();
@@ -57,13 +55,13 @@ namespace LIBRETRO
     void GenerateSettings();
 
     // Frontend variables
-    ADDON::CHelper_libXBMC_addon* m_addon;
+    CGameLibRetro*                m_addon;
     std::string                   m_profileDirectory;
 
     // Settings variables
     LibretroSettings   m_settings;
     bool               m_bChanged;
     bool               m_bGenerated; // True if settings and language files have been generated
-    P8PLATFORM::CMutex m_mutex;
+    std::mutex         m_mutex;
   };
 } // namespace LIBRETRO
diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp
index 15522f77..9793efa8 100644
--- a/src/settings/Settings.cpp
+++ b/src/settings/Settings.cpp
@@ -36,11 +36,11 @@ CSettings& CSettings::Get(void)
   return _instance;
 }
 
-void CSettings::SetSetting(const std::string& strName, const void* value)
+void CSettings::SetSetting(const std::string& strName, const kodi::CSettingValue& value)
 {
   if (strName == SETTING_CROP_OVERSCAN)
   {
-    m_bCropOverscan = *static_cast<const bool*>(value);
+    m_bCropOverscan = value.GetBoolean();
     //dsyslog("Setting \"%s\" set to %f", SETTING_CROP_OVERSCAN, m_bCropOverscan ? "true" : "false");
   }
 
diff --git a/src/settings/Settings.h b/src/settings/Settings.h
index a55e049d..0d9d4949 100644
--- a/src/settings/Settings.h
+++ b/src/settings/Settings.h
@@ -19,6 +19,7 @@
  */
 #pragma once
 
+#include <kodi/AddonBase.h>
 #include <string>
 
 namespace LIBRETRO
@@ -33,7 +34,7 @@ namespace LIBRETRO
 
     bool IsInitialized(void) const { return m_bInitialized; }
 
-    void SetSetting(const std::string& strName, const void* value);
+    void SetSetting(const std::string& strName, const kodi::CSettingValue& value);
 
     /*!
      * \brief True if the libretro core should crop overscan
diff --git a/src/utils/PathUtils.cpp b/src/utils/PathUtils.cpp
deleted file mode 100644
index cf92d805..00000000
--- a/src/utils/PathUtils.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- *      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 "PathUtils.h"
-
-#include <string.h>
-
-using namespace LIBRETRO;
-
-void PathUtils::RemoveSlashAtEnd(std::string& path)
-{
-  if (!path.empty())
-  {
-    char last = path[path.size() - 1];
-    if (last == '/' || last == '\\')
-      path.erase(path.size() - 1);
-  }
-}
-
-std::string PathUtils::GetBasename(const std::string& path)
-{
-  char last = path[path.size() - 1];
-  if (last == '/' || last == '\\')
-    return "";
-
-  const char* s = strrchr(const_cast<char*>(path.c_str()), '/');
-  if (s == nullptr)
-    return path;
-
-  s++;
-
-  return s;
-}
diff --git a/src/utils/PathUtils.h b/src/utils/PathUtils.h
deleted file mode 100644
index 71a3797d..00000000
--- a/src/utils/PathUtils.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- *      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 LIBRETRO
-{
-  class PathUtils
-  {
-  public:
-    /*!
-     * \brief Remove the slash at the end of a path
-     *
-     * NOTE: Trailing slash causes some libretro cores to fail
-     */
-    static void RemoveSlashAtEnd(std::string& path);
-
-    /*!
-     * \brief Get the base filename, or empty if path ends in a / or \
-     */
-    static std::string GetBasename(const std::string& path);
-  };
-}
diff --git a/src/video/VideoStream.cpp b/src/video/VideoStream.cpp
index 6e8d8d59..77d60588 100644
--- a/src/video/VideoStream.cpp
+++ b/src/video/VideoStream.cpp
@@ -22,35 +22,35 @@
 #include "VideoGeometry.h"
 #include "libretro/LibretroEnvironment.h"
 
-#include "libKODI_game.h"
+#include "client.h"
 
 using namespace LIBRETRO;
 
 CVideoStream::CVideoStream() :
-  m_frontend(nullptr),
+  m_addon(nullptr),
   m_geometry(new CVideoGeometry)
 {
 }
 
-void CVideoStream::Initialize(CHelper_libKODI_game* frontend)
+void CVideoStream::Initialize(CGameLibRetro* addon)
 {
-  m_frontend = frontend;
+  m_addon = addon;
 }
 
 void CVideoStream::Deinitialize()
 {
-  if (m_frontend == nullptr)
+  if (m_addon == nullptr)
     return;
 
   CloseStream();
 
-  m_frontend = nullptr;
+  m_addon = nullptr;
 }
 
 void CVideoStream::SetGeometry(const CVideoGeometry &geometry)
 {
   // Close stream so it can be reopened with the updated geometry
-  if (m_frontend != nullptr)
+  if (m_addon != nullptr)
     CloseStream();
 
   *m_geometry = geometry;
@@ -58,7 +58,7 @@ void CVideoStream::SetGeometry(const CVideoGeometry &geometry)
 
 void CVideoStream::EnableHardwareRendering(const game_stream_hw_framebuffer_properties &properties)
 {
-  if (m_frontend == nullptr)
+  if (m_addon == nullptr)
     return;
 
   CloseStream();
@@ -68,23 +68,23 @@ void CVideoStream::EnableHardwareRendering(const game_stream_hw_framebuffer_prop
   streamProps.type = GAME_STREAM_HW_FRAMEBUFFER;
   streamProps.hw_framebuffer = properties;
 
-  m_stream = m_frontend->OpenStream(streamProps);
+  m_stream.Open(streamProps);
   m_streamType = GAME_STREAM_HW_FRAMEBUFFER;
 }
 
 uintptr_t CVideoStream::GetHwFramebuffer()
 {
-  if (m_frontend == nullptr)
+  if (m_addon == nullptr)
     return 0;
 
-  if (m_stream == nullptr || m_streamType != GAME_STREAM_HW_FRAMEBUFFER)
+  if (!m_stream.IsOpen() || m_streamType != GAME_STREAM_HW_FRAMEBUFFER)
     return 0;
 
   if (!m_framebuffer)
   {
     m_framebuffer.reset(new game_stream_buffer{});
 
-    if (!m_frontend->GetStreamBuffer(m_stream, 0, 0, *m_framebuffer))
+    if (!m_stream.GetBuffer(0, 0, *m_framebuffer))
       return 0;
   }
 
@@ -93,10 +93,10 @@ uintptr_t CVideoStream::GetHwFramebuffer()
 
 bool CVideoStream::GetSwFramebuffer(unsigned int width, unsigned int height, GAME_PIXEL_FORMAT requestedFormat, game_stream_sw_framebuffer_buffer &framebuffer)
 {
-  if (m_frontend == nullptr)
+  if (m_addon == nullptr)
     return false;
 
-  if (m_stream == nullptr)
+  if (!m_stream.IsOpen())
   {
     game_stream_properties properties{};
 
@@ -108,18 +108,18 @@ bool CVideoStream::GetSwFramebuffer(unsigned int width, unsigned int height, GAM
     properties.sw_framebuffer.max_height = m_geometry->MaxHeight();
     properties.sw_framebuffer.aspect_ratio = m_geometry->AspectRatio();
 
-    m_stream = m_frontend->OpenStream(properties);
+    m_stream.Open(properties);
     m_streamType = GAME_STREAM_SW_FRAMEBUFFER;
   }
 
-  if (m_stream == nullptr || m_streamType != GAME_STREAM_SW_FRAMEBUFFER)
+  if (!m_stream.IsOpen() || m_streamType != GAME_STREAM_SW_FRAMEBUFFER)
     return false;
 
   if (!m_framebuffer)
   {
     m_framebuffer.reset(new game_stream_buffer{});
 
-    if (!m_frontend->GetStreamBuffer(m_stream, width, height, *m_framebuffer))
+    if (!m_stream.GetBuffer(width, height, *m_framebuffer))
       return false;
   }
 
@@ -130,7 +130,7 @@ bool CVideoStream::GetSwFramebuffer(unsigned int width, unsigned int height, GAM
 
 void CVideoStream::AddFrame(const uint8_t* data, unsigned int size, unsigned int width, unsigned int height, GAME_PIXEL_FORMAT format, GAME_VIDEO_ROTATION rotation)
 {
-  if (m_frontend == nullptr)
+  if (m_addon == nullptr)
     return;
 
   // Only care if format changes for video stream
@@ -143,7 +143,7 @@ void CVideoStream::AddFrame(const uint8_t* data, unsigned int size, unsigned int
     }
   }
 
-  if (m_stream == nullptr)
+  if (!m_stream.IsOpen())
   {
     game_stream_properties properties{};
 
@@ -155,14 +155,14 @@ void CVideoStream::AddFrame(const uint8_t* data, unsigned int size, unsigned int
     properties.video.max_height = m_geometry->MaxHeight();
     properties.video.aspect_ratio = m_geometry->AspectRatio();
 
-    m_stream = m_frontend->OpenStream(properties);
+    m_stream.Open(properties);
     m_streamType = GAME_STREAM_VIDEO;
 
     // Save format to detect unwanted changes
     m_format = format;
   }
 
-  if (m_stream == nullptr)
+  if (!m_stream.IsOpen())
     return;
 
   game_stream_packet packet{};
@@ -193,15 +193,15 @@ void CVideoStream::AddFrame(const uint8_t* data, unsigned int size, unsigned int
     return;
   }
 
-  m_frontend->AddStreamData(m_stream, packet);
+  m_stream.AddData(packet);
 }
 
 void CVideoStream::RenderHwFrame()
 {
-  if (m_frontend == nullptr)
+  if (m_addon == nullptr)
     return;
 
-  if (m_stream == nullptr || m_streamType != GAME_STREAM_HW_FRAMEBUFFER)
+  if (!m_stream.IsOpen() || m_streamType != GAME_STREAM_HW_FRAMEBUFFER)
     return;
 
   if (!m_framebuffer)
@@ -212,30 +212,29 @@ void CVideoStream::RenderHwFrame()
   packet.type = GAME_STREAM_HW_FRAMEBUFFER;
   packet.hw_framebuffer.framebuffer = m_framebuffer->hw_framebuffer.framebuffer;
 
-  m_frontend->AddStreamData(m_stream, packet);
+  m_stream.AddData(packet);
 }
 
 void CVideoStream::OnFrameEnd()
 {
-  if (m_frontend == nullptr)
+  if (m_addon == nullptr)
     return;
 
-  if (m_stream == nullptr)
+  if (!m_stream.IsOpen())
     return;
 
   if (!m_framebuffer)
     return;
 
-  m_frontend->ReleaseStreamBuffer(m_stream, *m_framebuffer);
+  m_stream.ReleaseBuffer(*m_framebuffer);
   m_framebuffer.reset();
 }
 
 void CVideoStream::CloseStream()
 {
-  if (m_stream != nullptr)
+  if (m_stream.IsOpen())
   {
-    m_frontend->CloseStream(m_stream);
-    m_stream = nullptr;
+    m_stream.Close();
     m_format = GAME_PIXEL_FORMAT_UNKNOWN;
   }
 }
diff --git a/src/video/VideoStream.h b/src/video/VideoStream.h
index 3ecfd383..b878be50 100644
--- a/src/video/VideoStream.h
+++ b/src/video/VideoStream.h
@@ -19,12 +19,12 @@
  */
 #pragma once
 
-#include "kodi_game_types.h"
+#include <kodi/addon-instance/Game.h>
 
 #include <memory>
 #include <stdint.h>
 
-class CHelper_libKODI_game;
+class CGameLibRetro;
 
 namespace LIBRETRO
 {
@@ -35,7 +35,7 @@ namespace LIBRETRO
   public:
     CVideoStream();
 
-    void Initialize(CHelper_libKODI_game* frontend);
+    void Initialize(CGameLibRetro* addon);
     void Deinitialize();
 
     void SetGeometry(const CVideoGeometry &geometry);
@@ -55,10 +55,10 @@ namespace LIBRETRO
     void CloseStream();
 
     // Initialization parameters
-    CHelper_libKODI_game* m_frontend;
+    CGameLibRetro* m_addon;
 
     // Stream properties
-    void *m_stream = nullptr;
+    kodi::addon::CInstanceGame::CStream m_stream;
     std::unique_ptr<CVideoGeometry> m_geometry;
     GAME_STREAM_TYPE m_streamType = GAME_STREAM_UNKNOWN;
     GAME_PIXEL_FORMAT m_format = GAME_PIXEL_FORMAT_UNKNOWN; // Guard against libretro changing formats