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"
14 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
15 //FUCK YOU. SERIOUSLY.
16 #define EXTRA_OPENFLAGS O_BINARY
18 #define EXTRA_OPENFLAGS 0
21 void moviefile::brief_info::binary_io(int _stream
)
23 binarystream::input
in(_stream
);
24 sysregion
= in
.string();
25 //Discard the settings.
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
) {
43 std::string h
= s
.string_implicit();
44 if(n
> 2 * ROM_SLOT_COUNT
)
47 this->hashxml
[n
>> 1] = h
;
49 this->hash
[n
>> 1] = h
;
50 }},{TAG_ROMHINT
, [this](binarystream::input
& s
) {
52 std::string h
= s
.string_implicit();
53 if(n
> ROM_SLOT_COUNT
)
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 {
72 out
.extension(TAG_MOVIE_TIME
, [this](binarystream::output
& s
) {
73 s
.number(this->movie_rtc_second
);
74 s
.number(this->movie_rtc_subsecond
);
77 out
.extension(TAG_PROJECT_ID
, [this](binarystream::output
& s
) {
78 s
.string_implicit(this->projectid
);
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
);
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;
90 s
.string_implicit(this->romimg_sha256
[i
]);
92 out
.extension(TAG_ROMHASH
, [this, i
](binarystream::output
& s
) {
93 if(!this->romxml_sha256
[i
].length()) return;
95 s
.string_implicit(this->romxml_sha256
[i
]);
97 out
.extension(TAG_ROMHINT
, [this, i
](binarystream::output
& s
) {
98 if(!this->namehint
[i
].length()) return;
100 s
.string_implicit(this->namehint
[i
]);
104 out
.extension(TAG_RRDATA
, [this, &rrd
](binarystream::output
& s
) {
105 std::vector
<char> _rrd
;
107 s
.blob_implicit(_rrd
);
110 for(auto i
: movie_sram
)
111 out
.extension(TAG_MOVIE_SRAM
, [&i
](binarystream::output
& s
) {
113 s
.blob_implicit(i
.second
);
116 out
.extension(TAG_ANCHOR_SAVE
, [this](binarystream::output
& s
) {
117 s
.blob_implicit(this->anchor_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
)
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());
143 out
.extension(TAG_SAVE_SRAM
, [&i
](binarystream::output
& s
) {
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
) {
164 s
.string_implicit(i
.second
);
167 for(auto i
: active_macros
)
168 out
.extension(TAG_MACRO
, [&i
](binarystream::output
& s
) {
170 s
.string_implicit(i
.first
);
173 for(auto i
: ramcontent
) {
174 out
.extension(TAG_RAMCONTENT
, [&i
](binarystream::output
& s
) {
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;
202 gametype
= &romtype
.lookup_sysregion(tmp
);
203 } catch(std::bad_alloc
& e
) {
205 } catch(std::exception
& e
) {
206 throw std::runtime_error("Illegal game type '" + tmp
+ "'");
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());
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
)
258 romxml_sha256
[n
>> 1] = h
;
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
)
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
)
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
);
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
;
314 if(lseek(s
, 5, SEEK_SET
) < 0) {
316 (stringfmt() << "Can't read the file: " << strerror(err
)).throwex();
318 binarystream::input
b(s
);
325 //Okay, read the extension packets.
327 {TAG_BRANCH_NAME
, [this, &name
](binarystream::input
& s
) {
328 name
= s
.string_implicit();
329 }},{TAG_MOVIE
, [this, &r
, &name
](binarystream::input
& s
) {
331 }},{TAG_BRANCH
, [this, &r
, &name
](binarystream::input
& s
) {
334 }, binarystream::null_default
);
339 void moviefile_branch_extractor_binary::read(const std::string
& name
, portctrl::frame_vector
& v
)
342 if(lseek(s
, 5, SEEK_SET
) < 0) {
344 (stringfmt() << "Can't read the file: " << strerror(err
)).throwex();
346 binarystream::input
b(s
);
354 //Okay, read the extension packets.
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
) {
364 }},{TAG_BRANCH
, [this, &v
, &mname
, &name
, &done
](binarystream::input
& s
) {
371 }, binarystream::null_default
);
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
);
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
;
393 if(lseek(s
, 5, SEEK_SET
) < 0) {
395 (stringfmt() << "Can't read the file: " << strerror(err
)).throwex();
397 binarystream::input
b(s
);
404 //Okay, read the extension packets.
406 {TAG_SAVESTATE
, [this, &r
](binarystream::input
& s
) {
408 }},{TAG_SAVE_SRAM
, [this, &r
](binarystream::input
& s
) {
409 r
.insert(s
.string());
411 }, binarystream::null_default
);
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) {
423 (stringfmt() << "Can't read the file: " << strerror(err
)).throwex();
425 binarystream::input
b(s
);
433 //Okay, read the extension packets.
435 {TAG_SAVESTATE
, [this, mname
, _v
, &done
](binarystream::input
& s
) {
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
);
449 }},{TAG_SAVE_SRAM
, [this, mname
, _v
, &done
](binarystream::input
& s
) {
450 std::string sname
= s
.string();
453 s
.blob_implicit(*_v
);
457 }, binarystream::null_default
);
460 (stringfmt() << "Can't find branch '" << name
<< "' in file.").throwex();
462 (stringfmt() << "Can't find savestate in file.").throwex();