summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/save.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/save.cpp')
-rw-r--r--src/emu/save.cpp465
1 files changed, 18 insertions, 447 deletions
diff --git a/src/emu/save.cpp b/src/emu/save.cpp
index eff71cd6870..dea222227ee 100644
--- a/src/emu/save.cpp
+++ b/src/emu/save.cpp
@@ -23,7 +23,6 @@
***************************************************************************/
#include "emu.h"
-#include "emuopts.h"
#include "coreutil.h"
@@ -61,11 +60,10 @@ enum
//-------------------------------------------------
save_manager::save_manager(running_machine &machine)
- : m_machine(machine)
- , m_reg_allowed(true)
- , m_illegal_regs(0)
+ : m_machine(machine),
+ m_reg_allowed(true),
+ m_illegal_regs(0)
{
- m_rewind = std::make_unique<rewinder>(*this);
}
@@ -171,8 +169,8 @@ void save_manager::save_memory(device_t *device, const char *module, const char
totalname = string_format("%s/%X/%s", module, index, name);
// look for duplicates and an entry to insert in front of
- std::vector<std::unique_ptr<state_entry>>::iterator insert_after = m_entry_list.begin();
- for (auto it = m_entry_list.begin(); it != m_entry_list.end(); ++it)
+ std::vector<std::unique_ptr<state_entry>>::iterator insert_after = m_entry_list.begin();
+ for (auto it = m_entry_list.begin(); it!= m_entry_list.end(); ++it)
{
// stop when we find an entry whose name is after ours
if (it->get()->m_name.compare(totalname)>0)
@@ -185,7 +183,7 @@ void save_manager::save_memory(device_t *device, const char *module, const char
}
// insert us into the list
- m_entry_list.insert(insert_after, std::make_unique<state_entry>(val, totalname.c_str(), device, module, tag ? tag : "", index, valsize, valcount));
+ m_entry_list.insert(insert_after,std::make_unique<state_entry>(val, totalname.c_str(), device, module, tag ? tag : "", index, valsize, valcount));
}
@@ -215,19 +213,18 @@ save_error save_manager::check_file(running_machine &machine, emu_file &file, co
return validate_header(header, gamename, sig, errormsg, "");
}
-
//-------------------------------------------------
// dispatch_postload - invoke all registered
// postload callbacks for updates
//-------------------------------------------------
+
void save_manager::dispatch_postload()
{
for (auto &func : m_postload_list)
func->m_func();
}
-
//-------------------------------------------------
// read_file - read the data from a file
//-------------------------------------------------
@@ -272,19 +269,18 @@ save_error save_manager::read_file(emu_file &file)
return STATERR_NONE;
}
-
//-------------------------------------------------
// dispatch_presave - invoke all registered
// presave callbacks for updates
//-------------------------------------------------
+
void save_manager::dispatch_presave()
{
for (auto &func : m_presave_list)
func->m_func();
}
-
//-------------------------------------------------
// write_file - writes the data to a file
//-------------------------------------------------
@@ -419,451 +415,26 @@ save_manager::state_callback::state_callback(save_prepost_delegate callback)
//-------------------------------------------------
-// ram_state - constructor
-//-------------------------------------------------
-
-ram_state::ram_state(save_manager &save)
- : m_save(save)
- , m_data()
- , m_valid(false)
- , m_time(m_save.machine().time())
-{
- m_data.reserve(get_size(save));
- m_data.clear();
- m_data.rdbuf()->clear();
- m_data.seekp(0);
- m_data.seekg(0);
-}
-
-
-//-------------------------------------------------
-// get_size - utility function to get the
-// uncompressed size of a state
-//-------------------------------------------------
-
-size_t ram_state::get_size(save_manager &save)
-{
- size_t totalsize = 0;
-
- for (auto &entry : save.m_entry_list)
- {
- totalsize += entry->m_typesize * entry->m_typecount;
- }
-
- return totalsize + HEADER_SIZE;
-}
-
-
-//-------------------------------------------------
-// save - write the current machine state to the
-// allocated stream
-//-------------------------------------------------
-
-save_error ram_state::save()
-{
- // initialize
- 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)
- {
- u32 totalsize = entry->m_typesize * entry->m_typecount;
- m_data.write((char *)entry->m_data, totalsize);
-
- // check for any errors
- if (!m_data)
- return STATERR_WRITE_ERROR;
- }
-
- // final confirmation
- m_valid = true;
- m_time = m_save.machine().time();
-
- return STATERR_NONE;
-}
-
-
-//-------------------------------------------------
-// load - restore the machine state from the
-// stream
-//-------------------------------------------------
-
-save_error ram_state::load()
-{
- // initialize
- m_data.seekg(0);
-
- // if we have illegal registrations, return an error
- 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)
- {
- u32 totalsize = entry->m_typesize * entry->m_typecount;
- m_data.read((char *)entry->m_data, totalsize);
-
- // 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;
-}
-
-
-//-------------------------------------------------
-// rewinder - constuctor
-//-------------------------------------------------
-
-rewinder::rewinder(save_manager &save)
- : m_save(save)
- , m_enabled(save.machine().options().rewind())
- , m_capacity(save.machine().options().rewind_capacity())
-{
-}
-
-
-//-------------------------------------------------
-// check_size - shrink the state list if it is
-// about to hit the capacity
-//-------------------------------------------------
-
-void rewinder::check_size()
-{
- // safety check that shouldn't be allowed to trigger
- if (m_state_list.size() > m_capacity)
- {
- // drop all states beyond capacity
- uint32_t count = m_state_list.size() - m_capacity;
- m_state_list.erase(m_state_list.begin(), m_state_list.begin() + count);
- }
-
- // check if we're about to hit capacity
- if (m_state_list.size() == m_capacity)
- {
- // check the last state
- ram_state *last = m_state_list.back().get();
-
- // if we're not on top of the list, no need to move states around
- if (!last->m_valid)
- return;
-
- // we can now get the first state and invalidate it
- std::unique_ptr<ram_state> first(std::move(m_state_list.front()));
- first->m_valid = false;
-
- // move it to the end for future use
- m_state_list.push_back(std::move(first));
- m_state_list.erase(m_state_list.begin());
- }
-}
-
-
-//-------------------------------------------------
-// report_error - report rewind success or
-// error type
-//-------------------------------------------------
-
-void rewinder::report_error(save_error error, rewind_operation operation, int index)
-{
- const char *const opname = (operation == rewind_operation::LOAD) ? "load" : "save";
-
- switch (error)
- {
- // internal saveload failures
- case STATERR_ILLEGAL_REGISTRATIONS:
- m_save.machine().popmessage("Error: Unable to %s state due to illegal registrations. See error.log for details.", opname);
- break;
-
- case STATERR_INVALID_HEADER:
- m_save.machine().popmessage("Error: Unable to %s state due to an invalid header. Make sure the save state is correct for this machine.", opname);
- break;
-
- case STATERR_READ_ERROR:
- m_save.machine().popmessage("Error: Unable to %s state due to a read error.", opname);
- break;
-
- case STATERR_WRITE_ERROR:
- m_save.machine().popmessage("Error: Unable to %s state due to a write error.", opname);
- break;
-
- // external saveload failures
- case STATERR_NOT_FOUND:
- if (operation == rewind_operation::LOAD)
- m_save.machine().popmessage("No rewind state to load.");
- break;
-
- // success
- case STATERR_NONE:
- {
- const char *const warning = (m_save.machine().system().flags & MACHINE_SUPPORTS_SAVE) ? ""
- : "\nWarning: Save states are not officially supported for this machine.";
-
- // figure out the qantity of valid states
- int invalid = get_first_invalid_index();
-
- // all states are valid
- if (invalid == REWIND_INDEX_NONE)
- invalid = m_state_list.size();
-
- if (operation == rewind_operation::SAVE)
- m_save.machine().popmessage("Rewind state %i captured.\nRewind state count: %i.%s", invalid - 1, invalid, warning);
- else
- m_save.machine().popmessage("Rewind state %i loaded.\nRewind state count: %i.%s", index, invalid, warning);
- }
- break;
-
- // something that shouldn't be allowed to happen
- default:
- m_save.machine().popmessage("Error: Unknown error during state %s.", opname);
- break;
- }
-}
-
-
-//-------------------------------------------------
-// get_current_index - get the index of the
-// state to assume as current
-//-------------------------------------------------
-
-int rewinder::get_current_index()
-{
- // nowhere to search
- if (m_state_list.empty())
- return REWIND_INDEX_NONE;
-
- // fetch the current machine time
- attotime curtime = m_save.machine().time();
-
- // find the state at the current time, or at least the first one after
- for (auto it = m_state_list.begin(); it < m_state_list.end(); ++it)
- if (it->get()->m_time >= curtime)
- return it - m_state_list.begin();
-
- // all states are older
- return REWIND_INDEX_NONE;
-}
-
-
-//-------------------------------------------------
-// get_first_invalid_index - get the index of the
-// first invalid state
-//-------------------------------------------------
-
-int rewinder::get_first_invalid_index()
-{
- for (auto it = m_state_list.begin(); it < m_state_list.end(); ++it)
- if (!it->get()->m_valid)
- return it - m_state_list.begin();
-
- // all states are valid
- return REWIND_INDEX_NONE;
-}
-
-
-//-------------------------------------------------
-// invalidate - mark all the future states as
-// invalid
-//-------------------------------------------------
-
-int rewinder::invalidate()
-{
- // fetch the current state index
- int index = get_current_index();
-
- // more invalid states may be farther back, account for them too
- int invalid = get_first_invalid_index();
-
- // roll back if we can
- if (invalid != REWIND_INDEX_NONE && (index == REWIND_INDEX_NONE || invalid < index))
- index = invalid;
-
- if (index != REWIND_INDEX_NONE)
- {
- // if it's the last state in the list, skip further invalidation
- if (++index >= m_state_list.size())
- return index;
-
- // invalidate all the future states, as the current input might have changed
- for (auto it = m_state_list.begin() + index; it < m_state_list.end(); ++it)
- it->get()->m_valid = false;
- }
-
- // index of the first invalid state
- return index;
-}
-
-
-//-------------------------------------------------
-// capture - record a single state
-//-------------------------------------------------
-
-void rewinder::capture()
-{
- // fetch the current state index and invalidate the future states
- int index = invalidate();
-
- if (index == REWIND_INDEX_NONE)
- {
- // no current state, create one
- std::unique_ptr<ram_state> state = std::make_unique<ram_state>(m_save);
- save_error error = state->save();
-
- // validate the state
- if (error == STATERR_NONE)
- {
- // it's safe to append
- m_state_list.push_back(std::move(state));
- }
- else
- {
- // internal error, complain and evacuate
- report_error(error, rewind_operation::SAVE);
- return;
- }
- }
- else
- {
- // update the existing state
- ram_state *state = m_state_list.at(--index).get();
- save_error error = state->save();
-
- // validate the state
- if (error != STATERR_NONE)
- {
- // internal error, complain and evacuate
- report_error(error, rewind_operation::SAVE);
- return;
- }
- }
-
- // make sure we still fit in
- check_size();
-
- // success
- report_error(STATERR_NONE, rewind_operation::SAVE);
-}
-
-
-//-------------------------------------------------
-// step - single step back in time
-//-------------------------------------------------
-
-void rewinder::step()
-{
- // check presence of states
- if (m_state_list.empty())
- {
- // no states, complain and evacuate
- report_error(STATERR_NOT_FOUND, rewind_operation::LOAD);
- return;
- }
-
- // fetch the current state index
- int index = get_current_index();
-
- // if there is room, retreat
- if (index != REWIND_INDEX_FIRST)
- {
- // we may be on top of the list, when all states are older
- if (index == REWIND_INDEX_NONE)
- {
- // use the last consecutively valid state, to ensure rewinder integrity
- index = get_first_invalid_index();
-
- if (index == REWIND_INDEX_NONE)
- // all states are valid
- index = m_state_list.size();
- else if (index == REWIND_INDEX_FIRST)
- {
- // no valid states, complain and evacuate
- report_error(STATERR_NOT_FOUND, rewind_operation::LOAD);
- return;
- }
- }
-
- // obtain the state pointer
- ram_state *state = m_state_list.at(--index).get();
-
- // try to load and report the result
- report_error(state->load(), rewind_operation::LOAD, index);
- return;
- }
-
- // no valid states, complain
- report_error(STATERR_NOT_FOUND, rewind_operation::LOAD);
-}
-
-
-//-------------------------------------------------
// state_entry - constructor
//-------------------------------------------------
state_entry::state_entry(void *data, const char *name, device_t *device, const char *module, const char *tag, int index, u8 size, u32 count)
- : m_data(data)
- , m_name(name)
- , m_device(device)
- , m_module(module)
- , m_tag(tag)
- , m_index(index)
- , m_typesize(size)
- , m_typecount(count)
- , m_offset(0)
+ : m_data(data),
+ m_name(name),
+ m_device(device),
+ m_module(module),
+ m_tag(tag),
+ m_index(index),
+ m_typesize(size),
+ m_typecount(count),
+ m_offset(0)
{
}
//-------------------------------------------------
// flip_data - reverse the endianness of a
-// block of data
+// block of data
//-------------------------------------------------
void state_entry::flip_data()