If initsram/initstate points to LSS file, pull the matching member
[lsnes.git] / src / core / moviefile-binary.cpp
blobb115dfa52f801d272107e4e4d4b7b7e552e59908
1 #include "core/moviefile-binary.hpp"
2 #include "core/moviefile-common.hpp"
3 #include "core/moviefile.hpp"
4 #include "library/binarystream.hpp"
5 #include "library/minmax.hpp"
6 #include "library/serialization.hpp"
7 #include "library/string.hpp"
8 #include "library/zip.hpp"
10 #include <cstring>
11 #include <cerrno>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
15 //FUCK YOU. SERIOUSLY.
16 #define EXTRA_OPENFLAGS O_BINARY
17 #else
18 #define EXTRA_OPENFLAGS 0
19 #endif
21 void moviefile::brief_info::binary_io(int _stream)
23 binarystream::input in(_stream);
24 sysregion = in.string();
25 //Discard the settings.
26 while(in.byte()) {
27 in.string();
28 in.string();
30 in.extension({
31 {TAG_CORE_VERSION, [this](binarystream::input& s) {
32 this->corename = s.string_implicit();
33 }},{TAG_PROJECT_ID, [this](binarystream::input& s) {
34 this->projectid = s.string_implicit();
35 }},{TAG_SAVESTATE, [this](binarystream::input& s) {
36 this->current_frame = s.number();
37 }},{TAG_RRDATA, [this](binarystream::input& s) {
38 std::vector<char> c_rrdata;
39 s.blob_implicit(c_rrdata);
40 this->rerecords = rrdata_set::count(c_rrdata);
41 }},{TAG_ROMHASH, [this](binarystream::input& s) {
42 uint8_t n = s.byte();
43 std::string h = s.string_implicit();
44 if(n > 2 * ROM_SLOT_COUNT)
45 return;
46 if(n & 1)
47 this->hashxml[n >> 1] = h;
48 else
49 this->hash[n >> 1] = h;
50 }},{TAG_ROMHINT, [this](binarystream::input& s) {
51 uint8_t n = s.byte();
52 std::string h = s.string_implicit();
53 if(n > ROM_SLOT_COUNT)
54 return;
55 this->hint[n] = h;
57 }, binarystream::null_default);
60 void moviefile::binary_io(int _stream, rrdata_set& rrd) throw(std::bad_alloc, std::runtime_error)
62 binarystream::output out(_stream);
63 out.string(gametype->get_name());
64 moviefile_write_settings<binarystream::output>(out, settings, gametype->get_type().get_settings(),
65 [](binarystream::output& s, const std::string& name, const std::string& value) -> void {
66 s.byte(0x01);
67 s.string(name);
68 s.string(value);
69 });
70 out.byte(0x00);
72 out.extension(TAG_MOVIE_TIME, [this](binarystream::output& s) {
73 s.number(this->movie_rtc_second);
74 s.number(this->movie_rtc_subsecond);
75 });
77 out.extension(TAG_PROJECT_ID, [this](binarystream::output& s) {
78 s.string_implicit(this->projectid);
79 });
81 out.extension(TAG_CORE_VERSION, [this](binarystream::output& s) {
82 this->coreversion = this->gametype->get_type().get_core_identifier();
83 s.string_implicit(this->coreversion);
84 });
86 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
87 out.extension(TAG_ROMHASH, [this, i](binarystream::output& s) {
88 if(!this->romimg_sha256[i].length()) return;
89 s.byte(2 * i);
90 s.string_implicit(this->romimg_sha256[i]);
91 });
92 out.extension(TAG_ROMHASH, [this, i](binarystream::output& s) {
93 if(!this->romxml_sha256[i].length()) return;
94 s.byte(2 * i + 1);
95 s.string_implicit(this->romxml_sha256[i]);
96 });
97 out.extension(TAG_ROMHINT, [this, i](binarystream::output& s) {
98 if(!this->namehint[i].length()) return;
99 s.byte(i);
100 s.string_implicit(this->namehint[i]);
104 out.extension(TAG_RRDATA, [this, &rrd](binarystream::output& s) {
105 std::vector<char> _rrd;
106 rrd.write(_rrd);
107 s.blob_implicit(_rrd);
110 for(auto i : movie_sram)
111 out.extension(TAG_MOVIE_SRAM, [&i](binarystream::output& s) {
112 s.string(i.first);
113 s.blob_implicit(i.second);
116 out.extension(TAG_ANCHOR_SAVE, [this](binarystream::output& s) {
117 s.blob_implicit(this->anchor_savestate);
119 if(is_savestate) {
120 out.extension(TAG_SAVESTATE, [this](binarystream::output& s) {
121 s.number(this->save_frame);
122 s.number(this->lagged_frames);
123 s.number(this->rtc_second);
124 s.number(this->rtc_subsecond);
125 s.number(this->pollcounters.size());
126 for(auto i : this->pollcounters)
127 s.number32(i);
128 s.byte(this->poll_flag ? 0x01 : 0x00);
129 s.blob_implicit(this->savestate);
130 }, true, out.numberbytes(save_frame) + out.numberbytes(lagged_frames) + out.numberbytes(rtc_second) +
131 out.numberbytes(rtc_subsecond) + out.numberbytes(pollcounters.size()) +
132 4 * pollcounters.size() + 1 + savestate.size());
134 out.extension(TAG_HOSTMEMORY, [this](binarystream::output& s) {
135 s.blob_implicit(this->host_memory);
138 out.extension(TAG_SCREENSHOT, [this](binarystream::output& s) {
139 s.blob_implicit(this->screenshot);
140 }, true, screenshot.size());
142 for(auto i : sram) {
143 out.extension(TAG_SAVE_SRAM, [&i](binarystream::output& s) {
144 s.string(i.first);
145 s.blob_implicit(i.second);
150 out.extension(TAG_GAMENAME, [this](binarystream::output& s) {
151 s.string_implicit(this->gamename);
154 for(auto i : subtitles)
155 out.extension(TAG_SUBTITLE, [&i](binarystream::output& s) {
156 s.number(i.first.get_frame());
157 s.number(i.first.get_length());
158 s.string_implicit(i.second);
161 for(auto i : authors)
162 out.extension(TAG_AUTHOR, [&i](binarystream::output& s) {
163 s.string(i.first);
164 s.string_implicit(i.second);
167 for(auto i : active_macros)
168 out.extension(TAG_MACRO, [&i](binarystream::output& s) {
169 s.number(i.second);
170 s.string_implicit(i.first);
173 for(auto i : ramcontent) {
174 out.extension(TAG_RAMCONTENT, [&i](binarystream::output& s) {
175 s.string(i.first);
176 s.blob_implicit(i.second);
180 int64_t next_bnum = 0;
181 std::map<std::string, uint64_t> branch_table;
182 for(auto& i : branches) {
183 branch_table[i.first] = next_bnum++;
184 out.extension(TAG_BRANCH_NAME, [&i](binarystream::output& s) {
185 s.string_implicit(i.first);
186 }, false, i.first.length());
187 uint32_t tag = (&i.second == input) ? TAG_MOVIE : TAG_BRANCH;
188 out.extension(tag, [&i](binarystream::output& s) {
189 i.second.save_binary(s);
190 }, true, i.second.binary_size());
194 void moviefile::binary_io(int _stream, core_type& romtype) throw(std::bad_alloc, std::runtime_error)
196 binarystream::input in(_stream);
197 std::string tmp = in.string();
198 std::string next_branch;
199 std::map<uint64_t, std::string> branch_table;
200 uint64_t next_bnum = 0;
201 try {
202 gametype = &romtype.lookup_sysregion(tmp);
203 } catch(std::bad_alloc& e) {
204 throw;
205 } catch(std::exception& e) {
206 throw std::runtime_error("Illegal game type '" + tmp + "'");
208 while(in.byte()) {
209 std::string name = in.string();
210 settings[name] = in.string();
212 auto ctrldata = gametype->get_type().controllerconfig(settings);
213 portctrl::type_set& ports = portctrl::type_set::make(ctrldata.ports, ctrldata.portindex());
214 input = NULL;
216 in.extension({
217 {TAG_ANCHOR_SAVE, [this](binarystream::input& s) {
218 s.blob_implicit(this->anchor_savestate);
219 }},{TAG_AUTHOR, [this](binarystream::input& s) {
220 std::string a = s.string();
221 std::string b = s.string_implicit();
222 this->authors.push_back(std::make_pair(a, b));
223 }},{TAG_CORE_VERSION, [this](binarystream::input& s) {
224 this->coreversion = s.string_implicit();
225 }},{TAG_GAMENAME, [this](binarystream::input& s) {
226 this->gamename = s.string_implicit();
227 }},{TAG_HOSTMEMORY, [this](binarystream::input& s) {
228 s.blob_implicit(this->host_memory);
229 }},{TAG_MACRO, [this](binarystream::input& s) {
230 uint64_t n = s.number();
231 this->active_macros[s.string_implicit()] = n;
232 }},{TAG_BRANCH_NAME, [this, &branch_table, &next_bnum, &next_branch](binarystream::input& s) {
233 branch_table[next_bnum++] = next_branch = s.string_implicit();
234 }},{TAG_MOVIE, [this, &ports, &next_branch](binarystream::input& s) {
235 branches[next_branch].clear(ports);
236 branches[next_branch].load_binary(s);
237 input = &branches[next_branch];
238 }},{TAG_BRANCH, [this, &ports, &next_branch](binarystream::input& s) {
239 branches[next_branch].clear(ports);
240 branches[next_branch].load_binary(s);
241 }},{TAG_MOVIE_SRAM, [this](binarystream::input& s) {
242 std::string a = s.string();
243 s.blob_implicit(this->movie_sram[a]);
244 }},{TAG_RAMCONTENT, [this](binarystream::input& s) {
245 std::string a = s.string();
246 s.blob_implicit(this->ramcontent[a]);
247 }},{TAG_MOVIE_TIME, [this](binarystream::input& s) {
248 this->movie_rtc_second = s.number();
249 this->movie_rtc_subsecond = s.number();
250 }},{TAG_PROJECT_ID, [this](binarystream::input& s) {
251 this->projectid = s.string_implicit();
252 }},{TAG_ROMHASH, [this](binarystream::input& s) {
253 uint8_t n = s.byte();
254 std::string h = s.string_implicit();
255 if(n > 2 * ROM_SLOT_COUNT)
256 return;
257 if(n & 1)
258 romxml_sha256[n >> 1] = h;
259 else
260 romimg_sha256[n >> 1] = h;
261 }},{TAG_ROMHINT, [this](binarystream::input& s) {
262 uint8_t n = s.byte();
263 std::string h = s.string_implicit();
264 if(n > ROM_SLOT_COUNT)
265 return;
266 namehint[n] = h;
267 }},{TAG_RRDATA, [this](binarystream::input& s) {
268 s.blob_implicit(this->c_rrdata);
269 this->rerecords = (stringfmt() << rrdata_set::count(c_rrdata)).str();
270 }},{TAG_SAVE_SRAM, [this](binarystream::input& s) {
271 std::string a = s.string();
272 s.blob_implicit(this->sram[a]);
273 }},{TAG_SAVESTATE, [this](binarystream::input& s) {
274 this->is_savestate = true;
275 this->save_frame = s.number();
276 this->lagged_frames = s.number();
277 this->rtc_second = s.number();
278 this->rtc_subsecond = s.number();
279 this->pollcounters.resize(s.number());
280 for(auto& i : this->pollcounters)
281 i = s.number32();
282 this->poll_flag = (s.byte() != 0);
283 s.blob_implicit(this->savestate);
284 }},{TAG_SCREENSHOT, [this](binarystream::input& s) {
285 s.blob_implicit(this->screenshot);
286 }},{TAG_SUBTITLE, [this](binarystream::input& s) {
287 uint64_t f = s.number();
288 uint64_t l = s.number();
289 std::string x = s.string_implicit();
290 this->subtitles[moviefile_subtiming(f, l)] = x;
292 }, binarystream::null_default);
294 create_default_branch(ports);
297 moviefile_branch_extractor_binary::moviefile_branch_extractor_binary(const std::string& filename)
299 s = open(filename.c_str(), O_RDONLY | EXTRA_OPENFLAGS);
300 if(s < 0) {
301 int err = errno;
302 (stringfmt() << "Can't open file '" << filename << "' for reading: " << strerror(err)).throwex();
306 moviefile_branch_extractor_binary::~moviefile_branch_extractor_binary()
310 std::set<std::string> moviefile_branch_extractor_binary::enumerate()
312 std::set<std::string> r;
313 std::string name;
314 if(lseek(s, 5, SEEK_SET) < 0) {
315 int err = errno;
316 (stringfmt() << "Can't read the file: " << strerror(err)).throwex();
318 binarystream::input b(s);
319 //Skip the headers.
320 b.string();
321 while(b.byte()) {
322 b.string();
323 b.string();
325 //Okay, read the extension packets.
326 b.extension({
327 {TAG_BRANCH_NAME, [this, &name](binarystream::input& s) {
328 name = s.string_implicit();
329 }},{TAG_MOVIE, [this, &r, &name](binarystream::input& s) {
330 r.insert(name);
331 }},{TAG_BRANCH, [this, &r, &name](binarystream::input& s) {
332 r.insert(name);
334 }, binarystream::null_default);
336 return r;
339 void moviefile_branch_extractor_binary::read(const std::string& name, portctrl::frame_vector& v)
341 std::string mname;
342 if(lseek(s, 5, SEEK_SET) < 0) {
343 int err = errno;
344 (stringfmt() << "Can't read the file: " << strerror(err)).throwex();
346 binarystream::input b(s);
347 bool done = false;
348 //Skip the headers.
349 b.string();
350 while(b.byte()) {
351 b.string();
352 b.string();
354 //Okay, read the extension packets.
355 b.extension({
356 {TAG_BRANCH_NAME, [this, &mname](binarystream::input& s) {
357 mname = s.string_implicit();
358 }},{TAG_MOVIE, [this, &v, &mname, &name, &done](binarystream::input& s) {
359 if(name != mname)
360 return;
361 v.clear();
362 v.load_binary(s);
363 done = true;
364 }},{TAG_BRANCH, [this, &v, &mname, &name, &done](binarystream::input& s) {
365 if(name != mname)
366 return;
367 v.clear();
368 v.load_binary(s);
369 done = true;
371 }, binarystream::null_default);
372 if(!done)
373 (stringfmt() << "Can't find branch '" << name << "' in file.").throwex();
376 moviefile_sram_extractor_binary::moviefile_sram_extractor_binary(const std::string& filename)
378 s = open(filename.c_str(), O_RDONLY | EXTRA_OPENFLAGS);
379 if(s < 0) {
380 int err = errno;
381 (stringfmt() << "Can't open file '" << filename << "' for reading: " << strerror(err)).throwex();
385 moviefile_sram_extractor_binary::~moviefile_sram_extractor_binary()
389 std::set<std::string> moviefile_sram_extractor_binary::enumerate()
391 std::set<std::string> r;
392 std::string name;
393 if(lseek(s, 5, SEEK_SET) < 0) {
394 int err = errno;
395 (stringfmt() << "Can't read the file: " << strerror(err)).throwex();
397 binarystream::input b(s);
398 //Skip the headers.
399 b.string();
400 while(b.byte()) {
401 b.string();
402 b.string();
404 //Okay, read the extension packets.
405 b.extension({
406 {TAG_SAVESTATE, [this, &r](binarystream::input& s) {
407 r.insert("");
408 }},{TAG_SAVE_SRAM, [this, &r](binarystream::input& s) {
409 r.insert(s.string());
411 }, binarystream::null_default);
413 return r;
416 void moviefile_sram_extractor_binary::read(const std::string& name, std::vector<char>& v)
418 //Char and uint8_t are the same representation, right?
419 std::vector<char>* _v = &v;
420 std::string mname = name;
421 if(lseek(s, 5, SEEK_SET) < 0) {
422 int err = errno;
423 (stringfmt() << "Can't read the file: " << strerror(err)).throwex();
425 binarystream::input b(s);
426 bool done = false;
427 //Skip the headers.
428 b.string();
429 while(b.byte()) {
430 b.string();
431 b.string();
433 //Okay, read the extension packets.
434 b.extension({
435 {TAG_SAVESTATE, [this, mname, _v, &done](binarystream::input& s) {
436 if(mname == "") {
437 //This savestate.
438 s.number(); //Frame of save.
439 s.number(); //Lagged frames.
440 s.number(); //RTC second.
441 s.number(); //RTC subsecond.
442 size_t pctrs = s.number(); //Number of poll counters.
443 for(size_t i = 0; i < pctrs; i++)
444 s.number32(); //Poll counters.
445 s.byte(); //Poll flag.
446 s.blob_implicit(*_v);
447 done = true;
449 }},{TAG_SAVE_SRAM, [this, mname, _v, &done](binarystream::input& s) {
450 std::string sname = s.string();
451 if(sname == mname) {
452 //This SRAM.
453 s.blob_implicit(*_v);
454 done = true;
457 }, binarystream::null_default);
458 if(!done) {
459 if(name != "")
460 (stringfmt() << "Can't find branch '" << name << "' in file.").throwex();
461 else
462 (stringfmt() << "Can't find savestate in file.").throwex();