summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2020-04-08 23:09:24 +1000
committer Vas Crabb <vas@vastheman.com>2020-04-08 23:34:03 +1000
commit08ba9b0536178d83b89e2380e8323727055cec29 (patch)
treec2d83e239b412dfde2a06f437fee2ba0dbac3c3a
parent8a4e16d9681ee1c8010628099861ed8f8c080c7e (diff)
template save/load code - the copy-pasta had become a liability (nw)
-rw-r--r--src/emu/save.cpp327
-rw-r--r--src/emu/save.h21
2 files changed, 161 insertions, 187 deletions
diff --git a/src/emu/save.cpp b/src/emu/save.cpp
index c345ef3e6ac..615578d80f2 100644
--- a/src/emu/save.cpp
+++ b/src/emu/save.cpp
@@ -240,119 +240,159 @@ void save_manager::dispatch_postload()
//-------------------------------------------------
-// read_file - read the data from a file
+// dispatch_presave - invoke all registered
+// presave callbacks for updates
//-------------------------------------------------
-save_error save_manager::read_file(emu_file &file)
+void save_manager::dispatch_presave()
{
- // if we have illegal registrations, return an error
- if (m_illegal_regs > 0)
- return STATERR_ILLEGAL_REGISTRATIONS;
-
- // read the header and turn on compression for the rest of the file
- file.compress(FCOMPRESS_NONE);
- file.seek(0, SEEK_SET);
- u8 header[HEADER_SIZE];
- if (file.read(header, sizeof(header)) != sizeof(header))
- return STATERR_READ_ERROR;
- file.compress(FCOMPRESS_MEDIUM);
+ for (auto &func : m_presave_list)
+ func->m_func();
+}
- // verify the header and report an error if it doesn't match
- u32 sig = signature();
- if (validate_header(header, machine().system().name, sig, nullptr, "Error: ") != STATERR_NONE)
- return STATERR_INVALID_HEADER;
- // determine whether or not to flip the data when done
- bool flip = NATIVE_ENDIAN_VALUE_LE_BE((header[9] & SS_MSB_FIRST) != 0, (header[9] & SS_MSB_FIRST) == 0);
+//-------------------------------------------------
+// write_file - writes the data to a file
+//-------------------------------------------------
- // read all the data, flipping if necessary
- for (auto &entry : m_entry_list)
- {
- const u32 blocksize = entry->m_typesize * entry->m_typecount;
- u8 *data = reinterpret_cast<u8 *>(entry->m_data);
- for (u32 b = 0; entry->m_blockcount > b; ++b, data += (entry->m_typesize * entry->m_stride))
- if (file.read(data, blocksize) != blocksize)
- return STATERR_READ_ERROR;
+save_error save_manager::write_file(emu_file &file)
+{
+ return do_write(
+ [] (size_t total_size) { return true; },
+ [&file] (const void *data, size_t size) { return file.write(data, size) == size; },
+ [&file] ()
+ {
+ file.compress(FCOMPRESS_NONE);
+ file.seek(0, SEEK_SET);
+ return true;
+ },
+ [&file] ()
+ {
+ file.compress(FCOMPRESS_MEDIUM);
+ return true;
+ });
+}
- // handle flipping
- if (flip)
- entry->flip_data();
- }
- // call the post-load functions
- dispatch_postload();
+//-------------------------------------------------
+// read_file - read the data from a file
+//-------------------------------------------------
- return STATERR_NONE;
+save_error save_manager::read_file(emu_file &file)
+{
+ return do_read(
+ [] (size_t total_size) { return true; },
+ [&file] (void *data, size_t size) { return file.read(data, size) == size; },
+ [&file] ()
+ {
+ file.compress(FCOMPRESS_NONE);
+ file.seek(0, SEEK_SET);
+ return true;
+ },
+ [&file] ()
+ {
+ file.compress(FCOMPRESS_MEDIUM);
+ return true;
+ });
}
//-------------------------------------------------
-// dispatch_presave - invoke all registered
-// presave callbacks for updates
+// write_stream - write the current machine state
+// to an output stream
//-------------------------------------------------
-void save_manager::dispatch_presave()
+save_error save_manager::write_stream(std::ostream &str)
{
- for (auto &func : m_presave_list)
- func->m_func();
+ return do_write(
+ [] (size_t total_size) { return true; },
+ [&str] (const void *data, size_t size)
+ {
+ return bool(str.write(reinterpret_cast<const char *>(data), size));
+ },
+ [] () { return true; },
+ [] () { return true; });
}
//-------------------------------------------------
-// write_file - writes the data to a file
+// read_stream - restore the machine state from
+// an input stream
//-------------------------------------------------
-save_error save_manager::write_file(emu_file &file)
+save_error save_manager::read_stream(std::istream &str)
{
- // if we have illegal registrations, return an error
- if (m_illegal_regs > 0)
- return STATERR_ILLEGAL_REGISTRATIONS;
+ return do_read(
+ [] (size_t total_size) { return true; },
+ [&str] (void *data, size_t size)
+ {
+ return bool(str.read(reinterpret_cast<char *>(data), size));
+ },
+ [] () { return true; },
+ [] () { return true; });
+}
- // generate the header
- u8 header[HEADER_SIZE];
- memcpy(&header[0], STATE_MAGIC_NUM, 8);
- header[8] = SAVE_VERSION;
- header[9] = NATIVE_ENDIAN_VALUE_LE_BE(0, SS_MSB_FIRST);
- strncpy((char *)&header[0x0a], machine().system().name, 0x1c - 0x0a);
- u32 sig = signature();
- *(u32 *)&header[0x1c] = little_endianize_int32(sig);
- // write the header and turn on compression for the rest of the file
- file.compress(FCOMPRESS_NONE);
- file.seek(0, SEEK_SET);
- if (file.write(header, sizeof(header)) != sizeof(header))
- return STATERR_WRITE_ERROR;
- file.compress(FCOMPRESS_MEDIUM);
+//-------------------------------------------------
+// write_buffer - write the current machine state
+// to an allocated buffer
+//-------------------------------------------------
- // call the pre-save functions
- dispatch_presave();
+save_error save_manager::write_buffer(void *buf, size_t size)
+{
+ return do_write(
+ [size] (size_t total_size) { return size == total_size; },
+ [ptr = reinterpret_cast<u8 *>(buf)] (const void *data, size_t size) mutable
+ {
+ memcpy(ptr, data, size);
+ ptr += size;
+ return true;
+ },
+ [] () { return true; },
+ [] () { return true; });
+}
- // then write all the data
- for (auto &entry : m_entry_list)
- {
- const u32 blocksize = entry->m_typesize * entry->m_typecount;
- const u8 *data = reinterpret_cast<const u8 *>(entry->m_data);
- for (u32 b = 0; entry->m_blockcount > b; ++b, data += (entry->m_typesize * entry->m_stride))
- if (file.write(data, blocksize) != blocksize)
- return STATERR_WRITE_ERROR;
- }
- return STATERR_NONE;
+
+//-------------------------------------------------
+// read_buffer - restore the machine state from a
+// buffer
+//-------------------------------------------------
+
+save_error save_manager::read_buffer(const void *buf, size_t size)
+{
+ const u8 *ptr = reinterpret_cast<const u8 *>(buf);
+ const u8 *const end = ptr + size;
+ return do_read(
+ [size] (size_t total_size) { return size == total_size; },
+ [&ptr, &end] (void *data, size_t size) -> bool
+ {
+ if ((ptr + size) > end)
+ return false;
+ memcpy(data, ptr, size);
+ ptr += size;
+ return true;
+ },
+ [] () { return true; },
+ [] () { return true; });
}
//-------------------------------------------------
-// save - write the current machine state to the
-// allocated stream
+// do_write - serialisation logic
//-------------------------------------------------
-save_error save_manager::write_buffer(u8 *data, size_t size)
+template <typename T, typename U, typename V, typename W>
+inline save_error save_manager::do_write(T check_space, U write_block, V start_header, W start_data)
{
// if we have illegal registrations, return an error
if (m_illegal_regs > 0)
return STATERR_ILLEGAL_REGISTRATIONS;
- // verify the buffer length
- if (size != ram_state::get_size(*this))
+ // check for sufficient space
+ size_t total_size = HEADER_SIZE;
+ for (const auto &entry : m_entry_list)
+ total_size += entry->m_typesize * entry->m_typecount * entry->m_blockcount;
+ if (!check_space(total_size))
return STATERR_WRITE_ERROR;
// generate the header
@@ -364,11 +404,9 @@ save_error save_manager::write_buffer(u8 *data, size_t size)
u32 sig = signature();
*(u32 *)&header[0x1c] = little_endianize_int32(sig);
- // write the header
- memcpy(data, header, sizeof(header));
-
- // advance the pointer
- u8 *byte_ptr = data + sizeof(header);
+ // write the header and turn on compression for the rest of the file
+ if (!start_header() || !write_block(header, sizeof(header)) || !start_data())
+ return STATERR_WRITE_ERROR;
// call the pre-save functions
dispatch_presave();
@@ -376,40 +414,38 @@ save_error save_manager::write_buffer(u8 *data, size_t size)
// then write all the data
for (auto &entry : m_entry_list)
{
- u32 totalsize = entry->m_typesize * entry->m_typecount;
-
- // check bounds before writing
- if (byte_ptr + totalsize > data + size)
- return STATERR_WRITE_ERROR;
-
- memcpy(byte_ptr, entry->m_data, totalsize);
- byte_ptr += totalsize;
+ const u32 blocksize = entry->m_typesize * entry->m_typecount;
+ const u8 *data = reinterpret_cast<const u8 *>(entry->m_data);
+ for (u32 b = 0; entry->m_blockcount > b; ++b, data += (entry->m_typesize * entry->m_stride))
+ if (!write_block(data, blocksize))
+ return STATERR_WRITE_ERROR;
}
return STATERR_NONE;
}
//-------------------------------------------------
-// load - restore the machine state from the
-// stream
+// do_read - deserialisation logic
//-------------------------------------------------
-save_error save_manager::read_buffer(u8 *data, size_t size)
+template <typename T, typename U, typename V, typename W>
+inline save_error save_manager::do_read(T check_length, U read_block, V start_header, W start_data)
{
// if we have illegal registrations, return an error
if (m_illegal_regs > 0)
return STATERR_ILLEGAL_REGISTRATIONS;
- // verify the buffer length
- if (size != ram_state::get_size(*this))
- return STATERR_WRITE_ERROR;
+ // check for sufficient space
+ size_t total_size = HEADER_SIZE;
+ for (const auto &entry : m_entry_list)
+ total_size += entry->m_typesize * entry->m_typecount * entry->m_blockcount;
+ if (!check_length(total_size))
+ return STATERR_READ_ERROR;
- // read the header
+ // read the header and turn on compression for the rest of the file
u8 header[HEADER_SIZE];
- memcpy(header, data, sizeof(header));
-
- // advance the pointer
- u8 *byte_ptr = data + sizeof(header);
+ if (!start_header() || !read_block(header, sizeof(header)) || !start_data())
+ return STATERR_READ_ERROR;
// verify the header and report an error if it doesn't match
u32 sig = signature();
@@ -417,19 +453,16 @@ save_error save_manager::read_buffer(u8 *data, size_t size)
return STATERR_INVALID_HEADER;
// determine whether or not to flip the data when done
- bool flip = NATIVE_ENDIAN_VALUE_LE_BE((header[9] & SS_MSB_FIRST) != 0, (header[9] & SS_MSB_FIRST) == 0);
+ const bool flip = NATIVE_ENDIAN_VALUE_LE_BE((header[9] & SS_MSB_FIRST) != 0, (header[9] & SS_MSB_FIRST) == 0);
// read all the data, flipping if necessary
for (auto &entry : m_entry_list)
{
- u32 totalsize = entry->m_typesize * entry->m_typecount;
-
- // check bounds before reading
- if (byte_ptr + totalsize > data + size)
- return STATERR_READ_ERROR;
-
- memcpy(entry->m_data, byte_ptr, totalsize);
- byte_ptr += totalsize;
+ const u32 blocksize = entry->m_typesize * entry->m_typecount;
+ u8 *data = reinterpret_cast<u8 *>(entry->m_data);
+ for (u32 b = 0; entry->m_blockcount > b; ++b, data += (entry->m_typesize * entry->m_stride))
+ if (!read_block(data, blocksize))
+ return STATERR_READ_ERROR;
// handle flipping
if (flip)
@@ -583,41 +616,10 @@ save_error ram_state::save()
m_valid = false;
m_data.seekp(0);
- // if we have illegal registrations, return an error
- if (m_save.m_illegal_regs > 0)
- return STATERR_ILLEGAL_REGISTRATIONS;
-
- // generate the header
- u8 header[HEADER_SIZE];
- memcpy(&header[0], STATE_MAGIC_NUM, 8);
- header[8] = SAVE_VERSION;
- header[9] = NATIVE_ENDIAN_VALUE_LE_BE(0, SS_MSB_FIRST);
- strncpy((char *)&header[0x0a], m_save.machine().system().name, 0x1c - 0x0a);
- u32 sig = m_save.signature();
- *(u32 *)&header[0x1c] = little_endianize_int32(sig);
-
- // write the header
- m_data.write((char *)header, sizeof(header));
-
- // check for any errors
- if (!m_data)
- return STATERR_WRITE_ERROR;
-
- // call the pre-save functions
- m_save.dispatch_presave();
-
- // write all the data
- for (auto &entry : m_save.m_entry_list)
- {
- const u32 blocksize = entry->m_typesize * entry->m_typecount;
- const char *data = reinterpret_cast<const char *>(entry->m_data);
- for (u32 b = 0; entry->m_blockcount > b; ++b, data += (entry->m_typesize * entry->m_stride))
- m_data.write(data, blocksize);
-
- // check for any errors
- if (!m_data)
- return STATERR_WRITE_ERROR;
- }
+ // get the save manager to write state
+ const save_error err = m_save.write_stream(m_data);
+ if (err != STATERR_NONE)
+ return err;
// final confirmation
m_valid = true;
@@ -641,43 +643,8 @@ save_error ram_state::load()
if (m_save.m_illegal_regs > 0)
return STATERR_ILLEGAL_REGISTRATIONS;
- // read the header
- u8 header[HEADER_SIZE];
- m_data.read((char *)header, sizeof(header));
-
- // check for any errors
- if (!m_data)
- return STATERR_READ_ERROR;
-
- // verify the header and report an error if it doesn't match
- u32 sig = m_save.signature();
- if (m_save.validate_header(header, m_save.machine().system().name, sig, nullptr, "Error: ") != STATERR_NONE)
- return STATERR_INVALID_HEADER;
-
- // determine whether or not to flip the data when done
- bool flip = NATIVE_ENDIAN_VALUE_LE_BE((header[9] & SS_MSB_FIRST) != 0, (header[9] & SS_MSB_FIRST) == 0);
-
- // read all the data, flipping if necessary
- for (auto &entry : m_save.m_entry_list)
- {
- const u32 blocksize = entry->m_typesize * entry->m_typecount;
- char *data = reinterpret_cast<char *>(entry->m_data);
- for (u32 b = 0; entry->m_blockcount > b; ++b, data += (entry->m_typesize * entry->m_stride))
- m_data.read(data, blocksize);
-
- // check for any errors
- if (!m_data)
- return STATERR_READ_ERROR;
-
- // handle flipping
- if (flip)
- entry->flip_data();
- }
-
- // call the post-load functions
- m_save.dispatch_postload();
-
- return STATERR_NONE;
+ // get the save manager to load state
+ return m_save.write_stream(m_data);
}
diff --git a/src/emu/save.h b/src/emu/save.h
index a9f25488a00..f015534f02c 100644
--- a/src/emu/save.h
+++ b/src/emu/save.h
@@ -233,15 +233,13 @@ public:
save_error write_file(emu_file &file);
save_error read_file(emu_file &file);
- save_error write_buffer(u8 *data, size_t size);
- save_error read_buffer(u8 *data, size_t size);
+ save_error write_stream(std::ostream &str);
+ save_error read_stream(std::istream &str);
-private:
- // internal helpers
- u32 signature() const;
- void dump_registry() const;
- static save_error validate_header(const u8 *header, const char *gamename, u32 signature, void (CLIB_DECL *errormsg)(const char *fmt, ...), const char *error_prefix);
+ save_error write_buffer(void *buf, size_t size);
+ save_error read_buffer(const void *buf, size_t size);
+private:
// state callback item
class state_callback
{
@@ -252,6 +250,15 @@ private:
save_prepost_delegate m_func; // delegate
};
+ // internal helpers
+ template <typename T, typename U, typename V, typename W>
+ save_error do_write(T check_space, U write_block, V start_header, W start_data);
+ template <typename T, typename U, typename V, typename W>
+ save_error do_read(T check_length, U read_block, V start_header, W start_data);
+ u32 signature() const;
+ void dump_registry() const;
+ static save_error validate_header(const u8 *header, const char *gamename, u32 signature, void (CLIB_DECL *errormsg)(const char *fmt, ...), const char *error_prefix);
+
// internal state
running_machine & m_machine; // reference to our machine
std::unique_ptr<rewinder> m_rewind; // rewinder