Enable flexible structure saving.

This change introduces the ability to save arbitrary structures. To
make a structure eligible for saving, it must implement a new method
register_save(save_registrar &). Structs can then save their members
directly via the provided save_registrar. Nested structs and arrays of
structs are supported in this manner.

Note that state registration is not done via a virtual method or mix-in
interface because doing so would convert lightweight structures away
from being POD, and lightweight structures are one of the most common
situations where this system can be leveraged in MAME.

As a first example, the attotime struct has been changed to support
this mechanism for saving, allowing us to remove the special cases for
attotimes currently in save_manager.

(It would be tempting to do the same for bitmap_t, but bitmaps are
defined in lib/util and can't take a dependency on save.h so those
special cases will remain.)

This is a prologue to full hierarchical save state support, which I am
working on in another branch. The save_registrar class name and reg()
signatures match current work on that branch, so any changes made to
existing code to leverage struct saving in this way will port over
cleanly.
This commit is contained in:
Aaron Giles 2021-09-15 11:06:58 -07:00
parent da6f1fd6fc
commit de28f087ff
4 changed files with 99 additions and 34 deletions

View file

@ -8,9 +8,8 @@
***************************************************************************/
#include "emucore.h"
#include "eminline.h"
#include "attotime.h"
#include "emu.h"
//**************************************************************************
// GLOBAL VARIABLES
@ -170,3 +169,15 @@ std::string attotime::to_string() const
int nsec = t.attoseconds() / ATTOSECONDS_PER_NANOSECOND;
return util::string_format("%s%04d.%03d,%03d,%03d", sign, int(t.seconds()), nsec/1000000, (nsec/1000)%1000, nsec % 1000);
}
//-------------------------------------------------
// register_save - register for save states
//-------------------------------------------------
void attotime::register_save(save_registrar &save)
{
// use names without m_ prefix for backwards compatibility
save.reg(m_attoseconds, "attoseconds");
save.reg(m_seconds, "seconds");
}

View file

@ -164,6 +164,8 @@ public:
attotime &operator*=(u32 factor);
attotime &operator/=(u32 factor);
void register_save(save_registrar &save);
// members
seconds_t m_seconds;
attoseconds_t m_attoseconds;

View file

@ -210,6 +210,9 @@ class rom_entry;
// declared in romload.h
class rom_load_manager;
// declared in save.h
class save_registrar;
// declared in schedule.h
class device_scheduler;
class emu_timer;

View file

