1 #include "core/misc.hpp"
2 #include "core/movie.hpp"
3 #include "core/moviedata.hpp"
4 #include "core/moviefile.hpp"
5 #include "core/moviefile-common.hpp"
6 #include "library/zip.hpp"
7 #include "library/string.hpp"
8 #include "library/minmax.hpp"
9 #include "library/serialization.hpp"
10 #include "library/binarystream.hpp"
11 #include "interface/romtype.hpp"
18 #include <boost/iostreams/copy.hpp>
19 #include <boost/iostreams/device/back_inserter.hpp>
20 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
26 std::map
<std::string
, std::string
> read_settings(zip::reader
& r
)
28 std::map
<std::string
, std::string
> x
;
30 if(!regex_match("port[0-9]+|setting\\..+", i
))
34 if(i
.substr(0, 4) == "port")
38 if(r
.read_linefile(i
, v
, true))
44 std::map
<std::string
, uint64_t> read_active_macros(zip::reader
& r
, const std::string
& member
)
46 std::map
<std::string
, uint64_t> x
;
47 if(!r
.has_member(member
))
49 std::istream
& m
= r
[member
];
57 regex_results rx
= regex("([0-9]+) +(.*)", out
);
59 messages
<< "Warning: Bad macro state: '" << out
<< "'" << std::endl
;
63 uint64_t f
= parse_value
<uint64_t>(rx
[1]);
76 template<typename T
> std::string
pick_a_name(const std::map
<std::string
, T
>& map
, bool prefer_unnamed
)
78 if(prefer_unnamed
&& !map
.count(""))
82 std::string c
= (stringfmt() << "(unnamed branch #" << count
++ << ")").str();
88 void read_authors_file(zip::reader
& r
, std::vector
<std::pair
<std::string
, std::string
>>& authors
)
89 throw(std::bad_alloc
, std::runtime_error
)
91 std::istream
& m
= r
["authors"];
94 while(std::getline(m
, x
)) {
96 auto g
= split_author(x
);
106 std::string
read_rrdata(zip::reader
& r
, std::vector
<char>& out
) throw(std::bad_alloc
, std::runtime_error
)
108 r
.read_raw_file("rrdata", out
);
109 uint64_t count
= rrdata_set::count(out
);
110 std::ostringstream x
;
115 void read_subtitles(zip::reader
& r
, const std::string
& file
, std::map
<moviefile_subtiming
, std::string
>& x
)
118 if(!r
.has_member(file
))
120 std::istream
& m
= r
[file
];
124 std::getline(m
, out
);
126 auto r
= regex("([0-9]+)[ \t]+([0-9]+)[ \t]+(.*)", out
);
129 x
[moviefile_subtiming(parse_value
<uint64_t>(r
[1]), parse_value
<uint64_t>(r
[2]))] =
139 void read_input(zip::reader
& r
, const std::string
& mname
, controller_frame_vector
& input
)
140 throw(std::bad_alloc
, std::runtime_error
)
142 controller_frame tmp
= input
.blank_frame(false);
143 std::istream
& m
= r
[mname
];
146 while(std::getline(m
, x
)) {
149 tmp
.deserialize(x
.c_str());
160 void read_pollcounters(zip::reader
& r
, const std::string
& file
, std::vector
<uint32_t>& pctr
)
162 std::istream
& m
= r
[file
];
165 while(std::getline(m
, x
)) {
168 int32_t y
= parse_value
<int32_t>(x
);
186 std::string
get_namefile(const std::string
& input
)
190 return "branchname.0";
191 else if(s
= regex("input\\.([1-9][0-9]*)", input
))
192 return "branchname." + s
[1];
198 void moviefile::brief_info::load(zip::reader
& r
)
201 r
.read_linefile("systemid", tmp
);
202 if(tmp
.substr(0, 8) != "lsnes-rr")
203 throw std::runtime_error("Not lsnes movie");
204 r
.read_linefile("gametype", sysregion
);
205 r
.read_linefile("coreversion", corename
);
206 r
.read_linefile("projectid", projectid
);
207 if(r
.has_member("savestate"))
208 r
.read_numeric_file("saveframe", current_frame
);
211 r
.read_numeric_file("rerecords", rerecords
);
212 r
.read_linefile("rom.sha256", hash
[0], true);
213 r
.read_linefile("romxml.sha256", hashxml
[0], true);
214 r
.read_linefile("rom.hint", hint
[0], true);
216 if(r
.has_member("slot`.sha256"))
218 for(size_t i
= 1; i
< ROM_SLOT_COUNT
; i
++) {
219 r
.read_linefile((stringfmt() << "slot" << (char)(base
+ i
- 1) << ".sha256").str(), hash
[i
],
221 r
.read_linefile((stringfmt() << "slot" << (char)(base
+ i
- 1) << "xml.sha256").str(),
223 r
.read_linefile((stringfmt() << "slot" << (char)(base
+ i
- 1) << ".hint").str(), hint
[i
],
228 void moviefile::load(zip::reader
& r
, core_type
& romtype
) throw(std::bad_alloc
, std::runtime_error
)
231 r
.read_linefile("systemid", tmp
);
232 if(tmp
.substr(0, 8) != "lsnes-rr")
233 throw std::runtime_error("Not lsnes movie");
234 r
.read_linefile("controlsversion", tmp
);
236 throw std::runtime_error("Can't decode movie data");
237 r
.read_linefile("gametype", tmp
);
239 gametype
= &romtype
.lookup_sysregion(tmp
);
240 } catch(std::bad_alloc
& e
) {
242 } catch(std::exception
& e
) {
243 throw std::runtime_error("Illegal game type '" + tmp
+ "'");
245 settings
= read_settings(r
);
246 auto ctrldata
= gametype
->get_type().controllerconfig(settings
);
247 port_type_set
& ports
= port_type_set::make(ctrldata
.ports
, ctrldata
.portindex());
250 r
.read_linefile("gamename", gamename
, true);
251 r
.read_linefile("projectid", projectid
);
252 rerecords
= read_rrdata(r
, c_rrdata
);
253 r
.read_linefile("coreversion", coreversion
);
254 r
.read_linefile("rom.sha256", romimg_sha256
[0], true);
255 r
.read_linefile("romxml.sha256", romxml_sha256
[0], true);
256 r
.read_linefile("rom.hint", namehint
[0], true);
258 if(r
.has_member("slot`.sha256"))
260 for(size_t i
= 1; i
< ROM_SLOT_COUNT
; i
++) {
261 r
.read_linefile((stringfmt() << "slot" << (char)(base
+ i
- 1) << ".sha256").str(), romimg_sha256
[i
],
263 r
.read_linefile((stringfmt() << "slot" << (char)(base
+ i
- 1) << "xml.sha256").str(),
264 romxml_sha256
[i
], true);
265 r
.read_linefile((stringfmt() << "slot" << (char)(base
+ i
- 1) << ".hint").str(), namehint
[i
],
268 read_subtitles(r
, "subtitles", subtitles
);
269 movie_rtc_second
= DEFAULT_RTC_SECOND
;
270 movie_rtc_subsecond
= DEFAULT_RTC_SUBSECOND
;
271 r
.read_numeric_file("starttime.second", movie_rtc_second
, true);
272 r
.read_numeric_file("starttime.subsecond", movie_rtc_subsecond
, true);
273 rtc_second
= movie_rtc_second
;
274 rtc_subsecond
= movie_rtc_subsecond
;
275 if(r
.has_member("savestate.anchor"))
276 r
.read_raw_file("savestate.anchor", anchor_savestate
);
277 if(r
.has_member("savestate")) {
279 r
.read_numeric_file("saveframe", save_frame
, true);
280 r
.read_numeric_file("lagcounter", lagged_frames
, true);
281 read_pollcounters(r
, "pollcounters", pollcounters
);
282 if(r
.has_member("hostmemory"))
283 r
.read_raw_file("hostmemory", host_memory
);
284 r
.read_raw_file("savestate", savestate
);
286 if(name
.length() >= 5 && name
.substr(0, 5) == "sram.")
287 r
.read_raw_file(name
, sram
[name
.substr(5)]);
288 r
.read_raw_file("screenshot", screenshot
);
289 //If these can't be read, just use some (wrong) values.
290 r
.read_numeric_file("savetime.second", rtc_second
, true);
291 r
.read_numeric_file("savetime.subsecond", rtc_subsecond
, true);
292 uint64_t _poll_flag
= 2; //Legacy behaviour is the default.
293 r
.read_numeric_file("pollflag", _poll_flag
, true);
294 poll_flag
= _poll_flag
;
295 active_macros
= read_active_macros(r
, "macros");
298 if(name
.length() >= 8 && name
.substr(0, 8) == "initram.")
299 r
.read_raw_file(name
, ramcontent
[name
.substr(8)]);
300 if(rtc_subsecond
< 0 || movie_rtc_subsecond
< 0)
301 throw std::runtime_error("Invalid RTC subsecond value");
302 std::string name
= r
.find_first();
304 if(name
.length() >= 10 && name
.substr(0, 10) == "moviesram.")
305 r
.read_raw_file(name
, movie_sram
[name
.substr(10)]);
306 read_authors_file(r
, authors
);
308 std::map
<uint64_t, std::string
> branch_table
;
312 if(s
= regex("branchname\\.([0-9]+)", name
)) {
313 uint64_t n
= parse_value
<uint64_t>(s
[1]);
314 r
.read_linefile(name
, branch_table
[n
]);
315 branches
[branch_table
[n
]].clear(ports
);
321 if(name
== "input") {
322 std::string bname
= branch_table
.count(0) ? branch_table
[0] : pick_a_name(branches
, true);
323 if(!branches
.count(bname
)) branches
[bname
].clear(ports
);
324 read_input(r
, name
, branches
[bname
]);
325 input
= &branches
[bname
];
326 } else if(s
= regex("input\\.([1-9][0-9]*)", name
)) {
327 uint64_t n
= parse_value
<uint64_t>(s
[1]);
328 std::string bname
= branch_table
.count(n
) ? branch_table
[n
] : pick_a_name(branches
, false);
329 if(!branches
.count(bname
)) branches
[bname
].clear(ports
);
330 read_input(r
, name
, branches
[bname
]);
334 create_default_branch(ports
);
337 moviefile_branch_extractor_text::moviefile_branch_extractor_text(const std::string
& filename
)
342 moviefile_branch_extractor_text::~moviefile_branch_extractor_text()
346 std::set
<std::string
> moviefile_branch_extractor_text::enumerate()
348 std::set
<std::string
> r
;
351 std::string n
= get_namefile(i
);
353 if(z
.has_member(n
)) {
354 z
.read_linefile(n
, bname
);
363 void moviefile_branch_extractor_text::read(const std::string
& name
, controller_frame_vector
& v
)
365 std::set
<std::string
> r
;
369 std::string n
= get_namefile(i
);
373 z
.read_linefile(n
, bname
);
382 (stringfmt() << "Can't find branch '" << name
<< "' in file.").throwex();