mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-08-26 16:53:45 +02:00
Add a log message when loading libretro save state made with incompatible mkxp-z version
This commit is contained in:
parent
e342d6187c
commit
41557aba6e
7 changed files with 168 additions and 2 deletions
|
@ -20,7 +20,8 @@
|
|||
*/
|
||||
|
||||
#include "binding-sandbox.h"
|
||||
#include <cstring>
|
||||
#include "mkxp-polyfill.h" // std::strtol
|
||||
#include <string>
|
||||
#include <libretro.h>
|
||||
#include <zlib.h>
|
||||
#include "sharedstate.h"
|
||||
|
@ -535,6 +536,26 @@ void sandbox_binding_init::operator()() {
|
|||
static VALUE system_module;
|
||||
static VALUE cfg_module;
|
||||
|
||||
struct register_ruby_revision : boost::asio::coroutine {
|
||||
typedef decl_slots<wasm_ptr_t, VALUE, ID> slots;
|
||||
|
||||
void operator()() {
|
||||
BOOST_ASIO_CORO_REENTER (this) {
|
||||
SANDBOX_AWAIT_S(2, rb_intern, "RUBY_REVISION");
|
||||
SANDBOX_AWAIT_S(1, rb_const_get, sb()->rb_cObject(), SANDBOX_SLOT(2));
|
||||
SANDBOX_AWAIT_S(0, rb_string_value_cstr, &SANDBOX_SLOT(1));
|
||||
|
||||
struct sandbox_str_guard str = sb()->str(SANDBOX_SLOT(0));
|
||||
if (std::strlen(str) != 2 * sizeof mkxp_retro::ruby_revision) {
|
||||
std::abort();
|
||||
}
|
||||
for (size_t i = 0; i < sizeof mkxp_retro::ruby_revision; ++i) {
|
||||
mkxp_retro::ruby_revision[i] = std::strtol(std::string(str + 2 * i, 2).c_str(), nullptr, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct register_utf8_encoding : boost::asio::coroutine {
|
||||
typedef decl_slots<ID> slots;
|
||||
|
||||
|
@ -547,6 +568,7 @@ void sandbox_binding_init::operator()() {
|
|||
};
|
||||
|
||||
BOOST_ASIO_CORO_REENTER (this) {
|
||||
SANDBOX_AWAIT(register_ruby_revision);
|
||||
SANDBOX_AWAIT(register_utf8_encoding);
|
||||
SANDBOX_AWAIT(exception_binding_init);
|
||||
|
||||
|
|
70
binding-sandbox/hasher.cpp
Normal file
70
binding-sandbox/hasher.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
** hasher.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 - 2021 Amaryllis Kulla <ancurio@mapleshrine.eu>
|
||||
**
|
||||
** mkxp 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 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <picosha2.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
std::cerr << "[hasher] error: at least one argument must be passed to this program" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
picosha2::hash256_one_by_one digest;
|
||||
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
std::ifstream file(argv[i], std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "[hasher] error: could not open input file " << argv[i] << std::endl;
|
||||
return 2;
|
||||
}
|
||||
std::vector<uint8_t> buffer;
|
||||
for (;;) {
|
||||
uint8_t byte = file.get();
|
||||
if (file.eof()) {
|
||||
break;
|
||||
}
|
||||
buffer.push_back(byte);
|
||||
}
|
||||
digest.process(buffer.begin(), buffer.end());
|
||||
}
|
||||
|
||||
digest.finish();
|
||||
std::vector<uint8_t> bytes(picosha2::k_digest_size);
|
||||
digest.get_hash_bytes(bytes.begin(), bytes.end());
|
||||
|
||||
std::ofstream file(argv[1]);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "[hasher] error: could not open output file " << argv[1] << std::endl;
|
||||
return 3;
|
||||
}
|
||||
file << "#ifndef MKXPZ_BINDING_SANDBOX_HASH" << std::endl;
|
||||
file << "# define MKXPZ_BINDING_SANDBOX_HASH \"" << std::hex;
|
||||
for (uint8_t byte : bytes) {
|
||||
file << "\\x" << (unsigned int)byte;
|
||||
}
|
||||
file << '"' << std::endl << "#endif" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
global_sources += files(
|
||||
binding_sandbox_sources = files(
|
||||
'audio-binding.cpp',
|
||||
'binding-base.cpp',
|
||||
'binding-sandbox.cpp',
|
||||
|
@ -22,3 +22,32 @@ global_sources += files(
|
|||
'window-binding.cpp',
|
||||
'windowvx-binding.cpp',
|
||||
)
|
||||
|
||||
global_sources += binding_sandbox_sources
|
||||
|
||||
binding_sandbox_hasher = executable(
|
||||
'binding-sandbox-hasher',
|
||||
dependencies: subproject('picosha2').get_variable('picosha2'),
|
||||
sources: 'hasher.cpp',
|
||||
native: true,
|
||||
override_options: [
|
||||
'buildtype=release',
|
||||
'b_coverage=false',
|
||||
'b_lto=false',
|
||||
'b_ndebug=false',
|
||||
'b_pgo=off',
|
||||
'b_sanitize=none',
|
||||
'cpp_std=c++11',
|
||||
],
|
||||
)
|
||||
|
||||
global_sources += custom_target(
|
||||
'binding-sandbox-hash',
|
||||
input: binding_sandbox_sources,
|
||||
output: 'binding-sandbox-hash.h',
|
||||
command: [
|
||||
binding_sandbox_hasher,
|
||||
'@OUTPUT@',
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
||||
|
|
36
src/core.cpp
36
src/core.cpp
|
@ -40,6 +40,7 @@
|
|||
|
||||
#include "mkxp-polyfill.h" // std::mutex, std::strtoul
|
||||
#include "git-hash.h"
|
||||
#include "binding-sandbox-hash.h"
|
||||
|
||||
#include "al-util.h"
|
||||
#include "audio.h"
|
||||
|
@ -636,6 +637,8 @@ namespace mkxp_retro {
|
|||
uint8_t midi_chorus_override;
|
||||
uint8_t midi_reverb_override;
|
||||
|
||||
uint8_t ruby_revision[20];
|
||||
|
||||
uint64_t get_ticks_ms() noexcept {
|
||||
return frame_time / 1000;
|
||||
}
|
||||
|
@ -1622,6 +1625,19 @@ extern "C" RETRO_API bool retro_serialize(void *data, size_t len) {
|
|||
// Write 4-byte version: 1
|
||||
if (!sandbox_serialize((uint32_t)1, data, max_size)) return false;
|
||||
|
||||
// Write mkxp-z version
|
||||
if (!sandbox_serialize(MKXPZ_VERSION "/" MKXPZ_GIT_HASH, data, max_size)) return false;
|
||||
|
||||
// Write 20-byte Ruby revision
|
||||
RESERVE(sizeof ruby_revision);
|
||||
std::memcpy(data, ruby_revision, sizeof ruby_revision);
|
||||
ADVANCE(sizeof ruby_revision);
|
||||
|
||||
// Write 32-byte hash of binding-sandbox source files
|
||||
RESERVE(sizeof MKXPZ_BINDING_SANDBOX_HASH - 1);
|
||||
std::memcpy(data, MKXPZ_BINDING_SANDBOX_HASH, sizeof MKXPZ_BINDING_SANDBOX_HASH - 1);
|
||||
ADVANCE(sizeof MKXPZ_BINDING_SANDBOX_HASH - 1);
|
||||
|
||||
// Write the capacity of the VM memory
|
||||
if (!sandbox_serialize(sb()->memory_capacity(), data, max_size)) return false;
|
||||
|
||||
|
@ -1770,6 +1786,26 @@ extern "C" RETRO_API bool retro_unserialize(const void *data, size_t len) {
|
|||
if (version != 1) return false;
|
||||
}
|
||||
|
||||
// Read mkxp-z version that the save state was created by
|
||||
std::string mkxpz_version;
|
||||
if (!sandbox_deserialize(mkxpz_version, data, max_size)) return false;
|
||||
|
||||
// Make sure the Ruby revision matches that of that version of mkxp-z, since save state compatibility breaks when the Ruby version changes
|
||||
RESERVE(sizeof ruby_revision);
|
||||
if (std::memcmp(data, ruby_revision, sizeof ruby_revision)) {
|
||||
log_printf(RETRO_LOG_ERROR, "Failed to load save state because it uses a different Ruby version than the current version of mkxp-z; try using mkxp-z version %s to load this save state instead\n", mkxpz_version.c_str());
|
||||
return false;
|
||||
}
|
||||
ADVANCE(sizeof ruby_revision);
|
||||
|
||||
// Make sure the hash of the binding-sandbox source files matches that of that version of mkxp-z, since save state compatibility breaks when the sandbox bindings are modified
|
||||
RESERVE(sizeof MKXPZ_BINDING_SANDBOX_HASH - 1);
|
||||
if (std::memcmp(data, MKXPZ_BINDING_SANDBOX_HASH, sizeof MKXPZ_BINDING_SANDBOX_HASH - 1)) {
|
||||
log_printf(RETRO_LOG_ERROR, "Failed to load save state because the sandbox bindings used are incompatible with the current version of mkxp-z; try using mkxp-z version %s to load this save state instead\n", mkxpz_version.c_str());
|
||||
return false;
|
||||
}
|
||||
ADVANCE(sizeof MKXPZ_BINDING_SANDBOX_HASH - 1);
|
||||
|
||||
// Read the VM memory
|
||||
{
|
||||
wasm_size_t memory_capacity;
|
||||
|
|
|
@ -50,6 +50,8 @@ namespace mkxp_retro {
|
|||
extern uint8_t midi_chorus_override;
|
||||
extern uint8_t midi_reverb_override;
|
||||
|
||||
extern uint8_t ruby_revision[20];
|
||||
|
||||
uint64_t get_ticks_ms() noexcept;
|
||||
uint64_t get_ticks_us() noexcept;
|
||||
double get_refresh_rate() noexcept;
|
||||
|
|
2
subprojects/packagefiles/picosha2/meson.build
Normal file
2
subprojects/packagefiles/picosha2/meson.build
Normal file
|
@ -0,0 +1,2 @@
|
|||
project('picosha2', 'cpp', meson_version: '>=1.3.0')
|
||||
picosha2 = declare_dependency(include_directories: '.')
|
5
subprojects/picosha2.wrap
Normal file
5
subprojects/picosha2.wrap
Normal file
|
@ -0,0 +1,5 @@
|
|||
[wrap-git]
|
||||
url = https://github.com/okdshin/PicoSHA2
|
||||
revision = v1.0.1
|
||||
depth = 1
|
||||
patch_directory = picosha2
|
Loading…
Add table
Reference in a new issue