diff options
Diffstat (limited to 'src/emu/save.cpp')
-rw-r--r-- | src/emu/save.cpp | 465 |
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() |