1 #include "core/misc.hpp"
2 #include "core/moviedata.hpp"
3 #include "core/moviefile.hpp"
4 #include "core/rrdata.hpp"
5 #include "library/zip.hpp"
6 #include "library/string.hpp"
7 #include "library/minmax.hpp"
8 #include "library/binarystream.hpp"
9 #include "interface/romtype.hpp"
14 #include <boost/iostreams/copy.hpp>
15 #include <boost/iostreams/device/back_inserter.hpp>
16 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
20 #define DEFAULT_RTC_SECOND 1000000000ULL
21 #define DEFAULT_RTC_SUBSECOND 0ULL
25 TAG_ANCHOR_SAVE
= 0xf5e0fad7,
26 TAG_AUTHOR
= 0xafff97b4,
27 TAG_CORE_VERSION
= 0xe4344c7e,
28 TAG_GAMENAME
= 0xe80d6970,
29 TAG_HOSTMEMORY
= 0x3bf9d187,
30 TAG_MACRO
= 0xd261338f,
31 TAG_MOVIE
= 0xf3dca44b,
32 TAG_MOVIE_SRAM
= 0xbbc824b7,
33 TAG_MOVIE_TIME
= 0x18c3a975,
34 TAG_PROJECT_ID
= 0x359bfbab,
35 TAG_ROMHASH
= 0x0428acfc,
36 TAG_RRDATA
= 0xa3a07f71,
37 TAG_SAVE_SRAM
= 0xae9bfb2f,
38 TAG_SAVESTATE
= 0x2e5bc2ac,
39 TAG_SCREENSHOT
= 0xc6760d0e,
40 TAG_SUBTITLE
= 0x6a7054d3,
41 TAG_RAMCONTENT
= 0xd3ec3770,
42 TAG_ROMHINT
= 0x6f715830
45 void read_linefile(zip_reader
& r
, const std::string
& member
, std::string
& out
, bool conditional
= false)
46 throw(std::bad_alloc
, std::runtime_error
)
48 if(conditional
&& !r
.has_member(member
))
50 std::istream
& m
= r
[member
];
61 void write_linefile(zip_writer
& w
, const std::string
& member
, const std::string
& value
, bool conditional
= false)
62 throw(std::bad_alloc
, std::runtime_error
)
64 if(conditional
&& value
== "")
66 std::ostream
& m
= w
.create_file(member
);
68 m
<< value
<< std::endl
;
78 void binary_read_movie(binary_input_stream
& in
, controller_frame_vector
& v
)
80 uint64_t stride
= v
.get_stride();
81 uint64_t pageframes
= v
.get_frames_per_page();
84 uint64_t pagesize
= stride
* pageframes
;
85 while(in
.get_left()) {
86 v
.resize(vsize
+ pageframes
);
87 unsigned char* contents
= v
.get_page_buffer(pagenum
++);
88 uint64_t gcount
= min(pagesize
, in
.get_left());
89 in
.raw(contents
, gcount
);
90 vsize
+= (gcount
/ stride
);
95 void binary_write_movie(binary_output_stream
& out
, controller_frame_vector
& v
)
97 uint64_t pages
= v
.get_page_count();
98 uint64_t stride
= v
.get_stride();
99 uint64_t pageframes
= v
.get_frames_per_page();
100 uint64_t vsize
= v
.size();
101 out
.write_extension_tag(TAG_MOVIE
, vsize
* stride
);
104 uint64_t count
= (vsize
> pageframes
) ? pageframes
: vsize
;
105 size_t bytes
= count
* stride
;
106 unsigned char* content
= v
.get_page_buffer(pagenum
++);
107 out
.raw(content
, bytes
);
112 std::map
<std::string
, std::string
> read_settings(zip_reader
& r
)
114 std::map
<std::string
, std::string
> x
;
116 if(!regex_match("port[0-9]+|setting\\..+", i
))
119 if(i
.substr(0, 4) == "port")
123 read_linefile(r
, i
, x
[s
], true);
128 template<typename target
>
129 void write_settings(target
& w
, const std::map
<std::string
, std::string
>& settings
,
130 core_setting_group
& sgroup
, std::function
<void(target
& w
, const std::string
& name
,
131 const std::string
& value
)> writefn
)
133 for(auto i
: settings
) {
134 if(!sgroup
.settings
.count(i
.first
))
136 if(sgroup
.settings
.find(i
.first
)->second
.dflt
== i
.second
)
138 writefn(w
, i
.first
, i
.second
);
142 std::map
<std::string
, uint64_t> read_active_macros(zip_reader
& r
, const std::string
& member
)
144 std::map
<std::string
, uint64_t> x
;
145 if(!r
.has_member(member
))
147 std::istream
& m
= r
[member
];
151 std::getline(m
, out
);
155 regex_results rx
= regex("([0-9]+) +(.*)", out
);
157 messages
<< "Warning: Bad macro state: '" << out
<< "'" << std::endl
;
161 uint64_t f
= parse_value
<uint64_t>(rx
[1]);
174 void write_active_macros(zip_writer
& w
, const std::string
& member
, const std::map
<std::string
, uint64_t>& ma
)
178 std::ostream
& m
= w
.create_file(member
);
181 m
<< i
.second
<< " " << i
.first
<< std::endl
;
183 throw std::runtime_error("Can't write ZIP file member");
194 void read_numeric_file(zip_reader
& r
, const std::string
& member
, T
& out
, bool conditional
= false)
195 throw(std::bad_alloc
, std::runtime_error
)
198 read_linefile(r
, member
, _out
, conditional
);
199 if(conditional
&& _out
== "")
201 out
= parse_value
<int64_t>(_out
);
205 void write_numeric_file(zip_writer
& w
, const std::string
& member
, T value
) throw(std::bad_alloc
,
208 std::ostringstream x
;
210 write_linefile(w
, member
, x
.str());
213 void write_raw_file(zip_writer
& w
, const std::string
& member
, std::vector
<char>& content
) throw(std::bad_alloc
,
216 std::ostream
& m
= w
.create_file(member
);
218 m
.write(&content
[0], content
.size());
220 throw std::runtime_error("Can't write ZIP file member");
228 std::vector
<char> read_raw_file(zip_reader
& r
, const std::string
& member
) throw(std::bad_alloc
, std::runtime_error
)
230 std::vector
<char> out
;
231 std::istream
& m
= r
[member
];
233 boost::iostreams::back_insert_device
<std::vector
<char>> rd(out
);
234 boost::iostreams::copy(m
, rd
);
243 uint64_t decode_uint64(unsigned char* buf
)
245 return ((uint64_t)buf
[0] << 56) |
246 ((uint64_t)buf
[1] << 48) |
247 ((uint64_t)buf
[2] << 40) |
248 ((uint64_t)buf
[3] << 32) |
249 ((uint64_t)buf
[4] << 24) |
250 ((uint64_t)buf
[5] << 16) |
251 ((uint64_t)buf
[6] << 8) |
255 uint32_t decode_uint32(unsigned char* buf
)
257 return ((uint32_t)buf
[0] << 24) |
258 ((uint32_t)buf
[1] << 16) |
259 ((uint32_t)buf
[2] << 8) |
263 void read_authors_file(zip_reader
& r
, std::vector
<std::pair
<std::string
, std::string
>>& authors
) throw(std::bad_alloc
,
266 std::istream
& m
= r
["authors"];
269 while(std::getline(m
, x
)) {
271 auto g
= split_author(x
);
272 authors
.push_back(g
);
281 std::string
read_rrdata(zip_reader
& r
, std::vector
<char>& out
) throw(std::bad_alloc
, std::runtime_error
)
283 out
= read_raw_file(r
, "rrdata");
284 uint64_t count
= rrdata
.count(out
);
285 std::ostringstream x
;
290 void write_rrdata(zip_writer
& w
) throw(std::bad_alloc
, std::runtime_error
)
293 std::vector
<char> out
;
294 count
= rrdata
.write(out
);
295 write_raw_file(w
, "rrdata", out
);
296 std::ostream
& m2
= w
.create_file("rerecords");
298 m2
<< count
<< std::endl
;
300 throw std::runtime_error("Can't write ZIP file member");
308 void write_authors_file(zip_writer
& w
, std::vector
<std::pair
<std::string
, std::string
>>& authors
)
309 throw(std::bad_alloc
, std::runtime_error
)
311 std::ostream
& m
= w
.create_file("authors");
313 for(auto i
: authors
)
315 m
<< i
.first
<< std::endl
;
317 m
<< i
.first
<< "|" << i
.second
<< std::endl
;
319 throw std::runtime_error("Can't write ZIP file member");
327 void write_input(zip_writer
& w
, controller_frame_vector
& input
)
328 throw(std::bad_alloc
, std::runtime_error
)
330 std::ostream
& m
= w
.create_file("input");
332 char buffer
[MAX_SERIALIZED_SIZE
];
333 for(size_t i
= 0; i
< input
.size(); i
++) {
334 input
[i
].serialize(buffer
);
335 m
<< buffer
<< std::endl
;
338 throw std::runtime_error("Can't write ZIP file member");
346 void read_subtitles(zip_reader
& r
, const std::string
& file
, std::map
<moviefile_subtiming
, std::string
>& x
)
349 if(!r
.has_member(file
))
351 std::istream
& m
= r
[file
];
355 std::getline(m
, out
);
357 auto r
= regex("([0-9]+)[ \t]+([0-9]+)[ \t]+(.*)", out
);
360 x
[moviefile_subtiming(parse_value
<uint64_t>(r
[1]), parse_value
<uint64_t>(r
[2]))] =
371 void write_subtitles(zip_writer
& w
, const std::string
& file
, std::map
<moviefile_subtiming
, std::string
>& x
)
373 std::ostream
& m
= w
.create_file(file
);
376 m
<< i
.first
.get_frame() << " " << i
.first
.get_length() << " " << s_escape(i
.second
)
379 throw std::runtime_error("Can't write ZIP file member");
387 void read_input(zip_reader
& r
, controller_frame_vector
& input
, unsigned version
) throw(std::bad_alloc
,
390 controller_frame tmp
= input
.blank_frame(false);
391 std::istream
& m
= r
["input"];
394 while(std::getline(m
, x
)) {
397 tmp
.deserialize(x
.c_str());
408 void read_pollcounters(zip_reader
& r
, const std::string
& file
, std::vector
<uint32_t>& pctr
)
410 std::istream
& m
= r
[file
];
413 while(std::getline(m
, x
)) {
416 int32_t y
= parse_value
<int32_t>(x
);
434 void write_pollcounters(zip_writer
& w
, const std::string
& file
, const std::vector
<uint32_t>& pctr
)
436 std::ostream
& m
= w
.create_file(file
);
439 int32_t x
= i
& 0x7FFFFFFFUL
;
440 if((i
& 0x80000000UL
) == 0)
445 throw std::runtime_error("Can't write ZIP file member");
453 moviefile::brief_info::brief_info(const std::string
& filename
)
456 std::istream
& s
= open_file_relative(filename
, "");
459 if(!strcmp(buf
, "lsmv\x1A")) {
466 zip_reader
r(filename
);
468 read_linefile(r
, "systemid", tmp
);
469 if(tmp
.substr(0, 8) != "lsnes-rr")
470 throw std::runtime_error("Not lsnes movie");
471 read_linefile(r
, "gametype", sysregion
);
472 read_linefile(r
, "coreversion", corename
);
473 read_linefile(r
, "projectid", projectid
);
474 if(r
.has_member("savestate"))
475 read_numeric_file(r
, "saveframe", current_frame
);
478 read_numeric_file(r
, "rerecords", rerecords
);
479 read_linefile(r
, "rom.sha256", hash
[0], true);
480 read_linefile(r
, "romxml.sha256", hashxml
[0], true);
481 read_linefile(r
, "rom.hint", hint
[0], true);
483 if(r
.has_member("slot`.sha256"))
485 for(size_t i
= 1; i
< ROM_SLOT_COUNT
; i
++) {
486 read_linefile(r
, (stringfmt() << "slot" << (char)(base
+ i
- 1) << ".sha256").str(), hash
[i
],
488 read_linefile(r
, (stringfmt() << "slot" << (char)(base
+ i
- 1) << "xml.sha256").str(),
490 read_linefile(r
, (stringfmt() << "slot" << (char)(base
+ i
- 1) << ".hint").str(), hint
[i
],
495 void moviefile::brief_info::binary_io(std::istream
& _stream
)
497 binary_input_stream
in(_stream
);
498 sysregion
= in
.string();
499 //Discard the settings.
505 {TAG_CORE_VERSION
, [this](binary_input_stream
& s
) {
506 this->corename
= s
.string_implicit();
507 }},{TAG_PROJECT_ID
, [this](binary_input_stream
& s
) {
508 this->projectid
= s
.string_implicit();
509 }},{TAG_SAVESTATE
, [this](binary_input_stream
& s
) {
510 this->current_frame
= s
.number();
511 }},{TAG_RRDATA
, [this](binary_input_stream
& s
) {
512 std::vector
<char> c_rrdata
;
513 s
.blob_implicit(c_rrdata
);
514 this->rerecords
= rrdata
.count(c_rrdata
);
515 }},{TAG_ROMHASH
, [this](binary_input_stream
& s
) {
516 uint8_t n
= s
.byte();
517 std::string h
= s
.string_implicit();
518 if(n
> 2 * ROM_SLOT_COUNT
)
521 this->hashxml
[n
>> 1] = h
;
523 this->hash
[n
>> 1] = h
;
524 }},{TAG_ROMHINT
, [this](binary_input_stream
& s
) {
525 uint8_t n
= s
.byte();
526 std::string h
= s
.string_implicit();
527 if(n
> ROM_SLOT_COUNT
)
531 }, binary_null_default
);
534 moviefile::moviefile() throw(std::bad_alloc
)
536 static port_type_set dummy_types
;
537 force_corrupt
= false;
542 is_savestate
= false;
543 movie_rtc_second
= rtc_second
= DEFAULT_RTC_SECOND
;
544 movie_rtc_subsecond
= rtc_subsecond
= DEFAULT_RTC_SUBSECOND
;
545 start_paused
= false;
546 lazy_project_create
= true;
550 moviefile::moviefile(const std::string
& movie
, core_type
& romtype
) throw(std::bad_alloc
, std::runtime_error
)
553 start_paused
= false;
554 force_corrupt
= false;
555 is_savestate
= false;
556 lazy_project_create
= false;
559 std::istream
& s
= open_file_relative(movie
, "");
562 if(!strcmp(buf
, "lsmv\x1A")) {
563 binary_io(s
, romtype
);
570 read_linefile(r
, "systemid", tmp
);
571 if(tmp
.substr(0, 8) != "lsnes-rr")
572 throw std::runtime_error("Not lsnes movie");
573 read_linefile(r
, "controlsversion", tmp
);
575 throw std::runtime_error("Can't decode movie data");
576 read_linefile(r
, "gametype", tmp
);
578 gametype
= &romtype
.lookup_sysregion(tmp
);
579 } catch(std::bad_alloc
& e
) {
581 } catch(std::exception
& e
) {
582 throw std::runtime_error("Illegal game type '" + tmp
+ "'");
584 settings
= read_settings(r
);
585 auto ctrldata
= gametype
->get_type().controllerconfig(settings
);
586 port_type_set
& ports
= port_type_set::make(ctrldata
.ports
, ctrldata
.portindex());
589 read_linefile(r
, "gamename", gamename
, true);
590 read_linefile(r
, "projectid", projectid
);
591 rerecords
= read_rrdata(r
, c_rrdata
);
592 read_linefile(r
, "coreversion", coreversion
);
593 read_linefile(r
, "rom.sha256", romimg_sha256
[0], true);
594 read_linefile(r
, "romxml.sha256", romxml_sha256
[0], true);
595 read_linefile(r
, "rom.hint", namehint
[0], true);
597 if(r
.has_member("slot`.sha256"))
599 for(size_t i
= 1; i
< ROM_SLOT_COUNT
; i
++) {
600 read_linefile(r
, (stringfmt() << "slot" << (char)(base
+ i
- 1) << ".sha256").str(), romimg_sha256
[i
],
602 read_linefile(r
, (stringfmt() << "slot" << (char)(base
+ i
- 1) << "xml.sha256").str(),
603 romxml_sha256
[i
], true);
604 read_linefile(r
, (stringfmt() << "slot" << (char)(base
+ i
- 1) << ".hint").str(), namehint
[i
],
607 read_subtitles(r
, "subtitles", subtitles
);
608 movie_rtc_second
= DEFAULT_RTC_SECOND
;
609 movie_rtc_subsecond
= DEFAULT_RTC_SUBSECOND
;
610 read_numeric_file(r
, "starttime.second", movie_rtc_second
, true);
611 read_numeric_file(r
, "starttime.subsecond", movie_rtc_subsecond
, true);
612 rtc_second
= movie_rtc_second
;
613 rtc_subsecond
= movie_rtc_subsecond
;
614 if(r
.has_member("savestate.anchor"))
615 anchor_savestate
= read_raw_file(r
, "savestate.anchor");
616 if(r
.has_member("savestate")) {
618 read_numeric_file(r
, "saveframe", save_frame
, true);
619 read_numeric_file(r
, "lagcounter", lagged_frames
, true);
620 read_pollcounters(r
, "pollcounters", pollcounters
);
621 if(r
.has_member("hostmemory"))
622 host_memory
= read_raw_file(r
, "hostmemory");
623 savestate
= read_raw_file(r
, "savestate");
625 if(name
.length() >= 5 && name
.substr(0, 5) == "sram.")
626 sram
[name
.substr(5)] = read_raw_file(r
, name
);
627 screenshot
= read_raw_file(r
, "screenshot");
628 //If these can't be read, just use some (wrong) values.
629 read_numeric_file(r
, "savetime.second", rtc_second
, true);
630 read_numeric_file(r
, "savetime.subsecond", rtc_subsecond
, true);
631 uint64_t _poll_flag
= 2; //Legacy behaviour is the default.
632 read_numeric_file(r
, "pollflag", _poll_flag
, true);
633 poll_flag
= _poll_flag
;
634 active_macros
= read_active_macros(r
, "macros");
637 if(name
.length() >= 8 && name
.substr(0, 8) == "initram.")
638 ramcontent
[name
.substr(8)] = read_raw_file(r
, name
);
639 if(rtc_subsecond
< 0 || movie_rtc_subsecond
< 0)
640 throw std::runtime_error("Invalid RTC subsecond value");
641 std::string name
= r
.find_first();
643 if(name
.length() >= 10 && name
.substr(0, 10) == "moviesram.")
644 movie_sram
[name
.substr(10)] = read_raw_file(r
, name
);
645 read_authors_file(r
, authors
);
646 read_input(r
, input
, 0);
649 void moviefile::save(const std::string
& movie
, unsigned compression
, bool binary
) throw(std::bad_alloc
,
653 std::string tmp
= movie
+ ".tmp";
654 std::ofstream
strm(tmp
.c_str(), std::ios_base::binary
);
656 throw std::runtime_error("Can't open output file");
657 char buf
[5] = {'l', 's', 'm', 'v', 0x1A};
660 throw std::runtime_error("Failed to write to output file");
663 throw std::runtime_error("Failed to write to output file");
665 std::string backup
= movie
+ ".backup";
666 rename_file_overwrite(movie
.c_str(), backup
.c_str());
667 if(rename_file_overwrite(tmp
.c_str(), movie
.c_str()) < 0)
668 throw std::runtime_error("Can't rename '" + tmp
+ "' -> '" + movie
+ "'");
671 zip_writer
w(movie
, compression
);
672 write_linefile(w
, "gametype", gametype
->get_name());
673 write_settings
<zip_writer
>(w
, settings
, gametype
->get_type().get_settings(), [](zip_writer
& w
,
674 const std::string
& name
, const std::string
& value
) -> void {
675 if(regex_match("port[0-9]+", name
))
676 write_linefile(w
, name
, value
);
678 write_linefile(w
, "setting." + name
, value
);
680 write_linefile(w
, "gamename", gamename
, true);
681 write_linefile(w
, "systemid", "lsnes-rr1");
682 write_linefile(w
, "controlsversion", "0");
683 coreversion
= gametype
->get_type().get_core_identifier();
684 write_linefile(w
, "coreversion", coreversion
);
685 write_linefile(w
, "projectid", projectid
);
687 write_linefile(w
, "rom.sha256", romimg_sha256
[0], true);
688 write_linefile(w
, "romxml.sha256", romxml_sha256
[0], true);
689 write_linefile(w
, "rom.hint", namehint
[0], true);
690 for(size_t i
= 1; i
< ROM_SLOT_COUNT
; i
++) {
691 write_linefile(w
, (stringfmt() << "slot" << (char)(96 + i
) << ".sha256").str(), romimg_sha256
[i
],
693 write_linefile(w
, (stringfmt() << "slot" << (char)(96 + i
) << "xml.sha256").str(), romxml_sha256
[i
],
695 write_linefile(w
, (stringfmt() << "slot" << (char)(96 + i
) << ".hint").str(), namehint
[i
],
698 write_subtitles(w
, "subtitles", subtitles
);
699 for(auto i
: movie_sram
)
700 write_raw_file(w
, "moviesram." + i
.first
, i
.second
);
701 write_numeric_file(w
, "starttime.second", movie_rtc_second
);
702 write_numeric_file(w
, "starttime.subsecond", movie_rtc_subsecond
);
703 if(!anchor_savestate
.empty())
704 write_raw_file(w
, "savestate.anchor", anchor_savestate
);
706 write_numeric_file(w
, "saveframe", save_frame
);
707 write_numeric_file(w
, "lagcounter", lagged_frames
);
708 write_pollcounters(w
, "pollcounters", pollcounters
);
709 write_raw_file(w
, "hostmemory", host_memory
);
710 write_raw_file(w
, "savestate", savestate
);
711 write_raw_file(w
, "screenshot", screenshot
);
713 write_raw_file(w
, "sram." + i
.first
, i
.second
);
714 write_numeric_file(w
, "savetime.second", rtc_second
);
715 write_numeric_file(w
, "savetime.subsecond", rtc_subsecond
);
716 write_numeric_file(w
, "pollflag", poll_flag
);
717 write_active_macros(w
, "macros", active_macros
);
719 for(auto i
: ramcontent
)
720 write_raw_file(w
, "initram." + i
.first
, i
.second
);
721 write_authors_file(w
, authors
);
722 write_input(w
, input
);
728 Following need to be saved:
730 - settings (string name, value pairs)
731 - gamename (optional string)
732 - core version (string)
735 - ROM hashes (2*27 table of optional strings)
736 - Subtitles (list of number,number,string)
737 - SRAMs (dictionary string->blob.)
738 - Starttime (number,number)
739 - Anchor savestate (optional blob)
740 - Save frame (savestate-only, numeric).
741 - Lag counter (savestate-only, numeric).
742 - pollcounters (savestate-only, vector of numbers).
743 - hostmemory (savestate-only, blob).
744 - screenshot (savestate-only, blob).
745 - Save SRAMs (savestate-only, dictionary string->blob.)
746 - Save time (savestate-only, number,number)
747 - Poll flag (savestate-only, boolean)
748 - Macros (savestate-only, ???)
749 - Authors (list of string,string).
753 void moviefile::binary_io(std::ostream
& _stream
) throw(std::bad_alloc
, std::runtime_error
)
755 binary_output_stream
out(_stream
);
756 out
.string(gametype
->get_name());
757 write_settings
<binary_output_stream
>(out
, settings
, gametype
->get_type().get_settings(),
758 [](binary_output_stream
& s
, const std::string
& name
, const std::string
& value
) -> void {
765 out
.extension(TAG_MOVIE_TIME
, [this](binary_output_stream
& s
) {
766 s
.number(this->movie_rtc_second
);
767 s
.number(this->movie_rtc_subsecond
);
770 out
.extension(TAG_PROJECT_ID
, [this](binary_output_stream
& s
) {
771 s
.string_implicit(this->projectid
);
774 out
.extension(TAG_CORE_VERSION
, [this](binary_output_stream
& s
) {
775 this->coreversion
= this->gametype
->get_type().get_core_identifier();
776 s
.string_implicit(this->coreversion
);
779 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
780 out
.extension(TAG_ROMHASH
, [this, i
](binary_output_stream
& s
) {
781 if(!this->romimg_sha256
[i
].length()) return;
783 s
.string_implicit(this->romimg_sha256
[i
]);
785 out
.extension(TAG_ROMHASH
, [this, i
](binary_output_stream
& s
) {
786 if(!this->romxml_sha256
[i
].length()) return;
788 s
.string_implicit(this->romxml_sha256
[i
]);
790 out
.extension(TAG_ROMHINT
, [this, i
](binary_output_stream
& s
) {
791 if(!this->namehint
[i
].length()) return;
793 s
.string_implicit(this->namehint
[i
]);
797 out
.extension(TAG_RRDATA
, [this](binary_output_stream
& s
) {
799 std::vector
<char> rrd
;
800 count
= rrdata
.write(rrd
);
801 s
.blob_implicit(rrd
);
804 for(auto i
: movie_sram
)
805 out
.extension(TAG_MOVIE_SRAM
, [&i
](binary_output_stream
& s
) {
807 s
.blob_implicit(i
.second
);
810 out
.extension(TAG_ANCHOR_SAVE
, [this](binary_output_stream
& s
) {
811 s
.blob_implicit(this->anchor_savestate
);
814 out
.extension(TAG_SAVESTATE
, [this](binary_output_stream
& s
) {
815 s
.number(this->save_frame
);
816 s
.number(this->lagged_frames
);
817 s
.number(this->rtc_second
);
818 s
.number(this->rtc_subsecond
);
819 s
.number(this->pollcounters
.size());
820 for(auto i
: this->pollcounters
)
822 s
.byte(this->poll_flag
? 0x01 : 0x00);
823 s
.blob_implicit(this->savestate
);
824 }, true, out
.numberbytes(save_frame
) + out
.numberbytes(lagged_frames
) + out
.numberbytes(rtc_second
) +
825 out
.numberbytes(rtc_subsecond
) + out
.numberbytes(pollcounters
.size()) +
826 4 * pollcounters
.size() + 1 + savestate
.size());
828 out
.extension(TAG_HOSTMEMORY
, [this](binary_output_stream
& s
) {
829 s
.blob_implicit(this->host_memory
);
832 out
.extension(TAG_SCREENSHOT
, [this](binary_output_stream
& s
) {
833 s
.blob_implicit(this->screenshot
);
834 }, true, screenshot
.size());
837 out
.extension(TAG_SAVE_SRAM
, [&i
](binary_output_stream
& s
) {
839 s
.blob_implicit(i
.second
);
844 out
.extension(TAG_GAMENAME
, [this](binary_output_stream
& s
) {
845 s
.string_implicit(this->gamename
);
848 for(auto i
: subtitles
)
849 out
.extension(TAG_SUBTITLE
, [&i
](binary_output_stream
& s
) {
850 s
.number(i
.first
.get_frame());
851 s
.number(i
.first
.get_length());
852 s
.string_implicit(i
.second
);
855 for(auto i
: authors
)
856 out
.extension(TAG_AUTHOR
, [&i
](binary_output_stream
& s
) {
858 s
.string_implicit(i
.second
);
861 for(auto i
: active_macros
)
862 out
.extension(TAG_MACRO
, [&i
](binary_output_stream
& s
) {
864 s
.string_implicit(i
.first
);
867 for(auto i
: ramcontent
) {
868 out
.extension(TAG_RAMCONTENT
, [&i
](binary_output_stream
& s
) {
870 s
.blob_implicit(i
.second
);
874 binary_write_movie(out
, input
);
877 void moviefile::binary_io(std::istream
& _stream
, core_type
& romtype
) throw(std::bad_alloc
, std::runtime_error
)
879 binary_input_stream
in(_stream
);
880 std::string tmp
= in
.string();
882 gametype
= &romtype
.lookup_sysregion(tmp
);
883 } catch(std::bad_alloc
& e
) {
885 } catch(std::exception
& e
) {
886 throw std::runtime_error("Illegal game type '" + tmp
+ "'");
889 std::string name
= in
.string();
890 settings
[name
] = in
.string();
892 auto ctrldata
= gametype
->get_type().controllerconfig(settings
);
893 port_type_set
& ports
= port_type_set::make(ctrldata
.ports
, ctrldata
.portindex());
897 {TAG_ANCHOR_SAVE
, [this](binary_input_stream
& s
) {
898 s
.blob_implicit(this->anchor_savestate
);
899 }},{TAG_AUTHOR
, [this](binary_input_stream
& s
) {
900 std::string a
= s
.string();
901 std::string b
= s
.string_implicit();
902 this->authors
.push_back(std::make_pair(a
, b
));
903 }},{TAG_CORE_VERSION
, [this](binary_input_stream
& s
) {
904 this->coreversion
= s
.string_implicit();
905 }},{TAG_GAMENAME
, [this](binary_input_stream
& s
) {
906 this->gamename
= s
.string_implicit();
907 }},{TAG_HOSTMEMORY
, [this](binary_input_stream
& s
) {
908 s
.blob_implicit(this->host_memory
);
909 }},{TAG_MACRO
, [this](binary_input_stream
& s
) {
910 uint64_t n
= s
.number();
911 this->active_macros
[s
.string_implicit()] = n
;
912 }},{TAG_MOVIE
, [this](binary_input_stream
& s
) {
913 binary_read_movie(s
, input
);
914 }},{TAG_MOVIE_SRAM
, [this](binary_input_stream
& s
) {
915 std::string a
= s
.string();
916 s
.blob_implicit(this->movie_sram
[a
]);
917 }},{TAG_RAMCONTENT
, [this](binary_input_stream
& s
) {
918 std::string a
= s
.string();
919 s
.blob_implicit(this->ramcontent
[a
]);
920 }},{TAG_MOVIE_TIME
, [this](binary_input_stream
& s
) {
921 this->movie_rtc_second
= s
.number();
922 this->movie_rtc_subsecond
= s
.number();
923 }},{TAG_PROJECT_ID
, [this](binary_input_stream
& s
) {
924 this->projectid
= s
.string_implicit();
925 }},{TAG_ROMHASH
, [this](binary_input_stream
& s
) {
926 uint8_t n
= s
.byte();
927 std::string h
= s
.string_implicit();
928 if(n
> 2 * ROM_SLOT_COUNT
)
931 romxml_sha256
[n
>> 1] = h
;
933 romimg_sha256
[n
>> 1] = h
;
934 }},{TAG_ROMHINT
, [this](binary_input_stream
& s
) {
935 uint8_t n
= s
.byte();
936 std::string h
= s
.string_implicit();
937 if(n
> ROM_SLOT_COUNT
)
940 }},{TAG_RRDATA
, [this](binary_input_stream
& s
) {
941 s
.blob_implicit(this->c_rrdata
);
942 this->rerecords
= (stringfmt() << rrdata
.count(c_rrdata
)).str();
943 }},{TAG_SAVE_SRAM
, [this](binary_input_stream
& s
) {
944 std::string a
= s
.string();
945 s
.blob_implicit(this->sram
[a
]);
946 }},{TAG_SAVESTATE
, [this](binary_input_stream
& s
) {
947 this->is_savestate
= true;
948 this->save_frame
= s
.number();
949 this->lagged_frames
= s
.number();
950 this->rtc_second
= s
.number();
951 this->rtc_subsecond
= s
.number();
952 this->pollcounters
.resize(s
.number());
953 for(auto& i
: this->pollcounters
)
955 this->poll_flag
= (s
.byte() != 0);
956 s
.blob_implicit(this->savestate
);
957 }},{TAG_SCREENSHOT
, [this](binary_input_stream
& s
) {
958 s
.blob_implicit(this->screenshot
);
959 }},{TAG_SUBTITLE
, [this](binary_input_stream
& s
) {
960 uint64_t f
= s
.number();
961 uint64_t l
= s
.number();
962 std::string x
= s
.string_implicit();
963 this->subtitles
[moviefile_subtiming(f
, l
)] = x
;
965 }, binary_null_default
);
968 uint64_t moviefile::get_frame_count() throw()
970 return input
.count_frames();
975 const int BLOCK_SECONDS
= 0;
976 const int BLOCK_FRAMES
= 1;
977 const int STEP_W
= 2;
978 const int STEP_N
= 3;
981 uint64_t moviefile::get_movie_length() throw()
983 uint64_t frames
= get_frame_count();
985 return 100000000ULL * frames
/ 6;
988 gametype
->fill_framerate_magic(_magic
);
989 uint64_t t
= _magic
[BLOCK_SECONDS
] * 1000000000ULL * (frames
/ _magic
[BLOCK_FRAMES
]);
990 frames
%= _magic
[BLOCK_FRAMES
];
991 t
+= frames
* _magic
[STEP_W
] + (frames
* _magic
[STEP_N
] / _magic
[BLOCK_FRAMES
]);