2 #include <snes/snes.hpp>
3 #include <ui-libsnes/libsnes.hpp>
5 #include "core/misc.hpp"
6 #include "core/moviedata.hpp"
7 #include "core/moviefile.hpp"
8 #include "core/rrdata.hpp"
9 #include "core/zip.hpp"
12 #include <boost/iostreams/copy.hpp>
13 #include <boost/iostreams/device/back_inserter.hpp>
15 #define DEFAULT_RTC_SECOND 1000000000ULL
16 #define DEFAULT_RTC_SUBSECOND 0ULL
18 void strip_CR(std::string
& x
) throw(std::bad_alloc
)
20 if(x
.length() > 0 && x
[x
.length() - 1] == '\r') {
22 x
= x
.substr(0, x
.length() - 1);
28 void read_linefile(zip_reader
& r
, const std::string
& member
, std::string
& out
, bool conditional
= false)
29 throw(std::bad_alloc
, std::runtime_error
)
31 if(conditional
&& !r
.has_member(member
))
33 std::istream
& m
= r
[member
];
44 void read_numeric_file(zip_reader
& r
, const std::string
& member
, int64_t& out
, bool conditional
= false)
45 throw(std::bad_alloc
, std::runtime_error
)
48 read_linefile(r
, member
, _out
, conditional
);
49 if(conditional
&& _out
== "")
51 out
= parse_value
<int64_t>(_out
);
54 void write_linefile(zip_writer
& w
, const std::string
& member
, const std::string
& value
, bool conditional
= false)
55 throw(std::bad_alloc
, std::runtime_error
)
57 if(conditional
&& value
== "")
59 std::ostream
& m
= w
.create_file(member
);
61 m
<< value
<< std::endl
;
69 void write_numeric_file(zip_writer
& w
, const std::string
& member
, int64_t value
) throw(std::bad_alloc
,
74 write_linefile(w
, member
, x
.str());
77 void write_raw_file(zip_writer
& w
, const std::string
& member
, std::vector
<char>& content
) throw(std::bad_alloc
,
80 std::ostream
& m
= w
.create_file(member
);
82 m
.write(&content
[0], content
.size());
84 throw std::runtime_error("Can't write ZIP file member");
92 std::vector
<char> read_raw_file(zip_reader
& r
, const std::string
& member
) throw(std::bad_alloc
, std::runtime_error
)
94 std::vector
<char> out
;
95 std::istream
& m
= r
[member
];
97 boost::iostreams::back_insert_device
<std::vector
<char>> rd(out
);
98 boost::iostreams::copy(m
, rd
);
107 void read_authors_file(zip_reader
& r
, std::vector
<std::pair
<std::string
, std::string
>>& authors
) throw(std::bad_alloc
,
110 std::istream
& m
= r
["authors"];
113 while(std::getline(m
, x
)) {
115 auto g
= split_author(x
);
116 authors
.push_back(g
);
125 std::string
read_rrdata(zip_reader
& r
, std::vector
<char>& out
) throw(std::bad_alloc
, std::runtime_error
)
127 out
= read_raw_file(r
, "rrdata");
128 uint64_t count
= rrdata::count(out
);
129 std::ostringstream x
;
134 void write_rrdata(zip_writer
& w
) throw(std::bad_alloc
, std::runtime_error
)
137 std::vector
<char> out
;
138 count
= rrdata::write(out
);
139 write_raw_file(w
, "rrdata", out
);
140 std::ostream
& m2
= w
.create_file("rerecords");
142 m2
<< count
<< std::endl
;
144 throw std::runtime_error("Can't write ZIP file member");
152 void write_authors_file(zip_writer
& w
, std::vector
<std::pair
<std::string
, std::string
>>& authors
)
153 throw(std::bad_alloc
, std::runtime_error
)
155 std::ostream
& m
= w
.create_file("authors");
157 for(auto i
: authors
)
159 m
<< i
.first
<< std::endl
;
161 m
<< i
.first
<< "|" << i
.second
<< std::endl
;
163 throw std::runtime_error("Can't write ZIP file member");
171 void write_input(zip_writer
& w
, std::vector
<controls_t
>& input
, porttype_t port1
, porttype_t port2
)
172 throw(std::bad_alloc
, std::runtime_error
)
174 std::vector
<cencode::fn_t
> encoders
;
175 encoders
.push_back(port_types
[port1
].encoder
);
176 encoders
.push_back(port_types
[port2
].encoder
);
177 std::ostream
& m
= w
.create_file("input");
180 m
<< i
.tostring(encoders
) << std::endl
;
182 throw std::runtime_error("Can't write ZIP file member");
190 void read_input(zip_reader
& r
, std::vector
<controls_t
>& input
, porttype_t port1
, porttype_t port2
, unsigned version
)
191 throw(std::bad_alloc
, std::runtime_error
)
193 std::vector
<cdecode::fn_t
> decoders
;
194 decoders
.push_back(port_types
[port1
].decoder
);
195 decoders
.push_back(port_types
[port2
].decoder
);
196 std::istream
& m
= r
["input"];
199 while(std::getline(m
, x
)) {
202 input
.push_back(controls_t(x
, decoders
, version
));
213 porttype_t
parse_controller_type(const std::string
& type
, bool port
) throw(std::bad_alloc
, std::runtime_error
)
215 porttype_t port1
= PT_INVALID
;
216 for(unsigned i
= 0; i
<= PT_LAST_CTYPE
; i
++)
217 if(type
== port_types
[i
].name
&& (port
|| port_types
[i
].valid_port1
))
218 port1
= static_cast<porttype_t
>(i
);
219 if(port1
== PT_INVALID
)
220 throw std::runtime_error(std::string("Illegal port") + (port
? "2" : "1") + " device '" + type
+ "'");
225 moviefile::moviefile() throw(std::bad_alloc
)
227 force_corrupt
= false;
228 gametype
= GT_INVALID
;
234 is_savestate
= false;
235 movie_rtc_second
= rtc_second
= DEFAULT_RTC_SECOND
;
236 movie_rtc_subsecond
= rtc_subsecond
= DEFAULT_RTC_SUBSECOND
;
239 moviefile::moviefile(const std::string
& movie
) throw(std::bad_alloc
, std::runtime_error
)
241 force_corrupt
= false;
242 is_savestate
= false;
245 read_linefile(r
, "systemid", tmp
);
246 if(tmp
.substr(0, 8) != "lsnes-rr")
247 throw std::runtime_error("Not lsnes movie");
248 read_linefile(r
, "controlsversion", tmp
);
250 throw std::runtime_error("Can't decode movie data");
251 read_linefile(r
, "gametype", tmp
);
253 gametype
= gtype::togametype(tmp
);
254 } catch(std::bad_alloc
& e
) {
256 } catch(std::exception
& e
) {
257 throw std::runtime_error("Illegal game type '" + tmp
+ "'");
259 tmp
= port_types
[PT_GAMEPAD
].name
;
260 read_linefile(r
, "port1", tmp
, true);
261 port1
= port_type::lookup(tmp
, false).ptype
;
262 tmp
= port_types
[PT_NONE
].name
;
263 read_linefile(r
, "port2", tmp
, true);
264 port2
= port_type::lookup(tmp
, true).ptype
;
265 read_linefile(r
, "gamename", gamename
, true);
266 read_linefile(r
, "projectid", projectid
);
267 rerecords
= read_rrdata(r
, c_rrdata
);
268 read_linefile(r
, "coreversion", coreversion
);
269 read_linefile(r
, "rom.sha256", rom_sha256
, true);
270 read_linefile(r
, "romxml.sha256", romxml_sha256
, true);
271 read_linefile(r
, "slota.sha256", slota_sha256
, true);
272 read_linefile(r
, "slotaxml.sha256", slotaxml_sha256
, true);
273 read_linefile(r
, "slotb.sha256", slotb_sha256
, true);
274 read_linefile(r
, "slotbxml.sha256", slotbxml_sha256
, true);
275 movie_rtc_second
= DEFAULT_RTC_SECOND
;
276 movie_rtc_subsecond
= DEFAULT_RTC_SUBSECOND
;
277 read_numeric_file(r
, "starttime.second", movie_rtc_second
, true);
278 read_numeric_file(r
, "starttime.subsecond", movie_rtc_subsecond
, true);
279 rtc_second
= movie_rtc_second
;
280 rtc_subsecond
= movie_rtc_subsecond
;
281 if(r
.has_member("savestate")) {
283 movie_state
= read_raw_file(r
, "moviestate");
284 if(r
.has_member("hostmemory"))
285 host_memory
= read_raw_file(r
, "hostmemory");
286 savestate
= read_raw_file(r
, "savestate");
288 if(name
.length() >= 5 && name
.substr(0, 5) == "sram.")
289 sram
[name
.substr(5)] = read_raw_file(r
, name
);
290 screenshot
= read_raw_file(r
, "screenshot");
291 //If these can't be read, just use some (wrong) values.
292 read_numeric_file(r
, "savetime.second", rtc_second
, true);
293 read_numeric_file(r
, "savetime.subsecond", rtc_subsecond
, true);
295 if(rtc_subsecond
< 0 || movie_rtc_subsecond
< 0)
296 throw std::runtime_error("Invalid RTC subsecond value");
297 std::string name
= r
.find_first();
299 if(name
.length() >= 10 && name
.substr(0, 10) == "moviesram.")
300 movie_sram
[name
.substr(10)] = read_raw_file(r
, name
);
301 read_authors_file(r
, authors
);
302 read_input(r
, input
, port1
, port2
, 0);
305 void moviefile::save(const std::string
& movie
, unsigned compression
) throw(std::bad_alloc
, std::runtime_error
)
307 zip_writer
w(movie
, compression
);
308 write_linefile(w
, "gametype", gtype::tostring(gametype
));
309 if(port1
!= PT_GAMEPAD
)
310 write_linefile(w
, "port1", port_types
[port1
].name
);
312 write_linefile(w
, "port2", port_types
[port2
].name
);
313 write_linefile(w
, "gamename", gamename
, true);
314 write_linefile(w
, "systemid", "lsnes-rr1");
315 write_linefile(w
, "controlsversion", "0");
316 coreversion
= bsnes_core_version
;
317 write_linefile(w
, "coreversion", coreversion
);
318 write_linefile(w
, "projectid", projectid
);
320 write_linefile(w
, "rom.sha256", rom_sha256
, true);
321 write_linefile(w
, "romxml.sha256", romxml_sha256
, true);
322 write_linefile(w
, "slota.sha256", slota_sha256
, true);
323 write_linefile(w
, "slotaxml.sha256", slotaxml_sha256
, true);
324 write_linefile(w
, "slotb.sha256", slotb_sha256
, true);
325 write_linefile(w
, "slotbxml.sha256", slotbxml_sha256
, true);
326 for(auto i
: movie_sram
)
327 write_raw_file(w
, "moviesram." + i
.first
, i
.second
);
328 write_numeric_file(w
, "starttime.second", movie_rtc_second
);
329 write_numeric_file(w
, "starttime.subsecond", movie_rtc_subsecond
);
331 write_raw_file(w
, "moviestate", movie_state
);
332 write_raw_file(w
, "hostmemory", host_memory
);
333 write_raw_file(w
, "savestate", savestate
);
334 write_raw_file(w
, "screenshot", screenshot
);
336 write_raw_file(w
, "sram." + i
.first
, i
.second
);
337 write_numeric_file(w
, "savetime.second", rtc_second
);
338 write_numeric_file(w
, "savetime.subsecond", rtc_subsecond
);
340 write_authors_file(w
, authors
);
341 write_input(w
, input
, port1
, port2
);
346 uint64_t moviefile::get_frame_count() throw()
349 for(size_t i
= 0; i
< input
.size(); i
++) {
350 if(input
[i
](CONTROL_FRAME_SYNC
))
358 const int BLOCK_SECONDS
= 0;
359 const int BLOCK_FRAMES
= 1;
360 const int STEP_W
= 2;
361 const int STEP_N
= 3;
363 uint64_t magic
[2][4] = {
364 {178683, 10738636, 16639264, 596096},
365 {6448, 322445, 19997208, 266440}
369 uint64_t moviefile::get_movie_length(uint64_t framebias
) throw()
371 uint64_t frames
= get_frame_count();
372 if(frames
> framebias
)
376 uint64_t* _magic
= magic
[(gametype
== GT_SNES_PAL
|| gametype
== GT_SGB_PAL
) ? 1 : 0];
377 uint64_t t
= _magic
[BLOCK_SECONDS
] * 1000000000ULL * (frames
/ _magic
[BLOCK_FRAMES
]);
378 frames
%= _magic
[BLOCK_FRAMES
];
379 t
+= frames
* _magic
[STEP_W
] + (frames
* _magic
[STEP_N
] / _magic
[BLOCK_FRAMES
]);
383 gametype_t
gametype_compose(rom_type type
, rom_region region
)
387 return (region
== REGION_PAL
) ? GT_SNES_PAL
: GT_SNES_NTSC
;
390 case ROMTYPE_BSXSLOTTED
:
391 return GT_BSX_SLOTTED
;
392 case ROMTYPE_SUFAMITURBO
:
393 return GT_SUFAMITURBO
;
395 return (region
== REGION_PAL
) ? GT_SGB_PAL
: GT_SGB_NTSC
;
401 rom_region
gametype_region(gametype_t type
)
412 rom_type
gametype_romtype(gametype_t type
)
421 return ROMTYPE_BSXSLOTTED
;
423 return ROMTYPE_SUFAMITURBO
;