@ -70,9 +70,43 @@ typedef named_delegate<void ()> save_prepost_delegate;
// TYPE DEFINITIONS
//**************************************************************************
class save_manager;
class ram_state;
class rewinder;
// helper class to provide pass-through interface for saving struct items
class save_registrar
{
friend class save_manager;
public:
save_registrar(save_manager &manager, device_t *device, char const *module, char const *tag, int index, char const *valname) :
m_manager(manager),
m_device(device),
m_module(module),
m_tag(tag),
m_valname(valname),
m_inner_index(index),
m_outer_index(-1)
{
}
template <typename ItemType>
void reg(ItemType &value, const char *valname);
template <typename ItemType>
void reg(ItemType &value, const char *valname, int index);
private:
save_manager &m_manager;
device_t *m_device;
char const *m_module;
char const *m_tag;
char const *m_valname;
int m_inner_index;
int m_outer_index;
};
class save_manager
{
// stuff for working with arrays
@ -102,6 +136,13 @@ class save_manager
template <typename ItemType> struct is_atom : public std::false_type { };
template <typename ItemType> struct is_vector_safe : public std::false_type { };
// template to query if a type is a struct/union that can be saved; note that bitmap_t is explicitly
// excluded because it is handled as a special case
template <typename ItemType> struct is_saveable_struct
{
static constexpr bool value = (std::is_class<ItemType>::value || std::is_union<ItemType>::value) && !std::is_base_of<bitmap_t, ItemType>::value && !is_atom<ItemType>::value;
};
class state_entry
{
public:
@ -166,6 +207,23 @@ public:
save_memory(device, module, tag, index, valname, array_unwrap<ItemType>::ptr(value), array_unwrap<ItemType>::SIZE, array_unwrap<ItemType>::SAVE_COUNT);
}
// templatized wrappers for saving saveable structs
template<typename ItemType>
std::enable_if_t<is_saveable_struct<typename array_unwrap<ItemType>::underlying_type>::value && array_unwrap<ItemType>::SAVE_COUNT == 1>
save_item(device_t *device, const char *module, const char *tag, int index, ItemType &value, const char *valname)
{
save_registrar save(*this, device, module, tag, index, valname);
value.register_save(save);
}
template<typename ItemType>
std::enable_if_t<is_saveable_struct<typename array_unwrap<ItemType>::underlying_type>::value && array_unwrap<ItemType>::SAVE_COUNT != 1>
save_item(device_t *device, const char *module, const char *tag, int index, ItemType &value, const char *valname)
{
save_registrar save(*this, device, module, tag, index, valname);
for (save.m_outer_index = 0; save.m_outer_index < array_unwrap<ItemType>::SAVE_COUNT; save.m_outer_index++)
array_unwrap<ItemType>::ptr(value)[save.m_outer_index].register_save(save);
}
// templatized wrapper for structure members
template <typename ItemType, typename StructType, typename ElementType>
void save_item(device_t *device, const char *module, const char *tag, int index, ItemType &value, ElementType StructType::*element, const char *valname)
@ -236,37 +294,6 @@ public:
save_memory(device, module, tag, index, valname, &value.pix(0), value.bpp() / 8, value.rowpixels() * value.height());
}
// specializations for attotimes
template <typename ItemType>
std::enable_if_t<std::is_same<typename save_manager::array_unwrap<ItemType>::underlying_type, attotime>::value> save_item(device_t *device, const char *module, const char *tag, int index, ItemType &value, const char *valname)
{
std::string tempstr;
tempstr.assign(valname).append(".attoseconds");
save_item(device, module, tag, index, value, &attotime::m_attoseconds, tempstr.c_str());
tempstr.assign(valname).append(".seconds");
save_item(device, module, tag, index, value, &attotime::m_seconds, tempstr.c_str());
}
template <typename ItemType>
std::enable_if_t<std::is_same<typename save_manager::array_unwrap<ItemType>::underlying_type, attotime>::value> save_pointer(device_t *device, const char *module, const char *tag, int index, ItemType *value, const char *valname, u32 count)
{
std::string tempstr;
tempstr.assign(valname).append(".attoseconds");
save_item(device, module, tag, index, value, &attotime::m_attoseconds, tempstr.c_str(), count);
tempstr.assign(valname).append(".seconds");
save_item(device, module, tag, index, value, &attotime::m_seconds, tempstr.c_str(), count);
}
template <typename ItemType>
std::enable_if_t<std::is_same<typename save_manager::array_unwrap<ItemType>::underlying_type, attotime>::value> save_pointer(device_t *device, const char *module, const char *tag, int index, const std::unique_ptr<ItemType []> &value, const char *valname, u32 count)
{
std::string tempstr;
tempstr.assign(valname).append(".attoseconds");
save_item(device, module, tag, index, value, &attotime::m_attoseconds, tempstr.c_str(), count);
tempstr.assign(valname).append(".seconds");
save_item(device, module, tag, index, value, &attotime::m_seconds, tempstr.c_str(), count);
}
// global memory registration
template <typename ItemType>
void save_item(ItemType &value, const char *valname, int index = 0)
@ -377,6 +404,28 @@ public:
};
template <typename ItemType>
void save_registrar::reg(ItemType &value, const char *valname)
{
std::string fullname;
if (m_outer_index == -1)
fullname = string_format("%s.%s", m_valname, valname);
else
fullname = string_format("%s[%d].%s", m_valname, m_outer_index, valname);
m_manager.save_item(m_device, m_module, m_tag, m_inner_index, value, fullname.c_str());
}
template <typename ItemType>
void save_registrar::reg(ItemType &value, const char *valname, int index)
{
std::string fullname;
if (m_outer_index == -1)
fullname = string_format("%s.%s[%d]", m_valname, valname, index);
else
fullname = string_format("%s[%d].%s[%d]", m_valname, m_outer_index, valname, index);
m_manager.save_item(m_device, m_module, m_tag, m_inner_index, value, fullname.c_str());
}
// template specializations to enumerate the fundamental atomic types you are allowed to save
ALLOW_SAVE_TYPE_AND_VECTOR(char)
ALLOW_SAVE_TYPE (bool) // std::vector<bool> may be packed internally