2 #include <snes/snes.hpp>
3 #include <ui-libsnes/libsnes.hpp>
4 #include "moviefile.hpp"
9 #include <boost/iostreams/copy.hpp>
10 #include <boost/iostreams/device/back_inserter.hpp>
12 void strip_CR(std::string
& x
) throw(std::bad_alloc
)
14 if(x
.length() > 0 && x
[x
.length() - 1] == '\r') {
16 x
= x
.substr(0, x
.length() - 1);
22 void read_linefile(zip_reader
& r
, const std::string
& member
, std::string
& out
, bool conditional
= false)
23 throw(std::bad_alloc
, std::runtime_error
)
25 if(conditional
&& !r
.has_member(member
))
27 std::istream
& m
= r
[member
];
38 void write_linefile(zip_writer
& w
, const std::string
& member
, const std::string
& value
, bool conditional
= false)
39 throw(std::bad_alloc
, std::runtime_error
)
41 if(conditional
&& value
== "")
43 std::ostream
& m
= w
.create_file(member
);
45 m
<< value
<< std::endl
;
53 void write_raw_file(zip_writer
& w
, const std::string
& member
, std::vector
<char>& content
) throw(std::bad_alloc
,
56 std::ostream
& m
= w
.create_file(member
);
58 m
.write(&content
[0], content
.size());
60 throw std::runtime_error("Can't write ZIP file member");
68 std::vector
<char> read_raw_file(zip_reader
& r
, const std::string
& member
) throw(std::bad_alloc
, std::runtime_error
)
70 std::vector
<char> out
;
71 std::istream
& m
= r
[member
];
73 boost::iostreams::back_insert_device
<std::vector
<char>> rd(out
);
74 boost::iostreams::copy(m
, rd
);
83 void read_authors_file(zip_reader
& r
, std::vector
<std::pair
<std::string
, std::string
>>& authors
) throw(std::bad_alloc
,
86 std::istream
& m
= r
["authors"];
89 while(std::getline(m
, x
)) {
91 fieldsplitter
fields(x
);
92 std::string y
= static_cast<std::string
>(fields
);
93 std::string z
= static_cast<std::string
>(fields
);
94 authors
.push_back(std::make_pair(y
, z
));
103 std::string
read_rrdata(zip_reader
& r
, const std::string
& projectid
) throw(std::bad_alloc
, std::runtime_error
)
105 std::istream
& m
= r
["rrdata"];
108 rrdata::read_base(projectid
);
109 count
= rrdata::read(m
);
115 std::ostringstream x
;
120 void write_rrdata(zip_writer
& w
) throw(std::bad_alloc
, std::runtime_error
)
122 std::ostream
& m
= w
.create_file("rrdata");
125 count
= rrdata::write(m
);
127 throw std::runtime_error("Can't write ZIP file member");
133 std::ostream
& m2
= w
.create_file("rerecords");
135 m2
<< count
<< std::endl
;
137 throw std::runtime_error("Can't write ZIP file member");
145 void write_authors_file(zip_writer
& w
, std::vector
<std::pair
<std::string
, std::string
>>& authors
)
146 throw(std::bad_alloc
, std::runtime_error
)
148 std::ostream
& m
= w
.create_file("authors");
150 for(auto i
= authors
.begin(); i
!= authors
.end(); ++i
)
152 m
<< i
->first
<< std::endl
;
154 m
<< i
->first
<< "|" << i
->second
<< std::endl
;
156 throw std::runtime_error("Can't write ZIP file member");
164 void write_input(zip_writer
& w
, std::vector
<controls_t
>& input
, porttype_t port1
, porttype_t port2
)
165 throw(std::bad_alloc
, std::runtime_error
)
167 std::vector
<cencode::fn_t
> encoders
;
168 encoders
.push_back(port_types
[port1
].encoder
);
169 encoders
.push_back(port_types
[port2
].encoder
);
170 std::ostream
& m
= w
.create_file("input");
172 for(auto i
= input
.begin(); i
!= input
.end(); ++i
)
173 m
<< i
->tostring(encoders
) << std::endl
;
175 throw std::runtime_error("Can't write ZIP file member");
183 void read_input(zip_reader
& r
, std::vector
<controls_t
>& input
, porttype_t port1
, porttype_t port2
, unsigned version
)
184 throw(std::bad_alloc
, std::runtime_error
)
186 std::vector
<cdecode::fn_t
> decoders
;
187 decoders
.push_back(port_types
[port1
].decoder
);
188 decoders
.push_back(port_types
[port2
].decoder
);
189 std::istream
& m
= r
["input"];
192 while(std::getline(m
, x
)) {
195 input
.push_back(controls_t(x
, decoders
, version
));
206 porttype_t
parse_controller_type(const std::string
& type
, bool port
) throw(std::bad_alloc
, std::runtime_error
)
208 porttype_t port1
= PT_INVALID
;
209 for(unsigned i
= 0; i
<= PT_LAST_CTYPE
; i
++)
210 if(type
== port_types
[i
].name
&& (port
|| port_types
[i
].valid_port1
))
211 port1
= static_cast<porttype_t
>(i
);
212 if(port1
== PT_INVALID
)
213 throw std::runtime_error(std::string("Illegal port") + (port
? "2" : "1") + " device '" + type
+ "'");
218 moviefile::moviefile() throw(std::bad_alloc
)
220 gametype
= GT_INVALID
;
223 coreversion
= bsnes_core_version
;
224 projectid
= get_random_hexstring(40);
226 is_savestate
= false;
229 moviefile::moviefile(const std::string
& movie
) throw(std::bad_alloc
, std::runtime_error
)
231 is_savestate
= false;
234 read_linefile(r
, "systemid", tmp
);
235 if(tmp
.substr(0, 8) != "lsnes-rr")
236 throw std::runtime_error("Not lsnes movie");
237 read_linefile(r
, "controlsversion", tmp
);
239 throw std::runtime_error("Can't decode movie data");
240 read_linefile(r
, "gametype", tmp
);
242 gametype
= gtype::togametype(tmp
);
243 } catch(std::bad_alloc
& e
) {
245 } catch(std::exception
& e
) {
246 throw std::runtime_error("Illegal game type '" + tmp
+ "'");
248 tmp
= port_types
[PT_GAMEPAD
].name
;
249 read_linefile(r
, "port1", tmp
, true);
250 port1
= port_type::lookup(tmp
, false).ptype
;
251 tmp
= port_types
[PT_NONE
].name
;
252 read_linefile(r
, "port2", tmp
, true);
253 port2
= port_type::lookup(tmp
, true).ptype
;
254 read_linefile(r
, "gamename", gamename
, true);
255 read_linefile(r
, "projectid", projectid
);
256 rerecords
= read_rrdata(r
, projectid
);
257 read_linefile(r
, "coreversion", coreversion
);
258 read_linefile(r
, "rom.sha256", rom_sha256
, true);
259 read_linefile(r
, "romxml.sha256", romxml_sha256
, true);
260 read_linefile(r
, "slota.sha256", slota_sha256
, true);
261 read_linefile(r
, "slotaxml.sha256", slotaxml_sha256
, true);
262 read_linefile(r
, "slotb.sha256", slotb_sha256
, true);
263 read_linefile(r
, "slotbxml.sha256", slotbxml_sha256
, true);
264 if(r
.has_member("savestate")) {
266 movie_state
= read_raw_file(r
, "moviestate");
267 if(r
.has_member("hostmemory"))
268 host_memory
= read_raw_file(r
, "hostmemory");
269 savestate
= read_raw_file(r
, "savestate");
270 for(auto name
= r
.begin(); name
!= r
.end(); ++name
)
271 if((*name
).length() >= 5 && (*name
).substr(0, 5) == "sram.")
272 sram
[(*name
).substr(5)] = read_raw_file(r
, *name
);
273 screenshot
= read_raw_file(r
, "screenshot");
275 std::string name
= r
.find_first();
276 for(auto name
= r
.begin(); name
!= r
.end(); ++name
)
277 if((*name
).length() >= 10 && (*name
).substr(0, 10) == "moviesram.")
278 sram
[(*name
).substr(10)] = read_raw_file(r
, *name
);
279 read_authors_file(r
, authors
);
280 read_input(r
, input
, port1
, port2
, 0);
283 void moviefile::save(const std::string
& movie
, unsigned compression
) throw(std::bad_alloc
, std::runtime_error
)
285 zip_writer
w(movie
, compression
);
286 write_linefile(w
, "gametype", gtype::tostring(gametype
));
287 if(port1
!= PT_GAMEPAD
)
288 write_linefile(w
, "port1", port_types
[port1
].name
);
290 write_linefile(w
, "port2", port_types
[port2
].name
);
291 write_linefile(w
, "gamename", gamename
, true);
292 write_linefile(w
, "systemid", "lsnes-rr1");
293 write_linefile(w
, "controlsversion", "0");
294 coreversion
= bsnes_core_version
;
295 write_linefile(w
, "coreversion", coreversion
);
296 write_linefile(w
, "projectid", projectid
);
298 write_linefile(w
, "rom.sha256", rom_sha256
, true);
299 write_linefile(w
, "romxml.sha256", romxml_sha256
, true);
300 write_linefile(w
, "slota.sha256", slota_sha256
, true);
301 write_linefile(w
, "slotaxml.sha256", slotaxml_sha256
, true);
302 write_linefile(w
, "slotb.sha256", slotb_sha256
, true);
303 write_linefile(w
, "slotbxml.sha256", slotbxml_sha256
, true);
304 for(auto i
= movie_sram
.begin(); i
!= movie_sram
.end(); ++i
)
305 write_raw_file(w
, "moviesram." + i
->first
, i
->second
);
307 write_raw_file(w
, "moviestate", movie_state
);
308 write_raw_file(w
, "hostmemory", host_memory
);
309 write_raw_file(w
, "savestate", savestate
);
310 write_raw_file(w
, "screenshot", screenshot
);
311 for(auto i
= sram
.begin(); i
!= sram
.end(); ++i
)
312 write_raw_file(w
, "sram." + i
->first
, i
->second
);
314 write_authors_file(w
, authors
);
315 write_input(w
, input
, port1
, port2
);
320 uint64_t moviefile::get_frame_count() throw()
323 for(size_t i
= 0; i
< input
.size(); i
++) {
324 if(input
[i
](CONTROL_FRAME_SYNC
))
332 const int BLOCK_SECONDS
= 0;
333 const int BLOCK_FRAMES
= 1;
334 const int STEP_W
= 2;
335 const int STEP_N
= 3;
337 uint64_t magic
[2][4] = {
338 {178683, 10738636, 16639264, 596096},
339 {6448, 322445, 19997208, 266440}
343 uint64_t moviefile::get_movie_length() throw()
345 uint64_t frames
= get_frame_count();
346 uint64_t* _magic
= magic
[(gametype
== GT_SNES_PAL
|| gametype
== GT_SGB_PAL
) ? 1 : 0];
347 uint64_t t
= _magic
[BLOCK_SECONDS
] * 1000000000ULL * (frames
/ _magic
[BLOCK_FRAMES
]);
348 frames
%= _magic
[BLOCK_FRAMES
];
349 t
+= frames
* _magic
[STEP_W
] + (frames
* _magic
[STEP_N
] / _magic
[BLOCK_FRAMES
]);
353 gametype_t
gametype_compose(rom_type type
, rom_region region
)
357 return (region
== REGION_PAL
) ? GT_SNES_PAL
: GT_SNES_NTSC
;
360 case ROMTYPE_BSXSLOTTED
:
361 return GT_BSX_SLOTTED
;
362 case ROMTYPE_SUFAMITURBO
:
363 return GT_SUFAMITURBO
;
365 return (region
== REGION_PAL
) ? GT_SGB_PAL
: GT_SGB_NTSC
;
371 rom_region
gametype_region(gametype_t type
)
382 rom_type
gametype_romtype(gametype_t type
)
391 return ROMTYPE_BSXSLOTTED
;
393 return ROMTYPE_SUFAMITURBO
;