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
, bool as_state
) 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->dyn
.save_frame
);
122 s
.number(this->dyn
.lagged_frames
);
123 s
.number(this->dyn
.rtc_second
);
124 s
.number(this->dyn
.rtc_subsecond
);
125 s
.number(this->dyn
.pollcounters
.size());
126 for(auto i
: this->dyn
.pollcounters
)
128 s
.byte(this->dyn
.poll_flag
? 0x01 : 0x00);
129 s
.blob_implicit(this->dyn
.savestate
);
130 }, true, out
.numberbytes(dyn
.save_frame
) + out
.numberbytes(dyn
.lagged_frames
) +
131 out
.numberbytes(dyn
.rtc_second
) + out
.numberbytes(dyn
.rtc_subsecond
) +
132 out
.numberbytes(dyn
.pollcounters
.size()) + 4 * dyn
.pollcounters
.size() + 1 +
133 dyn
.savestate
.size());
135 out
.extension(TAG_HOSTMEMORY
, [this](binarystream::output
& s
) {
136 s
.blob_implicit(this->dyn
.host_memory
);
139 out
.extension(TAG_SCREENSHOT
, [this](binarystream::output
& s
) {
140 s
.blob_implicit(this->dyn
.screenshot
);
141 }, true, dyn
.screenshot
.size());
143 for(auto i
: dyn
.sram
) {
144 out
.extension(TAG_SAVE_SRAM
, [&i
](binarystream::output
& s
) {
146 s
.blob_implicit(i
.second
);
150 for(auto i
: dyn
.active_macros
)
151 out
.extension(TAG_MACRO
, [&i
](binarystream::output
& s
) {
153 s
.string_implicit(i
.first
);
157 out
.extension(TAG_GAMENAME
, [this](binarystream::output
& s
) {
158 s
.string_implicit(this->gamename
);
161 for(auto i
: subtitles
)
162 out
.extension(TAG_SUBTITLE
, [&i
](binarystream::output
& s
) {
163 s
.number(i
.first
.get_frame());
164 s
.number(i
.first
.get_length());
165 s
.string_implicit(i
.second
);
168 for(auto i
: authors
)
169 out
.extension(TAG_AUTHOR
, [&i
](binarystream::output
& s
) {
171 s
.string_implicit(i
.second
);
174 for(auto i
: ramcontent
) {
175 out
.extension(TAG_RAMCONTENT
, [&i
](binarystream::output
& s
) {
177 s
.blob_implicit(i
.second
);
181 int64_t next_bnum
= 0;
182 std::map
<std::string
, uint64_t> branch_table
;
183 for(auto& i
: branches
) {
184 branch_table
[i
.first
] = next_bnum
++;
185 out
.extension(TAG_BRANCH_NAME
, [&i
](binarystream::output
& s
) {
186 s
.string_implicit(i
.first
);
187 }, false, i
.first
.length());
188 uint32_t tag
= (&i
.second
== input
) ? TAG_MOVIE
: TAG_BRANCH
;
189 out
.extension(tag
, [&i
](binarystream::output
& s
) {
190 i
.second
.save_binary(s
);
191 }, true, i
.second
.binary_size());
195 void moviefile::binary_io(int _stream
, core_type
& romtype
) throw(std::bad_alloc
, std::runtime_error
)
197 binarystream::input
in(_stream
);
198 std::string tmp
= in
.string();
199 std::string next_branch
;
200 std::map
<uint64_t, std::string
> branch_table
;
201 uint64_t next_bnum
= 0;
203 gametype
= &romtype
.lookup_sysregion(tmp
);
204 } catch(std::bad_alloc
& e
) {
206 } catch(std::exception
& e
) {
207 throw std::runtime_error("Illegal game type '" + tmp
+ "'");
210 std::string name
= in
.string();
211 settings
[name
] = in
.string();
213 auto ctrldata
= gametype
->get_type().controllerconfig(settings
);
214 portctrl::type_set
& ports
= portctrl::type_set::make(ctrldata
.ports
, ctrldata
.portindex());
217 this->dyn
.save_frame
= 0; //If no savestate, ensure this is interpretted as a movie.
219 {TAG_ANCHOR_SAVE
, [this](binarystream::input
& s
) {
220 s
.blob_implicit(this->anchor_savestate
);
221 }},{TAG_AUTHOR
, [this](binarystream::input
& s
) {
222 std::string a
= s
.string();
223 std::string b
= s
.string_implicit();
224 this->authors
.push_back(std::make_pair(a
, b
));
225 }},{TAG_CORE_VERSION
, [this](binarystream::input
& s
) {
226 this->coreversion
= s
.string_implicit();
227 }},{TAG_GAMENAME
, [this](binarystream::input
& s
) {
228 this->gamename
= s
.string_implicit();
229 }},{TAG_HOSTMEMORY
, [this](binarystream::input
& s
) {
230 s
.blob_implicit(this->dyn
.host_memory
);
231 }},{TAG_MACRO
, [this](binarystream::input
& s
) {
232 uint64_t n
= s
.number();
233 this->dyn
.active_macros
[s
.string_implicit()] = n
;
234 }},{TAG_BRANCH_NAME
, [this, &branch_table
, &next_bnum
, &next_branch
](binarystream::input
& s
) {
235 branch_table
[next_bnum
++] = next_branch
= s
.string_implicit();
236 }},{TAG_MOVIE
, [this, &ports
, &next_branch
](binarystream::input
& s
) {
237 branches
[next_branch
].clear(ports
);
238 branches
[next_branch
].load_binary(s
);
239 input
= &branches
[next_branch
];
240 }},{TAG_BRANCH
, [this, &ports
, &next_branch
](binarystream::input
& s
) {
241 branches
[next_branch
].clear(ports
);
242 branches
[next_branch
].load_binary(s
);
243 }},{TAG_MOVIE_SRAM
, [this](binarystream::input
& s
) {
244 std::string a
= s
.string();
245 s
.blob_implicit(this->movie_sram
[a
]);
246 }},{TAG_RAMCONTENT
, [this](binarystream::input
& s
) {
247 std::string a
= s
.string();
248 s
.blob_implicit(this->ramcontent
[a
]);
249 }},{TAG_MOVIE_TIME
, [this](binarystream::input
& s
) {
250 this->movie_rtc_second
= s
.number();
251 this->movie_rtc_subsecond
= s
.number();
252 }},{TAG_PROJECT_ID
, [this](binarystream::input
& s
) {
253 this->projectid
= s
.string_implicit();
254 }},{TAG_ROMHASH
, [this](binarystream::input
& s
) {
255 uint8_t n
= s
.byte();
256 std::string h
= s
.string_implicit();
257 if(n
> 2 * ROM_SLOT_COUNT
)
260 romxml_sha256
[n
>> 1] = h
;
262 romimg_sha256
[n
>> 1] = h
;
263 }},{TAG_ROMHINT
, [this](binarystream::input
& s
) {
264 uint8_t n
= s
.byte();
265 std::string h
= s
.string_implicit();
266 if(n
> ROM_SLOT_COUNT
)
269 }},{TAG_RRDATA
, [this](binarystream::input
& s
) {
270 s
.blob_implicit(this->c_rrdata
);
271 this->rerecords
= (stringfmt() << rrdata_set::count(c_rrdata
)).str();
272 }},{TAG_SAVE_SRAM
, [this](binarystream::input
& s
) {
273 std::string a
= s
.string();
274 s
.blob_implicit(this->dyn
.sram
[a
]);
275 }},{TAG_SAVESTATE
, [this](binarystream::input
& s
) {
276 this->dyn
.save_frame
= s
.number();
277 this->dyn
.lagged_frames
= s
.number();
278 this->dyn
.rtc_second
= s
.number();
279 this->dyn
.rtc_subsecond
= s
.number();
280 this->dyn
.pollcounters
.resize(s
.number());
281 for(auto& i
: this->dyn
.pollcounters
)
283 this->dyn
.poll_flag
= (s
.byte() != 0);
284 s
.blob_implicit(this->dyn
.savestate
);
285 }},{TAG_SCREENSHOT
, [this](binarystream::input
& s
) {
286 s
.blob_implicit(this->dyn
.screenshot
);
287 }},{TAG_SUBTITLE
, [this](binarystream::input
& s
) {
288 uint64_t f
= s
.number();
289 uint64_t l
= s
.number();
290 std::string x
= s
.string_implicit();
291 this->subtitles
[moviefile_subtiming(f
, l
)] = x
;
293 }, binarystream::null_default
);
295 create_default_branch(ports
);
298 moviefile_branch_extractor_binary::moviefile_branch_extractor_binary(const std::string
& filename
)
300 s
= open(filename
.c_str(), O_RDONLY
| EXTRA_OPENFLAGS
);
303 (stringfmt() << "Can't open file '" << filename
<< "' for reading: " << strerror(err
)).throwex();
307 moviefile_branch_extractor_binary::~moviefile_branch_extractor_binary()
311 std::set
<std::string
> moviefile_branch_extractor_binary::enumerate()
313 std::set
<std::string
> r
;
315 if(lseek(s
, 5, SEEK_SET
) < 0) {
317 (stringfmt() << "Can't read the file: " << strerror(err
)).throwex();
319 binarystream::input
b(s
);
326 //Okay, read the extension packets.
328 {TAG_BRANCH_NAME
, [this, &name
](binarystream::input
& s
) {
329 name
= s
.string_implicit();
330 }},{TAG_MOVIE
, [this, &r
, &name
](binarystream::input
& s
) {
332 }},{TAG_BRANCH
, [this, &r
, &name
](binarystream::input
& s
) {
335 }, binarystream::null_default
);
340 void moviefile_branch_extractor_binary::read(const std::string
& name
, portctrl::frame_vector
& v
)
343 if(lseek(s
, 5, SEEK_SET
) < 0) {
345 (stringfmt() << "Can't read the file: " << strerror(err
)).throwex();
347 binarystream::input
b(s
);
355 //Okay, read the extension packets.
357 {TAG_BRANCH_NAME
, [this, &mname
](binarystream::input
& s
) {
358 mname
= s
.string_implicit();
359 }},{TAG_MOVIE
, [this, &v
, &mname
, &name
, &done
](binarystream::input
& s
) {
365 }},{TAG_BRANCH
, [this, &v
, &mname
, &name
, &done
](binarystream::input
& s
) {
372 }, binarystream::null_default
);
374 (stringfmt() << "Can't find branch '" << name
<< "' in file.").throwex();
377 moviefile_sram_extractor_binary::moviefile_sram_extractor_binary(const std::string
& filename
)
379 s
= open(filename
.c_str(), O_RDONLY
| EXTRA_OPENFLAGS
);
382 (stringfmt() << "Can't open file '" << filename
<< "' for reading: " << strerror(err
)).throwex();
386 moviefile_sram_extractor_binary::~moviefile_sram_extractor_binary()
390 std::set
<std::string
> moviefile_sram_extractor_binary::enumerate()
392 std::set
<std::string
> r
;
394 if(lseek(s
, 5, SEEK_SET
) < 0) {
396 (stringfmt() << "Can't read the file: " << strerror(err
)).throwex();
398 binarystream::input
b(s
);
405 //Okay, read the extension packets.
407 {TAG_SAVESTATE
, [this, &r
](binarystream::input
& s
) {
409 }},{TAG_SAVE_SRAM
, [this, &r
](binarystream::input
& s
) {
410 r
.insert(s
.string());
412 }, binarystream::null_default
);
417 void moviefile_sram_extractor_binary::read(const std::string
& name
, std::vector
<char>& v
)
419 //Char and uint8_t are the same representation, right?
420 std::vector
<char>* _v
= &v
;
421 std::string mname
= name
;
422 if(lseek(s
, 5, SEEK_SET
) < 0) {
424 (stringfmt() << "Can't read the file: " << strerror(err
)).throwex();
426 binarystream::input
b(s
);
434 //Okay, read the extension packets.
436 {TAG_SAVESTATE
, [this, mname
, _v
, &done
](binarystream::input
& s
) {
439 s
.number(); //Frame of save.
440 s
.number(); //Lagged frames.
441 s
.number(); //RTC second.
442 s
.number(); //RTC subsecond.
443 size_t pctrs
= s
.number(); //Number of poll counters.
444 for(size_t i
= 0; i
< pctrs
; i
++)
445 s
.number32(); //Poll counters.
446 s
.byte(); //Poll flag.
447 s
.blob_implicit(*_v
);
450 }},{TAG_SAVE_SRAM
, [this, mname
, _v
, &done
](binarystream::input
& s
) {
451 std::string sname
= s
.string();
454 s
.blob_implicit(*_v
);
458 }, binarystream::null_default
);
461 (stringfmt() << "Can't find branch '" << name
<< "' in file.").throwex();
463 (stringfmt() << "Can't find savestate in file.").throwex();