From 2d1b881c794286f0e090fbc837db1132c6ea042d Mon Sep 17 00:00:00 2001 From: feos Date: Mon, 24 Feb 2020 19:25:32 +0300 Subject: [PATCH] luaengine: save state to/from binary string buffer (#6354) * luaengine: save state to/from binary string buffer * account for error * luaL_error makes it exit immediately, but explicit return is required by compiler. actual return is nil if it fails. --- src/emu/save.cpp | 103 ++++++++++++++++++++++++++++++++ src/emu/save.h | 3 + src/frontend/mame/luaengine.cpp | 27 +++++++++ 3 files changed, 133 insertions(+) diff --git a/src/emu/save.cpp b/src/emu/save.cpp index 72a164345f2..c345ef3e6ac 100644 --- a/src/emu/save.cpp +++ b/src/emu/save.cpp @@ -340,6 +340,109 @@ save_error save_manager::write_file(emu_file &file) } +//------------------------------------------------- +// save - write the current machine state to the +// allocated stream +//------------------------------------------------- + +save_error save_manager::write_buffer(u8 *data, size_t size) +{ + // if we have illegal registrations, return an error + if (m_illegal_regs > 0) + return STATERR_ILLEGAL_REGISTRATIONS; + + // verify the buffer length + if (size != ram_state::get_size(*this)) + return STATERR_WRITE_ERROR; + + // generate the header + u8 header[HEADER_SIZE]; + memcpy(&header[0], STATE_MAGIC_NUM, 8); + header[8] = SAVE_VERSION; + header[9] = NATIVE_ENDIAN_VALUE_LE_BE(0, SS_MSB_FIRST); + strncpy((char *)&header[0x0a], machine().system().name, 0x1c - 0x0a); + u32 sig = signature(); + *(u32 *)&header[0x1c] = little_endianize_int32(sig); + + // write the header + memcpy(data, header, sizeof(header)); + + // advance the pointer + u8 *byte_ptr = data + sizeof(header); + + // call the pre-save functions + dispatch_presave(); + + // then write all the data + for (auto &entry : m_entry_list) + { + u32 totalsize = entry->m_typesize * entry->m_typecount; + + // check bounds before writing + if (byte_ptr + totalsize > data + size) + return STATERR_WRITE_ERROR; + + memcpy(byte_ptr, entry->m_data, totalsize); + byte_ptr += totalsize; + } + return STATERR_NONE; +} + + +//------------------------------------------------- +// load - restore the machine state from the +// stream +//------------------------------------------------- + +save_error save_manager::read_buffer(u8 *data, size_t size) +{ + // if we have illegal registrations, return an error + if (m_illegal_regs > 0) + return STATERR_ILLEGAL_REGISTRATIONS; + + // verify the buffer length + if (size != ram_state::get_size(*this)) + return STATERR_WRITE_ERROR; + + // read the header + u8 header[HEADER_SIZE]; + memcpy(header, data, sizeof(header)); + + // advance the pointer + u8 *byte_ptr = data + sizeof(header); + + // verify the header and report an error if it doesn't match + u32 sig = signature(); + if (validate_header(header, machine().system().name, sig, nullptr, "Error: ") != STATERR_NONE) + return STATERR_INVALID_HEADER; + + // determine whether or not to flip the data when done + bool flip = NATIVE_ENDIAN_VALUE_LE_BE((header[9] & SS_MSB_FIRST) != 0, (header[9] & SS_MSB_FIRST) == 0); + + // read all the data, flipping if necessary + for (auto &entry : m_entry_list) + { + u32 totalsize = entry->m_typesize * entry->m_typecount; + + // check bounds before reading + if (byte_ptr + totalsize > data + size) + return STATERR_READ_ERROR; + + memcpy(entry->m_data, byte_ptr, totalsize); + byte_ptr += totalsize; + + // handle flipping + if (flip) + entry->flip_data(); + } + + // call the post-load functions + dispatch_postload(); + + return STATERR_NONE; +} + + //------------------------------------------------- // signature - compute the signature, which // is a CRC over the structure of the data diff --git a/src/emu/save.h b/src/emu/save.h index 3cb00ed79c2..53576dd0d0b 100644 --- a/src/emu/save.h +++ b/src/emu/save.h @@ -232,6 +232,9 @@ public: static save_error check_file(running_machine &machine, emu_file &file, const char *gamename, void (CLIB_DECL *errormsg)(const char *fmt, ...)); save_error write_file(emu_file &file); save_error read_file(emu_file &file); + + save_error write_buffer(u8 *data, size_t size); + save_error read_buffer(u8 *data, size_t size); private: // internal helpers diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp index e8fe2f2fcb4..b5405f9722d 100644 --- a/src/frontend/mame/luaengine.cpp +++ b/src/frontend/mame/luaengine.cpp @@ -1279,6 +1279,8 @@ void lua_engine::initialize() * machine:soft_reset() - soft reset emulation * machine:save(filename) - save state to filename * machine:load(filename) - load state from filename + * machine:buffer_save() - return save state buffer as binary string + * machine:buffer_load(str) - load state from binary string buffer. returns true on success, otherwise nil * machine:popmessage(str) - print str as popup * machine:popmessage() - clear displayed popup message * machine:logerror(str) - print str to log @@ -1311,6 +1313,31 @@ void lua_engine::initialize() machine_type.set("soft_reset", &running_machine::schedule_soft_reset); machine_type.set("save", &running_machine::schedule_save); machine_type.set("load", &running_machine::schedule_load); + machine_type.set("buffer_save", [](running_machine &m, sol::this_state s) { + lua_State *L = s; + luaL_Buffer buff; + int size = ram_state::get_size(m.save()); + u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size); + save_error error = m.save().write_buffer(ptr, size); + if (error == STATERR_NONE) + { + luaL_pushresultsize(&buff, size); + return sol::make_reference(L, sol::stack_reference(L, -1)); + } + luaL_error(L, "State save error."); + return sol::make_reference(L, nullptr); + }); + machine_type.set("buffer_load", [](running_machine &m, sol::this_state s, std::string str) { + lua_State *L = s; + save_error error = m.save().read_buffer((u8 *)str.data(), str.size()); + if (error == STATERR_NONE) + return true; + else + { + luaL_error(L,"State load error."); + return false; + } + }); machine_type.set("system", &running_machine::system); machine_type.set("video", &running_machine::video); machine_type.set("sound", &running_machine::sound);