1 #include "core/messages.hpp"
2 #include "core/moviedata.hpp"
3 #include "core/moviefile-common.hpp"
4 #include "core/moviefile.hpp"
5 #include "library/binarystream.hpp"
6 #include "library/minmax.hpp"
7 #include "library/serialization.hpp"
8 #include "library/string.hpp"
9 #include "library/zip.hpp"
16 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
22 std::map
<std::string
, std::string
> read_settings(zip::reader
& r
)
24 std::map
<std::string
, std::string
> x
;
26 if(!regex_match("port[0-9]+|setting\\..+", i
))
30 if(i
.substr(0, 4) == "port")
34 if(r
.read_linefile(i
, v
, true))
40 std::map
<std::string
, uint64_t> read_active_macros(zip::reader
& r
, const std::string
& member
)
42 std::map
<std::string
, uint64_t> x
;
43 if(!r
.has_member(member
))
45 std::istream
& m
= r
[member
];
53 regex_results rx
= regex("([0-9]+) +(.*)", out
);
55 messages
<< "Warning: Bad macro state: '" << out
<< "'" << std::endl
;
59 uint64_t f
= parse_value
<uint64_t>(rx
[1]);
72 template<typename T
> std::string
pick_a_name(const std::map
<std::string
, T
>& map
, bool prefer_unnamed
)
74 if(prefer_unnamed
&& !map
.count(""))
78 std::string c
= (stringfmt() << "(unnamed branch #" << count
++ << ")").str();
84 void read_authors_file(zip::reader
& r
, std::vector
<std::pair
<std::string
, std::string
>>& authors
)
85 throw(std::bad_alloc
, std::runtime_error
)
87 std::istream
& m
= r
["authors"];
90 while(std::getline(m
, x
)) {
92 auto g
= split_author(x
);
102 std::string
read_rrdata(zip::reader
& r
, std::vector
<char>& out
) throw(std::bad_alloc
, std::runtime_error
)
104 r
.read_raw_file("rrdata", out
);
105 uint64_t count
= rrdata_set::count(out
);
106 std::ostringstream x
;
111 void read_subtitles(zip::reader
& r
, const std::string
& file
, std::map
<moviefile_subtiming
, std::string
>& x
)
114 if(!r
.has_member(file
))
116 std::istream
& m
= r
[file
];
120 std::getline(m
, out
);
122 auto r
= regex("([0-9]+)[ \t]+([0-9]+)[ \t]+(.*)", out
);
125 x
[moviefile_subtiming(parse_value
<uint64_t>(r
[1]), parse_value
<uint64_t>(r
[2]))] =
126 subtitle_commentary::s_unescape(r
[3]);
135 void read_input(zip::reader
& r
, const std::string
& mname
, portctrl::frame_vector
& input
)
136 throw(std::bad_alloc
, std::runtime_error
)
138 portctrl::frame tmp
= input
.blank_frame(false);
139 std::istream
& m
= r
[mname
];
142 while(std::getline(m
, x
)) {
145 tmp
.deserialize(x
.c_str());
156 void read_pollcounters(zip::reader
& r
, const std::string
& file
, std::vector
<uint32_t>& pctr
)
158 std::istream
& m
= r
[file
];
161 while(std::getline(m
, x
)) {
164 int32_t y
= parse_value
<int32_t>(x
);
182 std::string
get_namefile(const std::string
& input
)
186 return "branchname.0";
187 else if(s
= regex("input\\.([1-9][0-9]*)", input
))
188 return "branchname." + s
[1];
194 void moviefile::brief_info::load(zip::reader
& r
)
197 r
.read_linefile("systemid", tmp
);
198 if(tmp
.substr(0, 8) != "lsnes-rr")
199 throw std::runtime_error("Not lsnes movie");
200 r
.read_linefile("gametype", sysregion
);
201 r
.read_linefile("coreversion", corename
);
202 r
.read_linefile("projectid", projectid
);
203 if(r
.has_member("savestate"))
204 r
.read_numeric_file("saveframe", current_frame
);
207 r
.read_numeric_file("rerecords", rerecords
);
208 r
.read_linefile("rom.sha256", hash
[0], true);
209 r
.read_linefile("romxml.sha256", hashxml
[0], true);
210 r
.read_linefile("rom.hint", hint
[0], true);
212 if(r
.has_member("slot`.sha256"))
214 for(size_t i
= 1; i
< ROM_SLOT_COUNT
; i
++) {
215 r
.read_linefile((stringfmt() << "slot" << (char)(base
+ i
- 1) << ".sha256").str(), hash
[i
],
217 r
.read_linefile((stringfmt() << "slot" << (char)(base
+ i
- 1) << "xml.sha256").str(),
219 r
.read_linefile((stringfmt() << "slot" << (char)(base
+ i
- 1) << ".hint").str(), hint
[i
],
224 void moviefile::load(zip::reader
& r
, core_type
& romtype
) throw(std::bad_alloc
, std::runtime_error
)
227 r
.read_linefile("systemid", tmp
);
228 if(tmp
.substr(0, 8) != "lsnes-rr")
229 throw std::runtime_error("Not lsnes movie");
230 r
.read_linefile("controlsversion", tmp
);
232 throw std::runtime_error("Can't decode movie data");
233 r
.read_linefile("gametype", tmp
);
235 gametype
= &romtype
.lookup_sysregion(tmp
);
236 } catch(std::bad_alloc
& e
) {
238 } catch(std::exception
& e
) {
239 throw std::runtime_error("Illegal game type '" + tmp
+ "'");
241 settings
= read_settings(r
);
242 auto ctrldata
= gametype
->get_type().controllerconfig(settings
);
243 portctrl::type_set
& ports
= portctrl::type_set::make(ctrldata
.ports
, ctrldata
.portindex());
244 dyn
.save_frame
= 0; //Ensure load as movie if no savestate.
247 r
.read_linefile("gamename", gamename
, true);
248 r
.read_linefile("projectid", projectid
);
249 rerecords
= read_rrdata(r
, c_rrdata
);
250 r
.read_linefile("coreversion", coreversion
);
251 r
.read_linefile("rom.sha256", romimg_sha256
[0], true);
252 r
.read_linefile("romxml.sha256", romxml_sha256
[0], true);
253 r
.read_linefile("rom.hint", namehint
[0], true);
255 if(r
.has_member("slot`.sha256"))
257 for(size_t i
= 1; i
< ROM_SLOT_COUNT
; i
++) {
258 r
.read_linefile((stringfmt() << "slot" << (char)(base
+ i
- 1) << ".sha256").str(), romimg_sha256
[i
],
260 r
.read_linefile((stringfmt() << "slot" << (char)(base
+ i
- 1) << "xml.sha256").str(),
261 romxml_sha256
[i
], true);
262 r
.read_linefile((stringfmt() << "slot" << (char)(base
+ i
- 1) << ".hint").str(), namehint
[i
],
265 read_subtitles(r
, "subtitles", subtitles
);
266 movie_rtc_second
= DEFAULT_RTC_SECOND
;
267 movie_rtc_subsecond
= DEFAULT_RTC_SUBSECOND
;
268 r
.read_numeric_file("starttime.second", movie_rtc_second
, true);
269 r
.read_numeric_file("starttime.subsecond", movie_rtc_subsecond
, true);
270 dyn
.rtc_second
= movie_rtc_second
;
271 dyn
.rtc_subsecond
= movie_rtc_subsecond
;
272 if(r
.has_member("savestate.anchor"))
273 r
.read_raw_file("savestate.anchor", anchor_savestate
);
274 if(r
.has_member("savestate")) {
275 r
.read_numeric_file("saveframe", dyn
.save_frame
, true);
276 r
.read_numeric_file("lagcounter", dyn
.lagged_frames
, true);
277 read_pollcounters(r
, "pollcounters", dyn
.pollcounters
);
278 if(r
.has_member("hostmemory"))
279 r
.read_raw_file("hostmemory", dyn
.host_memory
);
280 r
.read_raw_file("savestate", dyn
.savestate
);
282 if(name
.length() >= 5 && name
.substr(0, 5) == "sram.")
283 r
.read_raw_file(name
, dyn
.sram
[name
.substr(5)]);
284 r
.read_raw_file("screenshot", dyn
.screenshot
);
285 //If these can't be read, just use some (wrong) values.
286 r
.read_numeric_file("savetime.second", dyn
.rtc_second
, true);
287 r
.read_numeric_file("savetime.subsecond", dyn
.rtc_subsecond
, true);
288 uint64_t _poll_flag
= 2; //Legacy behaviour is the default.
289 r
.read_numeric_file("pollflag", _poll_flag
, true);
290 dyn
.poll_flag
= _poll_flag
;
291 dyn
.active_macros
= read_active_macros(r
, "macros");
294 if(name
.length() >= 8 && name
.substr(0, 8) == "initram.")
295 r
.read_raw_file(name
, ramcontent
[name
.substr(8)]);
296 if(dyn
.rtc_subsecond
< 0 || movie_rtc_subsecond
< 0)
297 throw std::runtime_error("Invalid RTC subsecond value");
298 std::string name
= r
.find_first();
300 if(name
.length() >= 10 && name
.substr(0, 10) == "moviesram.")
301 r
.read_raw_file(name
, movie_sram
[name
.substr(10)]);
302 read_authors_file(r
, authors
);
304 std::map
<uint64_t, std::string
> branch_table
;
308 if(s
= regex("branchname\\.([0-9]+)", name
)) {
309 uint64_t n
= parse_value
<uint64_t>(s
[1]);
310 r
.read_linefile(name
, branch_table
[n
]);
311 branches
[branch_table
[n
]].clear(ports
);
317 if(name
== "input") {
318 std::string bname
= branch_table
.count(0) ? branch_table
[0] : pick_a_name(branches
, true);
319 if(!branches
.count(bname
)) branches
[bname
].clear(ports
);
320 read_input(r
, name
, branches
[bname
]);
321 input
= &branches
[bname
];
322 } else if(s
= regex("input\\.([1-9][0-9]*)", name
)) {
323 uint64_t n
= parse_value
<uint64_t>(s
[1]);
324 std::string bname
= branch_table
.count(n
) ? branch_table
[n
] : pick_a_name(branches
, false);
325 if(!branches
.count(bname
)) branches
[bname
].clear(ports
);
326 read_input(r
, name
, branches
[bname
]);
330 create_default_branch(ports
);
333 moviefile_branch_extractor_text::moviefile_branch_extractor_text(const std::string
& filename
)
338 moviefile_branch_extractor_text::~moviefile_branch_extractor_text()
342 std::set
<std::string
> moviefile_branch_extractor_text::enumerate()
344 std::set
<std::string
> r
;
347 std::string n
= get_namefile(i
);
349 if(z
.has_member(n
)) {
350 z
.read_linefile(n
, bname
);
359 void moviefile_branch_extractor_text::read(const std::string
& name
, portctrl::frame_vector
& v
)
361 std::set
<std::string
> r
;
365 std::string n
= get_namefile(i
);
369 z
.read_linefile(n
, bname
);
378 (stringfmt() << "Can't find branch '" << name
<< "' in file.").throwex();
382 moviefile_sram_extractor_text::moviefile_sram_extractor_text(const std::string
& filename
)
387 moviefile_sram_extractor_text::~moviefile_sram_extractor_text()
391 std::set
<std::string
> moviefile_sram_extractor_text::enumerate()
393 std::set
<std::string
> r
;
398 else if(rgx
= regex("sram\\.(.*)", i
))
404 void moviefile_sram_extractor_text::read(const std::string
& name
, std::vector
<char>& v
)
406 std::set
<std::string
> r
;
407 std::string realname
;
409 //Try to read savestate.
410 realname
= "savestate";
412 realname
= "sram." + name
;
415 z
.read_raw_file(realname
, v
);
418 (stringfmt() << "Can't find SRAM '" << name
<< "' in file.").throwex();
420 (stringfmt() << "Can't find savestate in file.").throwex();