diff options
author | 2020-04-08 23:09:24 +1000 | |
---|---|---|
committer | 2020-04-08 23:34:03 +1000 | |
commit | 08ba9b0536178d83b89e2380e8323727055cec29 (patch) | |
tree | c2d83e239b412dfde2a06f437fee2ba0dbac3c3a | |
parent | 8a4e16d9681ee1c8010628099861ed8f8c080c7e (diff) |
template save/load code - the copy-pasta had become a liability (nw)
-rw-r--r-- | src/emu/save.cpp | 327 | ||||
-rw-r--r-- | src/emu/save.h | 21 |
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 |