1 /* 2 * Copyright (C) 2018 Team Kodi 3 * This file is part of Kodi - https://kodi.tv 4 * 5 * SPDX-License-Identifier: GPL-2.0-or-later 6 * See LICENSES/README.md for more information. 7 */ 8 9 #include "SavestateFlatBuffer.h" 10 11 #include "savestate_generated.h" 12 #include "utils/log.h" 13 14 using namespace KODI; 15 using namespace RETRO; 16 17 namespace 18 { 19 const uint8_t SCHEMA_VERSION = 1; 20 21 /*! 22 * \brief The initial size of the FlatBuffer's memory buffer 23 * 24 * 1024 is the default size in the FlatBuffers header. We might as well use 25 * this until our size requirements are more known. 26 */ 27 const size_t INITIAL_FLATBUFFER_SIZE = 1024; 28 29 /*! 30 * \brief Translate the save type (RetroPlayer to FlatBuffers) 31 */ TranslateType(SAVE_TYPE type)32SaveType TranslateType(SAVE_TYPE type) 33 { 34 switch (type) 35 { 36 case SAVE_TYPE::AUTO: 37 return SaveType_Auto; 38 case SAVE_TYPE::MANUAL: 39 return SaveType_Manual; 40 default: 41 break; 42 } 43 44 return SaveType_Unknown; 45 } 46 47 /*! 48 * \brief Translate the save type (FlatBuffers to RetroPlayer) 49 */ TranslateType(SaveType type)50SAVE_TYPE TranslateType(SaveType type) 51 { 52 switch (type) 53 { 54 case SaveType_Auto: 55 return SAVE_TYPE::AUTO; 56 case SaveType_Manual: 57 return SAVE_TYPE::MANUAL; 58 default: 59 break; 60 } 61 62 return SAVE_TYPE::UNKNOWN; 63 } 64 } // namespace 65 CSavestateFlatBuffer()66CSavestateFlatBuffer::CSavestateFlatBuffer() 67 { 68 Reset(); 69 } 70 71 CSavestateFlatBuffer::~CSavestateFlatBuffer() = default; 72 Reset()73void CSavestateFlatBuffer::Reset() 74 { 75 m_builder.reset(new flatbuffers::FlatBufferBuilder(INITIAL_FLATBUFFER_SIZE)); 76 m_data.clear(); 77 m_savestate = nullptr; 78 } 79 Serialize(const uint8_t * & data,size_t & size) const80bool CSavestateFlatBuffer::Serialize(const uint8_t*& data, size_t& size) const 81 { 82 // Check if savestate was deserialized from vector or built with FlatBuffers 83 if (!m_data.empty()) 84 { 85 data = m_data.data(); 86 size = m_data.size(); 87 } 88 else 89 { 90 data = m_builder->GetBufferPointer(); 91 size = m_builder->GetSize(); 92 } 93 94 return true; 95 } 96 Type() const97SAVE_TYPE CSavestateFlatBuffer::Type() const 98 { 99 if (m_savestate != nullptr) 100 return TranslateType(m_savestate->type()); 101 102 return SAVE_TYPE::UNKNOWN; 103 } 104 SetType(SAVE_TYPE type)105void CSavestateFlatBuffer::SetType(SAVE_TYPE type) 106 { 107 m_type = type; 108 } 109 Slot() const110uint8_t CSavestateFlatBuffer::Slot() const 111 { 112 if (m_savestate != nullptr) 113 return m_savestate->slot(); 114 115 return 0; 116 } 117 SetSlot(uint8_t slot)118void CSavestateFlatBuffer::SetSlot(uint8_t slot) 119 { 120 m_slot = slot; 121 } 122 Label() const123std::string CSavestateFlatBuffer::Label() const 124 { 125 std::string label; 126 127 if (m_savestate != nullptr && m_savestate->label()) 128 label = m_savestate->label()->c_str(); 129 130 return label; 131 } 132 SetLabel(const std::string & label)133void CSavestateFlatBuffer::SetLabel(const std::string& label) 134 { 135 m_labelOffset.reset(new StringOffset{m_builder->CreateString(label)}); 136 } 137 Created() const138CDateTime CSavestateFlatBuffer::Created() const 139 { 140 CDateTime created; 141 142 if (m_savestate != nullptr && m_savestate->created()) 143 created.SetFromRFC1123DateTime(m_savestate->created()->c_str()); 144 145 return created; 146 } 147 SetCreated(const CDateTime & created)148void CSavestateFlatBuffer::SetCreated(const CDateTime& created) 149 { 150 m_createdOffset.reset(new StringOffset{m_builder->CreateString(created.GetAsRFC1123DateTime())}); 151 } 152 GameFileName() const153std::string CSavestateFlatBuffer::GameFileName() const 154 { 155 std::string gameFileName; 156 157 if (m_savestate != nullptr && m_savestate->game_file_name()) 158 gameFileName = m_savestate->game_file_name()->c_str(); 159 160 return gameFileName; 161 } 162 SetGameFileName(const std::string & gameFileName)163void CSavestateFlatBuffer::SetGameFileName(const std::string& gameFileName) 164 { 165 m_gameFileNameOffset.reset(new StringOffset{m_builder->CreateString(gameFileName)}); 166 } 167 TimestampFrames() const168uint64_t CSavestateFlatBuffer::TimestampFrames() const 169 { 170 return m_savestate->timestamp_frames(); 171 } 172 SetTimestampFrames(uint64_t timestampFrames)173void CSavestateFlatBuffer::SetTimestampFrames(uint64_t timestampFrames) 174 { 175 m_timestampFrames = timestampFrames; 176 } 177 TimestampWallClock() const178double CSavestateFlatBuffer::TimestampWallClock() const 179 { 180 if (m_savestate != nullptr) 181 return static_cast<double>(m_savestate->timestamp_wall_clock_ns()) / 1000.0 / 1000.0 / 1000.0; 182 183 return 0.0; 184 } 185 SetTimestampWallClock(double timestampWallClock)186void CSavestateFlatBuffer::SetTimestampWallClock(double timestampWallClock) 187 { 188 m_timestampWallClock = timestampWallClock; 189 } 190 GameClientID() const191std::string CSavestateFlatBuffer::GameClientID() const 192 { 193 std::string gameClientId; 194 195 if (m_savestate != nullptr && m_savestate->emulator_addon_id()) 196 gameClientId = m_savestate->emulator_addon_id()->c_str(); 197 198 return gameClientId; 199 } 200 SetGameClientID(const std::string & gameClientId)201void CSavestateFlatBuffer::SetGameClientID(const std::string& gameClientId) 202 { 203 m_emulatorAddonIdOffset.reset(new StringOffset{m_builder->CreateString(gameClientId)}); 204 } 205 GameClientVersion() const206std::string CSavestateFlatBuffer::GameClientVersion() const 207 { 208 std::string gameClientVersion; 209 210 if (m_savestate != nullptr && m_savestate->emulator_version()) 211 gameClientVersion = m_savestate->emulator_version()->c_str(); 212 213 return gameClientVersion; 214 } 215 SetGameClientVersion(const std::string & gameClientVersion)216void CSavestateFlatBuffer::SetGameClientVersion(const std::string& gameClientVersion) 217 { 218 m_emulatorVersionOffset.reset(new StringOffset{m_builder->CreateString(gameClientVersion)}); 219 } 220 GetMemoryData() const221const uint8_t* CSavestateFlatBuffer::GetMemoryData() const 222 { 223 if (m_savestate != nullptr && m_savestate->memory_data()) 224 return m_savestate->memory_data()->data(); 225 226 return nullptr; 227 } 228 GetMemorySize() const229size_t CSavestateFlatBuffer::GetMemorySize() const 230 { 231 if (m_savestate != nullptr && m_savestate->memory_data()) 232 return m_savestate->memory_data()->size(); 233 234 return 0; 235 } 236 GetMemoryBuffer(size_t size)237uint8_t* CSavestateFlatBuffer::GetMemoryBuffer(size_t size) 238 { 239 uint8_t* memoryBuffer = nullptr; 240 241 m_memoryDataOffset.reset( 242 new VectorOffset{m_builder->CreateUninitializedVector(size, &memoryBuffer)}); 243 244 return memoryBuffer; 245 } 246 Finalize()247void CSavestateFlatBuffer::Finalize() 248 { 249 // Helper class to build the nested Savestate table 250 SavestateBuilder savestateBuilder(*m_builder); 251 252 savestateBuilder.add_version(SCHEMA_VERSION); 253 254 savestateBuilder.add_type(TranslateType(m_type)); 255 256 savestateBuilder.add_slot(m_slot); 257 258 if (m_labelOffset) 259 { 260 savestateBuilder.add_label(*m_labelOffset); 261 m_labelOffset.reset(); 262 } 263 264 if (m_createdOffset) 265 { 266 savestateBuilder.add_created(*m_createdOffset); 267 m_createdOffset.reset(); 268 } 269 270 if (m_gameFileNameOffset) 271 { 272 savestateBuilder.add_game_file_name(*m_gameFileNameOffset); 273 m_gameFileNameOffset.reset(); 274 } 275 276 savestateBuilder.add_timestamp_frames(m_timestampFrames); 277 278 const uint64_t wallClockNs = 279 static_cast<uint64_t>(m_timestampWallClock * 1000.0 * 1000.0 * 1000.0); 280 savestateBuilder.add_timestamp_wall_clock_ns(wallClockNs); 281 282 if (m_emulatorAddonIdOffset) 283 { 284 savestateBuilder.add_emulator_addon_id(*m_emulatorAddonIdOffset); 285 m_emulatorAddonIdOffset.reset(); 286 } 287 288 if (m_emulatorVersionOffset) 289 { 290 savestateBuilder.add_emulator_version(*m_emulatorVersionOffset); 291 m_emulatorVersionOffset.reset(); 292 } 293 294 if (m_memoryDataOffset) 295 { 296 savestateBuilder.add_memory_data(*m_memoryDataOffset); 297 m_memoryDataOffset.reset(); 298 } 299 300 auto savestate = savestateBuilder.Finish(); 301 FinishSavestateBuffer(*m_builder, savestate); 302 303 m_savestate = GetSavestate(m_builder->GetBufferPointer()); 304 } 305 Deserialize(std::vector<uint8_t> data)306bool CSavestateFlatBuffer::Deserialize(std::vector<uint8_t> data) 307 { 308 flatbuffers::Verifier verifier(data.data(), data.size()); 309 if (VerifySavestateBuffer(verifier)) 310 { 311 const Savestate* savestate = GetSavestate(data.data()); 312 313 if (savestate->version() != SCHEMA_VERSION) 314 { 315 CLog::Log(LOGERROR, "RetroPlayer[SAVE): Schema version %u not supported, must be version %u", 316 savestate->version(), SCHEMA_VERSION); 317 } 318 else 319 { 320 m_data = std::move(data); 321 m_savestate = GetSavestate(m_data.data()); 322 return true; 323 } 324 } 325 326 return false; 327 } 328