Fix SA1 open bus
[lsnes.git] / src / core / moviefile-binary.cpp
blobd067468d8e0e59da5a599e5aa0ee6d1471d28f60
1 #include "core/misc.hpp"
2 #include "core/movie.hpp"
3 #include "core/moviedata.hpp"
4 #include "core/moviefile.hpp"
5 #include "core/moviefile-binary.hpp"
6 #include "core/moviefile-common.hpp"
7 #include "library/zip.hpp"
8 #include "library/string.hpp"
9 #include "library/minmax.hpp"
10 #include "library/serialization.hpp"
11 #include "library/binarystream.hpp"
12 #include "interface/romtype.hpp"
13 #include <cstring>
14 #include <cerrno>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
18 //FUCK YOU. SERIOUSLY.
19 #define EXTRA_OPENFLAGS O_BINARY
20 #else
21 #define EXTRA_OPENFLAGS 0
22 #endif
24 void moviefile::brief_info::binary_io(int _stream)
26 binarystream::input in(_stream);
27 sysregion = in.string();
28 //Discard the settings.
29 while(in.byte()) {
30 in.string();
31 in.string();
33 in.extension({
34 {TAG_CORE_VERSION, [this](binarystream::input& s) {
35 this->corename = s.string_implicit();
36 }},{TAG_PROJECT_ID, [this](binarystream::input& s) {
37 this->projectid = s.string_implicit();
38 }},{TAG_SAVESTATE, [this](binarystream::input& s) {
39 this->current_frame = s.number();
40 }},{TAG_RRDATA, [this](binarystream::input& s) {
41 std::vector<char> c_rrdata;
42 s.blob_implicit(c_rrdata);
43 this->rerecords = rrdata_set::count(c_rrdata);
44 }},{TAG_ROMHASH, [this](binarystream::input& s) {
45 uint8_t n = s.byte();
46 std::string h = s.string_implicit();
47 if(n > 2 * ROM_SLOT_COUNT)
48 return;
49 if(n & 1)
50 this->hashxml[n >> 1] = h;
51 else
52 this->hash[n >> 1] = h;
53 }},{TAG_ROMHINT, [this](binarystream::input& s) {
54 uint8_t n = s.byte();
55 std::string h = s.string_implicit();
56 if(n > ROM_SLOT_COUNT)
57 return;
58 this->hint[n] = h;
60 }, binarystream::null_default);
63 void moviefile::binary_io(int _stream, rrdata_set& rrd) throw(std::bad_alloc, std::runtime_error)
65 binarystream::output out(_stream);
66 out.string(gametype->get_name());
67 moviefile_write_settings<binarystream::output>(out, settings, gametype->get_type().get_settings(),
68 [](binarystream::output& s, const std::string& name, const std::string& value) -> void {
69 s.byte(0x01);
70 s.string(name);
71 s.string(value);
72 });
73 out.byte(0x00);
75 out.extension(TAG_MOVIE_TIME, [this](binarystream::output& s) {
76 s.number(this->movie_rtc_second);
77 s.number(this->movie_rtc_subsecond);
78 });
80 out.extension(TAG_PROJECT_ID, [this](binarystream::output& s) {
81 s.string_implicit(this->projectid);
82 });
84 out.extension(TAG_CORE_VERSION, [this](binarystream::output& s) {
85 this->coreversion = this->gametype->get_type().get_core_identifier();
86 s.string_implicit(this->coreversion);
87 });
89 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
90 out.extension(TAG_ROMHASH, [this, i](binarystream::output& s) {
91 if(!this->romimg_sha256[i].length()) return;
92 s.byte(2 * i);
93 s.string_implicit(this->romimg_sha256[i]);
94 });
95 out.extension(TAG_ROMHASH, [this, i](binarystream::output& s) {
96 if(!this->romxml_sha256[i].length()) return;
97 s.byte(2 * i + 1);
98 s.string_implicit(this->romxml_sha256[i]);
99 });
100 out.extension(TAG_ROMHINT, [this, i](binarystream::output& s) {
101 if(!this->namehint[i].length()) return;
102 s.byte(i);
103 s.string_implicit(this->namehint[i]);
107 out.extension(TAG_RRDATA, [this, &rrd](binarystream::output& s) {
108 std::vector<char> _rrd;
109 rrd.write(_rrd);
110 s.blob_implicit(_rrd);
113 for(auto i : movie_sram)
114 out.extension(TAG_MOVIE_SRAM, [&i](binarystream::output& s) {
115 s.string(i.first);
116 s.blob_implicit(i.second);
119 out.extension(TAG_ANCHOR_SAVE, [this](binarystream::output& s) {
120 s.blob_implicit(this->anchor_savestate);
122 if(is_savestate) {
123 out.extension(TAG_SAVESTATE, [this](binarystream::output& s) {
124 s.number(this->save_frame);
125 s.number(this->lagged_frames);
126 s.number(this->rtc_second);
127 s.number(this->rtc_subsecond);
128 s.number(this->pollcounters.size());
129 for(auto i : this->pollcounters)
130 s.number32(i);
131 s.byte(this->poll_flag ? 0x01 : 0x00);
132 s.blob_implicit(this->savestate);
133 }, true, out.numberbytes(save_frame) + out.numberbytes(lagged_frames) + out.numberbytes(rtc_second) +
134 out.numberbytes(rtc_subsecond) + out.numberbytes(pollcounters.size()) +
135 4 * pollcounters.size() + 1 + savestate.size());
137 out.extension(TAG_HOSTMEMORY, [this](binarystream::output& s) {
138 s.blob_implicit(this->host_memory);
141 out.extension(TAG_SCREENSHOT, [this](binarystream::output& s) {
142 s.blob_implicit(this->screenshot);
143 }, true, screenshot.size());
145 for(auto i : sram) {
146 out.extension(TAG_SAVE_SRAM, [&i](binarystream::output& s) {
147 s.string(i.first);
148 s.blob_implicit(i.second);
153 out.extension(TAG_GAMENAME, [this](binarystream::output& s) {
154 s.string_implicit(this->gamename);
157 for(auto i : subtitles)
158 out.extension(TAG_SUBTITLE, [&i](binarystream::output& s) {
159 s.number(i.first.get_frame());
160 s.number(i.first.get_length());
161 s.string_implicit(i.second);
164 for(auto i : authors)
165 out.extension(TAG_AUTHOR, [&i](binarystream::output& s) {
166 s.string(i.first);
167 s.string_implicit(i.second);
170 for(auto i : active_macros)
171 out.extension(TAG_MACRO, [&i](binarystream::output& s) {
172 s.number(i.second);
173 s.string_implicit(i.first);
176 for(auto i : ramcontent) {
177 out.extension(TAG_RAMCONTENT, [&i](binarystream::output& s) {
178 s.string(i.first);
179 s.blob_implicit(i.second);
183 int64_t next_bnum = 0;
184 std::map<std::string, uint64_t> branch_table;
185 for(auto& i : branches) {
186 branch_table[i.first] = next_bnum++;
187 out.extension(TAG_BRANCH_NAME, [&i](binarystream::output& s) {
188 s.string_implicit(i.first);
189 }, false, i.first.length());
190 uint32_t tag = (&i.second == input) ? TAG_MOVIE : TAG_BRANCH;
191 out.extension(tag, [&i](binarystream::output& s) {
192 i.second.save_binary(s);
193 }, true, i.second.binary_size());
197 void moviefile::binary_io(int _stream, core_type& romtype) throw(std::bad_alloc, std::runtime_error)
199 binarystream::input in(_stream);
200 std::string tmp = in.string();
201 std::string next_branch;
202 std::map<uint64_t, std::string> branch_table;
203 uint64_t next_bnum = 0;
204 try {
205 gametype = &romtype.lookup_sysregion(tmp);
206 } catch(std::bad_alloc& e) {
207 throw;
208 } catch(std::exception& e) {
209 throw std::runtime_error("Illegal game type '" + tmp + "'");
211 while(in.byte()) {
212 std::string name = in.string();
213 settings[name] = in.string();
215 auto ctrldata = gametype->get_type().controllerconfig(settings);
216 port_type_set& ports = port_type_set::make(ctrldata.ports, ctrldata.portindex());
217 input = NULL;
219 in.extension({
220 {TAG_ANCHOR_SAVE, [this](binarystream::input& s) {
221 s.blob_implicit(this->anchor_savestate);
222 }},{TAG_AUTHOR, [this](binarystream::input& s) {
223 std::string a = s.string();
224 std::string b = s.string_implicit();
225 this->authors.push_back(std::make_pair(a, b));
226 }},{TAG_CORE_VERSION, [this](binarystream::input& s) {
227 this->coreversion = s.string_implicit();
228 }},{TAG_GAMENAME, [this](binarystream::input& s) {
229 this->gamename = s.string_implicit();
230 }},{TAG_HOSTMEMORY, [this](binarystream::input& s) {
231 s.blob_implicit(this->host_memory);
232 }},{TAG_MACRO, [this](binarystream::input& s) {
233 uint64_t n = s.number();
234 this->active_macros[s.string_implicit()] = n;
235 }},{TAG_BRANCH_NAME, [this, &branch_table, &next_bnum, &next_branch](binarystream::input& s) {
236 branch_table[next_bnum++] = next_branch = s.string_implicit();
237 }},{TAG_MOVIE, [this, &ports, &next_branch](binarystream::input& s) {
238 branches[next_branch].clear(ports);
239 branches[next_branch].load_binary(s);
240 input = &branches[next_branch];
241 }},{TAG_BRANCH, [this, &ports, &next_branch](binarystream::input& s) {
242 branches[next_branch].clear(ports);
243 branches[next_branch].load_binary(s);
244 }},{TAG_MOVIE_SRAM, [this](binarystream::input& s) {
245 std::string a = s.string();
246 s.blob_implicit(this->movie_sram[a]);
247 }},{TAG_RAMCONTENT, [this](binarystream::input& s) {
248 std::string a = s.string();
249 s.blob_implicit(this->ramcontent[a]);
250 }},{TAG_MOVIE_TIME, [this](binarystream::input& s) {
251 this->movie_rtc_second = s.number();
252 this->movie_rtc_subsecond = s.number();
253 }},{TAG_PROJECT_ID, [this](binarystream::input& s) {
254 this->projectid = s.string_implicit();
255 }},{TAG_ROMHASH, [this](binarystream::input& s) {
256 uint8_t n = s.byte();
257 std::string h = s.string_implicit();
258 if(n > 2 * ROM_SLOT_COUNT)
259 return;
260 if(n & 1)
261 romxml_sha256[n >> 1] = h;
262 else
263 romimg_sha256[n >> 1] = h;
264 }},{TAG_ROMHINT, [this](binarystream::input& s) {
265 uint8_t n = s.byte();
266 std::string h = s.string_implicit();
267 if(n > ROM_SLOT_COUNT)
268 return;
269 namehint[n] = h;
270 }},{TAG_RRDATA, [this](binarystream::input& s) {
271 s.blob_implicit(this->c_rrdata);
272 this->rerecords = (stringfmt() << rrdata_set::count(c_rrdata)).str();
273 }},{TAG_SAVE_SRAM, [this](binarystream::input& s) {
274 std::string a = s.string();
275 s.blob_implicit(this->sram[a]);
276 }},{TAG_SAVESTATE, [this](binarystream::input& s) {
277 this->is_savestate = true;
278 this->save_frame = s.number();
279 this->lagged_frames = s.number();
280 this->rtc_second = s.number();
281 this->rtc_subsecond = s.number();
282 this->pollcounters.resize(s.number());
283 for(auto& i : this->pollcounters)
284 i = s.number32();
285 this->poll_flag = (s.byte() != 0);
286 s.blob_implicit(this->savestate);
287 }},{TAG_SCREENSHOT, [this](binarystream::input& s) {
288 s.blob_implicit(this->screenshot);
289 }},{TAG_SUBTITLE, [this](binarystream::input& s) {
290 uint64_t f = s.number();
291 uint64_t l = s.number();
292 std::string x = s.string_implicit();
293 this->subtitles[moviefile_subtiming(f, l)] = x;
295 }, binarystream::null_default);
297 create_default_branch(ports);
300 moviefile_branch_extractor_binary::moviefile_branch_extractor_binary(const std::string& filename)
302 s = open(filename.c_str(), O_RDONLY | EXTRA_OPENFLAGS);
303 if(s < 0) {
304 int err = errno;
305 (stringfmt() << "Can't open file '" << filename << "' for reading: " << strerror(err)).throwex();
309 moviefile_branch_extractor_binary::~moviefile_branch_extractor_binary()
313 std::set<std::string> moviefile_branch_extractor_binary::enumerate()
315 std::set<std::string> r;
316 std::string name;
317 if(lseek(s, 5, SEEK_SET) < 0) {
318 int err = errno;
319 (stringfmt() << "Can't read the file: " << strerror(err)).throwex();
321 binarystream::input b(s);
322 //Skip the headers.
323 b.string();
324 while(b.byte()) {
325 b.string();
326 b.string();
328 //Okay, read the extension packets.
329 b.extension({
330 {TAG_BRANCH_NAME, [this, &name](binarystream::input& s) {
331 name = s.string_implicit();
332 }},{TAG_MOVIE, [this, &r, &name](binarystream::input& s) {
333 r.insert(name);
334 }},{TAG_BRANCH, [this, &r, &name](binarystream::input& s) {
335 r.insert(name);
337 }, binarystream::null_default);
339 return r;
342 void moviefile_branch_extractor_binary::read(const std::string& name, controller_frame_vector& v)
344 std::string mname;
345 if(lseek(s, 5, SEEK_SET) < 0) {
346 int err = errno;
347 (stringfmt() << "Can't read the file: " << strerror(err)).throwex();
349 binarystream::input b(s);
350 bool done = false;
351 //Skip the headers.
352 b.string();
353 while(b.byte()) {
354 b.string();
355 b.string();
357 //Okay, read the extension packets.
358 b.extension({
359 {TAG_BRANCH_NAME, [this, &mname](binarystream::input& s) {
360 mname = s.string_implicit();
361 }},{TAG_MOVIE, [this, &v, &mname, &name, &done](binarystream::input& s) {
362 if(name != mname)
363 return;
364 v.clear();
365 v.load_binary(s);
366 done = true;
367 }},{TAG_BRANCH, [this, &v, &mname, &name, &done](binarystream::input& s) {
368 if(name != mname)
369 return;
370 v.clear();
371 v.load_binary(s);
372 done = true;
374 }, binarystream::null_default);
375 if(!done)
376 (stringfmt() << "Can't find branch '" << name << "' in file.").throwex();