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 void strip_CR(std::string
& x
) throw(std::bad_alloc
)
15 if(x
.length() > 0 && x
[x
.length() - 1] == '\r') {
17 x
= x
.substr(0, x
.length() - 1);
23 void read_linefile(zip_reader
& r
, const std::string
& member
, std::string
& out
, bool conditional
= false)
24 throw(std::bad_alloc
, std::runtime_error
)
26 if(conditional
&& !r
.has_member(member
))
28 std::istream
& m
= r
[member
];
39 void write_linefile(zip_writer
& w
, const std::string
& member
, const std::string
& value
, bool conditional
= false)
40 throw(std::bad_alloc
, std::runtime_error
)
42 if(conditional
&& value
== "")
44 std::ostream
& m
= w
.create_file(member
);
46 m
<< value
<< std::endl
;
54 void write_raw_file(zip_writer
& w
, const std::string
& member
, std::vector
<char>& content
) throw(std::bad_alloc
,
57 std::ostream
& m
= w
.create_file(member
);
59 m
.write(&content
[0], content
.size());
61 throw std::runtime_error("Can't write ZIP file member");
69 std::vector
<char> read_raw_file(zip_reader
& r
, const std::string
& member
) throw(std::bad_alloc
, std::runtime_error
)
71 std::vector
<char> out
;
72 std::istream
& m
= r
[member
];
74 boost::iostreams::back_insert_device
<std::vector
<char>> rd(out
);
75 boost::iostreams::copy(m
, rd
);
84 void read_authors_file(zip_reader
& r
, std::vector
<std::pair
<std::string
, std::string
>>& authors
) throw(std::bad_alloc
,
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
, const std::string
& projectid
) throw(std::bad_alloc
, std::runtime_error
)
104 std::istream
& m
= r
["rrdata"];
107 rrdata::read_base(projectid
);
108 count
= rrdata::read(m
);
114 std::ostringstream x
;
119 void write_rrdata(zip_writer
& w
) throw(std::bad_alloc
, std::runtime_error
)
121 std::ostream
& m
= w
.create_file("rrdata");
124 count
= rrdata::write(m
);
126 throw std::runtime_error("Can't write ZIP file member");
132 std::ostream
& m2
= w
.create_file("rerecords");
134 m2
<< count
<< std::endl
;
136 throw std::runtime_error("Can't write ZIP file member");
144 void write_authors_file(zip_writer
& w
, std::vector
<std::pair
<std::string
, std::string
>>& authors
)
145 throw(std::bad_alloc
, std::runtime_error
)
147 std::ostream
& m
= w
.create_file("authors");
149 for(auto i
= authors
.begin(); i
!= authors
.end(); ++i
)
151 m
<< i
->first
<< std::endl
;
153 m
<< i
->first
<< "|" << i
->second
<< std::endl
;
155 throw std::runtime_error("Can't write ZIP file member");
163 void write_input(zip_writer
& w
, std::vector
<controls_t
>& input
, porttype_t port1
, porttype_t port2
)
164 throw(std::bad_alloc
, std::runtime_error
)
166 std::vector
<cencode::fn_t
> encoders
;
167 encoders
.push_back(port_types
[port1
].encoder
);
168 encoders
.push_back(port_types
[port2
].encoder
);
169 std::ostream
& m
= w
.create_file("input");
171 for(auto i
= input
.begin(); i
!= input
.end(); ++i
)
172 m
<< i
->tostring(encoders
) << std::endl
;
174 throw std::runtime_error("Can't write ZIP file member");
182 void read_input(zip_reader
& r
, std::vector
<controls_t
>& input
, porttype_t port1
, porttype_t port2
, unsigned version
)
183 throw(std::bad_alloc
, std::runtime_error
)
185 std::vector
<cdecode::fn_t
> decoders
;
186 decoders
.push_back(port_types
[port1
].decoder
);
187 decoders
.push_back(port_types
[port2
].decoder
);
188 std::istream
& m
= r
["input"];
191 while(std::getline(m
, x
)) {
194 input
.push_back(controls_t(x
, decoders
, version
));
205 porttype_t
parse_controller_type(const std::string
& type
, bool port
) throw(std::bad_alloc
, std::runtime_error
)
207 porttype_t port1
= PT_INVALID
;
208 for(unsigned i
= 0; i
<= PT_LAST_CTYPE
; i
++)
209 if(type
== port_types
[i
].name
&& (port
|| port_types
[i
].valid_port1
))
210 port1
= static_cast<porttype_t
>(i
);
211 if(port1
== PT_INVALID
)
212 throw std::runtime_error(std::string("Illegal port") + (port
? "2" : "1") + " device '" + type
+ "'");
217 moviefile::moviefile() throw(std::bad_alloc
)
219 gametype
= GT_INVALID
;
222 coreversion
= bsnes_core_version
;
223 projectid
= get_random_hexstring(40);
225 is_savestate
= false;
228 moviefile::moviefile(const std::string
& movie
) throw(std::bad_alloc
, std::runtime_error
)
230 is_savestate
= false;
233 read_linefile(r
, "systemid", tmp
);
234 if(tmp
.substr(0, 8) != "lsnes-rr")
235 throw std::runtime_error("Not lsnes movie");
236 read_linefile(r
, "controlsversion", tmp
);
238 throw std::runtime_error("Can't decode movie data");
239 read_linefile(r
, "gametype", tmp
);
241 gametype
= gtype::togametype(tmp
);
242 } catch(std::bad_alloc
& e
) {
244 } catch(std::exception
& e
) {
245 throw std::runtime_error("Illegal game type '" + tmp
+ "'");
247 tmp
= port_types
[PT_GAMEPAD
].name
;
248 read_linefile(r
, "port1", tmp
, true);
249 port1
= port_type::lookup(tmp
, false).ptype
;
250 tmp
= port_types
[PT_NONE
].name
;
251 read_linefile(r
, "port2", tmp
, true);
252 port2
= port_type::lookup(tmp
, true).ptype
;
253 read_linefile(r
, "gamename", gamename
, true);
254 read_linefile(r
, "projectid", projectid
);
255 rerecords
= read_rrdata(r
, projectid
);
256 read_linefile(r
, "coreversion", coreversion
);
257 read_linefile(r
, "rom.sha256", rom_sha256
, true);
258 read_linefile(r
, "romxml.sha256", romxml_sha256
, true);
259 read_linefile(r
, "slota.sha256", slota_sha256
, true);
260 read_linefile(r
, "slotaxml.sha256", slotaxml_sha256
, true);
261 read_linefile(r
, "slotb.sha256", slotb_sha256
, true);
262 read_linefile(r
, "slotbxml.sha256", slotbxml_sha256
, true);
263 if(r
.has_member("savestate")) {
265 movie_state
= read_raw_file(r
, "moviestate");
266 if(r
.has_member("hostmemory"))
267 host_memory
= read_raw_file(r
, "hostmemory");
268 savestate
= read_raw_file(r
, "savestate");
269 for(auto name
= r
.begin(); name
!= r
.end(); ++name
)
270 if((*name
).length() >= 5 && (*name
).substr(0, 5) == "sram.")
271 sram
[(*name
).substr(5)] = read_raw_file(r
, *name
);
272 screenshot
= read_raw_file(r
, "screenshot");
274 std::string name
= r
.find_first();
275 for(auto name
= r
.begin(); name
!= r
.end(); ++name
)
276 if((*name
).length() >= 10 && (*name
).substr(0, 10) == "moviesram.")
277 sram
[(*name
).substr(10)] = read_raw_file(r
, *name
);
278 read_authors_file(r
, authors
);
279 read_input(r
, input
, port1
, port2
, 0);
282 void moviefile::save(const std::string
& movie
, unsigned compression
) throw(std::bad_alloc
, std::runtime_error
)
284 zip_writer
w(movie
, compression
);
285 write_linefile(w
, "gametype", gtype::tostring(gametype
));
286 if(port1
!= PT_GAMEPAD
)
287 write_linefile(w
, "port1", port_types
[port1
].name
);
289 write_linefile(w
, "port2", port_types
[port2
].name
);
290 write_linefile(w
, "gamename", gamename
, true);
291 write_linefile(w
, "systemid", "lsnes-rr1");
292 write_linefile(w
, "controlsversion", "0");
293 coreversion
= bsnes_core_version
;
294 write_linefile(w
, "coreversion", coreversion
);
295 write_linefile(w
, "projectid", projectid
);
297 write_linefile(w
, "rom.sha256", rom_sha256
, true);
298 write_linefile(w
, "romxml.sha256", romxml_sha256
, true);
299 write_linefile(w
, "slota.sha256", slota_sha256
, true);
300 write_linefile(w
, "slotaxml.sha256", slotaxml_sha256
, true);
301 write_linefile(w
, "slotb.sha256", slotb_sha256
, true);
302 write_linefile(w
, "slotbxml.sha256", slotbxml_sha256
, true);
303 for(auto i
= movie_sram
.begin(); i
!= movie_sram
.end(); ++i
)
304 write_raw_file(w
, "moviesram." + i
->first
, i
->second
);
306 write_raw_file(w
, "moviestate", movie_state
);
307 write_raw_file(w
, "hostmemory", host_memory
);
308 write_raw_file(w
, "savestate", savestate
);
309 write_raw_file(w
, "screenshot", screenshot
);
310 for(auto i
= sram
.begin(); i
!= sram
.end(); ++i
)
311 write_raw_file(w
, "sram." + i
->first
, i
->second
);
313 write_authors_file(w
, authors
);
314 write_input(w
, input
, port1
, port2
);
319 uint64_t moviefile::get_frame_count() throw()
322 for(size_t i
= 0; i
< input
.size(); i
++) {
323 if(input
[i
](CONTROL_FRAME_SYNC
))
331 const int BLOCK_SECONDS
= 0;
332 const int BLOCK_FRAMES
= 1;
333 const int STEP_W
= 2;
334 const int STEP_N
= 3;
336 uint64_t magic
[2][4] = {
337 {178683, 10738636, 16639264, 596096},
338 {6448, 322445, 19997208, 266440}
342 uint64_t moviefile::get_movie_length() throw()
344 uint64_t frames
= get_frame_count();
345 uint64_t* _magic
= magic
[(gametype
== GT_SNES_PAL
|| gametype
== GT_SGB_PAL
) ? 1 : 0];
346 uint64_t t
= _magic
[BLOCK_SECONDS
] * 1000000000ULL * (frames
/ _magic
[BLOCK_FRAMES
]);
347 frames
%= _magic
[BLOCK_FRAMES
];
348 t
+= frames
* _magic
[STEP_W
] + (frames
* _magic
[STEP_N
] / _magic
[BLOCK_FRAMES
]);
352 gametype_t
gametype_compose(rom_type type
, rom_region region
)
356 return (region
== REGION_PAL
) ? GT_SNES_PAL
: GT_SNES_NTSC
;
359 case ROMTYPE_BSXSLOTTED
:
360 return GT_BSX_SLOTTED
;
361 case ROMTYPE_SUFAMITURBO
:
362 return GT_SUFAMITURBO
;
364 return (region
== REGION_PAL
) ? GT_SGB_PAL
: GT_SGB_NTSC
;
370 rom_region
gametype_region(gametype_t type
)
381 rom_type
gametype_romtype(gametype_t type
)
390 return ROMTYPE_BSXSLOTTED
;
392 return ROMTYPE_SUFAMITURBO
;