diff options
| author | 2021-04-15 09:25:42 -0700 | |
|---|---|---|
| committer | 2021-04-15 09:25:42 -0700 | |
| commit | 2024b61079509b9f1ef692ff3f9e63007a831f6f (patch) | |
| tree | 7c43de9fb0afc17e74e94272dce8e66dbbfac9b1 /src/emu/save.cpp | |
| parent | 98e8ed32043d8d47fe63b6565c452dc61182fd02 (diff) | |
g update:
* Removed old saving mechanism entirely, including internal lists of
items.
* Removed old logic saving to streams/buffers/files.
* Removed old state item iterator; a new mechanism will need to be
created for this. Stubbed out debugger and LUA calls to it for now.
* Replaced the streams/buffers saves with the new binary save; these are
presumed to be 100% internal, so there is no header checking or other
associated logic.
* Stubbed in incomplete file handling for JSON-based saves; large arrays
are identified and referenced as external, though ZIP writing has not
been done yet.
* Redid JSON generation using an internal buffer and helpers for speed.
* Added sorting and pruning of save items after registration.
* Added detection of duplicate entries.
* Fixed display of long names in save window.
* Moved timers into their own container.
Diffstat (limited to 'src/emu/save.cpp')
| -rw-r--r-- | src/emu/save.cpp | 1497 |
1 files changed, 652 insertions, 845 deletions
diff --git a/src/emu/save.cpp b/src/emu/save.cpp index d0f8c63b856..418d914f2ff 100644 --- a/src/emu/save.cpp +++ b/src/emu/save.cpp @@ -54,525 +54,821 @@ enum #define STATE_MAGIC_NUM "MAMESAVE" + //************************************************************************** -// INITIALIZATION +// INLINE HELPERS //************************************************************************** //------------------------------------------------- -// save_manager - constructor +// json_append - append a string to the JSON +// stream //------------------------------------------------- -save_manager::save_manager(running_machine &machine) - : m_machine(machine) - , m_reg_allowed(true) - , m_illegal_regs(0) - , m_root_registrar(m_root_item) +save_zip_state &save_zip_state::json_append(char const *buffer) { - m_rewind = std::make_unique<rewinder>(*this); + json_check_reserve(); + while (*buffer != 0) + json_append(*buffer++); + return *this; } //------------------------------------------------- -// allow_registration - allow/disallow -// registrations to happen +// json_append_indent - append an indentation of +// the given depth to the JSON stream //------------------------------------------------- -void save_manager::allow_registration(bool allowed) +save_zip_state &save_zip_state::json_append_indent(int count) { - // allow/deny registration - m_reg_allowed = allowed; - if (!allowed) - { - // look for duplicates - std::sort(m_entry_list.begin(), m_entry_list.end(), - [] (std::unique_ptr<state_entry> const& a, std::unique_ptr<state_entry> const& b) { return a->m_name < b->m_name; }); - - int dupes_found = 0; - for (int i = 1; i < m_entry_list.size(); i++) - { - if (m_entry_list[i - 1]->m_name == m_entry_list[i]->m_name) - { - osd_printf_error("Duplicate save state registration entry (%s)\n", m_entry_list[i]->m_name); - dupes_found++; - } - } + for (int index = 0; index < count; index++) + json_append('\t'); + return *this; +} - if (dupes_found) - fatalerror("%d duplicate save state entries found.\n", dupes_found); - dump_registry(); +//------------------------------------------------- +// json_append_name - append a string-ified name +// to the JSON stream +//------------------------------------------------- - // everything is registered by now, evaluate the savestate size - m_rewind->clamp_capacity(); - } +save_zip_state &save_zip_state::json_append_name(char const *name) +{ + if (name == nullptr || name[0] == 0) + return *this; + return json_append('"').json_append(name).json_append('"').json_append(':'); } //------------------------------------------------- -// indexed_item - return an item with the given -// index +// json_append_signed - append a signed integer +// value to the JSON stream //------------------------------------------------- -const char *save_manager::indexed_item(int index, void *&base, u32 &valsize, u32 &valcount, u32 &blockcount, u32 &stride) const +save_zip_state &save_zip_state::json_append_signed(int64_t value) { - if (index >= m_entry_list.size() || index < 0) - return nullptr; + json_check_reserve(); + + // quote values that don't fit into a double + bool quote = (int64_t(double(value)) != value); + if (quote) + json_append('"'); + + // just use sprintf -- is there a faster way? + char buffer[20]; + sprintf(buffer, "%lld", value); + json_append(buffer); + + // end quotes + if (quote) + json_append('"'); + return *this; +} - state_entry *entry = m_entry_list.at(index).get(); - base = entry->m_data; - valsize = entry->m_typesize; - valcount = entry->m_typecount; - blockcount = entry->m_blockcount; - stride = entry->m_stride; - return entry->m_name.c_str(); +//------------------------------------------------- +// json_append_unsigned - append an unsigned +// integer value to the JSON stream +//------------------------------------------------- + +save_zip_state &save_zip_state::json_append_unsigned(uint64_t value) +{ + json_check_reserve(); + + // quote values that don't fit into a double + bool quote = (uint64_t(double(value)) != value); + if (quote) + json_append('"'); + + // just use sprintf -- is there a faster way? + char buffer[20]; + sprintf(buffer, "%llu", value); + json_append(buffer); + + // end quotes + if (quote) + json_append('"'); + return *this; } //------------------------------------------------- -// register_presave - register a pre-save -// function callback +// json_append_float - append a floating-point +// value to the JSON stream //------------------------------------------------- -void save_manager::register_presave(save_prepost_delegate func) +save_zip_state &save_zip_state::json_append_float(double value) { - // check for invalid timing - if (!m_reg_allowed) - fatalerror("Attempt to register callback function after state registration is closed!\n"); + json_check_reserve(); + char buffer[20]; + sprintf(buffer, "%g", value); + return json_append(buffer); +} - // scan for duplicates and push through to the end - for (auto &cb : m_presave_list) - if (cb->m_func == func) - fatalerror("Duplicate save state function (%s/%s)\n", cb->m_func.name(), func.name()); - // allocate a new entry - m_presave_list.push_back(std::make_unique<state_callback>(func)); -} +//************************************************************************** +// SAVE REGISTERED ITEM +//************************************************************************** //------------------------------------------------- -// state_save_register_postload - -// register a post-load function callback +// save_registered_item - constructor //------------------------------------------------- -void save_manager::register_postload(save_prepost_delegate func) +save_registered_item::save_registered_item() : + m_ptr_offset(0), + m_type(TYPE_CONTAINER), + m_native_size(0) { - // check for invalid timing - if (!m_reg_allowed) - fatalerror("Attempt to register callback function after state registration is closed!\n"); - - // scan for duplicates and push through to the end - for (auto &cb : m_postload_list) - if (cb->m_func == func) - fatalerror("Duplicate save state function (%s/%s)\n", cb->m_func.name(), func.name()); +} - // allocate a new entry - m_postload_list.push_back(std::make_unique<state_callback>(func)); +// constructor for a new item +save_registered_item::save_registered_item(uintptr_t ptr_offset, save_type type, uint32_t native_size, char const *name) : + m_ptr_offset(ptr_offset), + m_type(type), + m_native_size(native_size), + m_name(name) +{ + // cleanup names a bit + if (m_name[0] == '*') + m_name.erase(0, 1); + if (m_name[0] == 'm' && m_name[1] == '_') + m_name.erase(0, 2); } //------------------------------------------------- -// check_file - check if a file is a valid save -// state +// append - append a new item to the current one //------------------------------------------------- -save_error save_manager::check_file(running_machine &machine, emu_file &file, const char *gamename, void (CLIB_DECL *errormsg)(const char *fmt, ...)) +std::string type_string(save_registered_item::save_type type, uint32_t native_size) { - // if we want to validate the signature, compute it - u32 sig; - sig = machine.save().signature(); - - // seek to the beginning and read the header - file.compress(FCOMPRESS_NONE); - file.seek(0, SEEK_SET); - u8 header[HEADER_SIZE]; - if (file.read(header, sizeof(header)) != sizeof(header)) + switch (type) { - if (errormsg != nullptr) - (*errormsg)("Could not read %s save file header",emulator_info::get_appname()); - return STATERR_READ_ERROR; + case save_registered_item::TYPE_CONTAINER: return "CONTAINER"; + case save_registered_item::TYPE_POINTER: return "POINTER"; + case save_registered_item::TYPE_UNIQUE: return "UNIQUE"; + case save_registered_item::TYPE_VECTOR: return "VECTOR"; + case save_registered_item::TYPE_STRUCT: return "STRUCT"; + case save_registered_item::TYPE_BOOL: return "BOOL"; + case save_registered_item::TYPE_INT: return string_format("INT%d", 8 * native_size); + case save_registered_item::TYPE_UINT: return string_format("UINT%d", 8 * native_size); + case save_registered_item::TYPE_FLOAT: return string_format("FLOAT%d", 8 * native_size); + default: return string_format("ARRAY[%d]", int(type)); } +} + +save_registered_item &save_registered_item::append(uintptr_t ptr_offset, save_type type, uint32_t native_size, char const *name) +{ + // make sure there are no duplicates + if (find(name) != nullptr) + throw emu_fatalerror("Duplicate save state registration '%s'\n", name); - // let the generic header check work out the rest - return validate_header(header, gamename, sig, errormsg, ""); +//printf("%s '%s': adding %s '%s' @ %llX, size %d\n", type_string(m_type, m_native_size).c_str(), m_name.c_str(), type_string(type, native_size).c_str(), name, ptr_offset, native_size); + + // add the item to the back of the list + m_items.emplace_back(ptr_offset, type, native_size, name); + return m_items.back(); } //------------------------------------------------- -// dispatch_postload - invoke all registered -// postload callbacks for updates +// find - find a subitem by name //------------------------------------------------- -void save_manager::dispatch_postload() +save_registered_item *save_registered_item::find(char const *name) { - for (auto &func : m_postload_list) - func->m_func(); + // blank names can't be found this way + if (name[0] == 0) + return nullptr; + + // make sure there are no duplicates + for (auto &item : m_items) + if (strcmp(item.name(), name) == 0) + return &item; + return nullptr; } //------------------------------------------------- -// dispatch_presave - invoke all registered -// presave callbacks for updates +// sort_and_prune - prune empty subitems and +// sort them by name //------------------------------------------------- -void save_manager::dispatch_presave() +bool save_registered_item::sort_and_prune() { - for (auto &func : m_presave_list) - func->m_func(); + // only applies to arrays, structs, and containers; don't prune anything else + if (m_type >= TYPE_ARRAY && m_type != TYPE_STRUCT && m_type != TYPE_CONTAINER) + return false; + + // first prune any empty items + for (auto it = m_items.begin(); it != m_items.end(); ) + { + if (it->sort_and_prune()) + it = m_items.erase(it); + else + ++it; + } + + // then sort the rest if we have more than 1 + if (m_items.size() > 1) + m_items.sort([] (auto const &x, auto const &y) { return (std::strcmp(x.name(), y.name()) < 0); }); + + // return true if we have nothing + return (m_items.size() == 0); } //------------------------------------------------- -// write_file - writes the data to a file +// unwrap_and_update_objbase - unwrap trivial +// type and update the object base //------------------------------------------------- -save_error save_manager::write_file(emu_file &file) +bool save_registered_item::unwrap_and_update_objbase(uintptr_t &objbase) const { - 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; - }); + // update the base pointer with our local base/offset + objbase += m_ptr_offset; + + // switch off the type + switch (m_type) + { + // unique ptrs retrieve the pointer from their container + case TYPE_UNIQUE: + objbase = reinterpret_cast<uintptr_t>(reinterpret_cast<generic_unique *>(objbase)->get()); + return true; + + // vectors retrieve the pointer from their container + case TYPE_VECTOR: + objbase = reinterpret_cast<uintptr_t>(&(*reinterpret_cast<generic_vector *>(objbase))[0]); + return true; + + // pointers just extract the pointer directly + case TYPE_POINTER: + objbase = reinterpret_cast<uintptr_t>(*reinterpret_cast<generic_pointer *>(objbase)); + return true; + + // containers are always based at 0 + case TYPE_CONTAINER: + objbase = 0; + return false; + + // everything else is as-is + default: + return false; + } } //------------------------------------------------- -// read_file - read the data from a file +// save_binary - save this item and all owned +// items into a binary form //------------------------------------------------- -save_error save_manager::read_file(emu_file &file) +uint64_t save_registered_item::save_binary(uint8_t *ptr, uint64_t length, uintptr_t objbase) const { - 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] () + // update the base pointer and forward if a trivial unwrap + if (unwrap_and_update_objbase(objbase)) + return m_items.front().save_binary(ptr, length, objbase); + + // switch off the type + uint64_t offset = 0; + switch (m_type) + { + // boolean types save as a single byte + case TYPE_BOOL: + if (offset + 1 <= length) + ptr[offset] = *reinterpret_cast<bool const *>(objbase) ? 1 : 0; + offset++; + break; + + // integral/float types save as their native size + case TYPE_INT: + case TYPE_UINT: + case TYPE_FLOAT: + if (offset + m_native_size <= length) + memcpy(&ptr[offset], reinterpret_cast<void const *>(objbase), m_native_size); + offset += m_native_size; + break; + + // structs and containers iterate over owned items + case TYPE_CONTAINER: + case TYPE_STRUCT: + for (auto &item : m_items) + offset += item.save_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase); + break; + + // arrays are multiples of a single item + default: + if (m_type < TYPE_ARRAY) { - file.compress(FCOMPRESS_MEDIUM); - return true; - }); + auto &item = m_items.front(); + for (uint32_t rep = 0; rep < m_type; rep++) + offset += item.save_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase + rep * m_native_size); + } + break; + } + return offset; } //------------------------------------------------- -// write_stream - write the current machine state -// to an output stream +// restore_binary - restore this item and all +// owned items from binary form //------------------------------------------------- -save_error save_manager::write_stream(std::ostream &str) +uint64_t save_registered_item::restore_binary(uint8_t const *ptr, uint64_t length, uintptr_t objbase) const { - return do_write( - [] (size_t total_size) { return true; }, - [&str] (const void *data, size_t size) + // update the base pointer and forward if a trivial unwrap + if (unwrap_and_update_objbase(objbase)) + return m_items.front().restore_binary(ptr, length, objbase); + + // switch off the type + uint64_t offset = 0; + switch (m_type) + { + // boolean types save as a single byte + case TYPE_BOOL: + if (offset + 1 <= length) + *reinterpret_cast<bool *>(objbase) = (ptr[offset] != 0); + offset++; + break; + + // integral/float types save as their native size + case TYPE_INT: + case TYPE_UINT: + case TYPE_FLOAT: + if (offset + m_native_size <= length) + memcpy(reinterpret_cast<void *>(objbase), &ptr[offset], m_native_size); + offset += m_native_size; + break; + + // structs and containers iterate over owned items + case TYPE_CONTAINER: + case TYPE_STRUCT: + for (auto &item : m_items) + offset += item.restore_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase); + break; + + // arrays are multiples of a single item + default: + if (m_type < TYPE_ARRAY) { - return bool(str.write(reinterpret_cast<const char *>(data), size)); - }, - [] () { return true; }, - [] () { return true; }); + auto &item = m_items.front(); + for (uint32_t rep = 0; rep < m_type; rep++) + offset += item.restore_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase + rep * m_native_size); + } + break; + } + return offset; } //------------------------------------------------- -// read_stream - restore the machine state from -// an input stream +// save_json - save this item into a JSON stream //------------------------------------------------- -save_error save_manager::read_stream(std::istream &str) +void save_registered_item::save_json(save_zip_state &zipstate, char const *nameprefix, int indent, bool inline_form, uintptr_t objbase) { - return do_read( - [] (size_t total_size) { return true; }, - [&str] (void *data, size_t size) + // update the base pointer and forward if a trivial unwrap + if (unwrap_and_update_objbase(objbase)) + return m_items.front().save_json(zipstate, nameprefix, indent, inline_form, objbase); + + // update the name prefix + std::string localname = nameprefix; + if (m_name.length() != 0) + { + if (localname.length() != 0) + localname += "."; + localname += m_name; + } + + // output the name if present + zipstate.json_append_name(m_name.c_str()); + + // switch off the type + switch (m_type) + { + // boolean types + case TYPE_BOOL: + zipstate.json_append(*reinterpret_cast<bool const *>(objbase) ? "true" : "false"); + break; + + // signed integral types + case TYPE_INT: + zipstate.json_append_signed(read_int_signed(objbase, m_native_size)); + break; + + // unsigned integral types + case TYPE_UINT: + zipstate.json_append_unsigned(read_int_unsigned(objbase, m_native_size)); + break; + + // float types + case TYPE_FLOAT: + zipstate.json_append_float(read_float(objbase, m_native_size)); + break; + + // structs and containers iterate over owned items + case TYPE_CONTAINER: + case TYPE_STRUCT: + if (inline_form || compute_binary_size(objbase - m_ptr_offset) <= 16) { - return bool(str.read(reinterpret_cast<char *>(data), size)); - }, - [] () { return true; }, - [] () { return true; }); + // inline form outputs everything on a single line + zipstate.json_append('{'); + for (auto &item : m_items) + { + item.save_json(zipstate, localname.c_str(), indent, true, objbase); + if (&item != &m_items.back()) + zipstate.json_append(','); + } + zipstate.json_append('}'); + } + else + { + // normal form outputs each item on its own line, indented + zipstate.json_append('{').json_append_eol(); + for (auto &item : m_items) + { + zipstate.json_append_indent(indent + 1); + item.save_json(zipstate, localname.c_str(), indent + 1, false, objbase); + if (&item != &m_items.back()) + zipstate.json_append(','); + zipstate.json_append_eol(); + } + zipstate.json_append_indent(indent).json_append('}'); + } + break; + + // arrays are multiples of a single item + default: + if (m_type < TYPE_ARRAY) + { + auto &item = m_items.front(); + + // look for large arrays of ints/floats + save_registered_item *inner = &item; + u32 total = count(); + while (inner->type() < TYPE_ARRAY) + { + total *= inner->count(); + inner = &inner->m_items.front(); + } + if ((inner->type() == TYPE_INT || inner->type() == TYPE_UINT || inner->type() == TYPE_FLOAT) && total * inner->m_native_size >= save_zip_state::JSON_EXTERNAL_BINARY_THRESHOLD) + { + std::string filename = localname; + for (int index = 0; index < filename.length(); ) + if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.", filename[index]) == nullptr) + { + if (index != 0 && filename[index - 1] != '.') + filename[index++] = '.'; + else + filename.erase(index, 1); + } + else + index++; + + zipstate.json_append('[').json_append('{'); + zipstate.json_append_name("external_file"); + zipstate.json_append('"').json_append(filename.c_str()).json_append('"').json_append(','); + zipstate.json_append_name("unit"); + zipstate.json_append_signed(inner->m_native_size).json_append(','); + zipstate.json_append_name("count"); + zipstate.json_append_signed(total).json_append(','); + zipstate.json_append_name("little_endian"); + zipstate.json_append((ENDIANNESS_NATIVE == ENDIANNESS_LITTLE) ? "true" : "false"); + zipstate.json_append('}').json_append(']'); + + zipstate.add_data_file(filename.c_str(), item, reinterpret_cast<void *>(objbase)); + } + else + { + uint32_t item_size = item.compute_binary_size(objbase); + if (inline_form || m_type * item_size <= 16) + { + // strictly inline form outputs everything on a single line + zipstate.json_append('['); + for (uint32_t rep = 0; rep < m_type; rep++) + { + item.save_json(zipstate, localname.c_str(), 0, true, objbase + rep * m_native_size); + if (rep != m_type - 1) + zipstate.json_append(','); + } + zipstate.json_append(']'); + } + else + { + // normal form outputs a certain number of items per row + zipstate.json_append('[').json_append_eol(); + uint32_t items_per_row = 0; + if (item.m_type == TYPE_INT || item.m_type == TYPE_UINT || item.m_type == TYPE_FLOAT) + items_per_row = 32 / item_size; + if (items_per_row == 0) + items_per_row = 1; + + // iterate over the items + for (uint32_t rep = 0; rep < m_type; rep++) + { + if (rep % items_per_row == 0) + zipstate.json_append_indent(indent + 1); + item.save_json(zipstate, localname.c_str(), indent + 1, false, objbase + rep * m_native_size); + if (rep != m_type - 1) + zipstate.json_append(','); + if (rep % items_per_row == items_per_row - 1) + zipstate.json_append_eol(); + } + if (m_type % items_per_row != 0) + zipstate.json_append_eol(); + zipstate.json_append_indent(indent).json_append(']'); + } + } + } + break; + } } //------------------------------------------------- -// write_buffer - write the current machine state -// to an allocated buffer +// read_int_unsigned - read an unsigned integer +// of the given size //------------------------------------------------- -save_error save_manager::write_buffer(void *buf, size_t size) +uint64_t save_registered_item::read_int_unsigned(uintptr_t objbase, int 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; }); + switch (size) + { + case 1: return *reinterpret_cast<uint8_t const *>(objbase); + case 2: return *reinterpret_cast<uint16_t const *>(objbase); + case 4: return *reinterpret_cast<uint32_t const *>(objbase); + case 8: return *reinterpret_cast<uint64_t const *>(objbase); + } + return 0; } //------------------------------------------------- -// read_buffer - restore the machine state from a -// buffer +// read_int_signed - read a signed integer of the +// given size //------------------------------------------------- -save_error save_manager::read_buffer(const void *buf, size_t size) +int64_t save_registered_item::read_int_signed(uintptr_t objbase, int 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; }); + switch (size) + { + case 1: return *reinterpret_cast<int8_t const *>(objbase); + case 2: return *reinterpret_cast<int16_t const *>(objbase); + case 4: return *reinterpret_cast<int32_t const *>(objbase); + case 8: return *reinterpret_cast<int64_t const *>(objbase); + } + return 0; } //------------------------------------------------- -// do_write - serialisation logic +// read_float - read a floating-point value of the +// given 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) +double save_registered_item::read_float(uintptr_t objbase, int size) { - // if we have illegal registrations, return an error - if (m_illegal_regs > 0) - return STATERR_ILLEGAL_REGISTRATIONS; - - // 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; + switch (size) + { + case 4: return *reinterpret_cast<float const *>(objbase); + case 8: return *reinterpret_cast<double const *>(objbase); + } + return 0; +} - // 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 - if (!start_header() || !write_block(header, sizeof(header)) || !start_data()) - return STATERR_WRITE_ERROR; - // call the pre-save functions - dispatch_presave(); +//------------------------------------------------- +// write_int - write an integer of the given size +//------------------------------------------------- - // then write all the data - for (auto &entry : m_entry_list) +void save_registered_item::write_int(uintptr_t objbase, int size, uint64_t data) +{ + switch (size) { - 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_stride) - if (!write_block(data, blocksize)) - return STATERR_WRITE_ERROR; + case 1: *reinterpret_cast<uint8_t *>(objbase) = uint8_t(data); break; + case 2: *reinterpret_cast<uint16_t *>(objbase) = uint16_t(data); break; + case 4: *reinterpret_cast<uint32_t *>(objbase) = uint32_t(data); break; + case 8: *reinterpret_cast<uint64_t *>(objbase) = uint64_t(data); break; } - return STATERR_NONE; } //------------------------------------------------- -// do_read - deserialisation logic +// write_float - write a floating-point value of +// the given 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) +void save_registered_item::write_float(uintptr_t objbase, int size, double data) { - // if we have illegal registrations, return an error - if (m_illegal_regs > 0) - return STATERR_ILLEGAL_REGISTRATIONS; - - // 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; + switch (size) + { + case 4: *reinterpret_cast<float *>(objbase) = float(data); break; + case 8: *reinterpret_cast<double *>(objbase) = double(data); break; + } +} - // read the header and turn on compression for the rest of the file - u8 header[HEADER_SIZE]; - 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(); - 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 - const bool flip = NATIVE_ENDIAN_VALUE_LE_BE((header[9] & SS_MSB_FIRST) != 0, (header[9] & SS_MSB_FIRST) == 0); +//************************************************************************** +// SAVE MANAGER +//************************************************************************** - // read all the data, flipping if necessary - for (auto &entry : m_entry_list) +//------------------------------------------------- +// save_manager - constructor +//------------------------------------------------- + +save_manager::save_manager(running_machine &machine) : + m_machine(machine), + m_reg_allowed(true), + m_root_registrar(m_root_item) +{ + m_rewind = std::make_unique<rewinder>(*this); +} + + +//------------------------------------------------- +// allow_registration - allow/disallow +// registrations to happen +//------------------------------------------------- + +void save_manager::allow_registration(bool allowed) +{ + // allow/deny registration + m_reg_allowed = allowed; + if (!allowed) { - 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_stride) - if (!read_block(data, blocksize)) - return STATERR_READ_ERROR; - - // handle flipping - if (flip) - entry->flip_data(); + // prune and sort + m_root_item.sort_and_prune(); + + // dump out a sample JSON + { + save_zip_state state; + m_root_item.save_json(state); + printf("%s\n", state.json_string()); + } + + // everything is registered by now, evaluate the savestate size + m_rewind->clamp_capacity(); } +} - // call the post-load functions - dispatch_postload(); - return STATERR_NONE; +//------------------------------------------------- +// register_presave - register a pre-save +// function callback +//------------------------------------------------- + +void save_manager::register_presave(save_prepost_delegate func) +{ + // check for invalid timing + if (!m_reg_allowed) + fatalerror("Attempt to register callback function after state registration is closed!\n"); + + // scan for duplicates and push through to the end + for (auto &cb : m_presave_list) + if (cb->m_func == func) + fatalerror("Duplicate save state function (%s/%s)\n", cb->m_func.name(), func.name()); + + // allocate a new entry + m_presave_list.push_back(std::make_unique<state_callback>(func)); } //------------------------------------------------- -// signature - compute the signature, which -// is a CRC over the structure of the data +// state_save_register_postload - +// register a post-load function callback //------------------------------------------------- -u32 save_manager::signature() const +void save_manager::register_postload(save_prepost_delegate func) { - // iterate over entries - u32 crc = 0; - for (auto &entry : m_entry_list) - { - // add the entry name to the CRC - crc = core_crc32(crc, (u8 *)entry->m_name.c_str(), entry->m_name.length()); - - // add the type and size to the CRC - u32 temp[4]; - temp[0] = little_endianize_int32(entry->m_typesize); - temp[1] = little_endianize_int32(entry->m_typecount); - temp[2] = little_endianize_int32(entry->m_blockcount); - temp[3] = little_endianize_int32(entry->m_stride); - crc = core_crc32(crc, (u8 *)&temp[0], sizeof(temp)); - } - return crc; + // check for invalid timing + if (!m_reg_allowed) + fatalerror("Attempt to register callback function after state registration is closed!\n"); + + // scan for duplicates and push through to the end + for (auto &cb : m_postload_list) + if (cb->m_func == func) + fatalerror("Duplicate save state function (%s/%s)\n", cb->m_func.name(), func.name()); + + // allocate a new entry + m_postload_list.push_back(std::make_unique<state_callback>(func)); } //------------------------------------------------- -// dump_registry - dump the registry to the -// logfile +// dispatch_postload - invoke all registered +// postload callbacks for updates //------------------------------------------------- -void save_manager::dump_registry() const +void save_manager::dispatch_postload() { - for (auto &entry : m_entry_list) - LOG(("%s: %u x %u x %u (%u)\n", entry->m_name.c_str(), entry->m_typesize, entry->m_typecount, entry->m_blockcount, entry->m_stride)); + for (auto &func : m_postload_list) + func->m_func(); } //------------------------------------------------- -// validate_header - validate the data in the -// header +// dispatch_presave - invoke all registered +// presave callbacks for updates //------------------------------------------------- -save_error save_manager::validate_header(const u8 *header, const char *gamename, u32 signature, - void (CLIB_DECL *errormsg)(const char *fmt, ...), const char *error_prefix) +void save_manager::dispatch_presave() { - // check magic number - if (memcmp(header, STATE_MAGIC_NUM, 8)) - { - if (errormsg != nullptr) - (*errormsg)("%sThis is not a %s save file", error_prefix,emulator_info::get_appname()); - return STATERR_INVALID_HEADER; - } + for (auto &func : m_presave_list) + func->m_func(); +} - // check save state version - if (header[8] != SAVE_VERSION) - { - if (errormsg != nullptr) - (*errormsg)("%sWrong version in save file (version %d, expected %d)", error_prefix, header[8], SAVE_VERSION); - return STATERR_INVALID_HEADER; - } - // check gamename, if we were asked to - if (gamename != nullptr && strncmp(gamename, (const char *)&header[0x0a], 0x1c - 0x0a)) - { - if (errormsg != nullptr) - (*errormsg)("%s'File is not a valid savestate file for game '%s'.", error_prefix, gamename); - return STATERR_INVALID_HEADER; - } +//------------------------------------------------- +// save_binary - invoke all registered presave +// callbacks for updates and then generate the +// data in binary form +//------------------------------------------------- + +save_error save_manager::save_binary(void *buf, size_t size) +{ + // call the pre-save functions + dispatch_presave(); + + // write the output + u64 finalsize = m_root_item.save_binary(reinterpret_cast<u8 *>(buf), size); + if (finalsize != size) + return STATERR_WRITE_ERROR; - // check signature, if we were asked to - if (signature != 0) - { - u32 rawsig = *(u32 *)&header[0x1c]; - if (signature != little_endianize_int32(rawsig)) - { - if (errormsg != nullptr) - (*errormsg)("%sIncompatible save file (signature %08x, expected %08x)", error_prefix, little_endianize_int32(rawsig), signature); - return STATERR_INVALID_HEADER; - } - } return STATERR_NONE; } //------------------------------------------------- -// state_callback - constructor +// load_binary - restore all data and then call +// the postload callbacks //------------------------------------------------- -save_manager::state_callback::state_callback(save_prepost_delegate callback) - : m_func(std::move(callback)) +save_error save_manager::load_binary(void *buf, size_t size) { + // read the input + u64 finalsize = m_root_item.restore_binary(reinterpret_cast<u8 *>(buf), size); + if (finalsize != size) + return STATERR_READ_ERROR; + + // call the post-load functions + dispatch_postload(); + return STATERR_NONE; } //------------------------------------------------- -// ram_state - constructor +// save_file - invoke all registered presave +// callbacks for updates and then generate the +// data in JSON/ZIP form //------------------------------------------------- -ram_state::ram_state(save_manager &save) - : m_save(save) - , m_data() - , m_valid(false) - , m_time(m_save.machine().time()) +save_error save_manager::save_file(emu_file &file) { - m_data.reserve(get_size(save)); - m_data.clear(); - m_data.rdbuf()->clear(); - m_data.seekp(0); - m_data.seekg(0); + // call the pre-save functions + dispatch_presave(); + + // create the JSON and target all the output files + save_zip_state state; + m_root_item.save_json(state); + + // write the output + __debugbreak(); + + return STATERR_NONE; } //------------------------------------------------- -// get_size - utility function to get the -// uncompressed size of a state +// load_file - restore all data and then call +// the postload callbacks //------------------------------------------------- -size_t ram_state::get_size(save_manager &save) +save_error save_manager::load_file(emu_file &file) { - size_t totalsize = 0; + __debugbreak(); + + // call the post-load functions + dispatch_postload(); + return STATERR_NONE; +} + + + +//************************************************************************** +// RAM STATE +//************************************************************************** - for (auto &entry : save.m_entry_list) - totalsize += entry->m_typesize * entry->m_typecount * entry->m_blockcount; +//------------------------------------------------- +// ram_state - constructor +//------------------------------------------------- - return totalsize + HEADER_SIZE; +ram_state::ram_state(save_manager &save) : + m_valid(false), + m_time(m_save.machine().time()), + m_save(save) +{ } @@ -585,10 +881,9 @@ save_error ram_state::save() { // initialize m_valid = false; - m_data.seekp(0); // get the save manager to write state - const save_error err = m_save.write_stream(m_data); + const save_error err = m_save.save_binary(m_data); if (err != STATERR_NONE) return err; @@ -607,30 +902,28 @@ save_error ram_state::save() 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; - // get the save manager to load state - return m_save.read_stream(m_data); + return m_save.load_binary(m_data); } + +//************************************************************************** +// REWINDER +//************************************************************************** + //------------------------------------------------- // rewinder - constuctor //------------------------------------------------- -rewinder::rewinder(save_manager &save) - : m_save(save) - , m_enabled(save.machine().options().rewind()) - , m_capacity(save.machine().options().rewind_capacity()) - , m_current_index(REWIND_INDEX_NONE) - , m_first_invalid_index(REWIND_INDEX_NONE) - , m_first_time_warning(true) - , m_first_time_note(true) +rewinder::rewinder(save_manager &save) : + m_save(save), + m_enabled(save.machine().options().rewind()), + m_capacity(save.machine().options().rewind_capacity()), + m_current_index(REWIND_INDEX_NONE), + m_first_invalid_index(REWIND_INDEX_NONE), + m_first_time_warning(true), + m_first_time_note(true) { } @@ -646,7 +939,7 @@ void rewinder::clamp_capacity() return; const size_t total = m_capacity * 1024 * 1024; - const size_t single = ram_state::get_size(m_save); + const size_t single = m_save.binary_size(); // can't set below zero, but allow commandline to override options' upper limit if (total < 0) @@ -803,7 +1096,7 @@ bool rewinder::check_size() return false; // state sizes in bytes - const size_t singlesize = ram_state::get_size(m_save); + const size_t singlesize = m_save.binary_size(); size_t totalsize = m_state_list.size() * singlesize; // convert our limit from megabytes @@ -928,508 +1221,22 @@ void rewinder::report_error(save_error error, rewind_operation operation) } -//------------------------------------------------- -// state_entry - constructor -//------------------------------------------------- - -save_manager::state_entry::state_entry( - void *data, - std::string &&name, device_t *device, std::string &&module, std::string &&tag, int index, - u8 size, u32 valcount, u32 blockcount, u32 stride) - : m_data(data) - , m_name(std::move(name)) - , m_device(device) - , m_module(std::move(module)) - , m_tag(std::move(tag)) - , m_index(index) - , m_typesize(size) - , m_typecount(valcount) - , m_blockcount(blockcount) - , m_stride(stride) -{ -} - - -//------------------------------------------------- -// flip_data - reverse the endianness of a -// block of data -//------------------------------------------------- - -void save_manager::state_entry::flip_data() -{ - u8 *data = reinterpret_cast<u8 *>(m_data); - for (u32 b = 0; m_blockcount > b; ++b, data += m_stride) - { - u16 *data16; - u32 *data32; - u64 *data64; - - switch (m_typesize) - { - case 2: - data16 = reinterpret_cast<u16 *>(data); - for (u32 count = 0; count < m_typecount; count++) - data16[count] = swapendian_int16(data16[count]); - break; - - case 4: - data32 = reinterpret_cast<u32 *>(data); - for (u32 count = 0; count < m_typecount; count++) - data32[count] = swapendian_int32(data32[count]); - break; - - case 8: - data64 = reinterpret_cast<u64 *>(data); - for (u32 count = 0; count < m_typecount; count++) - data64[count] = swapendian_int64(data64[count]); - break; - } - } -} - - -//************************************************************************** -// INITIALIZATION -//************************************************************************** - -//------------------------------------------------- -// save_registered_item - constructor -//------------------------------------------------- - -save_registered_item::save_registered_item() : - m_ptr_offset(0), - m_type(TYPE_CONTAINER), - m_native_size(0) -{ -} - -// constructor for a new item -save_registered_item::save_registered_item(uintptr_t ptr_offset, save_type type, uint32_t native_size, char const *name) : - m_ptr_offset(ptr_offset), - m_type(type), - m_native_size(native_size), - m_name(name) -{ - // cleanup names a bit - if (m_name[0] == '*') - m_name.erase(0, 1); - if (m_name[0] == 'm' && m_name[1] == '_') - m_name.erase(0, 2); -} - - -//------------------------------------------------- -// append - append a new item to the current one -//------------------------------------------------- - -static std::string type_string(save_registered_item::save_type type, uint32_t native_size) -{ - switch (type) - { - case save_registered_item::TYPE_CONTAINER: return "CONTAINER"; - case save_registered_item::TYPE_POINTER: return "POINTER"; - case save_registered_item::TYPE_UNIQUE: return "UNIQUE"; - case save_registered_item::TYPE_VECTOR: return "VECTOR"; - case save_registered_item::TYPE_STRUCT: return "STRUCT"; - case save_registered_item::TYPE_BOOL: return "BOOL"; - case save_registered_item::TYPE_INT: return string_format("INT%d", 8 * native_size); - case save_registered_item::TYPE_UINT: return string_format("UINT%d", 8 * native_size); - case save_registered_item::TYPE_FLOAT: return string_format("FLOAT%d", 8 * native_size); - default: return string_format("ARRAY[%d]", int(type)); - } -} - -save_registered_item &save_registered_item::append(uintptr_t ptr_offset, save_type type, uint32_t native_size, char const *name) -{ -printf("%s '%s': adding %s '%s' @ %llX, size %d\n", type_string(m_type, m_native_size).c_str(), m_name.c_str(), type_string(type, native_size).c_str(), name, ptr_offset, native_size); - m_items.emplace_back(ptr_offset, type, native_size, name); - return m_items.back(); -} - - -//------------------------------------------------- -// unwrap_and_update_objbase - unwrap trivial -// type and update the object base -//------------------------------------------------- - -bool save_registered_item::unwrap_and_update_objbase(uintptr_t &objbase) const -{ - // update the base pointer with our local base/offset - objbase += m_ptr_offset; - - // switch off the type - switch (m_type) - { - // unique ptrs retrieve the pointer from their container - case TYPE_UNIQUE: - objbase = reinterpret_cast<uintptr_t>(reinterpret_cast<generic_unique *>(objbase)->get()); - return true; - - // vectors retrieve the pointer from their container - case TYPE_VECTOR: - objbase = reinterpret_cast<uintptr_t>(&(*reinterpret_cast<generic_vector *>(objbase))[0]); - return true; - - // pointers just extract the pointer directly - case TYPE_POINTER: - objbase = reinterpret_cast<uintptr_t>(*reinterpret_cast<generic_pointer *>(objbase)); - return true; - - // containers are always based at 0 - case TYPE_CONTAINER: - objbase = 0; - return false; - - // everything else is as-is - default: - return false; - } -} - - -//------------------------------------------------- -// save_binary - save this item and all owned -// items into a binary form -//------------------------------------------------- - -uint64_t save_registered_item::save_binary(uint8_t *ptr, uint64_t length, uintptr_t objbase) const -{ - // update the base pointer and forward if a trivial unwrap - if (unwrap_and_update_objbase(objbase)) - return m_items.front().save_binary(ptr, length, objbase); - - // switch off the type - uint64_t offset = 0; - switch (m_type) - { - // boolean types save as a single byte - case TYPE_BOOL: - if (offset + 1 <= length) - ptr[offset] = *reinterpret_cast<bool const *>(objbase) ? 1 : 0; - offset++; - break; - - // integral/float types save as their native size - case TYPE_INT: - case TYPE_UINT: - case TYPE_FLOAT: - if (offset + m_native_size <= length) - memcpy(&ptr[offset], reinterpret_cast<void const *>(objbase), m_native_size); - offset += m_native_size; - break; - - // structs and containers iterate over owned items - case TYPE_CONTAINER: - case TYPE_STRUCT: - for (auto &item : m_items) - offset += item.save_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase); - break; - - // arrays are multiples of a single item - default: - if (m_type < TYPE_ARRAY) - { - auto &item = m_items.front(); - for (uint32_t rep = 0; rep < m_type; rep++) - offset += item.save_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase + rep * m_native_size); - } - break; - } - return offset; -} - - -//------------------------------------------------- -// restore_binary - restore this item and all -// owned items from binary form -//------------------------------------------------- - -uint64_t save_registered_item::restore_binary(uint8_t const *ptr, uint64_t length, uintptr_t objbase) const -{ - // update the base pointer and forward if a trivial unwrap - if (unwrap_and_update_objbase(objbase)) - return m_items.front().restore_binary(ptr, length, objbase); - - // switch off the type - uint64_t offset = 0; - switch (m_type) - { - // boolean types save as a single byte - case TYPE_BOOL: - if (offset + 1 <= length) - *reinterpret_cast<bool *>(objbase) = (ptr[offset] != 0); - offset++; - break; - - // integral/float types save as their native size - case TYPE_INT: - case TYPE_UINT: - case TYPE_FLOAT: - if (offset + m_native_size <= length) - memcpy(reinterpret_cast<void *>(objbase), &ptr[offset], m_native_size); - offset += m_native_size; - break; - - // structs and containers iterate over owned items - case TYPE_CONTAINER: - case TYPE_STRUCT: - for (auto &item : m_items) - offset += item.restore_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase); - break; - - // arrays are multiples of a single item - default: - if (m_type < TYPE_ARRAY) - { - auto &item = m_items.front(); - for (uint32_t rep = 0; rep < m_type; rep++) - offset += item.restore_binary(&ptr[offset], (offset < length) ? length - offset : 0, objbase + rep * m_native_size); - } - break; - } - return offset; -} - - -//------------------------------------------------- -// save_json - save this item into a JSON stream -//------------------------------------------------- - -void save_registered_item::save_json(save_zip_state &zipstate, int indent, bool inline_form, uintptr_t objbase) -{ - // update the base pointer and forward if a trivial unwrap - if (unwrap_and_update_objbase(objbase)) - return m_items.front().save_json(zipstate, indent, inline_form, objbase); - - // output the name if present - auto &output = zipstate.json(); - if (m_name.length() > 0) - output << "\"" << m_name << "\": "; - - // switch off the type - switch (m_type) - { - // boolean types - case TYPE_BOOL: - output << (*reinterpret_cast<bool const *>(objbase) ? "true" : "false"); - break; - - // signed integral types - case TYPE_INT: - { - int64_t value = read_int_signed(objbase, m_native_size); - char const *quote = (value == int64_t(double(value))) ? "" : "\""; - output << quote << value << quote; - break; - } - - // unsigned integral types - case TYPE_UINT: - { - uint64_t value = read_int_unsigned(objbase, m_native_size); - char const *quote = (value == uint64_t(double(value))) ? "" : "\""; - output << quote << "0x" << std::setw(m_native_size * 2) << std::setfill('0') << std::hex << value << quote; - output << std::setw(0) << std::setfill(' ') << std::dec; - break; - } - - // float types - case TYPE_FLOAT: - { - double value = read_float(objbase, m_native_size); - output << value; - break; - } - - // structs and containers iterate over owned items - case TYPE_CONTAINER: - case TYPE_STRUCT: - if (inline_form || compute_binary_size(objbase - m_ptr_offset) <= 16) - { - // inline form outputs everything on a single line - output << "{ "; - for (auto &item : m_items) - { - item.save_json(zipstate, indent, true, objbase); - if (&item != &m_items.back()) - output << ", "; - } - output << " }"; - } - else - { - // normal form outputs each item on its own line, indented - output << "{" << std::endl; - for (auto &item : m_items) - { - output << std::setw(indent + 1) << std::setfill('\t') << "" << std::setw(0); - item.save_json(zipstate, indent + 1, false, objbase); - if (&item != &m_items.back()) - output << ","; - output << std::endl; - } - output << std::setw(indent) << std::setfill('\t') << "" << std::setw(0) - << "}"; - } - break; - - // arrays are multiples of a single item - default: - if (m_type < TYPE_ARRAY) - { - auto &item = m_items.front(); - uint32_t item_size = item.compute_binary_size(objbase); - if (inline_form || m_type * item_size <= 16) - { - // strictly inline form outputs everything on a single line - output << "[ "; - for (uint32_t rep = 0; rep < m_type; rep++) - { - item.save_json(zipstate, 0, true, objbase + rep * m_native_size); - if (rep != m_type - 1) - output << ","; - } - output << " ]"; - } - else - { - // normal form outputs a certain number of items per row - output << "[" << std::endl; - uint32_t items_per_row = 0; - if (item.m_type == TYPE_INT || item.m_type == TYPE_UINT || item.m_type == TYPE_FLOAT) - items_per_row = 32 / item_size; - if (items_per_row == 0) - items_per_row = 1; - - // iterate over the items - for (uint32_t rep = 0; rep < m_type; rep++) - { - if (rep % items_per_row == 0) - output << std::setw(indent + 1) << std::setfill('\t') << "" << std::setw(0); - item.save_json(zipstate, indent + 1, false, objbase + rep * m_native_size); - if (rep != m_type - 1) - output << ","; - if (rep % items_per_row == items_per_row - 1) - output << std::endl; - } - if (m_type % items_per_row != 0) - output << std::endl; - output << std::setw(indent) << std::setfill('\t') << "" << std::setw(0) - << "]"; - } - } - break; - } -} - - -//------------------------------------------------- -// read_int_unsigned - read an unsigned integer -// of the given size -//------------------------------------------------- - -uint64_t save_registered_item::read_int_unsigned(uintptr_t objbase, int size) -{ - switch (size) - { - case 1: return *reinterpret_cast<uint8_t const *>(objbase); - case 2: return *reinterpret_cast<uint16_t const *>(objbase); - case 4: return *reinterpret_cast<uint32_t const *>(objbase); - case 8: return *reinterpret_cast<uint64_t const *>(objbase); - } - return 0; -} - - -//------------------------------------------------- -// read_int_signed - read a signed integer of the -// given size -//------------------------------------------------- - -int64_t save_registered_item::read_int_signed(uintptr_t objbase, int size) -{ - switch (size) - { - case 1: return *reinterpret_cast<int8_t const *>(objbase); - case 2: return *reinterpret_cast<int16_t const *>(objbase); - case 4: return *reinterpret_cast<int32_t const *>(objbase); - case 8: return *reinterpret_cast<int64_t const *>(objbase); - } - return 0; -} - - -//------------------------------------------------- -// read_float - read a floating-point value of the -// given size -//------------------------------------------------- - -double save_registered_item::read_float(uintptr_t objbase, int size) -{ - switch (size) - { - case 4: return *reinterpret_cast<float const *>(objbase); - case 8: return *reinterpret_cast<double const *>(objbase); - } - return 0; -} - - -//------------------------------------------------- -// write_int - write an integer of the given size -//------------------------------------------------- - -void save_registered_item::write_int(uintptr_t objbase, int size, uint64_t data) -{ - switch (size) - { - case 1: *reinterpret_cast<uint8_t *>(objbase) = uint8_t(data); break; - case 2: *reinterpret_cast<uint16_t *>(objbase) = uint16_t(data); break; - case 4: *reinterpret_cast<uint32_t *>(objbase) = uint32_t(data); break; - case 8: *reinterpret_cast<uint64_t *>(objbase) = uint64_t(data); break; - } -} - - -//------------------------------------------------- -// write_float - write a floating-point value of -// the given size -//------------------------------------------------- - -void save_registered_item::write_float(uintptr_t objbase, int size, double data) -{ - switch (size) - { - case 4: *reinterpret_cast<float *>(objbase) = float(data); break; - case 8: *reinterpret_cast<double *>(objbase) = double(data); break; - } -} - void save_manager::test_dump() { save_zip_state state; m_root_item.save_json(state); - printf("%s\n", state.json().str().c_str()); + printf("%s\n", state.json_string()); } -save_zip_state::save_zip_state() +save_zip_state::save_zip_state() : + m_json_reserved(0), + m_json_offset(0) { -} - -void save_zip_state::add_data_file(char const *name, void *base, uint32_t size) -{ - m_file_list.emplace_back(name, base, size); + json_check_reserve(); } void save_zip_state::commit(FILE &output) { - add_data_file("save.json", &m_json.str()[0], m_json.str().length()); -} - -save_zip_state::file_entry::file_entry(char const *name, void *base, uint32_t size) : - m_name(name), - m_base(base), - m_size(size) -{ +// add_data_file("save.json", &m_json.str()[0], m_json.str().length()); } |
