summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2021-04-12 01:31:31 -0700
committer Aaron Giles <aaron@aarongiles.com>2021-04-12 01:31:31 -0700
commitf65453c2321f150a40e995ce4f2cef1355dae654 (patch)
treeb008c312ebfa00493a697fe0f398de8ab6adc0fc
parent902cce39106ed1f7c181c46c491f2c9e3b9a199d (diff)
Added save state window to debugger. Centralized unique/vector/pointer unwrapping.
-rw-r--r--src/emu/debug/debugvw.cpp4
-rw-r--r--src/emu/debug/debugvw.h1
-rw-r--r--src/emu/debug/dvsave.cpp449
-rw-r--r--src/emu/debug/dvsave.h104
-rw-r--r--src/emu/device.cpp13
-rw-r--r--src/emu/device.h17
-rw-r--r--src/emu/machine.cpp3
-rw-r--r--src/emu/save.cpp165
-rw-r--r--src/emu/save.h53
-rw-r--r--src/osd/modules/debugger/debugwin.cpp2
-rw-r--r--src/osd/modules/debugger/win/debugwin.h1
-rw-r--r--src/osd/modules/debugger/win/debugwininfo.cpp5
-rw-r--r--src/osd/modules/debugger/win/debugwininfo.h1
-rw-r--r--src/osd/modules/debugger/win/saveviewinfo.cpp29
-rw-r--r--src/osd/modules/debugger/win/saveviewinfo.h27
-rw-r--r--src/osd/modules/debugger/win/savewininfo.cpp58
-rw-r--r--src/osd/modules/debugger/win/savewininfo.h28
17 files changed, 887 insertions, 73 deletions
diff --git a/src/emu/debug/debugvw.cpp b/src/emu/debug/debugvw.cpp
index 120152b3d5f..a0cdcdfb94e 100644
--- a/src/emu/debug/debugvw.cpp
+++ b/src/emu/debug/debugvw.cpp
@@ -12,6 +12,7 @@
#include "express.h"
#include "debugvw.h"
#include "dvtext.h"
+#include "dvsave.h"
#include "dvstate.h"
#include "dvdisasm.h"
#include "dvmemory.h"
@@ -347,6 +348,9 @@ debug_view *debug_view_manager::alloc_view(debug_view_type type, debug_view_osd_
case DVT_STATE:
return append(new debug_view_state(machine(), osdupdate, osdprivate));
+ case DVT_SAVE:
+ return append(new debug_view_save(machine(), osdupdate, osdprivate));
+
case DVT_DISASSEMBLY:
return append(new debug_view_disasm(machine(), osdupdate, osdprivate));
diff --git a/src/emu/debug/debugvw.h b/src/emu/debug/debugvw.h
index 91c080bf07f..63c5ff94556 100644
--- a/src/emu/debug/debugvw.h
+++ b/src/emu/debug/debugvw.h
@@ -30,6 +30,7 @@ enum debug_view_type
DVT_NONE,
DVT_CONSOLE,
DVT_STATE,
+ DVT_SAVE,
DVT_DISASSEMBLY,
DVT_MEMORY,
DVT_LOG,
diff --git a/src/emu/debug/dvsave.cpp b/src/emu/debug/dvsave.cpp
new file mode 100644
index 00000000000..526f12cd96f
--- /dev/null
+++ b/src/emu/debug/dvsave.cpp
@@ -0,0 +1,449 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/*********************************************************************
+
+ dvsave.cpp
+
+ Save debugger view.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "dvsave.h"
+
+#include "debugvw.h"
+
+#include "screen.h"
+
+
+//**************************************************************************
+// DEBUG VIEW STATE
+//**************************************************************************
+
+//-------------------------------------------------
+// debug_view_save - constructor
+//-------------------------------------------------
+
+debug_view_save::debug_view_save(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate)
+ : debug_view(machine, DVT_SAVE, osdupdate, osdprivate)
+ , m_divider(40)
+{
+}
+
+
+//-------------------------------------------------
+// ~debug_view_save - destructor
+//-------------------------------------------------
+
+debug_view_save::~debug_view_save()
+{
+ reset();
+}
+
+
+//-------------------------------------------------
+// reset - delete all of our state items
+//-------------------------------------------------
+
+void debug_view_save::reset()
+{
+ // free all items in the state list
+ m_save_list.clear();
+}
+
+
+//-------------------------------------------------
+// recompute - recompute all info for the
+// registers view
+//-------------------------------------------------
+
+void debug_view_save::build_list_recursive(save_registered_item &item, uintptr_t objbase, int depth, int count)
+{
+ // update the base pointer and forward if a trivial unwrap
+ if (item.unwrap_and_update_objbase(objbase))
+ return build_list_recursive(item.subitems().front(), objbase, depth, count);
+
+ // switch off the type
+ switch (item.type())
+ {
+ // boolean types save as a single byte
+ case save_registered_item::TYPE_BOOL:
+ case save_registered_item::TYPE_INT:
+ case save_registered_item::TYPE_UINT:
+ case save_registered_item::TYPE_FLOAT:
+ m_save_list.push_back(save_item(item, objbase, depth, count));
+ break;
+
+ // structs and containers iterate over owned items
+ case save_registered_item::TYPE_CONTAINER:
+ case save_registered_item::TYPE_STRUCT:
+ if (item.subitems().size() == 0)
+ break;
+ m_save_list.push_back(save_item(item, objbase, depth, count));
+ for (auto &subitem : item.subitems())
+ build_list_recursive(subitem, objbase, depth + 1);
+ break;
+
+ // arrays are multiples of a single item
+ default:
+ if (m_type < save_registered_item::TYPE_ARRAY)
+ {
+ m_save_list.push_back(save_item(item, objbase, depth, count));
+ auto &subitem = item.subitems().front();
+ int items_per_row = 1;
+ if (subitem.type() == save_registered_item::TYPE_BOOL)
+ items_per_row = 32;
+ else if (subitem.type() == save_registered_item::TYPE_INT || subitem.type() == save_registered_item::TYPE_UINT)
+ {
+ if (subitem.native_size() <= 2)
+ items_per_row = 16 / subitem.native_size();
+ else
+ items_per_row = 32 / subitem.native_size();
+ }
+ else if (subitem.type() == save_registered_item::TYPE_FLOAT)
+ items_per_row = 4;
+ for (uint32_t rep = 0; rep < item.count(); rep += items_per_row)
+ build_list_recursive(subitem, objbase + rep * item.native_size(), depth + 1, std::min<int>(items_per_row, item.count() - rep));
+ }
+ break;
+ }
+}
+
+void debug_view_save::recompute()
+{
+ // start with a blank list
+ reset();
+
+ // build the list of items
+ build_list_recursive(machine().save().root_registrar().parent_item(), 0, 0);
+ m_save_list[0].set_collapse(false);
+ for (auto &item : m_save_list)
+ item.update_value();
+
+ // no longer need to recompute
+ m_recompute = false;
+}
+
+
+//-------------------------------------------------
+// view_notify - handle notification of updates
+// to cursor changes
+//-------------------------------------------------
+
+void debug_view_save::view_notify(debug_view_notification type)
+{
+ if (type == VIEW_NOTIFY_SOURCE_CHANGED)
+ m_recompute = true;
+}
+
+
+//-------------------------------------------------
+// view_update - update the contents of the
+// register view
+//-------------------------------------------------
+
+void debug_view_save::view_update()
+{
+ // if our assumptions changed, revisit them
+ if (m_recompute)
+ recompute();
+
+ // update the console info
+ m_total.x = m_divider + 90;
+ m_total.y = m_save_list.size();
+
+ // loop over rows
+ m_total.y = 0;
+ int collapse_depth = 100;
+ int depth_index[100];
+ debug_view_char *dest(&m_viewdata[0]);
+ std::string tempstr;
+ for (auto &item : m_save_list)
+ {
+ if (item.depth() > collapse_depth)
+ continue;
+ if (item.depth() == collapse_depth)
+ collapse_depth = 100;
+ if (item.depth() < collapse_depth)
+ {
+ if (m_total.y >= m_topleft.y && m_total.y < m_topleft.y + m_visible.y)
+ {
+ // get the name
+ char const *name = item.name();
+ if (name[0] == 0)
+ {
+ if (item.depth() == 0)
+ tempstr = "(root)";
+ else if (item.count() > 1)
+ tempstr = string_format("[%d-%d]", depth_index[item.depth()], depth_index[item.depth()] + item.count() - 1);
+ else
+ tempstr = string_format("[%d]", depth_index[item.depth()]);
+ name = tempstr.c_str();
+ }
+ std::string value = item.value();
+
+ // see if we changed
+ const u8 attrib(item.changed() ? DCA_CHANGED : DCA_NORMAL);
+
+ // build up a string
+ char temp[256];
+ int len = 0;
+ for (int index = 0; index < item.depth(); index++)
+ temp[len++] = temp[len++] = ' ';
+ temp[len++] = item.collapsible() ? (item.collapsed() ? '+' : '-') : ' ';
+ temp[len++] = ' ';
+
+ int namelen = strlen(name);
+ if (namelen + len < m_divider)
+ {
+ memcpy(&temp[len], name, namelen);
+ len += namelen;
+ }
+ while (len < m_divider)
+ temp[len++] = ' ';
+ temp[len++] = ' ';
+
+ int valuelen = std::min(value.length(), sizeof(temp) - m_divider - 4);
+ memcpy(&temp[len], value.c_str(), valuelen);
+ len += valuelen;
+
+ if (len < sizeof(temp) - 1)
+ memset(&temp[len], ' ', sizeof(temp) - 1 - len);
+ temp[sizeof(temp) - 1] = 0;
+
+ // copy data
+ int col = 0;
+ for (u32 effcol = m_topleft.x; (col < m_visible.x) && (effcol < len); ++dest, ++col)
+ {
+ dest->byte = temp[effcol++];
+ dest->attrib = attrib | ((effcol <= m_divider) ? DCA_ANCILLARY : DCA_NORMAL);
+ }
+
+ // fill the rest with blanks
+ while (col < m_visible.x)
+ {
+ dest->byte = ' ';
+ dest->attrib = DCA_NORMAL;
+ dest++;
+ col++;
+ }
+ }
+ m_total.y++;
+ if (item.collapsed())
+ collapse_depth = item.depth();
+
+ // reset the depth index if this is an array
+ depth_index[item.depth() + 1] = 0;
+ depth_index[item.depth()] += item.count();
+ }
+ }
+ for (int y = m_total.y; y < m_topleft.y + m_visible.y; y++)
+ {
+ for (int col = 0; col < m_visible.x; col++)
+ {
+ dest->byte = ' ';
+ dest->attrib = DCA_NORMAL;
+ dest++;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// view_click - handle a mouse click within the
+// current view
+//-------------------------------------------------
+
+void debug_view_save::view_click(const int button, const debug_view_xy& pos)
+{
+ int cury = 0;
+ int collapse_depth = 100;
+ for (auto &item : m_save_list)
+ {
+ if (item.depth() > collapse_depth)
+ continue;
+ if (item.depth() == collapse_depth)
+ collapse_depth = 100;
+ if (item.depth() < collapse_depth)
+ {
+ if (pos.y == cury)
+ {
+ if (!item.collapsible())
+ break;
+ item.set_collapse(!item.collapsed());
+ begin_update();
+ view_notify(VIEW_NOTIFY_VISIBLE_CHANGED);
+ m_update_pending = true;
+ end_update();
+ break;
+ }
+ cury++;
+ if (item.collapsed())
+ collapse_depth = item.depth();
+ }
+ }
+}
+
+
+
+std::string debug_view_save::save_item::value()
+{
+ // pre-subtract the item's ptr_offset as it will get re-applied at unwrap time
+ return value(m_item, m_count, m_objbase - m_item.ptr_offset(), m_collapsed);
+}
+
+
+std::string debug_view_save::save_item::value(save_registered_item &item, int count, uintptr_t objbase, bool collapsed)
+{
+ // update the base pointer and forward if a trivial unwrap
+ if (item.unwrap_and_update_objbase(objbase))
+ return value(item.subitems().front(), 0, objbase, collapsed);
+
+ char tempbuf[256];
+ tempbuf[0] = 0;
+ int pos = 0;
+
+ switch (item.type())
+ {
+ // boolean types
+ case save_registered_item::TYPE_BOOL:
+ if (count == 0)
+ return *reinterpret_cast<bool const *>(objbase) ? "true" : "false";
+ else
+ {
+ bool const *ptr = reinterpret_cast<bool const *>(objbase);
+ for (int index = 0; index < count; index++)
+ catprintf(tempbuf, pos, "%c ", *ptr++ ? 'T' : 'F');
+ return tempbuf;
+ }
+
+ // signed integral types
+ case save_registered_item::TYPE_INT:
+ if (count == 0)
+ catprintf(tempbuf, pos, "%lld", item.read_int_signed(objbase, item.native_size()));
+ else
+ {
+ static u8 const s_width[] = { 4,4,6,6,11,11,11,11,20,20,20,20,20,20,20,20 };
+ int size = item.native_size() & 15;
+ for (int index = 0; index < count; index++)
+ catprintf(tempbuf, pos, "%*lld ", collapsed ? 0 : s_width[size], item.read_int_signed(objbase + size * index, size));
+ }
+ return tempbuf;
+
+ // unsigned integral types
+ case save_registered_item::TYPE_UINT:
+ if (count == 0)
+ catprintf(tempbuf, pos, "0x%0*llX", collapsed ? 0 : 2 * item.native_size(), item.read_int_unsigned(objbase, item.native_size()));
+ else
+ {
+ int size = item.native_size() & 15;
+ for (int index = 0; index < count; index++)
+ catprintf(tempbuf, pos, "0x%0*llX ", collapsed ? 0 : 2 * size, item.read_int_unsigned(objbase + size * index, size));
+ }
+ return tempbuf;
+
+ // float types
+ case save_registered_item::TYPE_FLOAT:
+ if (count == 0)
+ catprintf(tempbuf, pos, "%g", item.read_float(objbase, item.native_size()));
+ else
+ {
+ int size = item.native_size() & 15;
+ for (int index = 0; index < count; index++)
+ catprintf(tempbuf, pos, "%*g ", collapsed ? 0 : 20, item.read_float(objbase + size * index, size));
+ }
+ return tempbuf;
+
+ // structs and containers iterate over owned items
+ case save_registered_item::TYPE_CONTAINER:
+ case save_registered_item::TYPE_STRUCT:
+ if (!collapsed || item.subitems().size() == 0)
+ return "";
+ else
+ {
+ catprintf(tempbuf, pos, "{");
+ bool first = true;
+ for (auto &subitem : item.subitems())
+ {
+ std::string val = value(subitem, 0, objbase, true);
+ if (val[0] == 0)
+ continue;
+ char const *name = subitem.name();
+ if (name[0] == 0)
+ name = subitem.subitems().front().name();
+ if (!catprintf(tempbuf, pos, "%s%s:%s", first ? "" : ",", name, val.c_str()))
+ break;
+ first = false;
+ }
+ catprintf(tempbuf, pos, "}");
+ return tempbuf;
+ }
+
+ // arrays are multiples of a single item
+ default:
+ if (item.type() < save_registered_item::TYPE_ARRAY)
+ {
+ if (!collapsed)
+ catprintf(tempbuf, pos, "[%d]", item.count());
+ else
+ {
+ catprintf(tempbuf, pos, "[");
+ auto &subitem = item.subitems().front();
+ for (int num = 0; num < item.count(); num++)
+ {
+ std::string val = value(subitem, 0, objbase + num * item.native_size(), true);
+ if (!catprintf(tempbuf, pos, "%s%s", (num == 0) ? "" : ",", val.c_str()))
+ break;
+ }
+ catprintf(tempbuf, pos, "]");
+ }
+ return tempbuf;
+ }
+ return "(unknown)";
+ }
+}
+
+
+bool debug_view_save::save_item::changed() const
+{
+ switch (m_item.type())
+ {
+ case save_registered_item::TYPE_BOOL:
+ case save_registered_item::TYPE_INT:
+ case save_registered_item::TYPE_UINT:
+ case save_registered_item::TYPE_FLOAT:
+ return (memcmp(reinterpret_cast<void const *>(m_objbase), m_prev_value, m_item.native_size()) != 0);
+
+ // structs and containers iterate over owned items
+ case save_registered_item::TYPE_CONTAINER:
+ case save_registered_item::TYPE_STRUCT:
+ return false;
+
+ // arrays are multiples of a single item
+ default:
+ return false;
+ }
+}
+
+
+void debug_view_save::save_item::update_value()
+{
+ switch (m_item.type())
+ {
+ case save_registered_item::TYPE_BOOL:
+ case save_registered_item::TYPE_INT:
+ case save_registered_item::TYPE_UINT:
+ case save_registered_item::TYPE_FLOAT:
+ memcpy(m_prev_value, reinterpret_cast<void const *>(m_objbase), m_item.native_size());
+ break;
+
+ // structs and containers iterate over owned items
+ case save_registered_item::TYPE_CONTAINER:
+ case save_registered_item::TYPE_STRUCT:
+ return;
+
+ // arrays are multiples of a single item
+ default:
+ return;
+ }
+}
diff --git a/src/emu/debug/dvsave.h b/src/emu/debug/dvsave.h
new file mode 100644
index 00000000000..9a85aa2c339
--- /dev/null
+++ b/src/emu/debug/dvsave.h
@@ -0,0 +1,104 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/*********************************************************************
+
+ dvsave.h
+
+ Save debugger view.
+
+***************************************************************************/
+
+#ifndef MAME_EMU_DEBUG_DVSAVE_H
+#define MAME_EMU_DEBUG_DVSAVE_H
+
+#pragma once
+
+#include "debugvw.h"
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// debug view for state
+class debug_view_save : public debug_view
+{
+ friend class debug_view_manager;
+
+ // construction/destruction
+ debug_view_save(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate);
+ virtual ~debug_view_save();
+
+protected:
+ // view overrides
+ virtual void view_update() override;
+ virtual void view_notify(debug_view_notification type) override;
+ virtual void view_click(const int button, const debug_view_xy& pos) override;
+
+private:
+ void build_list_recursive(save_registered_item &item, uintptr_t objbase, int depth, int count = 0);
+
+ class save_item
+ {
+ public:
+ save_item(save_registered_item &item, uintptr_t objbase, int depth, int count = 0) :
+ m_item(item),
+ m_objbase(objbase),
+ m_depth(depth),
+ m_count(count),
+ m_collapsed(count <= 1)
+ {
+ }
+
+ bool collapsible() const
+ {
+ return (m_item.type() == save_registered_item::TYPE_CONTAINER ||
+ m_item.type() == save_registered_item::TYPE_STRUCT ||
+ m_item.type() < save_registered_item::TYPE_ARRAY);
+ }
+ char const *name() const { return m_item.name(); }
+ std::string value();
+ save_registered_item::save_type type() const { return m_item.type(); }
+ int depth() const { return m_depth; }
+ int count() const { return m_count; }
+ bool collapsed() const { return m_collapsed; }
+ bool changed() const;
+ void update_value();
+
+ void set_collapse(bool collapse) { m_collapsed = collapse; }
+
+ private:
+ std::string value(save_registered_item &item, int count, uintptr_t objbase, bool collapsed);
+
+ template<int N, typename... Args>
+ bool catprintf(char (&buf)[N], int &pos, char *format, Args &&... args)
+ {
+ if (N - pos >= 1)
+ {
+ int result = snprintf(&buf[pos], N - 1 - pos, format, std::forward<Args>(args)...);
+ if (result >= 0)
+ pos += result;
+ buf[N - 1] = 0;
+ }
+ return (N - pos >= 1);
+ }
+
+ save_registered_item &m_item;
+ uintptr_t m_objbase;
+ uint8_t m_depth;
+ uint8_t m_count;
+ bool m_collapsed;
+ uint8_t m_prev_value[8];
+ };
+
+ // internal helpers
+ void reset();
+ void recompute();
+
+ // internal state
+ int m_divider;
+ std::vector<save_item> m_save_list; // list of items
+};
+
+
+#endif // MAME_EMU_DEBUG_DVSAVE_H
diff --git a/src/emu/device.cpp b/src/emu/device.cpp
index 84e67ac6db9..d6298f2c5f2 100644
--- a/src/emu/device.cpp
+++ b/src/emu/device.cpp
@@ -105,6 +105,7 @@ device_t::device_t(const machine_config &mconfig, device_type type, const char *
, m_config_complete(false)
, m_started(false)
, m_auto_finder_list(nullptr)
+ , m_unstructured_save(m_unstructured_root)
, m_save_registrations(0)
{
if (owner != nullptr)
@@ -686,18 +687,22 @@ void device_t::post_load()
void device_t::register_save(save_registrar &save)
{
- // register our save states
- save.reg(NAME(m_clock))
+ // register our save states in a core 'device' container
+ save_registrar(save, "device")
+ .reg(NAME(m_clock))
.reg(NAME(m_unscaled_clock))
.reg(NAME(m_clock_scale));
- // notify the interface
+ // let the interfaces save their states
for (device_interface &intf : interfaces())
intf.interface_register_save(save);
- // notify the device
+ // then the device itself
int state_registrations = machine().save().registration_count();
device_register_save(save);
+
+ // append any unstructured items
+ save.reg(m_unstructured_save, "unstructured");
m_save_registrations += machine().save().registration_count() - state_registrations;
// complain if registrations didn't happen
diff --git a/src/emu/device.h b/src/emu/device.h
index f2b63f2aa98..0e53183e179 100644
--- a/src/emu/device.h
+++ b/src/emu/device.h
@@ -617,13 +617,10 @@ public:
void ATTR_COLD save_item(ItemType &value, const char *valname, int index = -1)
{
assert(m_save);
- m_save->save_item(this, name(), tag(), (index == -1) ? 0 : index, value, valname);
- if (m_legacy_save.get() == nullptr)
- m_legacy_save = std::make_unique<save_registrar>(m_save->legacy_registrar(), tag());
if (index == -1)
- m_legacy_save->reg(value, valname);
+ m_unstructured_save.reg(value, valname);
else
- m_legacy_save->reg(value, string_format("%s[%d]", valname, index).c_str());
+ m_unstructured_save.reg(value, string_format("%s[%d]", valname, index).c_str());
}
template<typename ItemType, typename StructType, typename ElementType>
void ATTR_COLD save_item(ItemType &value, ElementType StructType::*element, const char *valname, int index = 0)
@@ -636,13 +633,10 @@ public:
void ATTR_COLD save_pointer(ItemType &&value, const char *valname, u32 count, int index = -1)
{
assert(m_save);
- m_save->save_pointer(this, name(), tag(), (index == -1) ? 0 : index, std::forward<ItemType>(value), valname, count);
- if (m_legacy_save.get() == nullptr)
- m_legacy_save = std::make_unique<save_registrar>(m_save->legacy_registrar(), tag());
if (index == -1)
- m_legacy_save->reg(value, valname, count);
+ m_unstructured_save.reg(value, valname, count);
else
- m_legacy_save->reg(value, string_format("%s[%d]", valname, index).c_str(), count);
+ m_unstructured_save.reg(value, string_format("%s[%d]", valname, index).c_str(), count);
}
template<typename ItemType, typename StructType, typename ElementType>
void ATTR_COLD save_pointer(ItemType &&value, ElementType StructType::*element, const char *valname, u32 count, int index = 0)
@@ -859,7 +853,8 @@ private:
mutable std::vector<rom_entry> m_rom_entries;
std::list<devcb_base *> m_callbacks;
std::vector<memory_view *> m_viewlist; // list of views
- std::unique_ptr<save_registrar> m_legacy_save; // bucket for legacy save stuff
+ save_registered_item m_unstructured_root; // root unstructured item
+ save_registrar m_unstructured_save; // registrar for unstructured items
int m_save_registrations; // number of save registrations
// string formatting buffer for logerror
diff --git a/src/emu/machine.cpp b/src/emu/machine.cpp
index 488c3519409..5034820b70b 100644
--- a/src/emu/machine.cpp
+++ b/src/emu/machine.cpp
@@ -260,11 +260,10 @@ void running_machine::start()
// register callbacks for the devices, then start them
add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&running_machine::reset_all_devices, this));
add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&running_machine::stop_all_devices, this));
- save_registrar device_registrar(m_save.root_registrar(), "devices");
save().register_presave(save_prepost_delegate(FUNC(running_machine::presave_all_devices), this));
start_all_devices();
for (device_t &device : device_enumerator(root_device()))
- device_registrar.reg(device, device.tag());
+ m_save.root_registrar().reg(device, device.tag());
save().register_postload(save_prepost_delegate(FUNC(running_machine::postload_all_devices), this));
// save outputs created before start time
diff --git a/src/emu/save.cpp b/src/emu/save.cpp
index 55e4e6ce600..b7b20ba5c2e 100644
--- a/src/emu/save.cpp
+++ b/src/emu/save.cpp
@@ -1081,14 +1081,55 @@ printf("%s '%s': adding %s '%s' @ %llX, size %d\n", type_string(m_type, m_native
//-------------------------------------------------
+// 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 with our local base/offset
- objbase += m_ptr_offset;
+ // 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;
@@ -1110,27 +1151,8 @@ uint64_t save_registered_item::save_binary(uint8_t *ptr, uint64_t length, uintpt
offset += m_native_size;
break;
- // unique ptrs retrieve the pointer from their container
- case TYPE_UNIQUE:
- objbase = reinterpret_cast<uintptr_t>(reinterpret_cast<generic_unique *>(objbase)->get());
- offset += m_items.front().save_binary(&ptr[offset], length, objbase);
- break;
-
- // vectors retrieve the pointer from their container
- case TYPE_VECTOR:
- objbase = reinterpret_cast<uintptr_t>(&(*reinterpret_cast<generic_vector *>(objbase))[0]);
- offset += m_items.front().save_binary(&ptr[offset], length, objbase);
- break;
-
- // vectors retrieve the pointer from their container
- case TYPE_POINTER:
- objbase = reinterpret_cast<uintptr_t>(*reinterpret_cast<generic_pointer *>(objbase));
- offset += m_items.front().save_binary(&ptr[offset], length, objbase);
- break;
-
// structs and containers iterate over owned items
case TYPE_CONTAINER:
- objbase = 0;
case TYPE_STRUCT:
for (auto &item : m_items)
offset += item.save_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase);
@@ -1157,8 +1179,9 @@ uint64_t save_registered_item::save_binary(uint8_t *ptr, uint64_t length, uintpt
uint64_t save_registered_item::restore_binary(uint8_t const *ptr, uint64_t length, uintptr_t objbase) const
{
- // update the base pointer with our local base/offset
- objbase += m_ptr_offset;
+ // 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;
@@ -1182,7 +1205,6 @@ uint64_t save_registered_item::restore_binary(uint8_t const *ptr, uint64_t lengt
// structs and containers iterate over owned items
case TYPE_CONTAINER:
- objbase = 0;
case TYPE_STRUCT:
for (auto &item : m_items)
offset += item.restore_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase);
@@ -1206,12 +1228,14 @@ uint64_t save_registered_item::restore_binary(uint8_t const *ptr, uint64_t lengt
// save_json - save this item into a JSON stream
//-------------------------------------------------
-void save_registered_item::save_json(std::ostringstream &output, int indent, bool inline_form, uintptr_t objbase)
+void save_registered_item::save_json(save_zip_state &zipstate, int indent, bool inline_form, uintptr_t objbase)
{
- // update the base pointer with our local base/offset
- objbase += m_ptr_offset;
+ // 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 << "\": ";
@@ -1245,29 +1269,11 @@ void save_registered_item::save_json(std::ostringstream &output, int indent, boo
// float types
case TYPE_FLOAT:
{
- double value = (m_native_size == 4) ? *reinterpret_cast<float const *>(objbase) : *reinterpret_cast<double const *>(objbase);
+ double value = read_float(objbase, m_native_size);
output << value;
break;
}
- // unique ptrs retrieve the pointer from their container
- case TYPE_UNIQUE:
- objbase = reinterpret_cast<uintptr_t>(reinterpret_cast<generic_unique *>(objbase)->get());
- m_items.front().save_json(output, indent, inline_form, objbase);
- break;
-
- // vectors retrieve the pointer from their container
- case TYPE_VECTOR:
- objbase = reinterpret_cast<uintptr_t>(&(*reinterpret_cast<generic_vector *>(objbase))[0]);
- m_items.front().save_json(output, indent, inline_form, objbase);
- break;
-
- // vectors retrieve the pointer from their container
- case TYPE_POINTER:
- objbase = reinterpret_cast<uintptr_t>(*reinterpret_cast<generic_pointer *>(objbase));
- m_items.front().save_json(output, indent, inline_form, objbase);
- break;
-
// structs and containers iterate over owned items
case TYPE_CONTAINER:
case TYPE_STRUCT:
@@ -1277,7 +1283,7 @@ void save_registered_item::save_json(std::ostringstream &output, int indent, boo
output << "{ ";
for (auto &item : m_items)
{
- item.save_json(output, indent, true, (m_type == TYPE_CONTAINER) ? 0 : objbase);
+ item.save_json(zipstate, indent, true, objbase);
if (&item != &m_items.back())
output << ", ";
}
@@ -1290,7 +1296,7 @@ void save_registered_item::save_json(std::ostringstream &output, int indent, boo
for (auto &item : m_items)
{
output << std::setw(indent + 1) << std::setfill('\t') << "" << std::setw(0);
- item.save_json(output, indent + 1, false, (m_type == TYPE_CONTAINER) ? 0 : objbase);
+ item.save_json(zipstate, indent + 1, false, objbase);
if (&item != &m_items.back())
output << ",";
output << std::endl;
@@ -1312,7 +1318,7 @@ void save_registered_item::save_json(std::ostringstream &output, int indent, boo
output << "[ ";
for (uint32_t rep = 0; rep < m_type; rep++)
{
- item.save_json(output, 0, true, objbase + rep * m_native_size);
+ item.save_json(zipstate, 0, true, objbase + rep * m_native_size);
if (rep != m_type - 1)
output << ",";
}
@@ -1333,7 +1339,7 @@ void save_registered_item::save_json(std::ostringstream &output, int indent, boo
{
if (rep % items_per_row == 0)
output << std::setw(indent + 1) << std::setfill('\t') << "" << std::setw(0);
- item.save_json(output, indent + 1, false, objbase + rep * m_native_size);
+ 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)
@@ -1387,6 +1393,22 @@ int64_t save_registered_item::read_int_signed(uintptr_t objbase, int size)
//-------------------------------------------------
+// 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
//-------------------------------------------------
@@ -1401,9 +1423,46 @@ void save_registered_item::write_int(uintptr_t objbase, int size, uint64_t data)
}
}
+
+//-------------------------------------------------
+// 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()
{
- std::ostringstream json;
- m_root_item.save_json(json);
- printf("%s\n", json.str().c_str());
+ save_zip_state state;
+ m_root_item.save_json(state);
+ printf("%s\n", state.json().str().c_str());
+}
+
+
+save_zip_state::save_zip_state()
+{
+}
+
+void save_zip_state::add_data_file(char const *name, void *base, uint32_t size)
+{
+ m_file_list.emplace_back(name, base, size);
+}
+
+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)
+{
}
diff --git a/src/emu/save.h b/src/emu/save.h
index 232b69f5748..eccabc8967c 100644
--- a/src/emu/save.h
+++ b/src/emu/save.h
@@ -42,7 +42,6 @@ enum save_error
};
-
//**************************************************************************
// MACROS
//**************************************************************************
@@ -87,6 +86,30 @@ typedef named_delegate<void ()> save_prepost_delegate;
// TYPE DEFINITIONS
//**************************************************************************
+class save_zip_state
+{
+public:
+ save_zip_state();
+
+ std::ostringstream &json() { return m_json; }
+
+ void add_data_file(char const *name, void *base, uint32_t size);
+
+ void commit(FILE &output);
+
+private:
+ struct file_entry
+ {
+ file_entry(char const *name, void *base, uint32_t size);
+
+ std::string m_name;
+ void *m_base;
+ uint32_t m_size;
+ };
+ std::ostringstream m_json;
+ std::list<file_entry> m_file_list;
+};
+
class save_registered_item
{
using generic_unique = std::unique_ptr<int> const;
@@ -114,9 +137,20 @@ public:
// constructor for a new item
save_registered_item(uintptr_t ptr_offset, save_type type, uint32_t native_size, char const *name);
+ // 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; }
+ uint32_t native_size() const { return m_native_size; }
+ uint32_t count() const { return (m_type < TYPE_ARRAY) ? m_type : 1; }
+ uintptr_t ptr_offset() const { return m_ptr_offset; }
+
// 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);
+ // update the object base and unwrap trivial items
+ bool unwrap_and_update_objbase(uintptr_t &objbase) const;
+
// compute the binary size by just saving with a null
uint64_t compute_binary_size(uintptr_t objbase = 0) const { return save_binary(nullptr, 0, objbase); }
@@ -127,17 +161,19 @@ 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(std::ostringstream &output, int indent = 0, bool inline_form = false, uintptr_t objbase = 0);
+ void save_json(save_zip_state &output, 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);
-private:
// internal 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);
void write_int(uintptr_t objbase, int size, uint64_t data);
+ void write_float(uintptr_t objbase, int size, double data);
+private:
// internal state
std::list<save_registered_item> m_items; // list of embedded items
uintptr_t m_ptr_offset; // pointer or offset
@@ -149,6 +185,7 @@ private:
class save_registrar
{
friend class save_manager;
+ friend class device_t;
// extended constructor for internal use only
save_registrar(save_registrar &parent, save_registered_item::save_type type, uint32_t native_size, char const *name, void *baseptr = nullptr, void *containerbase = nullptr) :
@@ -167,6 +204,16 @@ public:
// return a reference to the parent item
save_registered_item &parent_item() const { return m_parent; }
+ // append a bucket
+ save_registrar &reg(save_registrar &src, char const *name)
+ {
+ save_registrar container(*this, save_registered_item::TYPE_CONTAINER, 0, name);
+ auto &srcitems = src.parent_item().subitems();
+ auto &dstitems = container.parent_item().subitems();
+ dstitems.splice(dstitems.end(), srcitems);
+ return *this;
+ }
+
// enum types -- these are equivalent to ints of the save size
template<typename T>
std::enable_if_t<std::is_enum<T>::value, save_registrar> &reg(T &data, char const *name)
diff --git a/src/osd/modules/debugger/debugwin.cpp b/src/osd/modules/debugger/debugwin.cpp
index 18bbc07dd70..171f5f69b17 100644
--- a/src/osd/modules/debugger/debugwin.cpp
+++ b/src/osd/modules/debugger/debugwin.cpp
@@ -20,6 +20,7 @@
#include "win/logwininfo.h"
#include "win/memorywininfo.h"
#include "win/pointswininfo.h"
+#include "win/savewininfo.h"
#include "win/uimetrics.h"
#include "debugger.h"
@@ -65,6 +66,7 @@ protected:
virtual void create_disasm_window() override { create_window<disasmwin_info>(); }
virtual void create_log_window() override { create_window<logwin_info>(); }
virtual void create_points_window() override { create_window<pointswin_info>(); }
+ virtual void create_save_window() override { create_window<savewin_info>(); }
virtual void remove_window(debugwin_info &info) override;
virtual void show_all() override;
diff --git a/src/osd/modules/debugger/win/debugwin.h b/src/osd/modules/debugger/win/debugwin.h
index e999b87334f..833d391e0c0 100644
--- a/src/osd/modules/debugger/win/debugwin.h
+++ b/src/osd/modules/debugger/win/debugwin.h
@@ -41,6 +41,7 @@ public:
virtual void create_disasm_window() = 0;
virtual void create_log_window() = 0;
virtual void create_points_window() = 0;
+ virtual void create_save_window() = 0;
virtual void remove_window(debugwin_info &info) = 0;
virtual void show_all() = 0;
diff --git a/src/osd/modules/debugger/win/debugwininfo.cpp b/src/osd/modules/debugger/win/debugwininfo.cpp
index fdd486f3e80..b22fe3cb9ab 100644
--- a/src/osd/modules/debugger/win/debugwininfo.cpp
+++ b/src/osd/modules/debugger/win/debugwininfo.cpp
@@ -298,6 +298,10 @@ bool debugwin_info::handle_command(WPARAM wparam, LPARAM lparam)
debugger().create_points_window();
return true;
+ case ID_NEW_SAVE_WND:
+ debugger().create_save_window();
+ return true;
+
case ID_RUN_AND_HIDE:
debugger().hide_all();
[[fallthrough]];
@@ -541,6 +545,7 @@ HMENU debugwin_info::create_standard_menubar()
AppendMenu(debugmenu, MF_ENABLED, ID_NEW_MEMORY_WND, TEXT("New Memory Window\tCtrl+M"));
AppendMenu(debugmenu, MF_ENABLED, ID_NEW_DISASM_WND, TEXT("New Disassembly Window\tCtrl+D"));
AppendMenu(debugmenu, MF_ENABLED, ID_NEW_LOG_WND, TEXT("New Error Log Window\tCtrl+L"));
+ AppendMenu(debugmenu, MF_ENABLED, ID_NEW_SAVE_WND, TEXT("New Save State Window\tCtrl+E"));
AppendMenu(debugmenu, MF_ENABLED, ID_NEW_POINTS_WND, TEXT("New (Break|Watch)points Window\tCtrl+B"));
AppendMenu(debugmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
AppendMenu(debugmenu, MF_ENABLED, ID_RUN, TEXT("Run\tF5"));
diff --git a/src/osd/modules/debugger/win/debugwininfo.h b/src/osd/modules/debugger/win/debugwininfo.h
index b930189fffa..4a82d4ce735 100644
--- a/src/osd/modules/debugger/win/debugwininfo.h
+++ b/src/osd/modules/debugger/win/debugwininfo.h
@@ -62,6 +62,7 @@ protected:
ID_NEW_DISASM_WND,
ID_NEW_LOG_WND,
ID_NEW_POINTS_WND,
+ ID_NEW_SAVE_WND,
ID_RUN,
ID_RUN_AND_HIDE,
ID_RUN_VBLANK,
diff --git a/src/osd/modules/debugger/win/saveviewinfo.cpp b/src/osd/modules/debugger/win/saveviewinfo.cpp
new file mode 100644
index 00000000000..f71d35aeccf
--- /dev/null
+++ b/src/osd/modules/debugger/win/saveviewinfo.cpp
@@ -0,0 +1,29 @@
+// license:BSD-3-Clause
+// copyright-holders:Samuele Zannoli
+//============================================================
+//
+// saveviewinfo.c - Win32 debug save window handling
+//
+//============================================================
+
+#include "emu.h"
+#include "saveviewinfo.h"
+
+#include "debug/dvsave.h"
+
+
+saveview_info::saveview_info(debugger_windows_interface &debugger, debugwin_info &owner, HWND parent) :
+ debugview_info(debugger, owner, parent, DVT_SAVE)
+{
+}
+
+
+saveview_info::~saveview_info()
+{
+}
+
+
+void saveview_info::clear()
+{
+// view<debug_view_save>()->clear();
+}
diff --git a/src/osd/modules/debugger/win/saveviewinfo.h b/src/osd/modules/debugger/win/saveviewinfo.h
new file mode 100644
index 00000000000..2ea392b7ecb
--- /dev/null
+++ b/src/osd/modules/debugger/win/saveviewinfo.h
@@ -0,0 +1,27 @@
+// license:BSD-3-Clause
+// copyright-holders:Samuele Zannoli
+//============================================================
+//
+// saveviewinfo.h - Win32 debug save window handling
+//
+//============================================================
+#ifndef MAME_DEBUGGER_WIN_SAVEVIEWINFO_H
+#define MAME_DEBUGGER_WIN_SAVEVIEWINFO_H
+
+#pragma once
+
+#include "debugwin.h"
+
+#include "debugviewinfo.h"
+
+
+class saveview_info : public debugview_info
+{
+public:
+ saveview_info(debugger_windows_interface &debugger, debugwin_info &owner, HWND parent);
+ virtual ~saveview_info();
+
+ void clear();
+};
+
+#endif // MAME_DEBUGGER_WIN_SAVEVIEWINFO_H
diff --git a/src/osd/modules/debugger/win/savewininfo.cpp b/src/osd/modules/debugger/win/savewininfo.cpp
new file mode 100644
index 00000000000..a09607c3b93
--- /dev/null
+++ b/src/osd/modules/debugger/win/savewininfo.cpp
@@ -0,0 +1,58 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles, Vas Crabb
+//============================================================
+//
+// savewininfo.c - Win32 debug window handling
+//
+//============================================================
+
+#include "emu.h"
+#include "savewininfo.h"
+
+#include "debugviewinfo.h"
+#include "saveviewinfo.h"
+
+
+savewin_info::savewin_info(debugger_windows_interface &debugger) :
+ debugwin_info(debugger, false, "Save State Data", nullptr)
+{
+ if (!window())
+ return;
+
+ m_views[0].reset(new saveview_info(debugger, *this, window()));
+ if ((m_views[0] == nullptr) || !m_views[0]->is_valid())
+ {
+ m_views[0].reset();
+ return;
+ }
+
+ // create the save menu
+ HMENU const savemenu = CreatePopupMenu();
+ AppendMenu(GetMenu(window()), MF_ENABLED | MF_POPUP, (UINT_PTR)savemenu, TEXT("Save"));
+
+ // compute a client rect
+ RECT bounds;
+ bounds.top = bounds.left = 0;
+ bounds.right = m_views[0]->maxwidth() + (2 * EDGE_WIDTH);
+ bounds.bottom = 200;
+ AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);
+
+ // clamp the min/max size
+ set_maxwidth(bounds.right - bounds.left);
+
+ // position the window at the bottom-right
+ SetWindowPos(window(), HWND_TOP, 100, 100, bounds.right - bounds.left, bounds.bottom - bounds.top, SWP_SHOWWINDOW);
+
+ // recompute the children
+ debugwin_info::recompute_children();
+}
+
+
+savewin_info::~savewin_info()
+{
+}
+
+bool savewin_info::handle_command(WPARAM wparam, LPARAM lparam)
+{
+ return debugwin_info::handle_command(wparam, lparam);
+}
diff --git a/src/osd/modules/debugger/win/savewininfo.h b/src/osd/modules/debugger/win/savewininfo.h
new file mode 100644
index 00000000000..e75e4b43416
--- /dev/null
+++ b/src/osd/modules/debugger/win/savewininfo.h
@@ -0,0 +1,28 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles, Vas Crabb
+//============================================================
+//
+// savewininfo.h - Win32 debug window handling
+//
+//============================================================
+#ifndef MAME_DEBUGGER_WIN_SAVEWININFO_H
+#define MAME_DEBUGGER_WIN_SAVEWININFO_H
+
+#pragma once
+
+#include "debugwin.h"
+
+#include "debugwininfo.h"
+
+
+class savewin_info : public debugwin_info
+{
+public:
+ savewin_info(debugger_windows_interface &debugger);
+ virtual ~savewin_info();
+
+protected:
+ virtual bool handle_command(WPARAM wparam, LPARAM lparam) override;
+};
+
+#endif