2 #include <snes/snes.hpp>
3 #include <ui-libsnes/libsnes.hpp>
4 #include "moviefile.hpp"
8 #include "moviedata.hpp"
10 #include <boost/iostreams/copy.hpp>
11 #include <boost/iostreams/device/back_inserter.hpp>
13 #define DEFAULT_RTC_SECOND 1000000000ULL
14 #define DEFAULT_RTC_SUBSECOND 0ULL
16 void strip_CR(std::string
& x
) throw(std::bad_alloc
)
18 if(x
.length() > 0 && x
[x
.length() - 1] == '\r') {
20 x
= x
.substr(0, x
.length() - 1);
26 void read_linefile(zip_reader
& r
, const std::string
& member
, std::string
& out
, bool conditional
= false)
27 throw(std::bad_alloc
, std::runtime_error
)
29 if(conditional
&& !r
.has_member(member
))
31 std::istream
& m
= r
[member
];
42 void read_numeric_file(zip_reader
& r
, const std::string
& member
, int64_t& out
, bool conditional
= false)
43 throw(std::bad_alloc
, std::runtime_error
)
46 read_linefile(r
, member
, _out
, conditional
);
47 if(conditional
&& _out
== "")
49 out
= parse_value
<int64_t>(_out
);
52 void write_linefile(zip_writer
& w
, const std::string
& member
, const std::string
& value
, bool conditional
= false)
53 throw(std::bad_alloc
, std::runtime_error
)
55 if(conditional
&& value
== "")
57 std::ostream
& m
= w
.create_file(member
);
59 m
<< value
<< std::endl
;
67 void write_numeric_file(zip_writer
& w
, const std::string
& member
, int64_t value
) throw(std::bad_alloc
,
72 write_linefile(w
, member
, x
.str());
75 void write_raw_file(zip_writer
& w
, const std::string
& member
, std::vector
<char>& content
) throw(std::bad_alloc
,
78 std::ostream
& m
= w
.create_file(member
);
80 m
.write(&content
[0], content
.size());
82 throw std::runtime_error("Can't write ZIP file member");
90 std::vector
<char> read_raw_file(zip_reader
& r
, const std::string
& member
) throw(std::bad_alloc
, std::runtime_error
)
92 std::vector
<char> out
;
93 std::istream
& m
= r
[member
];
95 boost::iostreams::back_insert_device
<std::vector
<char>> rd(out
);
96 boost::iostreams::copy(m
, rd
);
105 void read_authors_file(zip_reader
& r
, std::vector
<std::pair
<std::string
, std::string
>>& authors
) throw(std::bad_alloc
,
108 std::istream
& m
= r
["authors"];
111 while(std::getline(m
, x
)) {
113 auto g
= split_author(x
);
114 authors
.push_back(g
);
123 std::string
read_rrdata(zip_reader
& r
, std::vector
<char>& out
) throw(std::bad_alloc
, std::runtime_error
)
125 out
= read_raw_file(r
, "rrdata");
126 uint64_t count
= rrdata::count(out
);
127 std::ostringstream x
;
132 void write_rrdata(zip_writer
& w
) throw(std::bad_alloc
, std::runtime_error
)
135 std::vector
<char> out
;
136 count
= rrdata::write(out
);
137 write_raw_file(w
, "rrdata", out
);
138 std::ostream
& m2
= w
.create_file("rerecords");
140 m2
<< count
<< std::endl
;
142 throw std::runtime_error("Can't write ZIP file member");
150 void write_authors_file(zip_writer
& w
, std::vector
<std::pair
<std::string
, std::string
>>& authors
)
151 throw(std::bad_alloc
, std::runtime_error
)
153 std::ostream
& m
= w
.create_file("authors");
155 for(auto i
: authors
)
157 m
<< i
.first
<< std::endl
;
159 m
<< i
.first
<< "|" << i
.second
<< std::endl
;
161 throw std::runtime_error("Can't write ZIP file member");
169 void write_input(zip_writer
& w
, std::vector
<controls_t
>& input
, porttype_t port1
, porttype_t port2
)
170 throw(std::bad_alloc
, std::runtime_error
)
172 std::vector
<cencode::fn_t
> encoders
;
173 encoders
.push_back(port_types
[port1
].encoder
);
174 encoders
.push_back(port_types
[port2
].encoder
);
175 std::ostream
& m
= w
.create_file("input");
178 m
<< i
.tostring(encoders
) << std::endl
;
180 throw std::runtime_error("Can't write ZIP file member");
188 void read_input(zip_reader
& r
, std::vector
<controls_t
>& input
, porttype_t port1
, porttype_t port2
, unsigned version
)
189 throw(std::bad_alloc
, std::runtime_error
)
191 std::vector
<cdecode::fn_t
> decoders
;
192 decoders
.push_back(port_types
[port1
].decoder
);
193 decoders
.push_back(port_types
[port2
].decoder
);
194 std::istream
& m
= r
["input"];
197 while(std::getline(m
, x
)) {
200 input
.push_back(controls_t(x
, decoders
, version
));
211 porttype_t
parse_controller_type(const std::string
& type
, bool port
) throw(std::bad_alloc
, std::runtime_error
)
213 porttype_t port1
= PT_INVALID
;
214 for(unsigned i
= 0; i
<= PT_LAST_CTYPE
; i
++)
215 if(type
== port_types
[i
].name
&& (port
|| port_types
[i
].valid_port1
))
216 port1
= static_cast<porttype_t
>(i
);
217 if(port1
== PT_INVALID
)
218 throw std::runtime_error(std::string("Illegal port") + (port
? "2" : "1") + " device '" + type
+ "'");
223 moviefile::moviefile() throw(std::bad_alloc
)
225 force_corrupt
= false;
226 gametype
= GT_INVALID
;
232 is_savestate
= false;
233 movie_rtc_second
= rtc_second
= DEFAULT_RTC_SECOND
;
234 movie_rtc_subsecond
= rtc_subsecond
= DEFAULT_RTC_SUBSECOND
;
237 moviefile::moviefile(const std::string
& movie
) throw(std::bad_alloc
, std::runtime_error
)
239 force_corrupt
= false;
240 is_savestate
= false;
243 read_linefile(r
, "systemid", tmp
);
244 if(tmp
.substr(0, 8) != "lsnes-rr")
245 throw std::runtime_error("Not lsnes movie");
246 read_linefile(r
, "controlsversion", tmp
);
248 throw std::runtime_error("Can't decode movie data");
249 read_linefile(r
, "gametype", tmp
);
251 gametype
= gtype::togametype(tmp
);
252 } catch(std::bad_alloc
& e
) {
254 } catch(std::exception
& e
) {
255 throw std::runtime_error("Illegal game type '" + tmp
+ "'");
257 tmp
= port_types
[PT_GAMEPAD
].name
;
258 read_linefile(r
, "port1", tmp
, true);
259 port1
= port_type::lookup(tmp
, false).ptype
;
260 tmp
= port_types
[PT_NONE
].name
;
261 read_linefile(r
, "port2", tmp
, true);
262 port2
= port_type::lookup(tmp
, true).ptype
;
263 read_linefile(r
, "gamename", gamename
, true);
264 read_linefile(r
, "projectid", projectid
);
265 rerecords
= read_rrdata(r
, c_rrdata
);
266 read_linefile(r
, "coreversion", coreversion
);
267 read_linefile(r
, "rom.sha256", rom_sha256
, true);
268 read_linefile(r
, "romxml.sha256", romxml_sha256
, true);
269 read_linefile(r
, "slota.sha256", slota_sha256
, true);
270 read_linefile(r
, "slotaxml.sha256", slotaxml_sha256
, true);
271 read_linefile(r
, "slotb.sha256", slotb_sha256
, true);
272 read_linefile(r
, "slotbxml.sha256", slotbxml_sha256
, true);
273 movie_rtc_second
= DEFAULT_RTC_SECOND
;
274 movie_rtc_subsecond
= DEFAULT_RTC_SUBSECOND
;
275 read_numeric_file(r
, "starttime.second", movie_rtc_second
, true);
276 read_numeric_file(r
, "starttime.subsecond", movie_rtc_subsecond
, true);
277 rtc_second
= movie_rtc_second
;
278 rtc_subsecond
= movie_rtc_subsecond
;
279 if(r
.has_member("savestate")) {
281 movie_state
= read_raw_file(r
, "moviestate");
282 if(r
.has_member("hostmemory"))
283 host_memory
= read_raw_file(r
, "hostmemory");
284 savestate
= read_raw_file(r
, "savestate");
286 if(name
.length() >= 5 && name
.substr(0, 5) == "sram.")
287 sram
[name
.substr(5)] = read_raw_file(r
, name
);
288 screenshot
= read_raw_file(r
, "screenshot");
289 //If these can't be read, just use some (wrong) values.
290 read_numeric_file(r
, "savetime.second", rtc_second
, true);
291 read_numeric_file(r
, "savetime.subsecond", rtc_subsecond
, true);
293 if(rtc_subsecond
< 0 || movie_rtc_subsecond
< 0)
294 throw std::runtime_error("Invalid RTC subsecond value");
295 std::string name
= r
.find_first();
297 if(name
.length() >= 10 && name
.substr(0, 10) == "moviesram.")
298 movie_sram
[name
.substr(10)] = read_raw_file(r
, name
);
299 read_authors_file(r
, authors
);
300 read_input(r
, input
, port1
, port2
, 0);
303 void moviefile::save(const std::string
& movie
, unsigned compression
) throw(std::bad_alloc
, std::runtime_error
)
305 zip_writer
w(movie
, compression
);
306 write_linefile(w
, "gametype", gtype::tostring(gametype
));
307 if(port1
!= PT_GAMEPAD
)
308 write_linefile(w
, "port1", port_types
[port1
].name
);
310 write_linefile(w
, "port2", port_types
[port2
].name
);
311 write_linefile(w
, "gamename", gamename
, true);
312 write_linefile(w
, "systemid", "lsnes-rr1");
313 write_linefile(w
, "controlsversion", "0");
314 coreversion
= bsnes_core_version
;
315 write_linefile(w
, "coreversion", coreversion
);
316 write_linefile(w
, "projectid", projectid
);
318 write_linefile(w
, "rom.sha256", rom_sha256
, true);
319 write_linefile(w
, "romxml.sha256", romxml_sha256
, true);
320 write_linefile(w
, "slota.sha256", slota_sha256
, true);
321 write_linefile(w
, "slotaxml.sha256", slotaxml_sha256
, true);
322 write_linefile(w
, "slotb.sha256", slotb_sha256
, true);
323 write_linefile(w
, "slotbxml.sha256", slotbxml_sha256
, true);
324 for(auto i
: movie_sram
)
325 write_raw_file(w
, "moviesram." + i
.first
, i
.second
);
326 write_numeric_file(w
, "starttime.second", movie_rtc_second
);
327 write_numeric_file(w
, "starttime.subsecond", movie_rtc_subsecond
);
329 write_raw_file(w
, "moviestate", movie_state
);
330 write_raw_file(w
, "hostmemory", host_memory
);
331 write_raw_file(w
, "savestate", savestate
);
332 write_raw_file(w
, "screenshot", screenshot
);
334 write_raw_file(w
, "sram." + i
.first
, i
.second
);
335 write_numeric_file(w
, "savetime.second", rtc_second
);
336 write_numeric_file(w
, "savetime.subsecond", rtc_subsecond
);
338 write_authors_file(w
, authors
);
339 write_input(w
, input
, port1
, port2
);
344 uint64_t moviefile::get_frame_count() throw()
347 for(size_t i
= 0; i
< input
.size(); i
++) {
348 if(input
[i
](CONTROL_FRAME_SYNC
))
356 const int BLOCK_SECONDS
= 0;
357 const int BLOCK_FRAMES
= 1;
358 const int STEP_W
= 2;
359 const int STEP_N
= 3;
361 uint64_t magic
[2][4] = {
362 {178683, 10738636, 16639264, 596096},
363 {6448, 322445, 19997208, 266440}
367 uint64_t moviefile::get_movie_length(uint64_t framebias
) throw()
369 uint64_t frames
= get_frame_count();
370 if(frames
> framebias
)
374 uint64_t* _magic
= magic
[(gametype
== GT_SNES_PAL
|| gametype
== GT_SGB_PAL
) ? 1 : 0];
375 uint64_t t
= _magic
[BLOCK_SECONDS
] * 1000000000ULL * (frames
/ _magic
[BLOCK_FRAMES
]);
376 frames
%= _magic
[BLOCK_FRAMES
];
377 t
+= frames
* _magic
[STEP_W
] + (frames
* _magic
[STEP_N
] / _magic
[BLOCK_FRAMES
]);
381 gametype_t
gametype_compose(rom_type type
, rom_region region
)
385 return (region
== REGION_PAL
) ? GT_SNES_PAL
: GT_SNES_NTSC
;
388 case ROMTYPE_BSXSLOTTED
:
389 return GT_BSX_SLOTTED
;
390 case ROMTYPE_SUFAMITURBO
:
391 return GT_SUFAMITURBO
;
393 return (region
== REGION_PAL
) ? GT_SGB_PAL
: GT_SGB_NTSC
;
399 rom_region
gametype_region(gametype_t type
)
410 rom_type
gametype_romtype(gametype_t type
)
419 return ROMTYPE_BSXSLOTTED
;
421 return ROMTYPE_SUFAMITURBO
;