openMSX
serialize.cc
Go to the documentation of this file.
1 #include "serialize.hh"
2 #include "Base64.hh"
3 #include "HexDump.hh"
4 #include "XMLLoader.hh"
5 #include "XMLElement.hh"
6 #include "ConfigException.hh"
7 #include "XMLException.hh"
8 #include "DeltaBlock.hh"
9 #include "MemBuffer.hh"
10 #include "FileOperations.hh"
11 #include "Version.hh"
12 #include "Date.hh"
13 #include "one_of.hh"
14 #include "stl.hh"
15 #include "build-info.hh"
16 #include "cstdiop.hh" // for dup()
17 #include <cstring>
18 #include <iostream>
19 #include <limits>
20 
21 using std::string;
22 using std::string_view;
23 
24 namespace openmsx {
25 
26 template<typename Derived>
27 void ArchiveBase<Derived>::attribute(const char* name, const char* value)
28 {
29  string valueStr(value);
30  self().attribute(name, valueStr);
31 }
32 template class ArchiveBase<MemOutputArchive>;
33 template class ArchiveBase<XmlOutputArchive>;
34 
36 
37 unsigned OutputArchiveBase2::generateID1(const void* p)
38 {
39  #ifdef linux
40  assert("Can't serialize ID of object located on the stack" &&
41  !addressOnStack(p));
42  #endif
43  ++lastId;
44  assert(!polyIdMap.contains(p));
45  polyIdMap.emplace_noDuplicateCheck(p, lastId);
46  return lastId;
47 }
48 unsigned OutputArchiveBase2::generateID2(
49  const void* p, const std::type_info& typeInfo)
50 {
51  #ifdef linux
52  assert("Can't serialize ID of object located on the stack" &&
53  !addressOnStack(p));
54  #endif
55  ++lastId;
56  auto key = std::pair(p, std::type_index(typeInfo));
57  assert(!idMap.contains(key));
58  idMap.emplace_noDuplicateCheck(key, lastId);
59  return lastId;
60 }
61 
62 unsigned OutputArchiveBase2::getID1(const void* p)
63 {
64  auto v = lookup(polyIdMap, p);
65  return v ? *v : 0;
66 }
67 unsigned OutputArchiveBase2::getID2(
68  const void* p, const std::type_info& typeInfo)
69 {
70  auto v = lookup(idMap, std::pair(p, std::type_index(typeInfo)));
71  return v ? *v : 0;
72 }
73 
74 
75 template<typename Derived>
77  const char* tag, const void* data_, size_t len, bool /*diff*/)
78 {
79  auto* data = static_cast<const uint8_t*>(data_);
80 
81  string encoding;
82  string tmp;
83  if (false) {
84  // useful for debugging
85  encoding = "hex";
86  tmp = HexDump::encode(data, len);
87  } else if (false) {
88  encoding = "base64";
89  tmp = Base64::encode(data, len);
90  } else {
91  encoding = "gz-base64";
92  // TODO check for overflow?
93  auto dstLen = uLongf(len + len / 1000 + 12 + 1); // worst-case
94  MemBuffer<uint8_t> buf(dstLen);
95  if (compress2(buf.data(), &dstLen,
96  reinterpret_cast<const Bytef*>(data),
97  uLong(len), 9)
98  != Z_OK) {
99  throw MSXException("Error while compressing blob.");
100  }
101  tmp = Base64::encode(buf.data(), dstLen);
102  }
103  this->self().beginTag(tag);
104  this->self().attribute("encoding", encoding);
105  Saver<string> saver;
106  saver(this->self(), tmp, false);
107  this->self().endTag(tag);
108 }
109 
112 
114 
116 {
117  auto v = lookup(idMap, id);
118  return v ? *v : nullptr;
119 }
120 
121 void InputArchiveBase2::addPointer(unsigned id, const void* p)
122 {
123  assert(!idMap.contains(id));
124  idMap.emplace_noDuplicateCheck(id, const_cast<void*>(p));
125 }
126 
127 unsigned InputArchiveBase2::getId(const void* ptr) const
128 {
129  for (const auto& [id, pt] : idMap) {
130  if (pt == ptr) return id;
131  }
132  return 0;
133 }
134 
135 template<typename Derived>
137  const char* tag, void* data, size_t len, bool /*diff*/)
138 {
139  this->self().beginTag(tag);
140  string encoding;
141  this->self().attribute("encoding", encoding);
142 
143  string_view tmp = this->self().loadStr();
144  this->self().endTag(tag);
145 
146  if (encoding == "gz-base64") {
147  auto [buf, bufSize] = Base64::decode(tmp);
148  auto dstLen = uLongf(len); // TODO check for overflow?
149  if ((uncompress(reinterpret_cast<Bytef*>(data), &dstLen,
150  reinterpret_cast<const Bytef*>(buf.data()), uLong(bufSize))
151  != Z_OK) ||
152  (dstLen != len)) {
153  throw MSXException("Error while decompressing blob.");
154  }
155  } else if (encoding == one_of("hex", "base64")) {
156  bool ok = (encoding == "hex")
157  ? HexDump::decode_inplace(tmp, static_cast<uint8_t*>(data), len)
158  : Base64 ::decode_inplace(tmp, static_cast<uint8_t*>(data), len);
159  if (!ok) {
160  throw XMLException(
161  "Length of decoded blob different from "
162  "expected value (", len, ')');
163  }
164  } else {
165  throw XMLException("Unsupported encoding \"", encoding, "\" for blob");
166  }
167 }
168 
169 template class InputArchiveBase<MemInputArchive>;
170 template class InputArchiveBase<XmlInputArchive>;
171 
173 
174 void MemOutputArchive::save(const std::string& s)
175 {
176  auto size = s.size();
177  uint8_t* buf = buffer.allocate(sizeof(size) + size);
178  memcpy(buf, &size, sizeof(size));
179  memcpy(buf + sizeof(size), s.data(), size);
180 }
181 
183 {
184  return buffer.release(size);
185 }
186 
188 
189 void MemInputArchive::load(std::string& s)
190 {
191  size_t length;
192  load(length);
193  s.resize(length);
194  if (length) {
195  get(&s[0], length);
196  }
197 }
198 
200 {
201  size_t length;
202  load(length);
203  const uint8_t* p = buffer.getCurrentPos();
204  buffer.skip(length);
205  return string_view(reinterpret_cast<const char*>(p), length);
206 }
207 
209 
210 // Too small inputs don't compress very well (often the compressed size is even
211 // bigger than the input). It also takes a relatively long time (because often
212 // compression has a relatively large setup time). I choose this value
213 // semi-arbitrary. I only made it >= 52 so that the (incompressible) RP5C01
214 // registers won't be compressed.
215 constexpr size_t SMALL_SIZE = 64;
216 void MemOutputArchive::serialize_blob(const char* /*tag*/, const void* data,
217  size_t len, bool diff)
218 {
219  // Delta-compress in-memory blobs, see DeltaBlock.hh for more details.
220  if (len > SMALL_SIZE) {
221  auto deltaBlockIdx = unsigned(deltaBlocks.size());
222  save(deltaBlockIdx); // see comment below in MemInputArchive
223  deltaBlocks.push_back(diff
224  ? lastDeltaBlocks.createNew(
225  data, static_cast<const uint8_t*>(data), len)
226  : lastDeltaBlocks.createNullDiff(
227  data, static_cast<const uint8_t*>(data), len));
228  } else {
229  uint8_t* buf = buffer.allocate(len);
230  memcpy(buf, data, len);
231  }
232 
233 }
234 
235 void MemInputArchive::serialize_blob(const char* /*tag*/, void* data,
236  size_t len, bool /*diff*/)
237 {
238  if (len > SMALL_SIZE) {
239  // Usually blobs are saved in the same order as they are loaded
240  // (via the serialize_blob() methods in respectively
241  // MemOutputArchive and MemInputArchive). In that case keeping
242  // track of the deltaBlockIdx in the savestate itself is
243  // redundant (it will simply be an increasing value). However
244  // in rare cases, via the {begin,end,skip)Section() methods, it
245  // is possible that certain blobs are stored in the savestate,
246  // but skipped while loading. That's why we do need the index.
247  unsigned deltaBlockIdx; load(deltaBlockIdx);
248  deltaBlocks[deltaBlockIdx]->apply(static_cast<uint8_t*>(data), len);
249  } else {
250  memcpy(data, buffer.getCurrentPos(), len);
251  buffer.skip(len);
252  }
253 }
254 
256 
258  : root("serial")
259 {
260  root.addAttribute("openmsx_version", Version::full());
261  root.addAttribute("date_time", Date::toString(time(nullptr)));
262  root.addAttribute("platform", TARGET_PLATFORM);
263  {
264  auto f = FileOperations::openFile(filename, "wb");
265  if (!f) goto error;
266  int duped_fd = dup(fileno(f.get()));
267  if (duped_fd == -1) goto error;
268  file = gzdopen(duped_fd, "wb9");
269  if (!file) {
270  ::close(duped_fd);
271  goto error;
272  }
273  current.push_back(&root);
274  return; // success
275  // on scope-exit 'File* f' is closed, and 'gzFile file'
276  // uses the dup()'ed file descriptor.
277  }
278 
279 error:
280  throw XMLException("Could not open compressed file \"", filename, "\"");
281 }
282 
284 {
285  if (!file) return; // already closed
286 
287  assert(current.back() == &root);
288  const char* header =
289  "<?xml version=\"1.0\" ?>\n"
290  "<!DOCTYPE openmsx-serialize SYSTEM 'openmsx-serialize.dtd'>\n";
291  string dump = root.dump();
292  if ((gzwrite(file, const_cast<char*>(header), unsigned(strlen(header))) == 0) ||
293  (gzwrite(file, const_cast<char*>(dump.data()), unsigned(dump.size())) == 0) ||
294  (gzclose(file) != Z_OK)) {
295  throw XMLException("Could not write savestate file.");
296  }
297 
298  file = nullptr;
299 }
300 
302 {
303  try {
304  close();
305  } catch (...) {
306  // Eat exception. Explicitly call close() if you want to handle errors.
307  }
308 }
309 
311 {
312  save(string(1, c));
313 }
314 void XmlOutputArchive::save(const string& str)
315 {
316  assert(!current.empty());
317  assert(current.back()->getData().empty());
318  current.back()->setData(str);
319 }
321 {
322  assert(!current.empty());
323  assert(current.back()->getData().empty());
324  current.back()->setData(b ? "true" : "false");
325 }
326 void XmlOutputArchive::save(unsigned char b)
327 {
328  save(unsigned(b));
329 }
330 void XmlOutputArchive::save(signed char c)
331 {
332  save(int(c));
333 }
335 {
336  save(int(c));
337 }
339 {
340  saveImpl(i);
341 }
342 void XmlOutputArchive::save(unsigned u)
343 {
344  saveImpl(u);
345 }
346 void XmlOutputArchive::save(unsigned long long ull)
347 {
348  saveImpl(ull);
349 }
350 
351 void XmlOutputArchive::attribute(const char* name, const string& str)
352 {
353  assert(!current.empty());
354  assert(!current.back()->hasAttribute(name));
355  current.back()->addAttribute(name, str);
356 }
357 void XmlOutputArchive::attribute(const char* name, int i)
358 {
359  attributeImpl(name, i);
360 }
361 void XmlOutputArchive::attribute(const char* name, unsigned u)
362 {
363  attributeImpl(name, u);
364 }
365 
366 void XmlOutputArchive::beginTag(const char* tag)
367 {
368  assert(!current.empty());
369  auto& elem = current.back()->addChild(tag);
370  current.push_back(&elem);
371 }
372 void XmlOutputArchive::endTag(const char* tag)
373 {
374  assert(!current.empty());
375  assert(current.back()->getName() == tag); (void)tag;
376  current.pop_back();
377 }
378 
380 
382  : rootElem(XMLLoader::load(filename, "openmsx-serialize.dtd"))
383 {
384  elems.emplace_back(&rootElem, 0);
385 }
386 
388 {
389  if (!elems.back().first->getChildren().empty()) {
390  throw XMLException("No child tags expected for primitive type");
391  }
392  return elems.back().first->getData();
393 }
395 {
396  t = loadStr();
397 }
399 {
400  std::string str;
401  load(str);
402  std::istringstream is(str);
403  is >> c;
404 }
406 {
407  string_view s = loadStr();
408  if (s == one_of("true", "1")) {
409  b = true;
410  } else if (s == one_of("false", "0")) {
411  b = false;
412  } else {
413  throw XMLException("Bad value found for boolean: ", s);
414  }
415 }
416 
417 // This function parses a number from a string. It's similar to the generic
418 // templatized XmlInputArchive::load() method, but _much_ faster. It does
419 // have some limitations though:
420 // - it can't handle leading whitespace
421 // - it can't handle extra characters at the end of the string
422 // - it can only handle one base (only decimal, not octal or hexadecimal)
423 // - it doesn't understand a leading '+' sign
424 // - it doesn't detect overflow or underflow (The generic implementation sets
425 // a 'bad' flag on the stream and clips the result to the min/max allowed
426 // value. Though this 'bad' flag was ignored by the openMSX code).
427 // This routine is only used to parse strings we've written ourselves (and the
428 // savestate/replay XML files are not meant to be manually edited). So the
429 // above limitations don't really matter. And we can use the speed gain.
430 template<bool IS_SIGNED> struct ConditionalNegate;
431 template<> struct ConditionalNegate<true> {
432  template<typename T> void operator()(bool negate, T& t) {
433  if (negate) t = -t; // ok to negate a signed type
434  }
435 };
436 template<> struct ConditionalNegate<false> {
437  template<typename T> void operator()(bool negate, T& /*t*/) {
438  assert(!negate); (void)negate; // can't negate unsigned type
439  }
440 };
441 template<typename T> static inline void fastAtoi(string_view str, T& t)
442 {
443  t = 0;
444  bool neg = false;
445  size_t i = 0;
446  size_t l = str.size();
447 
448  constexpr bool IS_SIGNED = std::numeric_limits<T>::is_signed;
449  if (IS_SIGNED) {
450  if (l == 0) return;
451  if (str[0] == '-') {
452  neg = true;
453  i = 1;
454  }
455  }
456  for (; i < l; ++i) {
457  unsigned d = str[i] - '0';
458  if (unlikely(d > 9)) {
459  throw XMLException("Invalid integer: ", str);
460  }
461  t = 10 * t + d;
462  }
463  // The following stuff does the equivalent of:
464  // if (neg) t = -t;
465  // Though this expression triggers a warning on VC++ when T is an
466  // unsigned type. This complex template stuff avoids the warning.
467  ConditionalNegate<IS_SIGNED> negateFunctor;
468  negateFunctor(neg, t);
469 }
471 {
472  string_view str = loadStr();
473  fastAtoi(str, i);
474 }
475 void XmlInputArchive::load(unsigned& u)
476 {
477  string_view str = loadStr();
478  fastAtoi(str, u);
479 }
480 void XmlInputArchive::load(unsigned long long& ull)
481 {
482  string_view str = loadStr();
483  fastAtoi(str, ull);
484 }
485 void XmlInputArchive::load(unsigned char& b)
486 {
487  unsigned i;
488  load(i);
489  b = i;
490 }
491 void XmlInputArchive::load(signed char& c)
492 {
493  int i;
494  load(i);
495  c = i;
496 }
498 {
499  int i;
500  load(i);
501  c = i;
502 }
503 
504 void XmlInputArchive::beginTag(const char* tag)
505 {
506  auto* child = elems.back().first->findNextChild(
507  tag, elems.back().second);
508  if (!child) {
509  string path;
510  for (auto& e : elems) {
511  strAppend(path, e.first->getName(), '/');
512  }
513  throw XMLException("No child tag \"", tag,
514  "\" found at location \"", path, '\"');
515  }
516  elems.emplace_back(child, 0);
517 }
518 void XmlInputArchive::endTag(const char* tag)
519 {
520  const auto& elem = *elems.back().first;
521  if (elem.getName() != tag) {
522  throw XMLException("End tag \"", elem.getName(),
523  "\" not equal to begin tag \"", tag, "\"");
524  }
525  auto& elem2 = const_cast<XMLElement&>(elem);
526  elem2.clearName(); // mark this elem for later beginTag() calls
527  elems.pop_back();
528 }
529 
530 void XmlInputArchive::attribute(const char* name, string& t)
531 {
532  try {
533  t = elems.back().first->getAttribute(name);
534  } catch (ConfigException& e) {
535  throw XMLException(std::move(e).getMessage());
536  }
537 }
538 void XmlInputArchive::attribute(const char* name, int& i)
539 {
540  attributeImpl(name, i);
541 }
542 void XmlInputArchive::attribute(const char* name, unsigned& u)
543 {
544  attributeImpl(name, u);
545 }
546 bool XmlInputArchive::hasAttribute(const char* name)
547 {
548  return elems.back().first->hasAttribute(name);
549 }
550 bool XmlInputArchive::findAttribute(const char* name, unsigned& value)
551 {
552  return elems.back().first->findAttributeInt(name, value);
553 }
555 {
556  return int(elems.back().first->getChildren().size());
557 }
558 
559 } // namespace openmsx
openmsx::XMLException
Definition: XMLException.hh:9
one_of.hh
openmsx::ConditionalNegate< true >::operator()
void operator()(bool negate, T &t)
Definition: serialize.cc:432
openmsx::XmlInputArchive::load
void load(T &t)
Definition: serialize.hh:918
ConfigException.hh
openmsx::XmlOutputArchive::saveChar
void saveChar(char c)
Definition: serialize.cc:310
openmsx::OutputArchiveBase< MemOutputArchive >
lookup
const Value * lookup(const hash_map< Key, Value, Hasher, Equal > &map, const Key2 &key)
Definition: hash_map.hh:91
unlikely
#define unlikely(x)
Definition: likely.hh:15
openmsx::ConditionalNegate
Definition: serialize.cc:430
serialize.hh
openmsx::XmlInputArchive::loadStr
std::string_view loadStr()
Definition: serialize.cc:387
openmsx::XmlInputArchive::endTag
void endTag(const char *tag)
Definition: serialize.cc:518
openmsx::ArchiveBase::attribute
void attribute(const char *name, T &t)
Load/store an attribute from/in the archive.
Definition: serialize.hh:231
openmsx::Saver
Definition: serialize_core.hh:440
openmsx::XmlOutputArchive::save
void save(const T &t)
Definition: serialize.hh:850
openmsx::OutputBuffer::release
MemBuffer< uint8_t > release(size_t &size)
Release ownership of the buffer.
Definition: SerializeBuffer.cc:60
XMLException.hh
utf8::unchecked::size
size_t size(std::string_view utf8)
Definition: utf8_unchecked.hh:227
openmsx::ConfigException
Definition: ConfigException.hh:9
Date.hh
openmsx::InputBuffer::getCurrentPos
const uint8_t * getCurrentPos() const
Return a pointer to the current position in the buffer.
Definition: SerializeBuffer.hh:210
openmsx::XmlInputArchive::findAttribute
bool findAttribute(const char *name, unsigned &value)
Definition: serialize.cc:550
gl::length
T length(const vecN< N, T > &x)
Definition: gl_vec.hh:348
t
TclObject t
Definition: TclObject_test.cc:264
openmsx::XmlInputArchive::countChildren
int countChildren() const
Definition: serialize.cc:554
openmsx::MSXException
Definition: MSXException.hh:10
hash_map::contains
bool contains(const K &k) const
Definition: hash_map.hh:83
openmsx::InputArchiveBase::serialize_blob
void serialize_blob(const char *tag, void *data, size_t len, bool diff=true)
Definition: serialize.cc:136
openmsx::XmlOutputArchive::saveImpl
void saveImpl(const T &t)
Definition: serialize.hh:844
XMLElement.hh
openmsx::MemInputArchive::load
void load(T &t)
Definition: serialize.hh:766
strAppend
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:644
openmsx::XmlOutputArchive::attribute
void attribute(const char *name, const T &t)
Definition: serialize.hh:890
openmsx::ArchiveBase< MemOutputArchive >
Base64::decode_inplace
bool decode_inplace(std::string_view input, uint8_t *output, size_t outSize)
Definition: Base64.cc:125
Version.hh
DeltaBlock.hh
openmsx::XmlInputArchive::beginTag
void beginTag(const char *tag)
Definition: serialize.cc:504
openmsx::ConditionalNegate< false >::operator()
void operator()(bool negate, T &)
Definition: serialize.cc:437
HexDump.hh
openmsx::XmlOutputArchive::beginTag
void beginTag(const char *tag)
Definition: serialize.cc:366
openmsx::MemOutputArchive::releaseBuffer
MemBuffer< uint8_t > releaseBuffer(size_t &size)
Definition: serialize.cc:182
hash_set::emplace_noDuplicateCheck
iterator emplace_noDuplicateCheck(Args &&... args)
Definition: hash_set.hh:468
openmsx::InputArchiveBase2::getPointer
void * getPointer(unsigned id)
Definition: serialize.cc:115
openmsx::InputBuffer::skip
void skip(size_t len)
Skip the given number of bytes.
Definition: SerializeBuffer.hh:199
openmsx::XmlOutputArchive::XmlOutputArchive
XmlOutputArchive(const std::string &filename)
Definition: serialize.cc:257
openmsx::XMLElement
Definition: XMLElement.hh:16
XMLLoader.hh
openmsx::MemBuffer< uint8_t >
one_of
Definition: one_of.hh:7
openmsx::filename
constexpr const char *const filename
Definition: FirmwareSwitch.cc:10
cstdiop.hh
build-info.hh
openmsx::XmlOutputArchive::endTag
void endTag(const char *tag)
Definition: serialize.cc:372
openmsx::XmlInputArchive::hasAttribute
bool hasAttribute(const char *name)
Definition: serialize.cc:546
MemBuffer.hh
openmsx::Date::toString
std::string toString(time_t time)
Definition: Date.cc:150
openmsx::XMLElement::addAttribute
void addAttribute(std::string name, std::string value)
Definition: XMLElement.cc:52
openmsx::XMLElement::clearName
void clearName()
Definition: XMLElement.hh:33
openmsx::InputArchiveBase< MemInputArchive >
openmsx::XMLElement::dump
std::string dump() const
Definition: XMLElement.cc:250
openmsx::XMLLoader::load
XMLElement load(string_view filename, string_view systemID)
Definition: XMLLoader.cc:34
openmsx::FileOperations::openFile
FILE_t openFile(const std::string &filename, const std::string &mode)
Call fopen() in a platform-independent manner.
Definition: FileOperations.cc:353
FileOperations.hh
openmsx::MemInputArchive::serialize_blob
void serialize_blob(const char *tag, void *data, size_t len, bool diff=true)
Definition: serialize.cc:235
openmsx::MemOutputArchive::save
void save(const T &t)
Definition: serialize.hh:655
openmsx::XmlInputArchive::XmlInputArchive
XmlInputArchive(const std::string &filename)
Definition: serialize.cc:381
openmsx::SMALL_SIZE
constexpr size_t SMALL_SIZE
Definition: serialize.cc:215
openmsx::MemBuffer::data
const T * data() const
Returns pointer to the start of the memory buffer.
Definition: MemBuffer.hh:81
openmsx::TclObject::size
unsigned size() const
Definition: TclObject.hh:158
openmsx::Version::full
static std::string full()
Definition: Version.cc:8
stl.hh
openmsx::MemOutputArchive::serialize_blob
void serialize_blob(const char *tag, const void *data, size_t len, bool diff=true)
Definition: serialize.cc:216
openmsx::XmlInputArchive::attribute
void attribute(const char *name, T &t)
Definition: serialize.hh:964
openmsx::InputArchiveBase2::addPointer
void addPointer(unsigned id, const void *p)
Definition: serialize.cc:121
openmsx::XmlInputArchive::attributeImpl
void attributeImpl(const char *name, T &t)
Definition: serialize.hh:957
openmsx::LastDeltaBlocks::createNullDiff
std::shared_ptr< DeltaBlock > createNullDiff(const void *id, const uint8_t *data, size_t size)
Definition: DeltaBlock.cc:398
openmsx::XmlOutputArchive::~XmlOutputArchive
~XmlOutputArchive()
Definition: serialize.cc:301
openmsx::XmlOutputArchive::close
void close()
Definition: serialize.cc:283
Base64.hh
HexDump::decode_inplace
bool decode_inplace(std::string_view input, uint8_t *output, size_t outSize)
Definition: HexDump.cc:72
openmsx::OutputBuffer::allocate
uint8_t * allocate(size_t len)
Reserve space to insert the given number of bytes.
Definition: SerializeBuffer.hh:111
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5
openmsx::LastDeltaBlocks::createNew
std::shared_ptr< DeltaBlock > createNew(const void *id, const uint8_t *data, size_t size)
Definition: DeltaBlock.cc:361
openmsx::XmlOutputArchive::attributeImpl
void attributeImpl(const char *name, const T &t)
Definition: serialize.hh:886
openmsx::MemInputArchive::loadStr
std::string_view loadStr()
Definition: serialize.cc:199
openmsx::XmlInputArchive::loadChar
void loadChar(char &c)
Definition: serialize.cc:398
openmsx::InputArchiveBase2::getId
unsigned getId(const void *p) const
Definition: serialize.cc:127
openmsx::OutputArchiveBase::serialize_blob
void serialize_blob(const char *tag, const void *data, size_t len, bool diff=true)
Definition: serialize.cc:76