Fix some memory leak complaints from Valgrind
[lsnes.git] / src / core / moviefile.cpp
blobb30d1037d3ec0e2ea5be6f1ea10438ce90c093b1
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"
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <iostream>
16 #include <algorithm>
17 #include <sstream>
18 #include <boost/iostreams/copy.hpp>
19 #include <boost/iostreams/device/back_inserter.hpp>
20 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
21 #include <windows.h>
22 #endif
24 namespace
26 std::map<std::string, moviefile*> memory_saves;
29 moviefile::brief_info::brief_info(const std::string& filename)
31 regex_results rr;
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];
46 return;
49 std::istream& s = zip::openrel(filename, "");
50 char buf[6] = {0};
51 s.read(buf, 5);
52 if(!strcmp(buf, "lsmv\x1A")) {
53 binary_io(s);
54 delete &s;
55 return;
57 delete &s;
59 zip::reader r(filename);
60 load(r);
63 moviefile::moviefile() throw(std::bad_alloc)
65 static port_type_set dummy_types;
66 force_corrupt = false;
67 gametype = NULL;
68 input = NULL;
69 coreversion = "";
70 projectid = "";
71 rerecords = "0";
72 is_savestate = false;
73 movie_rtc_second = rtc_second = DEFAULT_RTC_SECOND;
74 movie_rtc_subsecond = rtc_subsecond = DEFAULT_RTC_SUBSECOND;
75 start_paused = false;
76 lazy_project_create = true;
77 poll_flag = 0;
80 moviefile::moviefile(loaded_rom& rom, std::map<std::string, std::string>& c_settings, uint64_t rtc_sec,
81 uint64_t rtc_subsec)
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);
88 rerecords = "0";
89 is_savestate = false;
90 movie_rtc_second = rtc_second = rtc_sec;
91 movie_rtc_subsecond = rtc_subsecond = rtc_subsec;
92 start_paused = false;
93 lazy_project_create = true;
94 poll_flag = 0;
95 settings = c_settings;
96 input = NULL;
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.
102 rerecords = "0";
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)
113 regex_results rr;
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]];
118 copy_fields(s);
119 return;
121 input = NULL;
122 poll_flag = false;
123 start_paused = false;
124 force_corrupt = false;
125 is_savestate = false;
126 lazy_project_create = false;
128 std::istream& s = zip::openrel(movie, "");
129 char buf[6] = {0};
130 s.read(buf, 5);
131 if(!strcmp(buf, "lsmv\x1A")) {
132 binary_io(s, romtype);
133 delete &s;
134 return;
136 delete &s;
138 zip::reader r(movie);
139 load(r, romtype);
142 void moviefile::fixup_current_branch(const moviefile& mv)
144 input = NULL;
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)
153 regex_results rr;
154 if(rr = regex("\\$MEMORY:(.*)", movie)) {
155 auto tmp = new moviefile();
156 try {
157 tmp->copy_fields(*this);
158 memory_saves[rr[1]] = tmp;
159 } catch(...) {
160 delete tmp;
161 throw;
163 return;
165 if(binary) {
166 std::string tmp = movie + ".tmp";
167 std::ofstream strm(tmp.c_str(), std::ios_base::binary);
168 if(!strm)
169 throw std::runtime_error("Can't open output file");
170 char buf[5] = {'l', 's', 'm', 'v', 0x1A};
171 strm.write(buf, 5);
172 if(!strm)
173 throw std::runtime_error("Failed to write to output file");
174 binary_io(strm, rrd);
175 if(!strm)
176 throw std::runtime_error("Failed to write to output file");
177 strm.close();
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 + "'");
182 return;
184 zip::writer w(movie, compression);
185 save(w, rrd);
188 void moviefile::save(std::ostream& stream, rrdata_set& rrd) throw(std::bad_alloc, std::runtime_error)
190 zip::writer w(stream, 0);
191 save(w, rrd);
194 void moviefile::create_default_branch(port_type_set& ports)
196 if(input)
197 return;
198 //If there is a branch, it becomes default.
199 if(!branches.empty()) {
200 input = &(branches.begin()->second);
201 } else {
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();
213 namespace
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();
224 if(!gametype) {
225 return 100000000ULL * frames / 6;
227 uint64_t _magic[4];
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]);
232 return t;
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;
259 sram = mv.sram;
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)
275 input = &i.second;
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))
290 return;
291 branches[newname] = branches[oldname];
294 const std::string& moviefile::current_branch()
296 for(auto& i : branches)
297 if(&i.second == input)
298 return i.first;
299 static std::string tmp;
300 return tmp;
303 moviefile::branch_extractor::~branch_extractor()
305 delete real;
308 moviefile::branch_extractor::branch_extractor(const std::string& filename)
310 bool binary = false;
312 std::istream& s = zip::openrel(filename, "");
313 char buf[6] = {0};
314 s.read(buf, 5);
315 if(!strcmp(buf, "lsmv\x1A"))
316 binary = true;
317 delete &s;
319 if(binary)
320 real = new moviefile_branch_extractor_binary(filename);
321 else
322 real = new moviefile_branch_extractor_text(filename);