diff options
| author | 2021-04-18 01:05:58 -0700 | |
|---|---|---|
| committer | 2021-04-18 01:05:58 -0700 | |
| commit | d2965235277bb8cd0fb98b96edd6f4e5784778ac (patch) | |
| tree | 583fb1a35f440f90f38bfc163ae78d5f22aec4bb | |
| parent | f80df5e1477c1c30e6ea8ace4ea130c4f3a14e0a (diff) | |
More descriptive errors. Added compare mode and leveraged it to compare saved data against current state.
| -rw-r--r-- | src/emu/machine.cpp | 40 | ||||
| -rw-r--r-- | src/emu/save.cpp | 178 | ||||
| -rw-r--r-- | src/emu/save.h | 8 | ||||
| -rw-r--r-- | src/frontend/mame/luaengine.cpp | 2 |
4 files changed, 150 insertions, 78 deletions
diff --git a/src/emu/machine.cpp b/src/emu/machine.cpp index 2daa9c99999..f57a8624667 100644 --- a/src/emu/machine.cpp +++ b/src/emu/machine.cpp @@ -938,7 +938,9 @@ void running_machine::handle_saveload() auto const filerr = file.open(m_saveload_pending_file); if (filerr == osd_file::error::NONE) { + osd_ticks_t start = osd_ticks(); const char *const opnamed = (m_saveload_schedule == saveload_schedule::LOAD) ? "loaded" : "saved"; + osd_ticks_t end = osd_ticks(); // read/write the save state save_error saverr = (m_saveload_schedule == saveload_schedule::LOAD) ? m_save.load_file(file) : m_save.save_file(file); @@ -946,27 +948,39 @@ void running_machine::handle_saveload() // handle the result switch (saverr) { - case STATERR_ILLEGAL_REGISTRATIONS: - popmessage("Error: Unable to %s state due to illegal registrations. See error.log for details.", opname); - break; - - case STATERR_INVALID_HEADER: - popmessage("Error: Unable to %s state due to an invalid header. Make sure the save state is correct for this machine.", opname); + case STATERR_INVALID_FILE: + popmessage("Error: Unable to %s state due to an invalid file. Make sure the save state is correct for this machine.", opname); break; case STATERR_READ_ERROR: popmessage("Error: Unable to %s state due to a read error (file is likely corrupt).", opname); break; + case STATERR_MALFORMED_JSON: + popmessage("Error: Unable to %s state due to malformed JSON data (file is likely corrupt).", opname); + break; + + case STATERR_MISSING_FILE: + popmessage("Error: Unable to %s state due to missing file (file is likely corrupt).", opname); + break; + + case STATERR_INCOMPATIBLE_DATA: + popmessage("Error: Unable to %s state due to incompatible data (file is likely corrupt).", opname); + break; + case STATERR_WRITE_ERROR: popmessage("Error: Unable to %s state due to a write error. Verify there is enough disk space.", opname); break; + case STATERR_MISMATCH_WARNING: case STATERR_NONE: - if (!(m_system.flags & MACHINE_SUPPORTS_SAVE)) + if (saverr == STATERR_MISMATCH_WARNING) + popmessage("State was %sed successfully.\nWarning: Some missing/extra data was detected in the state; system may not behave 100%% correctly.", opnamed); + else if (!(m_system.flags & MACHINE_SUPPORTS_SAVE)) popmessage("State successfully %s.\nWarning: Save states are not officially supported for this machine.", opnamed); else popmessage("State successfully %s.", opnamed); + osd_printf_info("%s took %.3f seconds\n", opname, double(end - start) / double(osd_ticks_per_second())); break; default: @@ -988,6 +1002,18 @@ void running_machine::handle_saveload() popmessage("Error: Failed to open file for %s operation.", opname); } } + + // for save, try reload + if (m_saveload_schedule == saveload_schedule::SAVE) + { + osd_printf_info("Comparing load of just-saved data....\n"); + emu_file file(m_saveload_searchpath ? m_saveload_searchpath : "", OPEN_FLAG_READ); + auto const filerr = file.open(m_saveload_pending_file); + osd_ticks_t start = osd_ticks(); + m_save.compare_file(file); + osd_ticks_t end = osd_ticks(); + osd_printf_info("Compare took %.3f seconds\n", double(end - start) / double(osd_ticks_per_second())); + } } // unschedule the operation diff --git a/src/emu/save.cpp b/src/emu/save.cpp index 2245bfd1fb4..c23b27b0218 100644 --- a/src/emu/save.cpp +++ b/src/emu/save.cpp @@ -388,8 +388,8 @@ save_zip_state &save_zip_state::json_append_unsigned(uint64_t value) save_zip_state &save_zip_state::json_append_float(double value) { json_check_reserve(); - char buffer[20]; - sprintf(buffer, "%g", value); + char buffer[32]; + sprintf(buffer, "%1.17g", value); return json_append(buffer); } @@ -879,7 +879,16 @@ class load_zip_state { public: // load_error is the exception we throw if anything bad happens - class load_error : public std::exception { }; + class load_error : public std::exception + { + public: + load_error(save_error err) : + m_error(err) { } + save_error error() const { return m_error; } + + private: + save_error m_error; + }; // construction load_zip_state(emu_file &file); @@ -926,11 +935,11 @@ public: // report an error; this implicitly throws to exit template<typename... Params> - void report_error(char const *format, Params &&... args) + void report_error(save_error type, char const *format, Params &&... args) { m_errors.append(string_format(format, std::forward<Params>(args)...)); m_errors += "\n"; - throw load_error(); + throw load_error(type); } private: @@ -1127,7 +1136,7 @@ save_error load_zip_state::init() // if nothing found, it's an error if (dir_entries == 0) - return STATERR_INVALID_HEADER; + return STATERR_INVALID_FILE; // read the central directory std::vector<u8> central(dir_size); @@ -1141,13 +1150,13 @@ save_error load_zip_state::init() { // find the start of entry if (offset + 46 >= dir_size) - return STATERR_INVALID_HEADER; + return STATERR_INVALID_FILE; if (central[offset] != 0x50 && central[offset + 1] != 0x4b && central[offset + 2] != 0x01 && central[offset + 3] != 0x02) - return STATERR_INVALID_HEADER; + return STATERR_INVALID_FILE; // only deflate is supported; anything else will be an error if (central[offset + 10] != 8) - return STATERR_INVALID_HEADER; + return STATERR_INVALID_FILE; // pull out all the interesting data u32 compsize = central[offset + 20] | (central[offset + 21] << 8) | (central[offset + 22] << 16) | (central[offset + 23] << 24); @@ -1229,27 +1238,27 @@ bool load_zip_state::read_data_recursive(zlib_read_streamer &zlib, save_register if (flip) switch (item.native_size()) { - case 2: - { - u16 *data = reinterpret_cast<u16 *>(base); - for (int index = 0; index < item.count(); index++) - data[index] = swapendian_int16(data[index]); - break; - } - case 4: - { - u32 *data = reinterpret_cast<u32 *>(base); - for (int index = 0; index < item.count(); index++) - data[index] = swapendian_int32(data[index]); - break; - } - case 8: - { - u64 *data = reinterpret_cast<u64 *>(base); - for (int index = 0; index < item.count(); index++) - data[index] = swapendian_int64(data[index]); - break; - } + case 2: + { + u16 *data = reinterpret_cast<u16 *>(base); + for (int index = 0; index < item.count(); index++) + data[index] = swapendian_int16(data[index]); + break; + } + case 4: + { + u32 *data = reinterpret_cast<u32 *>(base); + for (int index = 0; index < item.count(); index++) + data[index] = swapendian_int32(data[index]); + break; + } + case 8: + { + u64 *data = reinterpret_cast<u64 *>(base); + for (int index = 0; index < item.count(); index++) + data[index] = swapendian_int64(data[index]); + break; + } } } return true; @@ -1695,7 +1704,7 @@ void save_registered_item::restore_json(load_zip_state &input, char const *namep else if (input.json_matches("false")) value = false; else - input.report_error("%s: Unknown boolean value", localname.c_str()); + input.report_error(STATERR_MALFORMED_JSON, "%s: Unknown boolean value", localname.c_str()); if (mode == RESTORE_DATA) write_bool(objbase, value); else if (mode == COMPARE_DATA && read_bool(objbase) != value) @@ -1711,9 +1720,9 @@ void save_registered_item::restore_json(load_zip_state &input, char const *namep if (input.json_parse_number(dvalue)) { if (mode == RESTORE_DATA && !write_int_signed(objbase, m_native_size, dvalue)) - input.report_warning("%s: Value of out range: %g", localname.c_str(), dvalue); + input.report_warning("%s: Value of out range: %1.17g", localname.c_str(), dvalue); else if (mode == COMPARE_DATA && read_int_signed(objbase, m_native_size) != int64_t(dvalue)) - input.report_warning("%s: Compare failed: JSON says %I64d, data says %g", localname.c_str(), dvalue, read_int_signed(objbase, m_native_size)); + input.report_warning("%s: Compare failed: JSON says %I64d, data says %1.17g", localname.c_str(), dvalue, read_int_signed(objbase, m_native_size)); } else if (input.json_parse_int_string(ivalue)) { @@ -1723,7 +1732,7 @@ void save_registered_item::restore_json(load_zip_state &input, char const *namep input.report_warning("%s: Compare failed: JSON says %I64d, data says %I64d", localname.c_str(), ivalue, read_int_signed(objbase, m_native_size)); } else - input.report_error("%s: Expected integer value", localname.c_str()); + input.report_error(STATERR_INCOMPATIBLE_DATA, "%s: Expected integer value", localname.c_str()); break; } @@ -1735,9 +1744,9 @@ void save_registered_item::restore_json(load_zip_state &input, char const *namep if (input.json_parse_number(dvalue)) { if (mode == RESTORE_DATA && !write_int_unsigned(objbase, m_native_size, dvalue)) - input.report_warning("%s: Value of out range: %g", localname.c_str(), dvalue); + input.report_warning("%s: Value of out range: %1.17g", localname.c_str(), dvalue); else if (mode == COMPARE_DATA && read_int_unsigned(objbase, m_native_size) != uint64_t(dvalue)) - input.report_warning("%s: Compare failed: JSON says %I64d, data says %g", localname.c_str(), dvalue, read_int_unsigned(objbase, m_native_size)); + input.report_warning("%s: Compare failed: JSON says %I64d, data says %1.17g", localname.c_str(), dvalue, read_int_unsigned(objbase, m_native_size)); } else if (input.json_parse_uint_string(ivalue)) { @@ -1747,7 +1756,7 @@ void save_registered_item::restore_json(load_zip_state &input, char const *namep input.report_warning("%s: Compare failed: JSON says %I64d, data says %I64d", localname.c_str(), ivalue, read_int_unsigned(objbase, m_native_size)); } else - input.report_error("%s: Expected integer value", localname.c_str()); + input.report_error(STATERR_INCOMPATIBLE_DATA, "%s: Expected integer value", localname.c_str()); break; } @@ -1756,11 +1765,11 @@ void save_registered_item::restore_json(load_zip_state &input, char const *namep { double value; if (!input.json_parse_number(value)) - input.report_error("%s: Expected number", localname.c_str()); + input.report_error(STATERR_INCOMPATIBLE_DATA, "%s: Expected number", localname.c_str()); if (mode == RESTORE_DATA && !write_float(objbase, m_native_size, value)) - input.report_warning("%s: Value of out range: %g", localname.c_str(), value); + input.report_warning("%s: Value of out range: %1.17g", localname.c_str(), value); else if (mode == COMPARE_DATA && read_float(objbase, m_native_size) != value) - input.report_warning("%s: Compare failed: JSON says %g, data says %g", localname.c_str(), value, read_float(objbase, m_native_size)); + input.report_warning("%s: Compare failed: JSON says %1.17g, data says %1.17g", localname.c_str(), value, read_float(objbase, m_native_size)); break; } @@ -1768,15 +1777,15 @@ void save_registered_item::restore_json(load_zip_state &input, char const *namep case TYPE_CONTAINER: case TYPE_STRUCT: if (!input.json_matches('{')) - input.report_error("%s: Expected '{'", localname.c_str()); + input.report_error(STATERR_INCOMPATIBLE_DATA, "%s: Expected '{'", localname.c_str()); if (!input.json_matches('}')) while (1) { std::string name; if (!input.json_parse_string(name) || name == "") - input.report_error("%s: Expected name within struct", localname.c_str()); + input.report_error(STATERR_MALFORMED_JSON, "%s: Expected name within struct", localname.c_str()); if (!input.json_matches(':')) - input.report_error("%s: Expected ':'", localname.c_str()); + input.report_error(STATERR_MALFORMED_JSON, "%s: Expected ':'", localname.c_str()); save_registered_item *target = find(name.c_str()); if (target == nullptr) input.report_warning("%s: Found extraneous item '%s'", localname.c_str(), name.c_str()); @@ -1784,7 +1793,7 @@ void save_registered_item::restore_json(load_zip_state &input, char const *namep if (input.json_matches('}')) break; if (!input.json_matches(',')) - input.report_error("%s: Expected ','", localname.c_str()); + input.report_error(STATERR_MALFORMED_JSON, "%s: Expected ','", localname.c_str()); } break; @@ -1794,11 +1803,11 @@ void save_registered_item::restore_json(load_zip_state &input, char const *namep { auto &item = m_items.front(); if (!input.json_matches('[')) - input.report_error("%s: Expected '['", localname.c_str()); + input.report_error(STATERR_INCOMPATIBLE_DATA, "%s: Expected '['", localname.c_str()); if (parse_external_data(input, *this, localname.c_str(), mode, objbase)) { if (!input.json_matches(']')) - input.report_error("%s: Expected ']'", localname.c_str()); + input.report_error(STATERR_MALFORMED_JSON, "%s: Expected ']'", localname.c_str()); } else { @@ -1811,7 +1820,7 @@ void save_registered_item::restore_json(load_zip_state &input, char const *namep if (input.json_matches(']')) break; if (!input.json_matches(',')) - input.report_error("%s: Expected ','", localname.c_str()); + input.report_error(STATERR_MALFORMED_JSON, "%s: Expected ','", localname.c_str()); } if (rep != count()) input.report_warning("%s: Found %s array items than expected", localname.c_str(), (rep < count()) ? "fewer" : "more"); @@ -1845,9 +1854,9 @@ bool save_registered_item::parse_external_data(load_zip_state &input, save_regis { std::string name; if (!input.json_parse_string(name) || name == "") - input.report_error("%s: Expected name within struct", localname); + input.report_error(STATERR_MALFORMED_JSON, "%s: Expected name within struct", localname); if (!input.json_matches(':')) - input.report_error("%s: Expected ':'", localname); + input.report_error(STATERR_MALFORMED_JSON, "%s: Expected ':'", localname); valid = false; if (name == "external_file" && parsed_filename == "" && input.json_parse_string(parsed_filename) && parsed_filename != "") valid = true; @@ -1867,7 +1876,7 @@ bool save_registered_item::parse_external_data(load_zip_state &input, save_regis if (input.json_matches('}')) break; if (!input.json_matches(',')) - input.report_error("%s: Expected ','", localname); + input.report_error(STATERR_MALFORMED_JSON, "%s: Expected ','", localname); } if (!valid) { @@ -1879,11 +1888,11 @@ bool save_registered_item::parse_external_data(load_zip_state &input, save_regis u64 offset; u32 size; if (!input.find_file(parsed_filename.c_str(), offset, size)) - input.report_error("%s: Unable to find file '%s' in archive", localname, parsed_filename.c_str()); + input.report_error(STATERR_MISSING_FILE, "%s: Unable to find file '%s' in archive", localname, parsed_filename.c_str()); if (parsed_unit != 1 && parsed_unit != 2 && parsed_unit != 4 && parsed_unit != 8) - input.report_error("%s: Invalid unit size for external file, expected 1, 2, 4, or 8", localname); + input.report_error(STATERR_INCOMPATIBLE_DATA, "%s: Invalid unit size for external file, expected 1, 2, 4, or 8", localname); if (parsed_count == 0) - input.report_error("%s: Invalid count for external file", localname); + input.report_error(STATERR_INCOMPATIBLE_DATA, "%s: Invalid count for external file", localname); // look for the innermost registered item save_registered_item *inner = &baseitem.subitems().front(); @@ -1894,9 +1903,9 @@ bool save_registered_item::parse_external_data(load_zip_state &input, save_regis inner = &inner->subitems().front(); } if (!inner->is_int_or_float()) - input.report_error("%s: External file specified, but not valid for this type", localname); + input.report_error(STATERR_INCOMPATIBLE_DATA, "%s: External file specified, but not valid for this type", localname); if (parsed_unit != inner->native_size()) - input.report_error("%s: External file has mismatched unit size", localname); + input.report_error(STATERR_INCOMPATIBLE_DATA, "%s: External file has mismatched unit size", localname); // if parseonly, we're done if (parseonly) @@ -1906,7 +1915,7 @@ bool save_registered_item::parse_external_data(load_zip_state &input, save_regis bool flip = (parsed_little_endian != native_little_endian); zlib_read_streamer reader(input.file()); if (!reader.begin(offset) || !input.read_data_recursive(reader, *this, flip, size, objbase) || !reader.end()) - input.report_error("%s: Error reading file '%s' in archive", localname, parsed_filename.c_str()); + input.report_error(STATERR_READ_ERROR, "%s: Error reading file '%s' in archive", localname, parsed_filename.c_str()); return true; } @@ -2218,19 +2227,57 @@ save_error save_manager::load_file(emu_file &file) m_root_item.restore_json(state); char const *warnings = state.warnings(); if (warnings != nullptr) - printf("WARNINGS during state load:\n%s", warnings); + { + osd_printf_warning("WARNINGS during state load:\n%s", warnings); + err = STATERR_MISMATCH_WARNING; + } } - catch (load_zip_state::load_error &) + catch (load_zip_state::load_error &loaderr) { char const *errors = state.errors(); if (errors != nullptr) - printf("ERRORS during state load:\n%s", errors); - return STATERR_READ_ERROR; + osd_printf_error("ERRORS during state load:\n%s", errors); + return loaderr.error(); } // call the post-load functions dispatch_postload(); - return STATERR_NONE; + return err; +} + + +//------------------------------------------------- +// compare_file - act as if we're loading the +// file, but just compare data instead +//------------------------------------------------- + +save_error save_manager::compare_file(emu_file &file) +{ + // create the JSON and target all the output files + load_zip_state state(file); + save_error err = state.init(); + if (err != STATERR_NONE) + return err; + + // restore_json will throw on parse errors and the like + try + { + m_root_item.restore_json(state, "", save_registered_item::COMPARE_DATA); + char const *warnings = state.warnings(); + if (warnings != nullptr) + { + osd_printf_warning("WARNINGS during state compare:\n%s", warnings); + err = STATERR_MISMATCH_WARNING; + } + } + catch (load_zip_state::load_error &loaderr) + { + char const *errors = state.errors(); + if (errors != nullptr) + osd_printf_error("ERRORS during state compare:\n%s", errors); + return loaderr.error(); + } + return err; } @@ -2535,13 +2582,8 @@ void rewinder::report_error(save_error error, rewind_operation operation) switch (error) { // internal saveload failures - case STATERR_ILLEGAL_REGISTRATIONS: - m_save.machine().logerror("Rewind error: Unable to %s state due to illegal registrations.", opname); - m_save.machine().popmessage("Rewind error occured. See error.log for details."); - break; - - case STATERR_INVALID_HEADER: - m_save.machine().logerror("Rewind error: Unable to %s state due to an invalid header. " + case STATERR_INVALID_FILE: + m_save.machine().logerror("Rewind error: Unable to %s state due to an invalid file. " "Make sure the save state is correct for this machine.\n", opname); m_save.machine().popmessage("Rewind error occured. See error.log for details."); break; diff --git a/src/emu/save.h b/src/emu/save.h index 84ce55ae353..368a9584882 100644 --- a/src/emu/save.h +++ b/src/emu/save.h @@ -35,10 +35,13 @@ enum save_error { STATERR_NONE, + STATERR_MISMATCH_WARNING, STATERR_NOT_FOUND, - STATERR_ILLEGAL_REGISTRATIONS, - STATERR_INVALID_HEADER, + STATERR_INVALID_FILE, STATERR_READ_ERROR, + STATERR_MALFORMED_JSON, + STATERR_MISSING_FILE, + STATERR_INCOMPATIBLE_DATA, STATERR_WRITE_ERROR, STATERR_DISABLED }; @@ -455,6 +458,7 @@ public: // disk file processing (external) save_error save_file(emu_file &file); save_error load_file(emu_file &file); + save_error compare_file(emu_file &file); private: // state callback item diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp index 0c994752d99..3aec2b1fd3a 100644 --- a/src/frontend/mame/luaengine.cpp +++ b/src/frontend/mame/luaengine.cpp @@ -1252,7 +1252,7 @@ void lua_engine::initialize() // FIXME: this needs to schedule loading from the buffer and return asynchronously somehow // right now it's broken by anonymous timers, synchronize, etc. save_error error = m.save().load_binary((u8 *)str.data(), str.size()); - if (error == STATERR_NONE) + if (error == STATERR_NONE || error == STATERR_MISMATCH_WARNING) { return true; } |
