lsnes rr2-β24
[lsnes.git] / src / core / moviefile-binary.cpp
blob19fa86b271a294e2f23e8b86e7ed6fb9cd7af8ac
1 #include "core/moviefile-binary.hpp"
2 #include "core/moviefile-common.hpp"
3 #include "core/moviefile.hpp"
4 #include "library/binarystream.hpp"
5 #include "library/minmax.hpp"
6 #include "library/serialization.hpp"
7 #include "library/string.hpp"
8 #include "library/zip.hpp"
10 #include <cstring>
11 #include <cerrno>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
15 //FUCK YOU. SERIOUSLY.
16 #define EXTRA_OPENFLAGS O_BINARY
17 #else
18 #define EXTRA_OPENFLAGS 0
19 #endif
21 void moviefile::brief_info::binary_io(int _stream)
23 binarystream::input in(_stream);
24 sysregion = in.string();
25 //Discard the settings.
26 while(in.byte()) {
27 in.string();
28 in.string();
30 in.extension({
31 {TAG_CORE_VERSION, [this](binarystream::input& s) {
32 this->corename = s.string_implicit();
33 }},{TAG_PROJECT_ID, [this](binarystream::input& s) {
34 this->projectid = s.string_implicit();
35 }},{TAG_SAVESTATE, [this](binarystream::input& s) {
36 this->current_frame = s.number();
37 }},{TAG_RRDATA, [this](binarystream::input& s) {
38 std::vector<char> c_rrdata;
39 s.blob_implicit(c_rrdata);
40 this->rerecords = rrdata_set::count(c_rrdata);
41 }},{TAG_ROMHASH, [this](binarystream::input& s) {
42 uint8_t n = s.byte();
43 std::string h = s.string_implicit();
44 if(n > 2 * ROM_SLOT_COUNT)
45 return;
46 if(n & 1)
47 this->hashxml[n >> 1] = h;
48 else
49 this->hash[n >> 1] = h;
50 }},{TAG_ROMHINT, [this](binarystream::input& s) {
51 uint8_t n = s.byte();
52 std::string h = s.string_implicit();
53 if(n > ROM_SLOT_COUNT)
54 return;
55 this->hint[n] = h;
57 }, binarystream::null_default);
60 void moviefile::binary_io(int _stream, rrdata_set& rrd, bool as_state) throw(std::bad_alloc, std::runtime_error)
62 binarystream::output out(_stream);
63 out.string(gametype->get_name());
64 moviefile_write_settings<binarystream::output>(out, settings, gametype->get_type().get_settings(),
65 [](binarystream::output& s, const std::string& name, const std::string& value) -> void {
66 s.byte(0x01);
67 s.string(name);
68 s.string(value);
69 });
70 out.byte(0x00);
72 out.extension(TAG_MOVIE_TIME, [this](binarystream::output& s) {
73 s.number(this->movie_rtc_second);
74 s.number(this->movie_rtc_subsecond);
75 });
77 out.extension(TAG_PROJECT_ID, [this](binarystream::output& s) {
78 s.string_implicit(this->projectid);
79 });
81 out.extension(TAG_CORE_VERSION, [this](binarystream::output& s) {
82 this->coreversion = this->gametype->get_type().get_core_identifier();
83 s.string_implicit(this->coreversion);
84 });
86 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
87 out.extension(TAG_ROMHASH, [this, i](binarystream::output& s) {
88 if(!this->romimg_sha256[i].length()) return;
89 s.byte(2 * i);
90 s.string_implicit(this->romimg_sha256[i]);
91 });
92 out.extension(TAG_ROMHASH, [this, i](binarystream::output& s) {
93 if(!this->romxml_sha256[i].length()) return;
94 s.byte(2 * i + 1);
95 s.string_implicit(this->romxml_sha256[i]);
96 });
97 out.extension(TAG_ROMHINT, [this, i](binarystream::output& s) {
98 if(!this->namehint[i].length()) return;
99 s.byte(i);
100 s.string_implicit(this->namehint[i]);
104 out.extension(TAG_RRDATA, [this, &rrd](binarystream::output& s) {
105 std::vector<char> _rrd;
106 rrd.write(_rrd);
107 s.blob_implicit(_rrd);
110 for(auto i : movie_sram)
111 out.extension(TAG_MOVIE_SRAM, [&i](binarystream::output& s) {
112 s.string(i.first);
113 s.blob_implicit(i.second);
116 out.extension(TAG_ANCHOR_SAVE, [this](binarystream::output& s) {
117 s.blob_implicit(this->anchor_savestate);
119 if(as_state) {
120 out.extension(TAG_SAVESTATE, [this](binarystream::output& s) {
121 s.number(this->dyn.save_frame);
122 s.number(this->dyn.lagged_frames);
123 s.number(this->dyn.rtc_second);
124 s.number(this->dyn.rtc_subsecond);
125 s.number(this->dyn.pollcounters.size());
126 for(auto i : this->dyn.pollcounters)
127 s.number32(i);
128 s.byte(this->dyn.poll_flag ? 0x01 : 0x00);
129 s.blob_implicit(this->dyn.savestate);
130 }, true, out.numberbytes(dyn.save_frame) + out.numberbytes(dyn.lagged_frames) +
131 out.numberbytes(dyn.rtc_second) + out.numberbytes(dyn.rtc_subsecond) +
132 out.numberbytes(dyn.pollcounters.size()) + 4 * dyn.pollcounters.size() + 1 +
133 dyn.savestate.size());
135 out.extension(TAG_HOSTMEMORY, [this](binarystream::output& s) {
136 s.blob_implicit(this->dyn.host_memory);
139 out.extension(TAG_SCREENSHOT, [this](binarystream::output& s) {
140 s.blob_implicit(this->dyn.screenshot);
141 }, true, dyn.screenshot.size());
143 for(auto i : dyn.sram) {
144 out.extension(TAG_SAVE_SRAM, [&i](binarystream::output& s) {
145 s.string(i.first);
146 s.blob_implicit(i.second);
150 for(auto i : dyn.active_macros)
151 out.extension(TAG_MACRO, [&i](binarystream::output& s) {
152 s.number(i.second);
153 s.string_implicit(i.first);
157 out.extension(TAG_GAMENAME, [this](binarystream::output& s) {
158 s.string_implicit(this->gamename);
161 for(auto i : subtitles)
162 out.extension(TAG_SUBTITLE, [&i](binarystream::output& s) {
163 s.number(i.first.get_frame());
164 s.number(i.first.get_length());
165 s.string_implicit(i.second);
168 for(auto i : authors)
169 out.extension(TAG_AUTHOR, [&i](binarystream::output& s) {
170 s.string(i.first);
171 s.string_implicit(i.second);
174 for(auto i : ramcontent) {
175 out.extension(TAG_RAMCONTENT, [&i](binarystream::output& s) {
176 s.string(i.first);
177 s.blob_implicit(i.second);
181 int64_t next_bnum = 0;
182 std::map<std::string, uint64_t> branch_table;
183 for(auto& i : branches) {
184 branch_table[i.first] = next_bnum++;
185 out.extension(TAG_BRANCH_NAME, [&i](binarystream::output& s) {
186 s.string_implicit(i.first);
187 }, false, i.first.length());
188 uint32_t tag = (&i.second == input) ? TAG_MOVIE : TAG_BRANCH;
189 out.extension(tag, [&i](binarystream::output& s) {
190 i.second.save_binary(s);
191 }, true, i.second.binary_size());
195 void moviefile::binary_io(int _stream, core_type& romtype) throw(std::bad_alloc, std::runtime_error)
197 binarystream::input in(_stream);
198 std::string tmp = in.string();
199 std::string next_branch;
200 std::map<uint64_t, std::string> branch_table;
201 uint64_t next_bnum = 0;
202 try {
203 gametype = &romtype.lookup_sysregion(tmp);
204 } catch(std::bad_alloc& e) {
205 throw;
206 } catch(std::exception& e) {
207 throw std::runtime_error("Illegal game type '" + tmp + "'");
209 while(in.byte()) {
210 std::string name = in.string();
211 settings[name] = in.string();
213 auto ctrldata = gametype->get_type().controllerconfig(settings);
214 portctrl::type_set& ports = portctrl::type_set::make(ctrldata.ports, ctrldata.portindex());
215 input = NULL;
217 this->dyn.save_frame = 0; //If no savestate, ensure this is interpretted as a movie.
218 in.extension({
219 {TAG_ANCHOR_SAVE, [this](binarystream::input& s) {
220 s.blob_implicit(this->anchor_savestate);
221 }},{TAG_AUTHOR, [this](binarystream::input& s) {
222 std::string a = s.string();
223 std::string b = s.string_implicit();
224 this->authors.push_back(std::make_pair(a, b));
225 }},{TAG_CORE_VERSION, [this](binarystream::input& s) {
226 this->coreversion = s.string_implicit();
227 }},{TAG_GAMENAME, [this](binarystream::input& s) {
228 this->gamename = s.string_implicit();
229 }},{TAG_HOSTMEMORY, [this](binarystream::input& s) {
230 s.blob_implicit(this->dyn.host_memory);
231 }},{TAG_MACRO, [this](binarystream::input& s) {
232 uint64_t n = s.number();
233 this->dyn.active_macros[s.string_implicit()] = n;
234 }},{TAG_BRANCH_NAME, [this, &branch_table, &next_bnum, &next_branch](binarystream::input& s) {
235 branch_table[next_bnum++] = next_branch = s.string_implicit();
236 }},{TAG_MOVIE, [this, &ports, &next_branch](binarystream::input& s) {
237 branches[next_branch].clear(ports);
238 branches[next_branch].load_binary(s);
239 input = &branches[next_branch];
240 }},{TAG_BRANCH, [this, &ports, &next_branch](binarystream::input& s) {
241 branches[next_branch].clear(ports);
242 branches[next_branch].load_binary(s);
243 }},{TAG_MOVIE_SRAM, [this](binarystream::input& s) {
244 std::string a = s.string();
245 s.blob_implicit(this->movie_sram[a]);
246 }},{TAG_RAMCONTENT, [this](binarystream::input& s) {
247 std::string a = s.string();
248 s.blob_implicit(this->ramcontent[a]);
249 }},{TAG_MOVIE_TIME, [this](binarystream::input& s) {
250 this->movie_rtc_second = s.number();
251 this->movie_rtc_subsecond = s.number();
252 }},{TAG_PROJECT_ID, [this](binarystream::input& s) {
253 this->projectid = s.string_implicit();
254 }},{TAG_ROMHASH, [this](binarystream::input& s) {
255 uint8_t n = s.byte();
256 std::string h = s.string_implicit();
257 if(n > 2 * ROM_SLOT_COUNT)
258 return;
259 if(n & 1)
260 romxml_sha256[n >> 1] = h;
261 else
262 romimg_sha256[n >> 1] = h;
263 }},{TAG_ROMHINT, [this](binarystream::input& s) {
264 uint8_t n = s.byte();
265 std::string h = s.string_implicit();
266 if(n > ROM_SLOT_COUNT)
267 return;
268 namehint[n] = h;
269 }},{TAG_RRDATA, [this](binarystream::input& s) {
270 s.blob_implicit(this->c_rrdata);
271 this->rerecords = (stringfmt() << rrdata_set::count(c_rrdata)).str();
272 }},{TAG_SAVE_SRAM, [this](binarystream::input& s) {
273 std::string a = s.string();
274 s.blob_implicit(this->dyn.sram[a]);
275 }},{TAG_SAVESTATE, [this](binarystream::input& s) {
276 this->dyn.save_frame = s.number();
277 this->dyn.lagged_frames = s.number();
278 this->dyn.rtc_second = s.number();
279 this->dyn.rtc_subsecond = s.number();
280 this->dyn.pollcounters.resize(s.number());
281 for(auto& i : this->dyn.pollcounters)
282 i = s.number32();
283 this->dyn.poll_flag = (s.byte() != 0);
284 s.blob_implicit(this->dyn.savestate);
285 }},{TAG_SCREENSHOT, [this](binarystream::input& s) {
286 s.blob_implicit(this->dyn.screenshot);
287 }},{TAG_SUBTITLE, [this](binarystream::input& s) {
288 uint64_t f = s.number();
289 uint64_t l = s.number();
290 std::string x = s.string_implicit();
291 this->subtitles[moviefile_subtiming(f, l)] = x;
293 }, binarystream::null_default);
295 create_default_branch(ports);
298 moviefile_branch_extractor_binary::moviefile_branch_extractor_binary(const std::string& filename)
300 s = open(filename.c_str(), O_RDONLY | EXTRA_OPENFLAGS);
301 if(s < 0) {
302 int err = errno;
303 (stringfmt() << "Can't open file '" << filename << "' for reading: " << strerror(err)).throwex();
307 moviefile_branch_extractor_binary::~moviefile_branch_extractor_binary()
311 std::set<std::string> moviefile_branch_extractor_binary::enumerate()
313 std::set<std::string> r;
314 std::string name;
315 if(lseek(s, 5, SEEK_SET) < 0) {
316 int err = errno;
317 (stringfmt() << "Can't read the file: " << strerror(err)).throwex();
319 binarystream::input b(s);
320 //Skip the headers.
321 b.string();
322 while(b.byte()) {
323 b.string();
324 b.string();
326 //Okay, read the extension packets.
327 b.extension({
328 {TAG_BRANCH_NAME, [this, &name](binarystream::input& s) {
329 name = s.string_implicit();
330 }},{TAG_MOVIE, [this, &r, &name](binarystream::input& s) {
331 r.insert(name);
332 }},{TAG_BRANCH, [this, &r, &name](binarystream::input& s) {
333 r.insert(name);
335 }, binarystream::null_default);
337 return r;
340 void moviefile_branch_extractor_binary::read(const std::string& name, portctrl::frame_vector& v)
342 std::string mname;
343 if(lseek(s, 5, SEEK_SET) < 0) {
344 int err = errno;
345 (stringfmt() << "Can't read the file: " << strerror(err)).throwex();
347 binarystream::input b(s);
348 bool done = false;
349 //Skip the headers.
350 b.string();
351 while(b.byte()) {
352 b.string();
353 b.string();
355 //Okay, read the extension packets.
356 b.extension({
357 {TAG_BRANCH_NAME, [this, &mname](binarystream::input& s) {
358 mname = s.string_implicit();
359 }},{TAG_MOVIE, [this, &v, &mname, &name, &done](binarystream::input& s) {
360 if(name != mname)
361 return;
362 v.clear();
363 v.load_binary(s);
364 done = true;
365 }},{TAG_BRANCH, [this, &v, &mname, &name, &done](binarystream::input& s) {
366 if(name != mname)
367 return;
368 v.clear();
369 v.load_binary(s);
370 done = true;
372 }, binarystream::null_default);
373 if(!done)
374 (stringfmt() << "Can't find branch '" << name << "' in file.").throwex();
377 moviefile_sram_extractor_binary::moviefile_sram_extractor_binary(const std::string& filename)
379 s = open(filename.c_str(), O_RDONLY | EXTRA_OPENFLAGS);
380 if(s < 0) {
381 int err = errno;
382 (stringfmt() << "Can't open file '" << filename << "' for reading: " << strerror(err)).throwex();
386 moviefile_sram_extractor_binary::~moviefile_sram_extractor_binary()
390 std::set<std::string> moviefile_sram_extractor_binary::enumerate()
392 std::set<std::string> r;
393 std::string name;
394 if(lseek(s, 5, SEEK_SET) < 0) {
395 int err = errno;
396 (stringfmt() << "Can't read the file: " << strerror(err)).throwex();
398 binarystream::input b(s);
399 //Skip the headers.
400 b.string();
401 while(b.byte()) {
402 b.string();
403 b.string();
405 //Okay, read the extension packets.
406 b.extension({
407 {TAG_SAVESTATE, [this, &r](binarystream::input& s) {
408 r.insert("");
409 }},{TAG_SAVE_SRAM, [this, &r](binarystream::input& s) {
410 r.insert(s.string());
412 }, binarystream::null_default);
414 return r;
417 void moviefile_sram_extractor_binary::read(const std::string& name, std::vector<char>& v)
419 //Char and uint8_t are the same representation, right?
420 std::vector<char>* _v = &v;
421 std::string mname = name;
422 if(lseek(s, 5, SEEK_SET) < 0) {
423 int err = errno;
424 (stringfmt() << "Can't read the file: " << strerror(err)).throwex();
426 binarystream::input b(s);
427 bool done = false;
428 //Skip the headers.
429 b.string();
430 while(b.byte()) {
431 b.string();
432 b.string();
434 //Okay, read the extension packets.
435 b.extension({
436 {TAG_SAVESTATE, [this, mname, _v, &done](binarystream::input& s) {
437 if(mname == "") {
438 //This savestate.
439 s.number(); //Frame of save.
440 s.number(); //Lagged frames.
441 s.number(); //RTC second.
442 s.number(); //RTC subsecond.
443 size_t pctrs = s.number(); //Number of poll counters.
444 for(size_t i = 0; i < pctrs; i++)
445 s.number32(); //Poll counters.
446 s.byte(); //Poll flag.
447 s.blob_implicit(*_v);
448 done = true;
450 }},{TAG_SAVE_SRAM, [this, mname, _v, &done](binarystream::input& s) {
451 std::string sname = s.string();
452 if(sname == mname) {
453 //This SRAM.
454 s.blob_implicit(*_v);
455 done = true;
458 }, binarystream::null_default);
459 if(!done) {
460 if(name != "")
461 (stringfmt() << "Can't find branch '" << name << "' in file.").throwex();
462 else
463 (stringfmt() << "Can't find savestate in file.").throwex();