1 #include "core/misc.hpp"
2 #include "core/movie.hpp"
3 #include "core/moviedata.hpp"
4 #include "core/moviefile.hpp"
5 #include "core/moviefile-common.hpp"
6 #include "library/zip.hpp"
7 #include "library/string.hpp"
8 #include "library/minmax.hpp"
9 #include "library/serialization.hpp"
10 #include "library/binarystream.hpp"
11 #include "interface/romtype.hpp"
18 #include <boost/iostreams/copy.hpp>
19 #include <boost/iostreams/device/back_inserter.hpp>
20 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
26 std::map
<std::string
, moviefile
*> memory_saves
;
29 moviefile::brief_info::brief_info(const std::string
& filename
)
32 if(rr
= regex("\\$MEMORY:(.*)", filename
)) {
33 if(!memory_saves
.count(rr
[1]) && memory_saves
[rr
[1]])
34 throw std::runtime_error("No such memory save");
35 moviefile
& mv
= *memory_saves
[rr
[1]];
36 sysregion
= mv
.gametype
->get_name();
37 corename
= mv
.coreversion
;
38 projectid
= mv
.projectid
;
39 current_frame
= mv
.is_savestate
? mv
.save_frame
: 0;
40 rerecords
= mv
.rerecords_mem
;
41 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
42 hash
[i
] = mv
.romimg_sha256
[i
];
43 hashxml
[i
] = mv
.romxml_sha256
[i
];
44 hint
[i
] = mv
.namehint
[i
];
49 std::istream
& s
= zip::openrel(filename
, "");
52 if(!strcmp(buf
, "lsmv\x1A")) {
59 zip::reader
r(filename
);
63 moviefile::moviefile() throw(std::bad_alloc
)
65 static port_type_set dummy_types
;
66 force_corrupt
= false;
73 movie_rtc_second
= rtc_second
= DEFAULT_RTC_SECOND
;
74 movie_rtc_subsecond
= rtc_subsecond
= DEFAULT_RTC_SUBSECOND
;
76 lazy_project_create
= true;
80 moviefile::moviefile(loaded_rom
& rom
, std::map
<std::string
, std::string
>& c_settings
, uint64_t rtc_sec
,
83 static port_type_set dummy_types
;
84 force_corrupt
= false;
85 gametype
= &rom
.rtype
->combine_region(*rom
.region
);
86 coreversion
= rom
.rtype
->get_core_identifier();
87 projectid
= get_random_hexstring(40);
90 movie_rtc_second
= rtc_second
= rtc_sec
;
91 movie_rtc_subsecond
= rtc_subsecond
= rtc_subsec
;
93 lazy_project_create
= true;
95 settings
= c_settings
;
97 auto ctrldata
= rom
.rtype
->controllerconfig(settings
);
98 port_type_set
& ports
= port_type_set::make(ctrldata
.ports
, ctrldata
.portindex());
99 create_default_branch(ports
);
100 if(!rom
.rtype
->isnull()) {
101 //Initialize the remainder.
103 for(size_t i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
104 romimg_sha256
[i
] = rom
.romimg
[i
].sha_256
.read();
105 romxml_sha256
[i
] = rom
.romxml
[i
].sha_256
.read();
106 namehint
[i
] = rom
.romimg
[i
].namehint
;
111 moviefile::moviefile(const std::string
& movie
, core_type
& romtype
) throw(std::bad_alloc
, std::runtime_error
)
114 if(rr
= regex("\\$MEMORY:(.*)", movie
)) {
115 if(!memory_saves
.count(rr
[1]) || !memory_saves
[rr
[1]])
116 throw std::runtime_error("No such memory save");
117 moviefile
& s
= *memory_saves
[rr
[1]];
123 start_paused
= false;
124 force_corrupt
= false;
125 is_savestate
= false;
126 lazy_project_create
= false;
128 std::istream
& s
= zip::openrel(movie
, "");
131 if(!strcmp(buf
, "lsmv\x1A")) {
132 binary_io(s
, romtype
);
138 zip::reader
r(movie
);
142 void moviefile::fixup_current_branch(const moviefile
& mv
)
145 for(auto& i
: mv
.branches
)
146 if(&i
.second
== mv
.input
)
147 input
= &branches
[i
.first
];
150 void moviefile::save(const std::string
& movie
, unsigned compression
, bool binary
, rrdata_set
& rrd
)
151 throw(std::bad_alloc
, std::runtime_error
)
154 if(rr
= regex("\\$MEMORY:(.*)", movie
)) {
155 auto tmp
= new moviefile();
157 tmp
->copy_fields(*this);
158 memory_saves
[rr
[1]] = tmp
;
166 std::string tmp
= movie
+ ".tmp";
167 std::ofstream
strm(tmp
.c_str(), std::ios_base::binary
);
169 throw std::runtime_error("Can't open output file");
170 char buf
[5] = {'l', 's', 'm', 'v', 0x1A};
173 throw std::runtime_error("Failed to write to output file");
174 binary_io(strm
, rrd
);
176 throw std::runtime_error("Failed to write to output file");
178 std::string backup
= movie
+ ".backup";
179 zip::rename_overwrite(movie
.c_str(), backup
.c_str());
180 if(zip::rename_overwrite(tmp
.c_str(), movie
.c_str()) < 0)
181 throw std::runtime_error("Can't rename '" + tmp
+ "' -> '" + movie
+ "'");
184 zip::writer
w(movie
, compression
);
188 void moviefile::save(std::ostream
& stream
, rrdata_set
& rrd
) throw(std::bad_alloc
, std::runtime_error
)
190 zip::writer
w(stream
, 0);
194 void moviefile::create_default_branch(port_type_set
& ports
)
198 //If there is a branch, it becomes default.
199 if(!branches
.empty()) {
200 input
= &(branches
.begin()->second
);
202 //Otherwise, just create a branch.
203 branches
[""].clear(ports
);
204 input
= &branches
[""];
208 uint64_t moviefile::get_frame_count() throw()
210 return input
->count_frames();
215 const int BLOCK_SECONDS
= 0;
216 const int BLOCK_FRAMES
= 1;
217 const int STEP_W
= 2;
218 const int STEP_N
= 3;
221 uint64_t moviefile::get_movie_length() throw()
223 uint64_t frames
= get_frame_count();
225 return 100000000ULL * frames
/ 6;
228 gametype
->fill_framerate_magic(_magic
);
229 uint64_t t
= _magic
[BLOCK_SECONDS
] * 1000000000ULL * (frames
/ _magic
[BLOCK_FRAMES
]);
230 frames
%= _magic
[BLOCK_FRAMES
];
231 t
+= frames
* _magic
[STEP_W
] + (frames
* _magic
[STEP_N
] / _magic
[BLOCK_FRAMES
]);
235 moviefile
*& moviefile::memref(const std::string
& slot
)
237 return memory_saves
[slot
];
240 void moviefile::copy_fields(const moviefile
& mv
)
242 force_corrupt
= mv
.force_corrupt
;
243 gametype
= mv
.gametype
;
244 settings
= mv
.settings
;
245 coreversion
= mv
.coreversion
;
246 gamename
= mv
.gamename
;
247 projectid
= mv
.projectid
;
248 rerecords
= mv
.rerecords
;
249 rerecords_mem
= mv
.rerecords_mem
;
250 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
251 romimg_sha256
[i
] = mv
.romimg_sha256
[i
];
252 romxml_sha256
[i
] = mv
.romxml_sha256
[i
];
253 namehint
[i
] = mv
.namehint
[i
];
255 authors
= mv
.authors
;
256 movie_sram
= mv
.movie_sram
;
257 ramcontent
= mv
.ramcontent
;
258 is_savestate
= mv
.is_savestate
;
260 savestate
= mv
.savestate
;
261 anchor_savestate
= mv
.anchor_savestate
;
262 host_memory
= mv
.host_memory
;
263 screenshot
= mv
.screenshot
;
264 save_frame
= mv
.save_frame
;
265 lagged_frames
= mv
.lagged_frames
;
266 pollcounters
= mv
.pollcounters
;
267 poll_flag
= mv
.poll_flag
;
268 c_rrdata
= mv
.c_rrdata
;
269 branches
= mv
.branches
;
271 //Copy the active branch.
272 input
= &branches
.begin()->second
;
273 for(auto& i
: branches
)
274 if(mv
.branches
.count(i
.first
) && &mv
.branches
.find(i
.first
)->second
== mv
.input
)
277 rtc_second
= mv
.rtc_second
;
278 rtc_subsecond
= mv
.rtc_subsecond
;
279 movie_rtc_second
= mv
.movie_rtc_second
;
280 movie_rtc_subsecond
= mv
.movie_rtc_subsecond
;
281 start_paused
= mv
.start_paused
;
282 lazy_project_create
= mv
.lazy_project_create
;
283 subtitles
= mv
.subtitles
;
284 active_macros
= mv
.active_macros
;
287 void moviefile::fork_branch(const std::string
& oldname
, const std::string
& newname
)
289 if(oldname
== newname
|| branches
.count(newname
))
291 branches
[newname
] = branches
[oldname
];
294 const std::string
& moviefile::current_branch()
296 for(auto& i
: branches
)
297 if(&i
.second
== input
)
299 static std::string tmp
;
303 moviefile::branch_extractor::~branch_extractor()
308 moviefile::branch_extractor::branch_extractor(const std::string
& filename
)
312 std::istream
& s
= zip::openrel(filename
, "");
315 if(!strcmp(buf
, "lsmv\x1A"))
320 real
= new moviefile_branch_extractor_binary(filename
);
322 real
= new moviefile_branch_extractor_text(filename
);