/* * Copyright (C) 2016-2018 Team Kodi * This file is part of Kodi - https://kodi.tv * * SPDX-License-Identifier: GPL-2.0-or-later * See LICENSES/README.md for more information. */ #include "GameClientInGameSaves.h" #include "GameClient.h" #include "GameClientTranslator.h" #include "ServiceBroker.h" #include "filesystem/Directory.h" #include "filesystem/File.h" #include "games/GameServices.h" #include "utils/URIUtils.h" #include "utils/log.h" #include using namespace KODI; using namespace GAME; #define INGAME_SAVES_DIRECTORY "InGameSaves" #define INGAME_SAVES_EXTENSION_SAVE_RAM ".sav" #define INGAME_SAVES_EXTENSION_RTC ".rtc" CGameClientInGameSaves::CGameClientInGameSaves(CGameClient* addon, const AddonInstance_Game* dllStruct) : m_gameClient(addon), m_dllStruct(dllStruct) { assert(m_gameClient != nullptr); assert(m_dllStruct != nullptr); } void CGameClientInGameSaves::Load() { Load(GAME_MEMORY_SAVE_RAM); Load(GAME_MEMORY_RTC); } void CGameClientInGameSaves::Save() { Save(GAME_MEMORY_SAVE_RAM); Save(GAME_MEMORY_RTC); } std::string CGameClientInGameSaves::GetPath(GAME_MEMORY memoryType) { const CGameServices& gameServices = CServiceBroker::GetGameServices(); std::string path = URIUtils::AddFileToFolder(gameServices.GetSavestatesFolder(), INGAME_SAVES_DIRECTORY); if (!XFILE::CDirectory::Exists(path)) XFILE::CDirectory::Create(path); // Append save game filename std::string gamePath = URIUtils::GetFileName(m_gameClient->GetGamePath()); path = URIUtils::AddFileToFolder(path, gamePath.empty() ? m_gameClient->ID() : gamePath); // Append file extension switch (memoryType) { case GAME_MEMORY_SAVE_RAM: return path + INGAME_SAVES_EXTENSION_SAVE_RAM; case GAME_MEMORY_RTC: return path + INGAME_SAVES_EXTENSION_RTC; default: break; } return std::string(); } void CGameClientInGameSaves::Load(GAME_MEMORY memoryType) { uint8_t* gameMemory = nullptr; size_t size = 0; try { m_dllStruct->toAddon->GetMemory(m_dllStruct, memoryType, &gameMemory, &size); } catch (...) { CLog::Log(LOGERROR, "GAME: {}: Exception caught in GetMemory()", m_gameClient->ID()); } const std::string path = GetPath(memoryType); if (size > 0 && XFILE::CFile::Exists(path)) { XFILE::CFile file; if (file.Open(path)) { ssize_t read = file.Read(gameMemory, size); if (read == static_cast(size)) { CLog::Log(LOGINFO, "GAME: In-game saves ({}) loaded from {}", CGameClientTranslator::ToString(memoryType), path); } else { CLog::Log(LOGERROR, "GAME: Failed to read in-game saves ({}): {}/{} bytes read", CGameClientTranslator::ToString(memoryType), read, size); } } else { CLog::Log(LOGERROR, "GAME: Unable to open in-game saves ({}) from file {}", CGameClientTranslator::ToString(memoryType), path); } } else { CLog::Log(LOGDEBUG, "GAME: No in-game saves ({}) to load", CGameClientTranslator::ToString(memoryType)); } } void CGameClientInGameSaves::Save(GAME_MEMORY memoryType) { uint8_t* gameMemory = nullptr; size_t size = 0; try { m_dllStruct->toAddon->GetMemory(m_dllStruct, memoryType, &gameMemory, &size); } catch (...) { CLog::Log(LOGERROR, "GAME: {}: Exception caught in GetMemory()", m_gameClient->ID()); } if (size > 0) { const std::string path = GetPath(memoryType); XFILE::CFile file; if (file.OpenForWrite(path, true)) { ssize_t written = 0; written = file.Write(gameMemory, size); file.Close(); if (written == static_cast(size)) { CLog::Log(LOGINFO, "GAME: In-game saves ({}) written to {}", CGameClientTranslator::ToString(memoryType), path); } else { CLog::Log(LOGERROR, "GAME: Failed to write in-game saves ({}): {}/{} bytes written", CGameClientTranslator::ToString(memoryType), written, size); } } else { CLog::Log(LOGERROR, "GAME: Unable to open in-game saves ({}) from file {}", CGameClientTranslator::ToString(memoryType), path); } } else { CLog::Log(LOGDEBUG, "GAME: No in-game saves ({}) to save", CGameClientTranslator::ToString(memoryType)); } }