diff options
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 |
commit | f65453c2321f150a40e995ce4f2cef1355dae654 (patch) | |
tree | b008c312ebfa00493a697fe0f398de8ab6adc0fc | |
parent | 902cce39106ed1f7c181c46c491f2c9e3b9a199d (diff) |
Added save state window to debugger. Centralized unique/vector/pointer unwrapping.
-rw-r--r-- | src/emu/debug/debugvw.cpp | 4 | ||||
-rw-r--r-- | src/emu/debug/debugvw.h | 1 | ||||
-rw-r--r-- | src/emu/debug/dvsave.cpp | 449 | ||||
-rw-r--r-- | src/emu/debug/dvsave.h | 104 | ||||
-rw-r--r-- | src/emu/device.cpp | 13 | ||||
-rw-r--r-- | src/emu/device.h | 17 | ||||
-rw-r--r-- | src/emu/machine.cpp | 3 | ||||
-rw-r--r-- | src/emu/save.cpp | 165 | ||||
-rw-r--r-- | src/emu/save.h | 53 | ||||
-rw-r--r-- | src/osd/modules/debugger/debugwin.cpp | 2 | ||||
-rw-r--r-- | src/osd/modules/debugger/win/debugwin.h | 1 | ||||
-rw-r--r-- | src/osd/modules/debugger/win/debugwininfo.cpp | 5 | ||||
-rw-r--r-- | src/osd/modules/debugger/win/debugwininfo.h | 1 | ||||
-rw-r--r-- | src/osd/modules/debugger/win/saveviewinfo.cpp | 29 | ||||
-rw-r--r-- | src/osd/modules/debugger/win/saveviewinfo.h | 27 | ||||
-rw-r--r-- | src/osd/modules/debugger/win/savewininfo.cpp | 58 | ||||
-rw-r--r-- | src/osd/modules/debugger/win/savewininfo.h | 28 |
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 ®(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> ®(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 |