summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--src/emu/machine.cpp40
-rw-r--r--src/emu/save.cpp178
-rw-r--r--src/emu/save.h8
-rw-r--r--src/frontend/mame/luaengine.cpp2
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;
}