summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2021-04-15 09:25:42 -0700
committer Aaron Giles <aaron@aarongiles.com>2021-04-15 09:25:42 -0700
commit2024b61079509b9f1ef692ff3f9e63007a831f6f (patch)
tree7c43de9fb0afc17e74e94272dce8e66dbbfac9b1
parent98e8ed32043d8d47fe63b6565c452dc61182fd02 (diff)
g update:
* Removed old saving mechanism entirely, including internal lists of items. * Removed old logic saving to streams/buffers/files. * Removed old state item iterator; a new mechanism will need to be created for this. Stubbed out debugger and LUA calls to it for now. * Replaced the streams/buffers saves with the new binary save; these are presumed to be 100% internal, so there is no header checking or other associated logic. * Stubbed in incomplete file handling for JSON-based saves; large arrays are identified and referenced as external, though ZIP writing has not been done yet. * Redid JSON generation using an internal buffer and helpers for speed. * Added sorting and pruning of save items after registration. * Added detection of duplicate entries. * Fixed display of long names in save window. * Moved timers into their own container.
-rw-r--r--src/emu/debug/debugcmd.cpp2
-rw-r--r--src/emu/debug/dvmemory.cpp5
-rw-r--r--src/emu/debug/dvsave.cpp9
-rw-r--r--src/emu/device.cpp8
-rw-r--r--src/emu/machine.cpp4
-rw-r--r--src/emu/save.cpp1497
-rw-r--r--src/emu/save.h220
-rw-r--r--src/emu/schedule.cpp7
-rw-r--r--src/emu/schedule.h3
-rw-r--r--src/frontend/mame/luaengine.cpp11
10 files changed, 792 insertions, 974 deletions
diff --git a/src/emu/debug/debugcmd.cpp b/src/emu/debug/debugcmd.cpp
index 33ccb530f20..ec9f9a2d66c 100644
--- a/src/emu/debug/debugcmd.cpp
+++ b/src/emu/debug/debugcmd.cpp
@@ -130,6 +130,7 @@ debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu
symtable.add("cpunum", std::bind(&debugger_commands::get_cpunum, this));
/* add all single-entry save state globals */
+#if 0
for (int itemnum = 0; itemnum < MAX_GLOBALS; itemnum++)
{
void *base;
@@ -153,6 +154,7 @@ debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu
std::bind(&debugger_commands::global_set, this, &m_global_array[itemnum], _1));
}
}
+#endif
/* add all the commands */
m_console.register_command("help", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_help, this, _1, _2));
diff --git a/src/emu/debug/dvmemory.cpp b/src/emu/debug/dvmemory.cpp
index 3ae8008f0a2..cb76055986e 100644
--- a/src/emu/debug/dvmemory.cpp
+++ b/src/emu/debug/dvmemory.cpp
@@ -142,7 +142,6 @@ void debug_view_memory::enumerate_sources()
{
// start with an empty list
m_source_list.clear();
- m_source_list.reserve(machine().save().registration_count());
// first add all the devices' address spaces
for (device_memory_interface &memintf : memory_interface_enumerator(machine().root_device()))
@@ -168,7 +167,7 @@ void debug_view_memory::enumerate_sources()
util::string_format("Region '%s'", region.second->name()),
*region.second.get()));
}
-
+/*
// finally add all global array symbols in ASCII order
std::string name;
std::size_t const firstsave = m_source_list.size();
@@ -190,7 +189,7 @@ void debug_view_memory::enumerate_sources()
// reset the source to a known good entry
if (!m_source_list.empty())
- set_source(*m_source_list[0]);
+ set_source(*m_source_list[0]);*/
}
diff --git a/src/emu/debug/dvsave.cpp b/src/emu/debug/dvsave.cpp
index 526f12cd96f..67fc5707a97 100644
--- a/src/emu/debug/dvsave.cpp
+++ b/src/emu/debug/dvsave.cpp
@@ -193,12 +193,9 @@ void debug_view_save::view_update()
temp[len++] = item.collapsible() ? (item.collapsed() ? '+' : '-') : ' ';
temp[len++] = ' ';
- int namelen = strlen(name);
- if (namelen + len < m_divider)
- {
- memcpy(&temp[len], name, namelen);
- len += namelen;
- }
+ int namelen = std::min<int>(strlen(name), m_divider - 1 - len);
+ memcpy(&temp[len], name, namelen);
+ len += namelen;
while (len < m_divider)
temp[len++] = ' ';
temp[len++] = ' ';
diff --git a/src/emu/device.cpp b/src/emu/device.cpp
index d6298f2c5f2..4f61cd405a2 100644
--- a/src/emu/device.cpp
+++ b/src/emu/device.cpp
@@ -578,9 +578,9 @@ void device_t::start()
intf.interface_pre_start();
// start the device, tracking how many state registrations they did
- int state_registrations = machine().save().registration_count();
+ size_t state_registrations = machine().save().binary_size();
device_start();
- m_save_registrations += machine().save().registration_count() - state_registrations;
+ m_save_registrations += machine().save().binary_size() - state_registrations;
// let the interfaces do their post-work
for (device_interface &intf : interfaces())
@@ -698,12 +698,12 @@ void device_t::register_save(save_registrar &save)
intf.interface_register_save(save);
// then the device itself
- int state_registrations = machine().save().registration_count();
+ int state_registrations = machine().save().binary_size();
device_register_save(save);
// append any unstructured items
save.reg(m_unstructured_save, "unstructured");
- m_save_registrations += machine().save().registration_count() - state_registrations;
+ m_save_registrations += machine().save().binary_size() - state_registrations;
// complain if registrations didn't happen
device_execute_interface *exec;
diff --git a/src/emu/machine.cpp b/src/emu/machine.cpp
index 5034820b70b..2daa9c99999 100644
--- a/src/emu/machine.cpp
+++ b/src/emu/machine.cpp
@@ -295,8 +295,6 @@ void running_machine::start()
schedule_load("auto");
manager().update_machine();
-
- m_save.test_dump();
}
@@ -943,7 +941,7 @@ void running_machine::handle_saveload()
const char *const opnamed = (m_saveload_schedule == saveload_schedule::LOAD) ? "loaded" : "saved";
// read/write the save state
- save_error saverr = (m_saveload_schedule == saveload_schedule::LOAD) ? m_save.read_file(file) : m_save.write_file(file);
+ save_error saverr = (m_saveload_schedule == saveload_schedule::LOAD) ? m_save.load_file(file) : m_save.save_file(file);
// handle the result
switch (saverr)
diff --git a/src/emu/save.cpp b/src/emu/save.cpp
index d0f8c63b856..418d914f2ff 100644
--- a/src/emu/save.cpp
+++ b/src/emu/save.cpp
@@ -54,525 +54,821 @@ enum
#define STATE_MAGIC_NUM "MAMESAVE"
+
//**************************************************************************
-// INITIALIZATION
+// INLINE HELPERS
//**************************************************************************
//-------------------------------------------------
-// save_manager - constructor
+// json_append - append a string to the JSON
+// stream
//-------------------------------------------------
-save_manager::save_manager(running_machine &machine)
- : m_machine(machine)
- , m_reg_allowed(true)
- , m_illegal_regs(0)
- , m_root_registrar(m_root_item)
+save_zip_state &save_zip_state::json_append(char const *buffer)
{
- m_rewind = std::make_unique<rewinder>(*this);
+ json_check_reserve();
+ while (*buffer != 0)
+ json_append(*buffer++);
+ return *this;
}
//-------------------------------------------------
-// allow_registration - allow/disallow
-// registrations to happen
+// json_append_indent - append an indentation of
+// the given depth to the JSON stream
//-------------------------------------------------
-void save_manager::allow_registration(bool allowed)
+save_zip_state &save_zip_state::json_append_indent(int count)
{
- // allow/deny registration
- m_reg_allowed = allowed;
- if (!allowed)
- {
- // look for duplicates
- std::sort(m_entry_list.begin(), m_entry_list.end(),
- [] (std::unique_ptr<state_entry> const& a, std::unique_ptr<state_entry> const& b) { return a->m_name < b->m_name; });
-
- int dupes_found = 0;
- for (int i = 1; i < m_entry_list.size(); i++)
- {
- if (m_entry_list[i - 1]->m_name == m_entry_list[i]->m_name)
- {
- osd_printf_error("Duplicate save state registration entry (%s)\n", m_entry_list[i]->m_name);
- dupes_found++;
- }
- }
+ for (int index = 0; index < count; index++)
+ json_append('\t');
+ return *this;
+}
- if (dupes_found)
- fatalerror("%d duplicate save state entries found.\n", dupes_found);
- dump_registry();
+//-------------------------------------------------
+// json_append_name - append a string-ified name
+// to the JSON stream
+//-------------------------------------------------
- // everything is registered by now, evaluate the savestate size
- m_rewind->clamp_capacity();
- }
+save_zip_state &save_zip_state::json_append_name(char const *name)
+{
+ if (name == nullptr || name[0] == 0)
+ return *this;
+ return json_append('"').json_append(name).json_append('"').json_append(':');
}
//-------------------------------------------------
-// indexed_item - return an item with the given
-// index
+// json_append_signed - append a signed integer
+// value to the JSON stream
//-------------------------------------------------
-const char *save_manager::indexed_item(int index, void *&base, u32 &valsize, u32 &valcount, u32 &blockcount, u32 &stride) const
+save_zip_state &save_zip_state::json_append_signed(int64_t value)
{
- if (index >= m_entry_list.size() || index < 0)
- return nullptr;
+ json_check_reserve();
+
+ // quote values that don't fit into a double
+ bool quote = (int64_t(double(value)) != value);
+ if (quote)
+ json_append('"');
+
+ // just use sprintf -- is there a faster way?
+ char buffer[20];
+ sprintf(buffer, "%lld", value);
+ json_append(buffer);
+
+ // end quotes
+ if (quote)
+ json_append('"');
+ return *this;
+}
- state_entry *entry = m_entry_list.at(index).get();
- base = entry->m_data;
- valsize = entry->m_typesize;
- valcount = entry->m_typecount;
- blockcount = entry->m_blockcount;
- stride = entry->m_stride;
- return entry->m_name.c_str();
+//-------------------------------------------------
+// json_append_unsigned - append an unsigned
+// integer value to the JSON stream
+//-------------------------------------------------
+
+save_zip_state &save_zip_state::json_append_unsigned(uint64_t value)
+{
+ json_check_reserve();
+
+ // quote values that don't fit into a double
+ bool quote = (uint64_t(double(value)) != value);
+ if (quote)
+ json_append('"');
+
+ // just use sprintf -- is there a faster way?
+ char buffer[20];
+ sprintf(buffer, "%llu", value);
+ json_append(buffer);
+
+ // end quotes
+ if (quote)
+ json_append('"');
+ return *this;
}
//-------------------------------------------------
-// register_presave - register a pre-save
-// function callback
+// json_append_float - append a floating-point
+// value to the JSON stream
//-------------------------------------------------
-void save_manager::register_presave(save_prepost_delegate func)
+save_zip_state &save_zip_state::json_append_float(double value)
{
- // check for invalid timing
- if (!m_reg_allowed)
- fatalerror("Attempt to register callback function after state registration is closed!\n");
+ json_check_reserve();
+ char buffer[20];
+ sprintf(buffer, "%g", value);
+ return json_append(buffer);
+}
- // scan for duplicates and push through to the end
- for (auto &cb : m_presave_list)
- if (cb->m_func == func)
- fatalerror("Duplicate save state function (%s/%s)\n", cb->m_func.name(), func.name());
- // allocate a new entry
- m_presave_list.push_back(std::make_unique<state_callback>(func));
-}
+//**************************************************************************
+// SAVE REGISTERED ITEM
+//**************************************************************************
//-------------------------------------------------
-// state_save_register_postload -
-// register a post-load function callback
+// save_registered_item - constructor
//-------------------------------------------------
-void save_manager::register_postload(save_prepost_delegate func)
+save_registered_item::save_registered_item() :
+ m_ptr_offset(0),
+ m_type(TYPE_CONTAINER),
+ m_native_size(0)
{
- // check for invalid timing
- if (!m_reg_allowed)
- fatalerror("Attempt to register callback function after state registration is closed!\n");
-
- // scan for duplicates and push through to the end
- for (auto &cb : m_postload_list)
- if (cb->m_func == func)
- fatalerror("Duplicate save state function (%s/%s)\n", cb->m_func.name(), func.name());
+}
- // allocate a new entry
- m_postload_list.push_back(std::make_unique<state_callback>(func));
+// constructor for a new item
+save_registered_item::save_registered_item(uintptr_t ptr_offset, save_type type, uint32_t native_size, char const *name) :
+ m_ptr_offset(ptr_offset),
+ m_type(type),
+ m_native_size(native_size),
+ m_name(name)
+{
+ // cleanup names a bit
+ if (m_name[0] == '*')
+ m_name.erase(0, 1);
+ if (m_name[0] == 'm' && m_name[1] == '_')
+ m_name.erase(0, 2);
}
//-------------------------------------------------
-// check_file - check if a file is a valid save
-// state
+// append - append a new item to the current one
//-------------------------------------------------
-save_error save_manager::check_file(running_machine &machine, emu_file &file, const char *gamename, void (CLIB_DECL *errormsg)(const char *fmt, ...))
+std::string type_string(save_registered_item::save_type type, uint32_t native_size)
{
- // if we want to validate the signature, compute it
- u32 sig;
- sig = machine.save().signature();
-
- // seek to the beginning and read the header
- file.compress(FCOMPRESS_NONE);
- file.seek(0, SEEK_SET);
- u8 header[HEADER_SIZE];
- if (file.read(header, sizeof(header)) != sizeof(header))
+ switch (type)
{
- if (errormsg != nullptr)
- (*errormsg)("Could not read %s save file header",emulator_info::get_appname());
- return STATERR_READ_ERROR;
+ case save_registered_item::TYPE_CONTAINER: return "CONTAINER";
+ case save_registered_item::TYPE_POINTER: return "POINTER";
+ case save_registered_item::TYPE_UNIQUE: return "UNIQUE";
+ case save_registered_item::TYPE_VECTOR: return "VECTOR";
+ case save_registered_item::TYPE_STRUCT: return "STRUCT";
+ case save_registered_item::TYPE_BOOL: return "BOOL";
+ case save_registered_item::TYPE_INT: return string_format("INT%d", 8 * native_size);
+ case save_registered_item::TYPE_UINT: return string_format("UINT%d", 8 * native_size);
+ case save_registered_item::TYPE_FLOAT: return string_format("FLOAT%d", 8 * native_size);
+ default: return string_format("ARRAY[%d]", int(type));
}
+}
+
+save_registered_item &save_registered_item::append(uintptr_t ptr_offset, save_type type, uint32_t native_size, char const *name)
+{
+ // make sure there are no duplicates
+ if (find(name) != nullptr)
+ throw emu_fatalerror("Duplicate save state registration '%s'\n", name);
- // let the generic header check work out the rest
- return validate_header(header, gamename, sig, errormsg, "");
+//printf("%s '%s': adding %s '%s' @ %llX, size %d\n", type_string(m_type, m_native_size).c_str(), m_name.c_str(), type_string(type, native_size).c_str(), name, ptr_offset, native_size);
+
+ // add the item to the back of the list
+ m_items.emplace_back(ptr_offset, type, native_size, name);
+ return m_items.back();
}
//-------------------------------------------------
-// dispatch_postload - invoke all registered
-// postload callbacks for updates
+// find - find a subitem by name
//-------------------------------------------------
-void save_manager::dispatch_postload()
+save_registered_item *save_registered_item::find(char const *name)
{
- for (auto &func : m_postload_list)
- func->m_func();
+ // blank names can't be found this way
+ if (name[0] == 0)
+ return nullptr;
+
+ // make sure there are no duplicates
+ for (auto &item : m_items)
+ if (strcmp(item.name(), name) == 0)
+ return &item;
+ return nullptr;
}
//-------------------------------------------------
-// dispatch_presave - invoke all registered
-// presave callbacks for updates
+// sort_and_prune - prune empty subitems and
+// sort them by name
//-------------------------------------------------
-void save_manager::dispatch_presave()
+bool save_registered_item::sort_and_prune()
{
- for (auto &func : m_presave_list)
- func->m_func();
+ // only applies to arrays, structs, and containers; don't prune anything else
+ if (m_type >= TYPE_ARRAY && m_type != TYPE_STRUCT && m_type != TYPE_CONTAINER)
+ return false;
+
+ // first prune any empty items
+ for (auto it = m_items.begin(); it != m_items.end(); )
+ {
+ if (it->sort_and_prune())
+ it = m_items.erase(it);
+ else
+ ++it;
+ }
+
+ // then sort the rest if we have more than 1
+ if (m_items.size() > 1)
+ m_items.sort([] (auto const &x, auto const &y) { return (std::strcmp(x.name(), y.name()) < 0); });
+
+ // return true if we have nothing
+ return (m_items.size() == 0);
}
//-------------------------------------------------
-// write_file - writes the data to a file
+// unwrap_and_update_objbase - unwrap trivial
+// type and update the object base
//-------------------------------------------------
-save_error save_manager::write_file(emu_file &file)
+bool save_registered_item::unwrap_and_update_objbase(uintptr_t &objbase) const
{
- return do_write(
- [] (size_t total_size) { return true; },
- [&file] (const void *data, size_t size) { return file.write(data, size) == size; },
- [&file] ()
- {
- file.compress(FCOMPRESS_NONE);
- file.seek(0, SEEK_SET);
- return true;
- },
- [&file] ()
- {
- file.compress(FCOMPRESS_MEDIUM);
- return true;
- });
+ // update the base pointer with our local base/offset
+ objbase += m_ptr_offset;
+
+ // switch off the type
+ switch (m_type)
+ {
+ // unique ptrs retrieve the pointer from their container
+ case TYPE_UNIQUE:
+ objbase = reinterpret_cast<uintptr_t>(reinterpret_cast<generic_unique *>(objbase)->get());
+ return true;
+
+ // vectors retrieve the pointer from their container
+ case TYPE_VECTOR:
+ objbase = reinterpret_cast<uintptr_t>(&(*reinterpret_cast<generic_vector *>(objbase))[0]);
+ return true;
+
+ // pointers just extract the pointer directly
+ case TYPE_POINTER:
+ objbase = reinterpret_cast<uintptr_t>(*reinterpret_cast<generic_pointer *>(objbase));
+ return true;
+
+ // containers are always based at 0
+ case TYPE_CONTAINER:
+ objbase = 0;
+ return false;
+
+ // everything else is as-is
+ default:
+ return false;
+ }
}
//-------------------------------------------------
-// read_file - read the data from a file
+// save_binary - save this item and all owned
+// items into a binary form
//-------------------------------------------------
-save_error save_manager::read_file(emu_file &file)
+uint64_t save_registered_item::save_binary(uint8_t *ptr, uint64_t length, uintptr_t objbase) const
{
- return do_read(
- [] (size_t total_size) { return true; },
- [&file] (void *data, size_t size) { return file.read(data, size) == size; },
- [&file] ()
- {
- file.compress(FCOMPRESS_NONE);
- file.seek(0, SEEK_SET);
- return true;
- },
- [&file] ()
+ // update the base pointer and forward if a trivial unwrap
+ if (unwrap_and_update_objbase(objbase))
+ return m_items.front().save_binary(ptr, length, objbase);
+
+ // switch off the type
+ uint64_t offset = 0;
+ switch (m_type)
+ {
+ // boolean types save as a single byte
+ case TYPE_BOOL:
+ if (offset + 1 <= length)
+ ptr[offset] = *reinterpret_cast<bool const *>(objbase) ? 1 : 0;
+ offset++;
+ break;
+
+ // integral/float types save as their native size
+ case TYPE_INT:
+ case TYPE_UINT:
+ case TYPE_FLOAT:
+ if (offset + m_native_size <= length)
+ memcpy(&ptr[offset], reinterpret_cast<void const *>(objbase), m_native_size);
+ offset += m_native_size;
+ break;
+
+ // structs and containers iterate over owned items
+ case TYPE_CONTAINER:
+ case TYPE_STRUCT:
+ for (auto &item : m_items)
+ offset += item.save_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase);
+ break;
+
+ // arrays are multiples of a single item
+ default:
+ if (m_type < TYPE_ARRAY)
{
- file.compress(FCOMPRESS_MEDIUM);
- return true;
- });
+ auto &item = m_items.front();
+ for (uint32_t rep = 0; rep < m_type; rep++)
+ offset += item.save_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase + rep * m_native_size);
+ }
+ break;
+ }
+ return offset;
}
//-------------------------------------------------
-// write_stream - write the current machine state
-// to an output stream
+// restore_binary - restore this item and all
+// owned items from binary form
//-------------------------------------------------
-save_error save_manager::write_stream(std::ostream &str)
+uint64_t save_registered_item::restore_binary(uint8_t const *ptr, uint64_t length, uintptr_t objbase) const
{
- return do_write(
- [] (size_t total_size) { return true; },
- [&str] (const void *data, size_t size)
+ // update the base pointer and forward if a trivial unwrap
+ if (unwrap_and_update_objbase(objbase))
+ return m_items.front().restore_binary(ptr, length, objbase);
+
+ // switch off the type
+ uint64_t offset = 0;
+ switch (m_type)
+ {
+ // boolean types save as a single byte
+ case TYPE_BOOL:
+ if (offset + 1 <= length)
+ *reinterpret_cast<bool *>(objbase) = (ptr[offset] != 0);
+ offset++;
+ break;
+
+ // integral/float types save as their native size
+ case TYPE_INT:
+ case TYPE_UINT:
+ case TYPE_FLOAT:
+ if (offset + m_native_size <= length)
+ memcpy(reinterpret_cast<void *>(objbase), &ptr[offset], m_native_size);
+ offset += m_native_size;
+ break;
+
+ // structs and containers iterate over owned items
+ case TYPE_CONTAINER:
+ case TYPE_STRUCT:
+ for (auto &item : m_items)
+ offset += item.restore_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase);
+ break;
+
+ // arrays are multiples of a single item
+ default:
+ if (m_type < TYPE_ARRAY)
{
- return bool(str.write(reinterpret_cast<const char *>(data), size));
- },
- [] () { return true; },
- [] () { return true; });
+ auto &item = m_items.front();
+ for (uint32_t rep = 0; rep < m_type; rep++)
+ offset += item.restore_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase + rep * m_native_size);
+ }
+ break;
+ }
+ return offset;
}
//-------------------------------------------------
-// read_stream - restore the machine state from
-// an input stream
+// save_json - save this item into a JSON stream
//-------------------------------------------------
-save_error save_manager::read_stream(std::istream &str)
+void save_registered_item::save_json(save_zip_state &zipstate, char const *nameprefix, int indent, bool inline_form, uintptr_t objbase)
{
- return do_read(
- [] (size_t total_size) { return true; },
- [&str] (void *data, size_t size)
+ // update the base pointer and forward if a trivial unwrap
+ if (unwrap_and_update_objbase(objbase))
+ return m_items.front().save_json(zipstate, nameprefix, indent, inline_form, objbase);
+
+ // update the name prefix
+ std::string localname = nameprefix;
+ if (m_name.length() != 0)
+ {
+ if (localname.length() != 0)
+ localname += ".";
+ localname += m_name;
+ }
+
+ // output the name if present
+ zipstate.json_append_name(m_name.c_str());
+
+ // switch off the type
+ switch (m_type)
+ {
+ // boolean types
+ case TYPE_BOOL:
+ zipstate.json_append(*reinterpret_cast<bool const *>(objbase) ? "true" : "false");
+ break;
+
+ // signed integral types
+ case TYPE_INT:
+ zipstate.json_append_signed(read_int_signed(objbase, m_native_size));
+ break;
+
+ // unsigned integral types
+ case TYPE_UINT:
+ zipstate.json_append_unsigned(read_int_unsigned(objbase, m_native_size));
+ break;
+
+ // float types
+ case TYPE_FLOAT:
+ zipstate.json_append_float(read_float(objbase, m_native_size));
+ break;
+
+ // structs and containers iterate over owned items
+ case TYPE_CONTAINER:
+ case TYPE_STRUCT:
+ if (inline_form || compute_binary_size(objbase - m_ptr_offset) <= 16)
{
- return bool(str.read(reinterpret_cast<char *>(data), size));
- },
- [] () { return true; },
- [] () { return true; });
+ // inline form outputs everything on a single line
+ zipstate.json_append('{');
+ for (auto &item : m_items)
+ {
+ item.save_json(zipstate, localname.c_str(), indent, true, objbase);
+ if (&item != &m_items.back())
+ zipstate.json_append(',');
+ }
+ zipstate.json_append('}');
+ }
+ else
+ {
+ // normal form outputs each item on its own line, indented
+ zipstate.json_append('{').json_append_eol();
+ for (auto &item : m_items)
+ {
+ zipstate.json_append_indent(indent + 1);
+ item.save_json(zipstate, localname.c_str(), indent + 1, false, objbase);
+ if (&item != &m_items.back())
+ zipstate.json_append(',');
+ zipstate.json_append_eol();
+ }
+ zipstate.json_append_indent(indent).json_append('}');
+ }
+ break;
+
+ // arrays are multiples of a single item
+ default:
+ if (m_type < TYPE_ARRAY)
+ {
+ auto &item = m_items.front();
+
+ // look for large arrays of ints/floats
+ save_registered_item *inner = &item;
+ u32 total = count();
+ while (inner->type() < TYPE_ARRAY)
+ {
+ total *= inner->count();
+ inner = &inner->m_items.front();
+ }
+ if ((inner->type() == TYPE_INT || inner->type() == TYPE_UINT || inner->type() == TYPE_FLOAT) && total * inner->m_native_size >= save_zip_state::JSON_EXTERNAL_BINARY_THRESHOLD)
+ {
+ std::string filename = localname;
+ for (int index = 0; index < filename.length(); )
+ if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.", filename[index]) == nullptr)
+ {
+ if (index != 0 && filename[index - 1] != '.')
+ filename[index++] = '.';
+ else
+ filename.erase(index, 1);
+ }
+ else
+ index++;
+
+ zipstate.json_append('[').json_append('{');
+ zipstate.json_append_name("external_file");
+ zipstate.json_append('"').json_append(filename.c_str()).json_append('"').json_append(',');
+ zipstate.json_append_name("unit");
+ zipstate.json_append_signed(inner->m_native_size).json_append(',');
+ zipstate.json_append_name("count");
+ zipstate.json_append_signed(total).json_append(',');
+ zipstate.json_append_name("little_endian");
+ zipstate.json_append((ENDIANNESS_NATIVE == ENDIANNESS_LITTLE) ? "true" : "false");
+ zipstate.json_append('}').json_append(']');
+
+ zipstate.add_data_file(filename.c_str(), item, reinterpret_cast<void *>(objbase));
+ }
+ else
+ {
+ uint32_t item_size = item.compute_binary_size(objbase);
+ if (inline_form || m_type * item_size <= 16)
+ {
+ // strictly inline form outputs everything on a single line
+ zipstate.json_append('[');
+ for (uint32_t rep = 0; rep < m_type; rep++)
+ {
+ item.save_json(zipstate, localname.c_str(), 0, true, objbase + rep * m_native_size);
+ if (rep != m_type - 1)
+ zipstate.json_append(',');
+ }
+ zipstate.json_append(']');
+ }
+ else
+ {
+ // normal form outputs a certain number of items per row
+ zipstate.json_append('[').json_append_eol();
+ uint32_t items_per_row = 0;
+ if (item.m_type == TYPE_INT || item.m_type == TYPE_UINT || item.m_type == TYPE_FLOAT)
+ items_per_row = 32 / item_size;
+ if (items_per_row == 0)
+ items_per_row = 1;
+
+ // iterate over the items
+ for (uint32_t rep = 0; rep < m_type; rep++)
+ {
+ if (rep % items_per_row == 0)
+ zipstate.json_append_indent(indent + 1);
+ item.save_json(zipstate, localname.c_str(), indent + 1, false, objbase + rep * m_native_size);
+ if (rep != m_type - 1)
+ zipstate.json_append(',');
+ if (rep % items_per_row == items_per_row - 1)
+ zipstate.json_append_eol();
+ }
+ if (m_type % items_per_row != 0)
+ zipstate.json_append_eol();
+ zipstate.json_append_indent(indent).json_append(']');
+ }
+ }
+ }
+ break;
+ }
}
//-------------------------------------------------
-// write_buffer - write the current machine state
-// to an allocated buffer
+// read_int_unsigned - read an unsigned integer
+// of the given size
//-------------------------------------------------
-save_error save_manager::write_buffer(void *buf, size_t size)
+uint64_t save_registered_item::read_int_unsigned(uintptr_t objbase, int size)
{
- return do_write(
- [size] (size_t total_size) { return size == total_size; },
- [ptr = reinterpret_cast<u8 *>(buf)] (const void *data, size_t size) mutable
- {
- memcpy(ptr, data, size);
- ptr += size;
- return true;
- },
- [] () { return true; },
- [] () { return true; });
+ switch (size)
+ {
+ case 1: return *reinterpret_cast<uint8_t const *>(objbase);
+ case 2: return *reinterpret_cast<uint16_t const *>(objbase);
+ case 4: return *reinterpret_cast<uint32_t const *>(objbase);
+ case 8: return *reinterpret_cast<uint64_t const *>(objbase);
+ }
+ return 0;
}
//-------------------------------------------------
-// read_buffer - restore the machine state from a
-// buffer
+// read_int_signed - read a signed integer of the
+// given size
//-------------------------------------------------
-save_error save_manager::read_buffer(const void *buf, size_t size)
+int64_t save_registered_item::read_int_signed(uintptr_t objbase, int size)
{
- const u8 *ptr = reinterpret_cast<const u8 *>(buf);
- const u8 *const end = ptr + size;
- return do_read(
- [size] (size_t total_size) { return size == total_size; },
- [&ptr, &end] (void *data, size_t size) -> bool
- {
- if ((ptr + size) > end)
- return false;
- memcpy(data, ptr, size);
- ptr += size;
- return true;
- },
- [] () { return true; },
- [] () { return true; });
+ switch (size)
+ {
+ case 1: return *reinterpret_cast<int8_t const *>(objbase);
+ case 2: return *reinterpret_cast<int16_t const *>(objbase);
+ case 4: return *reinterpret_cast<int32_t const *>(objbase);
+ case 8: return *reinterpret_cast<int64_t const *>(objbase);
+ }
+ return 0;
}
//-------------------------------------------------
-// do_write - serialisation logic
+// read_float - read a floating-point value of the
+// given size
//-------------------------------------------------
-template <typename T, typename U, typename V, typename W>
-inline save_error save_manager::do_write(T check_space, U write_block, V start_header, W start_data)
+double save_registered_item::read_float(uintptr_t objbase, int size)
{
- // if we have illegal registrations, return an error
- if (m_illegal_regs > 0)
- return STATERR_ILLEGAL_REGISTRATIONS;
-
- // check for sufficient space
- size_t total_size = HEADER_SIZE;
- for (const auto &entry : m_entry_list)
- total_size += entry->m_typesize * entry->m_typecount * entry->m_blockcount;
- if (!check_space(total_size))
- return STATERR_WRITE_ERROR;
+ switch (size)
+ {
+ case 4: return *reinterpret_cast<float const *>(objbase);
+ case 8: return *reinterpret_cast<double const *>(objbase);
+ }
+ return 0;
+}
- // 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 and turn on compression for the rest of the file
- if (!start_header() || !write_block(header, sizeof(header)) || !start_data())
- return STATERR_WRITE_ERROR;
- // call the pre-save functions
- dispatch_presave();
+//-------------------------------------------------
+// write_int - write an integer of the given size
+//-------------------------------------------------
- // then write all the data
- for (auto &entry : m_entry_list)
+void save_registered_item::write_int(uintptr_t objbase, int size, uint64_t data)
+{
+ switch (size)
{
- const u32 blocksize = entry->m_typesize * entry->m_typecount;
- const u8 *data = reinterpret_cast<const u8 *>(entry->m_data);
- for (u32 b = 0; entry->m_blockcount > b; ++b, data += entry->m_stride)
- if (!write_block(data, blocksize))
- return STATERR_WRITE_ERROR;
+ case 1: *reinterpret_cast<uint8_t *>(objbase) = uint8_t(data); break;
+ case 2: *reinterpret_cast<uint16_t *>(objbase) = uint16_t(data); break;
+ case 4: *reinterpret_cast<uint32_t *>(objbase) = uint32_t(data); break;
+ case 8: *reinterpret_cast<uint64_t *>(objbase) = uint64_t(data); break;
}
- return STATERR_NONE;
}
//-------------------------------------------------
-// do_read - deserialisation logic
+// write_float - write a floating-point value of
+// the given size
//-------------------------------------------------
-template <typename T, typename U, typename V, typename W>
-inline save_error save_manager::do_read(T check_length, U read_block, V start_header, W start_data)
+void save_registered_item::write_float(uintptr_t objbase, int size, double data)
{
- // if we have illegal registrations, return an error
- if (m_illegal_regs > 0)
- return STATERR_ILLEGAL_REGISTRATIONS;
-
- // check for sufficient space
- size_t total_size = HEADER_SIZE;
- for (const auto &entry : m_entry_list)
- total_size += entry->m_typesize * entry->m_typecount * entry->m_blockcount;
- if (!check_length(total_size))
- return STATERR_READ_ERROR;
+ switch (size)
+ {
+ case 4: *reinterpret_cast<float *>(objbase) = float(data); break;
+ case 8: *reinterpret_cast<double *>(objbase) = double(data); break;
+ }
+}
- // read the header and turn on compression for the rest of the file
- u8 header[HEADER_SIZE];
- if (!start_header() || !read_block(header, sizeof(header)) || !start_data())
- return STATERR_READ_ERROR;
- // 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
- const bool flip = NATIVE_ENDIAN_VALUE_LE_BE((header[9] & SS_MSB_FIRST) != 0, (header[9] & SS_MSB_FIRST) == 0);
+//**************************************************************************
+// SAVE MANAGER
+//**************************************************************************
- // read all the data, flipping if necessary
- for (auto &entry : m_entry_list)
+//-------------------------------------------------
+// save_manager - constructor
+//-------------------------------------------------
+
+save_manager::save_manager(running_machine &machine) :
+ m_machine(machine),
+ m_reg_allowed(true),
+ m_root_registrar(m_root_item)
+{
+ m_rewind = std::make_unique<rewinder>(*this);
+}
+
+
+//-------------------------------------------------
+// allow_registration - allow/disallow
+// registrations to happen
+//-------------------------------------------------
+
+void save_manager::allow_registration(bool allowed)
+{
+ // allow/deny registration
+ m_reg_allowed = allowed;
+ if (!allowed)
{
- const u32 blocksize = entry->m_typesize * entry->m_typecount;
- u8 *data = reinterpret_cast<u8 *>(entry->m_data);
- for (u32 b = 0; entry->m_blockcount > b; ++b, data += entry->m_stride)
- if (!read_block(data, blocksize))
- return STATERR_READ_ERROR;
-
- // handle flipping
- if (flip)
- entry->flip_data();
+ // prune and sort
+ m_root_item.sort_and_prune();
+
+ // dump out a sample JSON
+ {
+ save_zip_state state;
+ m_root_item.save_json(state);
+ printf("%s\n", state.json_string());
+ }
+
+ // everything is registered by now, evaluate the savestate size
+ m_rewind->clamp_capacity();
}
+}
- // call the post-load functions
- dispatch_postload();
- return STATERR_NONE;
+//-------------------------------------------------
+// register_presave - register a pre-save
+// function callback
+//-------------------------------------------------
+
+void save_manager::register_presave(save_prepost_delegate func)
+{
+ // check for invalid timing
+ if (!m_reg_allowed)
+ fatalerror("Attempt to register callback function after state registration is closed!\n");
+
+ // scan for duplicates and push through to the end
+ for (auto &cb : m_presave_list)
+ if (cb->m_func == func)
+ fatalerror("Duplicate save state function (%s/%s)\n", cb->m_func.name(), func.name());
+
+ // allocate a new entry
+ m_presave_list.push_back(std::make_unique<state_callback>(func));
}
//-------------------------------------------------
-// signature - compute the signature, which
-// is a CRC over the structure of the data
+// state_save_register_postload -
+// register a post-load function callback
//-------------------------------------------------
-u32 save_manager::signature() const
+void save_manager::register_postload(save_prepost_delegate func)
{
- // iterate over entries
- u32 crc = 0;
- for (auto &entry : m_entry_list)
- {
- // add the entry name to the CRC
- crc = core_crc32(crc, (u8 *)entry->m_name.c_str(), entry->m_name.length());
-
- // add the type and size to the CRC
- u32 temp[4];
- temp[0] = little_endianize_int32(entry->m_typesize);
- temp[1] = little_endianize_int32(entry->m_typecount);
- temp[2] = little_endianize_int32(entry->m_blockcount);
- temp[3] = little_endianize_int32(entry->m_stride);
- crc = core_crc32(crc, (u8 *)&temp[0], sizeof(temp));
- }
- return crc;
+ // check for invalid timing
+ if (!m_reg_allowed)
+ fatalerror("Attempt to register callback function after state registration is closed!\n");
+
+ // scan for duplicates and push through to the end
+ for (auto &cb : m_postload_list)
+ if (cb->m_func == func)
+ fatalerror("Duplicate save state function (%s/%s)\n", cb->m_func.name(), func.name());
+
+ // allocate a new entry
+ m_postload_list.push_back(std::make_unique<state_callback>(func));
}
//-------------------------------------------------
-// dump_registry - dump the registry to the
-// logfile
+// dispatch_postload - invoke all registered
+// postload callbacks for updates
//-------------------------------------------------
-void save_manager::dump_registry() const
+void save_manager::dispatch_postload()
{
- for (auto &entry : m_entry_list)
- LOG(("%s: %u x %u x %u (%u)\n", entry->m_name.c_str(), entry->m_typesize, entry->m_typecount, entry->m_blockcount, entry->m_stride));
+ for (auto &func : m_postload_list)
+ func->m_func();
}
//-------------------------------------------------
-// validate_header - validate the data in the
-// header
+// dispatch_presave - invoke all registered
+// presave callbacks for updates
//-------------------------------------------------
-save_error save_manager::validate_header(const u8 *header, const char *gamename, u32 signature,
- void (CLIB_DECL *errormsg)(const char *fmt, ...), const char *error_prefix)
+void save_manager::dispatch_presave()
{
- // check magic number
- if (memcmp(header, STATE_MAGIC_NUM, 8))
- {
- if (errormsg != nullptr)
- (*errormsg)("%sThis is not a %s save file", error_prefix,emulator_info::get_appname());
- return STATERR_INVALID_HEADER;
- }
+ for (auto &func : m_presave_list)
+ func->m_func();
+}
- // check save state version
- if (header[8] != SAVE_VERSION)
- {
- if (errormsg != nullptr)
- (*errormsg)("%sWrong version in save file (version %d, expected %d)", error_prefix, header[8], SAVE_VERSION);
- return STATERR_INVALID_HEADER;
- }
- // check gamename, if we were asked to
- if (gamename != nullptr && strncmp(gamename, (const char *)&header[0x0a], 0x1c - 0x0a))
- {
- if (errormsg != nullptr)
- (*errormsg)("%s'File is not a valid savestate file for game '%s'.", error_prefix, gamename);
- return STATERR_INVALID_HEADER;
- }
+//-------------------------------------------------
+// save_binary - invoke all registered presave
+// callbacks for updates and then generate the
+// data in binary form
+//-------------------------------------------------
+
+save_error save_manager::save_binary(void *buf, size_t size)
+{
+ // call the pre-save functions
+ dispatch_presave();
+
+ // write the output
+ u64 finalsize = m_root_item.save_binary(reinterpret_cast<u8 *>(buf), size);
+ if (finalsize != size)
+ return STATERR_WRITE_ERROR;
- // check signature, if we were asked to
- if (signature != 0)
- {
- u32 rawsig = *(u32 *)&header[0x1c];
- if (signature != little_endianize_int32(rawsig))
- {
- if (errormsg != nullptr)
- (*errormsg)("%sIncompatible save file (signature %08x, expected %08x)", error_prefix, little_endianize_int32(rawsig), signature);
- return STATERR_INVALID_HEADER;
- }
- }
return STATERR_NONE;
}
//-------------------------------------------------
-// state_callback - constructor
+// load_binary - restore all data and then call
+// the postload callbacks
//-------------------------------------------------
-save_manager::state_callback::state_callback(save_prepost_delegate callback)
- : m_func(std::move(callback))
+save_error save_manager::load_binary(void *buf, size_t size)
{
+ // read the input
+ u64 finalsize = m_root_item.restore_binary(reinterpret_cast<u8 *>(buf), size);
+ if (finalsize != size)
+ return STATERR_READ_ERROR;
+
+ // call the post-load functions
+ dispatch_postload();
+ return STATERR_NONE;
}
//-------------------------------------------------
-// ram_state - constructor
+// save_file - invoke all registered presave
+// callbacks for updates and then generate the
+// data in JSON/ZIP form
//-------------------------------------------------
-ram_state::ram_state(save_manager &save)
- : m_save(save)
- , m_data()
- , m_valid(false)
- , m_time(m_save.machine().time())
+save_error save_manager::save_file(emu_file &file)
{
- m_data.reserve(get_size(save));
- m_data.clear();
- m_data.rdbuf()->clear();
- m_data.seekp(0);
- m_data.seekg(0);
+ // call the pre-save functions
+ dispatch_presave();
+
+ // create the JSON and target all the output files
+ save_zip_state state;
+ m_root_item.save_json(state);
+
+ // write the output
+ __debugbreak();
+
+ return STATERR_NONE;
}
//-------------------------------------------------
-// get_size - utility function to get the
-// uncompressed size of a state
+// load_file - restore all data and then call
+// the postload callbacks
//-------------------------------------------------
-size_t ram_state::get_size(save_manager &save)
+save_error save_manager::load_file(emu_file &file)
{
- size_t totalsize = 0;
+ __debugbreak();
+
+ // call the post-load functions
+ dispatch_postload();
+ return STATERR_NONE;
+}
+
+
+
+//**************************************************************************
+// RAM STATE
+//**************************************************************************
- for (auto &entry : save.m_entry_list)
- totalsize += entry->m_typesize * entry->m_typecount * entry->m_blockcount;
+//-------------------------------------------------
+// ram_state - constructor
+//-------------------------------------------------
- return totalsize + HEADER_SIZE;
+ram_state::ram_state(save_manager &save) :
+ m_valid(false),
+ m_time(m_save.machine().time()),
+ m_save(save)
+{
}
@@ -585,10 +881,9 @@ save_error ram_state::save()
{
// initialize
m_valid = false;
- m_data.seekp(0);
// get the save manager to write state
- const save_error err = m_save.write_stream(m_data);
+ const save_error err = m_save.save_binary(m_data);
if (err != STATERR_NONE)
return err;
@@ -607,30 +902,28 @@ save_error ram_state::save()
save_error ram_state::load()
{
- // initialize
- m_data.seekg(0);
-
- // if we have illegal registrations, return an error
- if (m_save.m_illegal_regs > 0)
- return STATERR_ILLEGAL_REGISTRATIONS;
-
// get the save manager to load state
- return m_save.read_stream(m_data);
+ return m_save.load_binary(m_data);
}
+
+//**************************************************************************
+// REWINDER
+//**************************************************************************
+
//-------------------------------------------------
// rewinder - constuctor
//-------------------------------------------------
-rewinder::rewinder(save_manager &save)
- : m_save(save)
- , m_enabled(save.machine().options().rewind())
- , m_capacity(save.machine().options().rewind_capacity())
- , m_current_index(REWIND_INDEX_NONE)
- , m_first_invalid_index(REWIND_INDEX_NONE)
- , m_first_time_warning(true)
- , m_first_time_note(true)
+rewinder::rewinder(save_manager &save) :
+ m_save(save),
+ m_enabled(save.machine().options().rewind()),
+ m_capacity(save.machine().options().rewind_capacity()),
+ m_current_index(REWIND_INDEX_NONE),
+ m_first_invalid_index(REWIND_INDEX_NONE),
+ m_first_time_warning(true),
+ m_first_time_note(true)
{
}
@@ -646,7 +939,7 @@ void rewinder::clamp_capacity()
return;
const size_t total = m_capacity * 1024 * 1024;
- const size_t single = ram_state::get_size(m_save);
+ const size_t single = m_save.binary_size();
// can't set below zero, but allow commandline to override options' upper limit
if (total < 0)
@@ -803,7 +1096,7 @@ bool rewinder::check_size()
return false;
// state sizes in bytes
- const size_t singlesize = ram_state::get_size(m_save);
+ const size_t singlesize = m_save.binary_size();
size_t totalsize = m_state_list.size() * singlesize;
// convert our limit from megabytes
@@ -928,508 +1221,22 @@ void rewinder::report_error(save_error error, rewind_operation operation)
}
-//-------------------------------------------------
-// state_entry - constructor
-//-------------------------------------------------
-
-save_manager::state_entry::state_entry(
- void *data,
- std::string &&name, device_t *device, std::string &&module, std::string &&tag, int index,
- u8 size, u32 valcount, u32 blockcount, u32 stride)
- : m_data(data)
- , m_name(std::move(name))
- , m_device(device)
- , m_module(std::move(module))
- , m_tag(std::move(tag))
- , m_index(index)
- , m_typesize(size)
- , m_typecount(valcount)
- , m_blockcount(blockcount)
- , m_stride(stride)
-{
-}
-
-
-//-------------------------------------------------
-// flip_data - reverse the endianness of a
-// block of data
-//-------------------------------------------------
-
-void save_manager::state_entry::flip_data()
-{
- u8 *data = reinterpret_cast<u8 *>(m_data);
- for (u32 b = 0; m_blockcount > b; ++b, data += m_stride)
- {
- u16 *data16;
- u32 *data32;
- u64 *data64;
-
- switch (m_typesize)
- {
- case 2:
- data16 = reinterpret_cast<u16 *>(data);
- for (u32 count = 0; count < m_typecount; count++)
- data16[count] = swapendian_int16(data16[count]);
- break;
-
- case 4:
- data32 = reinterpret_cast<u32 *>(data);
- for (u32 count = 0; count < m_typecount; count++)
- data32[count] = swapendian_int32(data32[count]);
- break;
-
- case 8:
- data64 = reinterpret_cast<u64 *>(data);
- for (u32 count = 0; count < m_typecount; count++)
- data64[count] = swapendian_int64(data64[count]);
- break;
- }
- }
-}
-
-
-//**************************************************************************
-// INITIALIZATION
-//**************************************************************************
-
-//-------------------------------------------------
-// save_registered_item - constructor
-//-------------------------------------------------
-
-save_registered_item::save_registered_item() :
- m_ptr_offset(0),
- m_type(TYPE_CONTAINER),
- m_native_size(0)
-{
-}
-
-// constructor for a new item
-save_registered_item::save_registered_item(uintptr_t ptr_offset, save_type type, uint32_t native_size, char const *name) :
- m_ptr_offset(ptr_offset),
- m_type(type),
- m_native_size(native_size),
- m_name(name)
-{
- // cleanup names a bit
- if (m_name[0] == '*')
- m_name.erase(0, 1);
- if (m_name[0] == 'm' && m_name[1] == '_')
- m_name.erase(0, 2);
-}
-
-
-//-------------------------------------------------
-// append - append a new item to the current one
-//-------------------------------------------------
-
-static std::string type_string(save_registered_item::save_type type, uint32_t native_size)
-{
- switch (type)
- {
- case save_registered_item::TYPE_CONTAINER: return "CONTAINER";
- case save_registered_item::TYPE_POINTER: return "POINTER";
- case save_registered_item::TYPE_UNIQUE: return "UNIQUE";
- case save_registered_item::TYPE_VECTOR: return "VECTOR";
- case save_registered_item::TYPE_STRUCT: return "STRUCT";
- case save_registered_item::TYPE_BOOL: return "BOOL";
- case save_registered_item::TYPE_INT: return string_format("INT%d", 8 * native_size);
- case save_registered_item::TYPE_UINT: return string_format("UINT%d", 8 * native_size);
- case save_registered_item::TYPE_FLOAT: return string_format("FLOAT%d", 8 * native_size);
- default: return string_format("ARRAY[%d]", int(type));
- }
-}
-
-save_registered_item &save_registered_item::append(uintptr_t ptr_offset, save_type type, uint32_t native_size, char const *name)
-{
-printf("%s '%s': adding %s '%s' @ %llX, size %d\n", type_string(m_type, m_native_size).c_str(), m_name.c_str(), type_string(type, native_size).c_str(), name, ptr_offset, native_size);
- m_items.emplace_back(ptr_offset, type, native_size, name);
- return m_items.back();
-}
-
-
-//-------------------------------------------------
-// unwrap_and_update_objbase - unwrap trivial
-// type and update the object base
-//-------------------------------------------------
-
-bool save_registered_item::unwrap_and_update_objbase(uintptr_t &objbase) const
-{
- // update the base pointer with our local base/offset
- objbase += m_ptr_offset;
-
- // switch off the type
- switch (m_type)
- {
- // unique ptrs retrieve the pointer from their container
- case TYPE_UNIQUE:
- objbase = reinterpret_cast<uintptr_t>(reinterpret_cast<generic_unique *>(objbase)->get());
- return true;
-
- // vectors retrieve the pointer from their container
- case TYPE_VECTOR:
- objbase = reinterpret_cast<uintptr_t>(&(*reinterpret_cast<generic_vector *>(objbase))[0]);
- return true;
-
- // pointers just extract the pointer directly
- case TYPE_POINTER:
- objbase = reinterpret_cast<uintptr_t>(*reinterpret_cast<generic_pointer *>(objbase));
- return true;
-
- // containers are always based at 0
- case TYPE_CONTAINER:
- objbase = 0;
- return false;
-
- // everything else is as-is
- default:
- return false;
- }
-}
-
-
-//-------------------------------------------------
-// save_binary - save this item and all owned
-// items into a binary form
-//-------------------------------------------------
-
-uint64_t save_registered_item::save_binary(uint8_t *ptr, uint64_t length, uintptr_t objbase) const
-{
- // update the base pointer and forward if a trivial unwrap
- if (unwrap_and_update_objbase(objbase))
- return m_items.front().save_binary(ptr, length, objbase);
-
- // switch off the type
- uint64_t offset = 0;
- switch (m_type)
- {
- // boolean types save as a single byte
- case TYPE_BOOL:
- if (offset + 1 <= length)
- ptr[offset] = *reinterpret_cast<bool const *>(objbase) ? 1 : 0;
- offset++;
- break;
-
- // integral/float types save as their native size
- case TYPE_INT:
- case TYPE_UINT:
- case TYPE_FLOAT:
- if (offset + m_native_size <= length)
- memcpy(&ptr[offset], reinterpret_cast<void const *>(objbase), m_native_size);
- offset += m_native_size;
- break;
-
- // structs and containers iterate over owned items
- case TYPE_CONTAINER:
- case TYPE_STRUCT:
- for (auto &item : m_items)
- offset += item.save_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase);
- break;
-
- // arrays are multiples of a single item
- default:
- if (m_type < TYPE_ARRAY)
- {
- auto &item = m_items.front();
- for (uint32_t rep = 0; rep < m_type; rep++)
- offset += item.save_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase + rep * m_native_size);
- }
- break;
- }
- return offset;
-}
-
-
-//-------------------------------------------------
-// restore_binary - restore this item and all
-// owned items from binary form
-//-------------------------------------------------
-
-uint64_t save_registered_item::restore_binary(uint8_t const *ptr, uint64_t length, uintptr_t objbase) const
-{
- // update the base pointer and forward if a trivial unwrap
- if (unwrap_and_update_objbase(objbase))
- return m_items.front().restore_binary(ptr, length, objbase);
-
- // switch off the type
- uint64_t offset = 0;
- switch (m_type)
- {
- // boolean types save as a single byte
- case TYPE_BOOL:
- if (offset + 1 <= length)
- *reinterpret_cast<bool *>(objbase) = (ptr[offset] != 0);
- offset++;
- break;
-
- // integral/float types save as their native size
- case TYPE_INT:
- case TYPE_UINT:
- case TYPE_FLOAT:
- if (offset + m_native_size <= length)
- memcpy(reinterpret_cast<void *>(objbase), &ptr[offset], m_native_size);
- offset += m_native_size;
- break;
-
- // structs and containers iterate over owned items
- case TYPE_CONTAINER:
- case TYPE_STRUCT:
- for (auto &item : m_items)
- offset += item.restore_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase);
- break;
-
- // arrays are multiples of a single item
- default:
- if (m_type < TYPE_ARRAY)
- {
- auto &item = m_items.front();
- for (uint32_t rep = 0; rep < m_type; rep++)
- offset += item.restore_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase + rep * m_native_size);
- }
- break;
- }
- return offset;
-}
-
-
-//-------------------------------------------------
-// save_json - save this item into a JSON stream
-//-------------------------------------------------
-
-void save_registered_item::save_json(save_zip_state &zipstate, int indent, bool inline_form, uintptr_t objbase)
-{
- // update the base pointer and forward if a trivial unwrap
- if (unwrap_and_update_objbase(objbase))
- return m_items.front().save_json(zipstate, indent, inline_form, objbase);
-
- // output the name if present
- auto &output = zipstate.json();
- if (m_name.length() > 0)
- output << "\"" << m_name << "\": ";
-
- // switch off the type
- switch (m_type)
- {
- // boolean types
- case TYPE_BOOL:
- output << (*reinterpret_cast<bool const *>(objbase) ? "true" : "false");
- break;
-
- // signed integral types
- case TYPE_INT:
- {
- int64_t value = read_int_signed(objbase, m_native_size);
- char const *quote = (value == int64_t(double(value))) ? "" : "\"";
- output << quote << value << quote;
- break;
- }
-
- // unsigned integral types
- case TYPE_UINT:
- {
- uint64_t value = read_int_unsigned(objbase, m_native_size);
- char const *quote = (value == uint64_t(double(value))) ? "" : "\"";
- output << quote << "0x" << std::setw(m_native_size * 2) << std::setfill('0') << std::hex << value << quote;
- output << std::setw(0) << std::setfill(' ') << std::dec;
- break;
- }
-
- // float types
- case TYPE_FLOAT:
- {
- double value = read_float(objbase, m_native_size);
- output << value;
- break;
- }
-
- // structs and containers iterate over owned items
- case TYPE_CONTAINER:
- case TYPE_STRUCT:
- if (inline_form || compute_binary_size(objbase - m_ptr_offset) <= 16)
- {
- // inline form outputs everything on a single line
- output << "{ ";
- for (auto &item : m_items)
- {
- item.save_json(zipstate, indent, true, objbase);
- if (&item != &m_items.back())
- output << ", ";
- }
- output << " }";
- }
- else
- {
- // normal form outputs each item on its own line, indented
- output << "{" << std::endl;
- for (auto &item : m_items)
- {
- output << std::setw(indent + 1) << std::setfill('\t') << "" << std::setw(0);
- item.save_json(zipstate, indent + 1, false, objbase);
- if (&item != &m_items.back())
- output << ",";
- output << std::endl;
- }
- output << std::setw(indent) << std::setfill('\t') << "" << std::setw(0)
- << "}";
- }
- break;
-
- // arrays are multiples of a single item
- default:
- if (m_type < TYPE_ARRAY)
- {
- auto &item = m_items.front();
- uint32_t item_size = item.compute_binary_size(objbase);
- if (inline_form || m_type * item_size <= 16)
- {
- // strictly inline form outputs everything on a single line
- output << "[ ";
- for (uint32_t rep = 0; rep < m_type; rep++)
- {
- item.save_json(zipstate, 0, true, objbase + rep * m_native_size);
- if (rep != m_type - 1)
- output << ",";
- }
- output << " ]";
- }
- else
- {
- // normal form outputs a certain number of items per row
- output << "[" << std::endl;
- uint32_t items_per_row = 0;
- if (item.m_type == TYPE_INT || item.m_type == TYPE_UINT || item.m_type == TYPE_FLOAT)
- items_per_row = 32 / item_size;
- if (items_per_row == 0)
- items_per_row = 1;
-
- // iterate over the items
- for (uint32_t rep = 0; rep < m_type; rep++)
- {
- if (rep % items_per_row == 0)
- output << std::setw(indent + 1) << std::setfill('\t') << "" << std::setw(0);
- item.save_json(zipstate, indent + 1, false, objbase + rep * m_native_size);
- if (rep != m_type - 1)
- output << ",";
- if (rep % items_per_row == items_per_row - 1)
- output << std::endl;
- }
- if (m_type % items_per_row != 0)
- output << std::endl;
- output << std::setw(indent) << std::setfill('\t') << "" << std::setw(0)
- << "]";
- }
- }
- break;
- }
-}
-
-
-//-------------------------------------------------
-// read_int_unsigned - read an unsigned integer
-// of the given size
-//-------------------------------------------------
-
-uint64_t save_registered_item::read_int_unsigned(uintptr_t objbase, int size)
-{
- switch (size)
- {
- case 1: return *reinterpret_cast<uint8_t const *>(objbase);
- case 2: return *reinterpret_cast<uint16_t const *>(objbase);
- case 4: return *reinterpret_cast<uint32_t const *>(objbase);
- case 8: return *reinterpret_cast<uint64_t const *>(objbase);
- }
- return 0;
-}
-
-
-//-------------------------------------------------
-// read_int_signed - read a signed integer of the
-// given size
-//-------------------------------------------------
-
-int64_t save_registered_item::read_int_signed(uintptr_t objbase, int size)
-{
- switch (size)
- {
- case 1: return *reinterpret_cast<int8_t const *>(objbase);
- case 2: return *reinterpret_cast<int16_t const *>(objbase);
- case 4: return *reinterpret_cast<int32_t const *>(objbase);
- case 8: return *reinterpret_cast<int64_t const *>(objbase);
- }
- return 0;
-}
-
-
-//-------------------------------------------------
-// read_float - read a floating-point value of the
-// given size
-//-------------------------------------------------
-
-double save_registered_item::read_float(uintptr_t objbase, int size)
-{
- switch (size)
- {
- case 4: return *reinterpret_cast<float const *>(objbase);
- case 8: return *reinterpret_cast<double const *>(objbase);
- }
- return 0;
-}
-
-
-//-------------------------------------------------
-// write_int - write an integer of the given size
-//-------------------------------------------------
-
-void save_registered_item::write_int(uintptr_t objbase, int size, uint64_t data)
-{
- switch (size)
- {
- case 1: *reinterpret_cast<uint8_t *>(objbase) = uint8_t(data); break;
- case 2: *reinterpret_cast<uint16_t *>(objbase) = uint16_t(data); break;
- case 4: *reinterpret_cast<uint32_t *>(objbase) = uint32_t(data); break;
- case 8: *reinterpret_cast<uint64_t *>(objbase) = uint64_t(data); break;
- }
-}
-
-
-//-------------------------------------------------
-// write_float - write a floating-point value of
-// the given size
-//-------------------------------------------------
-
-void save_registered_item::write_float(uintptr_t objbase, int size, double data)
-{
- switch (size)
- {
- case 4: *reinterpret_cast<float *>(objbase) = float(data); break;
- case 8: *reinterpret_cast<double *>(objbase) = double(data); break;
- }
-}
-
void save_manager::test_dump()
{
save_zip_state state;
m_root_item.save_json(state);
- printf("%s\n", state.json().str().c_str());
+ printf("%s\n", state.json_string());
}
-save_zip_state::save_zip_state()
+save_zip_state::save_zip_state() :
+ m_json_reserved(0),
+ m_json_offset(0)
{
-}
-
-void save_zip_state::add_data_file(char const *name, void *base, uint32_t size)
-{
- m_file_list.emplace_back(name, base, size);
+ json_check_reserve();
}
void save_zip_state::commit(FILE &output)
{
- add_data_file("save.json", &m_json.str()[0], m_json.str().length());
-}
-
-save_zip_state::file_entry::file_entry(char const *name, void *base, uint32_t size) :
- m_name(name),
- m_base(base),
- m_size(size)
-{
+// add_data_file("save.json", &m_json.str()[0], m_json.str().length());
}
diff --git a/src/emu/save.h b/src/emu/save.h
index bf2654acc84..ad9b2eb6638 100644
--- a/src/emu/save.h
+++ b/src/emu/save.h
@@ -87,37 +87,95 @@ typedef named_delegate<void ()> save_prepost_delegate;
// TYPE DEFINITIONS
//**************************************************************************
+class save_registered_item;
+class ram_state;
+class rewinder;
+
+
+// ======================> save_zip_state
+
+// this class manages the creation of a ZIP file containing a JSON with most of
+// the save data, plus various binary files containing larger chunks of data
class save_zip_state
{
+ // intenral constants
+ static constexpr u32 JSON_EXPAND_CHUNK = 1024 * 1024;
+ static constexpr u32 JSON_EXPAND_THRESH = 1024;
+
public:
+ // the size threshold in bytes above which we will write an external file
+ static constexpr u32 JSON_EXTERNAL_BINARY_THRESHOLD = 4096;
+
+ // construction
save_zip_state();
- std::ostringstream &json() { return m_json; }
+ // simpler getters
+ char const *json_string() { m_json[m_json_offset] = 0; return &m_json[0]; }
+ int json_length() const { return m_json_offset; }
+
+ // append a character to the JSON stream
+ save_zip_state &json_append(char ch) { m_json[m_json_offset++] = ch; return *this; }
- void add_data_file(char const *name, void *base, uint32_t size);
+ // append an end-of-line sequence to the JSON stream
+ save_zip_state &json_append_eol() { return json_append(13).json_append(10); }
+ // additional JSON output helpers
+ save_zip_state &json_append(char const *buffer);
+ save_zip_state &json_append_indent(int count);
+ save_zip_state &json_append_name(char const *name);
+ save_zip_state &json_append_signed(int64_t value);
+ save_zip_state &json_append_unsigned(uint64_t value);
+ save_zip_state &json_append_float(double value);
+
+ // stage an item to be output as raw data
+ void add_data_file(char const *name, save_registered_item &item, void *base)
+ {
+ m_file_list.emplace_back(name, item, base);
+ }
+
+ // commit the results to the given file
void commit(FILE &output);
private:
- struct file_entry
+ // check the reserve; if we're getting close, expand out one more chunk
+ void json_check_reserve()
{
- file_entry(char const *name, void *base, uint32_t size);
+ if (m_json_reserved - m_json_offset < JSON_EXPAND_THRESH)
+ {
+ m_json_reserved += JSON_EXPAND_CHUNK;
+ m_json.resize(m_json_reserved);
+ }
+ }
+ // file_entry represents a single raw data file that will be written
+ struct file_entry
+ {
+ file_entry(char const *name, save_registered_item &item, void *base) : m_item(item), m_name(name), m_base(base) { }
+ save_registered_item &m_item;
std::string m_name;
void *m_base;
- uint32_t m_size;
};
- std::ostringstream m_json;
+
+ // internal state
std::list<file_entry> m_file_list;
+ std::vector<char> m_json;
+ u32 m_json_reserved;
+ u32 m_json_offset;
};
+
+// ======================> save_registered_item
+
+// this class manages a single item node in the hierarchy of registered save items
class save_registered_item
{
+ // generic types used as proxies for extracting pointers
using generic_unique = std::unique_ptr<int> const;
using generic_vector = std::vector<int> const;
using generic_pointer = void * const;
public:
+ // the various types supported
enum save_type : uint32_t
{
TYPE_ARRAY = 0xffff0000, // array is relative, and contains 1 data item that is replicated
@@ -138,7 +196,7 @@ public:
// constructor for a new item
save_registered_item(uintptr_t ptr_offset, save_type type, uint32_t native_size, char const *name);
- // getters
+ // simple getters
char const *name() const { return m_name.c_str(); }
save_type type() const { return m_type; }
std::list<save_registered_item> &subitems() { return m_items; }
@@ -149,6 +207,12 @@ public:
// append a new item to the current one
save_registered_item &append(uintptr_t ptr_offset, save_type type, uint32_t native_size, char const *name);
+ // find an item by name
+ save_registered_item *find(char const *name);
+
+ // sort subitems by name and prune any empty items
+ bool sort_and_prune();
+
// update the object base and unwrap trivial items
bool unwrap_and_update_objbase(uintptr_t &objbase) const;
@@ -162,12 +226,12 @@ public:
uint64_t restore_binary(uint8_t const *ptr, uint64_t length, uintptr_t objbase = 0) const;
// save this item into a JSON stream
- void save_json(save_zip_state &output, int indent = 0, bool inline_form = false, uintptr_t objbase = 0);
+ void save_json(save_zip_state &output, char const *nameprefix = "", int indent = 0, bool inline_form = false, uintptr_t objbase = 0);
// restore this item from a JSON stream
void restore_json(std::istringstream &input, uintptr_t objbase = 0);
- // internal helpers
+ // read/write helpers
uint64_t read_int_unsigned(uintptr_t objbase, int size);
int64_t read_int_signed(uintptr_t objbase, int size);
double read_float(uintptr_t objbase, int size);
@@ -183,6 +247,11 @@ private:
std::string m_name; // name of item
};
+
+// ======================> save_registrar
+
+// this class is the public interface to registration; it contains the heavily
+// templated registration helpers that do the right thing for all supported types
class save_registrar
{
friend class save_manager;
@@ -268,7 +337,7 @@ public:
if (data.get() == nullptr)
throw emu_fatalerror("Passed null pointer to save state registration.");
- save_registrar container(*this, save_registered_item::TYPE_UNIQUE, sizeof(data), "", &data, data.get());
+ save_registrar container(*this, save_registered_item::TYPE_UNIQUE, sizeof(data), name, &data, data.get());
container.reg(*data.get(), name);
return *this;
}
@@ -313,7 +382,7 @@ public:
return *this;
// create an outer container for the vector
- save_registrar container(*this, save_registered_item::TYPE_VECTOR, sizeof(data), "", &data, &data[0]);
+ save_registrar container(*this, save_registered_item::TYPE_VECTOR, sizeof(data), name, &data, &data[0]);
// then an array container within
save_registrar subcontainer(container, save_registered_item::save_type(data.size()), uintptr_t(&data[1]) - uintptr_t(&data[0]), name, &data[0]);
@@ -332,7 +401,7 @@ public:
if (data.get() == nullptr)
throw emu_fatalerror("Passed null pointer to save state registration.");
- save_registrar container(*this, save_registered_item::TYPE_UNIQUE, sizeof(data), "", &data, data.get());
+ save_registrar container(*this, save_registered_item::TYPE_UNIQUE, sizeof(data), name, &data, data.get());
save_registrar subcontainer(container, save_registered_item::save_type(count), uintptr_t(&data[1]) - uintptr_t(&data[0]), name, &data[0]);
subcontainer.reg(data[0], "");
@@ -396,62 +465,10 @@ SAVE_TYPE_AS_UINT(PAIR);
SAVE_TYPE_AS_UINT(PAIR64);
-
-
-class ram_state;
-class rewinder;
+// ======================> save_manager
class save_manager
{
- // stuff for working with arrays
- template <typename T> struct array_unwrap
- {
- using underlying_type = T;
- static constexpr std::size_t SAVE_COUNT = 1U;
- static constexpr std::size_t SIZE = sizeof(underlying_type);
- static underlying_type *ptr(T &value) { return &value; }
- };
- template <typename T, std::size_t N> struct array_unwrap<T [N]>
- {
- using underlying_type = typename array_unwrap<T>::underlying_type;
- static constexpr std::size_t SAVE_COUNT = N * array_unwrap<T>::SAVE_COUNT;
- static constexpr std::size_t SIZE = sizeof(underlying_type);
- static underlying_type *ptr(T (&value)[N]) { return array_unwrap<T>::ptr(value[0]); }
- };
- template <typename T, std::size_t N> struct array_unwrap<std::array<T, N> >
- {
- using underlying_type = typename array_unwrap<T>::underlying_type;
- static constexpr std::size_t SAVE_COUNT = N * array_unwrap<T>::SAVE_COUNT;
- static constexpr std::size_t SIZE = sizeof(underlying_type);
- static underlying_type *ptr(std::array<T, N> &value) { return array_unwrap<T>::ptr(value[0]); }
- };
-
- // set of templates to identify valid save types
- template <typename ItemType> struct is_atom { static constexpr bool value = false; };
- template <typename ItemType> struct is_vector_safe { static constexpr bool value = false; };
-
- class state_entry
- {
- public:
- // construction/destruction
- state_entry(void *data, std::string &&name, device_t *device, std::string &&module, std::string &&tag, int index, u8 size, u32 valcount, u32 blockcount, u32 stride);
-
- // helpers
- void flip_data();
-
- // state
- void * m_data; // pointer to the memory to save/restore
- std::string m_name; // full name
- device_t * m_device; // associated device, nullptr if none
- std::string m_module; // module name
- std::string m_tag; // tag name
- int m_index; // index
- u8 m_typesize; // size of the raw data type
- u32 m_typecount; // number of items in each block
- u32 m_blockcount; // number of blocks of items
- u32 m_stride; // stride between blocks of items in units of item size
- };
-
friend class ram_state;
friend class rewinder;
@@ -462,12 +479,11 @@ public:
// getters
running_machine &machine() const { return m_machine; }
rewinder *rewind() { return m_rewind.get(); }
- int registration_count() const { return m_entry_list.size() + m_root_item.compute_binary_size(); }
bool registration_allowed() const { return m_reg_allowed; }
+ save_registrar &root_registrar() { return m_root_registrar; }
// registration control
void allow_registration(bool allowed = true);
- const char *indexed_item(int index, void *&base, u32 &valsize, u32 &valcount, u32 &blockcount, u32 &stride) const;
// function registration
void register_presave(save_prepost_delegate func);
@@ -477,18 +493,18 @@ public:
void dispatch_presave();
void dispatch_postload();
- // file processing
- 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_stream(std::ostream &str);
- save_error read_stream(std::istream &str);
+ // binary file processing (internal)
+ size_t binary_size() { return m_root_item.compute_binary_size(); }
+ save_error save_binary(void *buf, size_t size);
+ save_error save_binary(std::vector<u8> &buffer) { buffer.resize(binary_size()); return save_binary(&buffer[0], buffer.size()); }
+ save_error load_binary(void *buf, size_t size);
+ save_error load_binary(std::vector<u8> &buffer) { return load_binary(&buffer[0], buffer.size()); }
- save_error write_buffer(void *buf, size_t size);
- save_error read_buffer(const void *buf, size_t size);
+ // disk file processing (external)
+ save_error save_file(emu_file &file);
+ save_error load_file(emu_file &file);
- save_registrar &root_registrar() { return m_root_registrar; }
+ // access to the root regist
void test_dump();
private:
@@ -497,52 +513,54 @@ private:
{
public:
// construction/destruction
- state_callback(save_prepost_delegate callback);
-
+ state_callback(save_prepost_delegate callback) : m_func(std::move(callback)) { }
save_prepost_delegate m_func; // delegate
};
- // internal helpers
- template <typename T, typename U, typename V, typename W>
- save_error do_write(T check_space, U write_block, V start_header, W start_data);
- template <typename T, typename U, typename V, typename W>
- save_error do_read(T check_length, U read_block, V start_header, W start_data);
- u32 signature() const;
- void dump_registry() const;
- static save_error validate_header(const u8 *header, const char *gamename, u32 signature, void (CLIB_DECL *errormsg)(const char *fmt, ...), const char *error_prefix);
-
// internal state
running_machine & m_machine; // reference to our machine
std::unique_ptr<rewinder> m_rewind; // rewinder
bool m_reg_allowed; // are registrations allowed?
- s32 m_illegal_regs; // number of illegal registrations
+ save_registered_item m_root_item; // the root item in the hierarchy
+ save_registrar m_root_registrar; // a registrar for adding to the root item
- save_registered_item m_root_item;
- save_registrar m_root_registrar;
-
- std::vector<std::unique_ptr<state_entry>> m_entry_list; // list of registered entries
std::vector<std::unique_ptr<ram_state>> m_ramstate_list; // list of ram states
std::vector<std::unique_ptr<state_callback>> m_presave_list; // list of pre-save functions
std::vector<std::unique_ptr<state_callback>> m_postload_list; // list of post-load functions
};
+
+// ======================> ram_state
+
class ram_state
{
- save_manager & m_save; // reference to save_manager
- util::vectorstream m_data; // save data buffer
-
public:
bool m_valid; // can we load this state?
attotime m_time; // machine timestamp
ram_state(save_manager &save);
- static size_t get_size(save_manager &save);
save_error save();
save_error load();
+
+private:
+ save_manager & m_save; // reference to save_manager
+ std::vector<u8> m_data; // save data buffer
};
+
+// ======================> rewinder
+
class rewinder
{
+public:
+ rewinder(save_manager &save);
+ bool enabled() { return m_enabled; }
+ void clamp_capacity();
+ void invalidate();
+ bool capture();
+ bool step();
+
+private:
save_manager & m_save; // reference to save_manager
bool m_enabled; // enable rewind savestates
size_t m_capacity; // total memory rewind states can occupy (MB, limited to 1-2048 in options)
@@ -568,14 +586,6 @@ class rewinder
bool check_size();
bool current_index_is_last() { return m_current_index == m_state_list.size() - 1; }
void report_error(save_error type, rewind_operation operation);
-
-public:
- rewinder(save_manager &save);
- bool enabled() { return m_enabled; }
- void clamp_capacity();
- void invalidate();
- bool capture();
- bool step();
};
#endif // MAME_EMU_SAVE_H
diff --git a/src/emu/schedule.cpp b/src/emu/schedule.cpp
index 4f140590560..6d63392123a 100644
--- a/src/emu/schedule.cpp
+++ b/src/emu/schedule.cpp
@@ -263,7 +263,7 @@ void emu_timer::register_save()
}
// save the bits in their own container
- save_registrar container(machine().scheduler().m_scheduler_container, string_format("timer:%s[%d]", name.c_str(), index).c_str());
+ save_registrar container(machine().scheduler().m_timer_registrar, string_format("%s[%d]", name.c_str(), index).c_str());
container.reg(NAME(m_param))
.reg(NAME(m_enabled))
.reg(NAME(m_period))
@@ -339,14 +339,15 @@ device_scheduler::device_scheduler(running_machine &machine) :
m_callback_timer_expire_time(attotime::zero),
m_suspend_changes_pending(true),
m_quantum_minimum(ATTOSECONDS_IN_NSEC(1) / 1000),
- m_scheduler_container(machine.save().root_registrar(), "scheduler")
+ m_scheduler_registrar(machine.save().root_registrar(), "scheduler"),
+ m_timer_registrar(m_scheduler_registrar, "timers")
{
// append a single never-expiring timer so there is always one in the list
m_timer_list = &m_timer_allocator.alloc()->init(machine, timer_expired_delegate(), nullptr, true);
m_timer_list->adjust(attotime::never);
// register global states
- m_scheduler_container.reg(NAME(m_basetime));
+ m_scheduler_registrar.reg(NAME(m_basetime));
machine.save().register_presave(save_prepost_delegate(FUNC(device_scheduler::presave), this));
machine.save().register_postload(save_prepost_delegate(FUNC(device_scheduler::postload), this));
}
diff --git a/src/emu/schedule.h b/src/emu/schedule.h
index a4bdfb985db..7d8d9ff1737 100644
--- a/src/emu/schedule.h
+++ b/src/emu/schedule.h
@@ -190,7 +190,8 @@ private:
attoseconds_t m_quantum_minimum; // duration of minimum quantum
// state saving
- save_registrar m_scheduler_container;
+ save_registrar m_scheduler_registrar;
+ save_registrar m_timer_registrar;
};
diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp
index c7800f70829..0c994752d99 100644
--- a/src/frontend/mame/luaengine.cpp
+++ b/src/frontend/mame/luaengine.cpp
@@ -996,6 +996,7 @@ void lua_engine::initialize()
* item:write(offset, value) - write entry value by index
*/
+/*
auto item_type = emu.new_usertype<save_item>("item", sol::call_constructor, sol::initializers([this](save_item &item, int index) {
if(machine().save().indexed_item(index, item.base, item.size, item.valcount, item.blockcount, item.stride))
{
@@ -1081,7 +1082,7 @@ void lua_engine::initialize()
break;
}
});
-
+*/
/* core_options library
*
@@ -1234,9 +1235,9 @@ void lua_engine::initialize()
// right now it's broken by anonymous timers, synchronize, etc.
lua_State *L = s;
luaL_Buffer buff;
- int size = ram_state::get_size(m.save());
+ size_t size = m.save().binary_size();
u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size);
- save_error error = m.save().write_buffer(ptr, size);
+ save_error error = m.save().save_binary(ptr, size);
if (error == STATERR_NONE)
{
luaL_pushresultsize(&buff, size);
@@ -1250,7 +1251,7 @@ void lua_engine::initialize()
{
// FIXME: this needs to schedule loading from the buffer and return asynchronously somehow
// right now it's broken by anonymous timers, synchronize, etc.
- save_error error = m.save().read_buffer((u8 *)str.data(), str.size());
+ save_error error = m.save().load_binary((u8 *)str.data(), str.size());
if (error == STATERR_NONE)
{
return true;
@@ -1420,6 +1421,7 @@ void lua_engine::initialize()
st_table[s->symbol()] = s.get();
return st_table;
});
+/*
// FIXME: turn into a wrapper - it's stupid slow to walk on every property access
// also, this mixes up things like RAM areas with stuff saved by the device itself, so there's potential for key conflicts
device_type["items"] = sol::property(
@@ -1442,6 +1444,7 @@ void lua_engine::initialize()
}
return table;
});
+ */
// FIXME: this is useless in its current form
device_type["roms"] = sol::property(
[this] (device_t &dev)