Fix some memory leak complaints from Valgrind
[lsnes.git] / src / core / moviefile-text-load.cpp
blob790368feaba7937b01a11aca038b16b849f0957d
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, std::string> read_settings(zip::reader& r)
28 std::map<std::string, std::string> x;
29 for(auto i : r) {
30 if(!regex_match("port[0-9]+|setting\\..+", i))
31 continue;
32 std::string s;
33 std::string v;
34 if(i.substr(0, 4) == "port")
35 s = i;
36 else
37 s = i.substr(8);
38 if(r.read_linefile(i, v, true))
39 x[s] = v;
41 return x;
44 std::map<std::string, uint64_t> read_active_macros(zip::reader& r, const std::string& member)
46 std::map<std::string, uint64_t> x;
47 if(!r.has_member(member))
48 return x;
49 std::istream& m = r[member];
50 try {
51 while(m) {
52 std::string out;
53 std::getline(m, out);
54 istrip_CR(out);
55 if(out == "")
56 continue;
57 regex_results rx = regex("([0-9]+) +(.*)", out);
58 if(!rx) {
59 messages << "Warning: Bad macro state: '" << out << "'" << std::endl;
60 continue;
62 try {
63 uint64_t f = parse_value<uint64_t>(rx[1]);
64 x[rx[2]] = f;
65 } catch(...) {
68 delete &m;
69 } catch(...) {
70 delete &m;
71 throw;
73 return x;
76 template<typename T> std::string pick_a_name(const std::map<std::string, T>& map, bool prefer_unnamed)
78 if(prefer_unnamed && !map.count(""))
79 return "";
80 size_t count = 1;
81 while(true) {
82 std::string c = (stringfmt() << "(unnamed branch #" << count++ << ")").str();
83 if(!map.count(c))
84 return c;
88 void read_authors_file(zip::reader& r, std::vector<std::pair<std::string, std::string>>& authors)
89 throw(std::bad_alloc, std::runtime_error)
91 std::istream& m = r["authors"];
92 try {
93 std::string x;
94 while(std::getline(m, x)) {
95 istrip_CR(x);
96 auto g = split_author(x);
97 authors.push_back(g);
99 delete &m;
100 } catch(...) {
101 delete &m;
102 throw;
106 std::string read_rrdata(zip::reader& r, std::vector<char>& out) throw(std::bad_alloc, std::runtime_error)
108 r.read_raw_file("rrdata", out);
109 uint64_t count = rrdata_set::count(out);
110 std::ostringstream x;
111 x << count;
112 return x.str();
115 void read_subtitles(zip::reader& r, const std::string& file, std::map<moviefile_subtiming, std::string>& x)
117 x.clear();
118 if(!r.has_member(file))
119 return;
120 std::istream& m = r[file];
121 try {
122 while(m) {
123 std::string out;
124 std::getline(m, out);
125 istrip_CR(out);
126 auto r = regex("([0-9]+)[ \t]+([0-9]+)[ \t]+(.*)", out);
127 if(!r)
128 continue;
129 x[moviefile_subtiming(parse_value<uint64_t>(r[1]), parse_value<uint64_t>(r[2]))] =
130 s_unescape(r[3]);
132 delete &m;
133 } catch(...) {
134 delete &m;
135 throw;
139 void read_input(zip::reader& r, const std::string& mname, controller_frame_vector& input)
140 throw(std::bad_alloc, std::runtime_error)
142 controller_frame tmp = input.blank_frame(false);
143 std::istream& m = r[mname];
144 try {
145 std::string x;
146 while(std::getline(m, x)) {
147 istrip_CR(x);
148 if(x != "") {
149 tmp.deserialize(x.c_str());
150 input.append(tmp);
153 delete &m;
154 } catch(...) {
155 delete &m;
156 throw;
160 void read_pollcounters(zip::reader& r, const std::string& file, std::vector<uint32_t>& pctr)
162 std::istream& m = r[file];
163 try {
164 std::string x;
165 while(std::getline(m, x)) {
166 istrip_CR(x);
167 if(x != "") {
168 int32_t y = parse_value<int32_t>(x);
169 uint32_t z = 0;
170 if(y < 0)
171 z = -(y + 1);
172 else {
173 z = y;
174 z |= 0x80000000UL;
176 pctr.push_back(z);
179 delete &m;
180 } catch(...) {
181 delete &m;
182 throw;
186 std::string get_namefile(const std::string& input)
188 regex_results s;
189 if(input == "input")
190 return "branchname.0";
191 else if(s = regex("input\\.([1-9][0-9]*)", input))
192 return "branchname." + s[1];
193 else
194 return "";
198 void moviefile::brief_info::load(zip::reader& r)
200 std::string tmp;
201 r.read_linefile("systemid", tmp);
202 if(tmp.substr(0, 8) != "lsnes-rr")
203 throw std::runtime_error("Not lsnes movie");
204 r.read_linefile("gametype", sysregion);
205 r.read_linefile("coreversion", corename);
206 r.read_linefile("projectid", projectid);
207 if(r.has_member("savestate"))
208 r.read_numeric_file("saveframe", current_frame);
209 else
210 current_frame = 0;
211 r.read_numeric_file("rerecords", rerecords);
212 r.read_linefile("rom.sha256", hash[0], true);
213 r.read_linefile("romxml.sha256", hashxml[0], true);
214 r.read_linefile("rom.hint", hint[0], true);
215 unsigned base = 97;
216 if(r.has_member("slot`.sha256"))
217 base = 96;
218 for(size_t i = 1; i < ROM_SLOT_COUNT; i++) {
219 r.read_linefile((stringfmt() << "slot" << (char)(base + i - 1) << ".sha256").str(), hash[i],
220 true);
221 r.read_linefile((stringfmt() << "slot" << (char)(base + i - 1) << "xml.sha256").str(),
222 hashxml[i], true);
223 r.read_linefile((stringfmt() << "slot" << (char)(base + i - 1) << ".hint").str(), hint[i],
224 true);
228 void moviefile::load(zip::reader& r, core_type& romtype) throw(std::bad_alloc, std::runtime_error)
230 std::string tmp;
231 r.read_linefile("systemid", tmp);
232 if(tmp.substr(0, 8) != "lsnes-rr")
233 throw std::runtime_error("Not lsnes movie");
234 r.read_linefile("controlsversion", tmp);
235 if(tmp != "0")
236 throw std::runtime_error("Can't decode movie data");
237 r.read_linefile("gametype", tmp);
238 try {
239 gametype = &romtype.lookup_sysregion(tmp);
240 } catch(std::bad_alloc& e) {
241 throw;
242 } catch(std::exception& e) {
243 throw std::runtime_error("Illegal game type '" + tmp + "'");
245 settings = read_settings(r);
246 auto ctrldata = gametype->get_type().controllerconfig(settings);
247 port_type_set& ports = port_type_set::make(ctrldata.ports, ctrldata.portindex());
249 branches.clear();
250 r.read_linefile("gamename", gamename, true);
251 r.read_linefile("projectid", projectid);
252 rerecords = read_rrdata(r, c_rrdata);
253 r.read_linefile("coreversion", coreversion);
254 r.read_linefile("rom.sha256", romimg_sha256[0], true);
255 r.read_linefile("romxml.sha256", romxml_sha256[0], true);
256 r.read_linefile("rom.hint", namehint[0], true);
257 unsigned base = 97;
258 if(r.has_member("slot`.sha256"))
259 base = 96;
260 for(size_t i = 1; i < ROM_SLOT_COUNT; i++) {
261 r.read_linefile((stringfmt() << "slot" << (char)(base + i - 1) << ".sha256").str(), romimg_sha256[i],
262 true);
263 r.read_linefile((stringfmt() << "slot" << (char)(base + i - 1) << "xml.sha256").str(),
264 romxml_sha256[i], true);
265 r.read_linefile((stringfmt() << "slot" << (char)(base + i - 1) << ".hint").str(), namehint[i],
266 true);
268 read_subtitles(r, "subtitles", subtitles);
269 movie_rtc_second = DEFAULT_RTC_SECOND;
270 movie_rtc_subsecond = DEFAULT_RTC_SUBSECOND;
271 r.read_numeric_file("starttime.second", movie_rtc_second, true);
272 r.read_numeric_file("starttime.subsecond", movie_rtc_subsecond, true);
273 rtc_second = movie_rtc_second;
274 rtc_subsecond = movie_rtc_subsecond;
275 if(r.has_member("savestate.anchor"))
276 r.read_raw_file("savestate.anchor", anchor_savestate);
277 if(r.has_member("savestate")) {
278 is_savestate = true;
279 r.read_numeric_file("saveframe", save_frame, true);
280 r.read_numeric_file("lagcounter", lagged_frames, true);
281 read_pollcounters(r, "pollcounters", pollcounters);
282 if(r.has_member("hostmemory"))
283 r.read_raw_file("hostmemory", host_memory);
284 r.read_raw_file("savestate", savestate);
285 for(auto name : r)
286 if(name.length() >= 5 && name.substr(0, 5) == "sram.")
287 r.read_raw_file(name, sram[name.substr(5)]);
288 r.read_raw_file("screenshot", screenshot);
289 //If these can't be read, just use some (wrong) values.
290 r.read_numeric_file("savetime.second", rtc_second, true);
291 r.read_numeric_file("savetime.subsecond", rtc_subsecond, true);
292 uint64_t _poll_flag = 2; //Legacy behaviour is the default.
293 r.read_numeric_file("pollflag", _poll_flag, true);
294 poll_flag = _poll_flag;
295 active_macros = read_active_macros(r, "macros");
297 for(auto name : r)
298 if(name.length() >= 8 && name.substr(0, 8) == "initram.")
299 r.read_raw_file(name, ramcontent[name.substr(8)]);
300 if(rtc_subsecond < 0 || movie_rtc_subsecond < 0)
301 throw std::runtime_error("Invalid RTC subsecond value");
302 std::string name = r.find_first();
303 for(auto name : r)
304 if(name.length() >= 10 && name.substr(0, 10) == "moviesram.")
305 r.read_raw_file(name, movie_sram[name.substr(10)]);
306 read_authors_file(r, authors);
308 std::map<uint64_t, std::string> branch_table;
309 //Load branch names.
310 for(auto name : r) {
311 regex_results s;
312 if(s = regex("branchname\\.([0-9]+)", name)) {
313 uint64_t n = parse_value<uint64_t>(s[1]);
314 r.read_linefile(name, branch_table[n]);
315 branches[branch_table[n]].clear(ports);
319 for(auto name : r) {
320 regex_results s;
321 if(name == "input") {
322 std::string bname = branch_table.count(0) ? branch_table[0] : pick_a_name(branches, true);
323 if(!branches.count(bname)) branches[bname].clear(ports);
324 read_input(r, name, branches[bname]);
325 input = &branches[bname];
326 } else if(s = regex("input\\.([1-9][0-9]*)", name)) {
327 uint64_t n = parse_value<uint64_t>(s[1]);
328 std::string bname = branch_table.count(n) ? branch_table[n] : pick_a_name(branches, false);
329 if(!branches.count(bname)) branches[bname].clear(ports);
330 read_input(r, name, branches[bname]);
334 create_default_branch(ports);
337 moviefile_branch_extractor_text::moviefile_branch_extractor_text(const std::string& filename)
338 : z(filename)
342 moviefile_branch_extractor_text::~moviefile_branch_extractor_text()
346 std::set<std::string> moviefile_branch_extractor_text::enumerate()
348 std::set<std::string> r;
349 for(auto& i : z) {
350 std::string bname;
351 std::string n = get_namefile(i);
352 if(n != "") {
353 if(z.has_member(n)) {
354 z.read_linefile(n, bname);
355 r.insert(bname);
356 } else
357 r.insert("");
360 return r;
363 void moviefile_branch_extractor_text::read(const std::string& name, controller_frame_vector& v)
365 std::set<std::string> r;
366 bool done = false;
367 for(auto& i : z) {
368 std::string bname;
369 std::string n = get_namefile(i);
370 if(n != "") {
371 std::string bname;
372 if(z.has_member(n))
373 z.read_linefile(n, bname);
374 if(name == bname) {
375 v.clear();
376 read_input(z, i, v);
377 done = true;
381 if(!done)
382 (stringfmt() << "Can't find branch '" << name << "' in file.").throwex();