Fix some memory leak complaints from Valgrind
[lsnes.git] / src / util / lsnes-dumpavi.cpp
blob25187b3e0b59335300efafcef5a9c4b7cb23dac3
1 #include "lsnes.hpp"
3 #include "core/advdumper.hpp"
4 #include "core/controller.hpp"
5 #include "core/command.hpp"
6 #include "core/dispatch.hpp"
7 #include "core/mainloop.hpp"
8 #include "core/framerate.hpp"
9 #include "core/keymapper.hpp"
10 #include "interface/romtype.hpp"
11 #include "core/loadlib.hpp"
12 #include "lua/lua.hpp"
13 #include "core/mainloop.hpp"
14 #include "core/misc.hpp"
15 #include "core/movie.hpp"
16 #include "core/moviedata.hpp"
17 #include "core/rom.hpp"
18 #include "core/romloader.hpp"
19 #include "core/settings.hpp"
20 #include "core/window.hpp"
21 #include "library/string.hpp"
23 #include <sys/time.h>
24 #include <sstream>
26 namespace
28 bool hashing_in_progress = false;
29 uint64_t hashing_left = 0;
30 int64_t last_update = 0;
32 void hash_callback(uint64_t left, uint64_t total)
34 if(left == 0xFFFFFFFFFFFFFFFFULL) {
35 hashing_in_progress = false;
36 std::cout << "Done." << std::endl;
37 last_update = get_utime() - 2000000;
38 return;
40 if(!hashing_in_progress) {
41 std::cout << "Hashing disc images..." << std::flush;
43 hashing_in_progress = true;
44 hashing_left = left;
45 uint64_t this_update = get_utime();
46 if(this_update < last_update - 1000000 || this_update > last_update + 1000000) {
47 std::cout << ((hashing_left + 524288) >> 20) << "..." << std::flush;
48 last_update = this_update;
52 class myavsnoop : public information_dispatch
54 public:
55 myavsnoop(adv_dumper& _dumper, uint64_t frames_to_dump)
56 : information_dispatch("myavsnoop-monitor"), dumper(_dumper)
58 frames_dumped = 0;
59 total = frames_to_dump;
62 ~myavsnoop() throw()
66 void on_frame(struct framebuffer::raw& _frame, uint32_t fps_n, uint32_t fps_d)
68 frames_dumped++;
69 if(frames_dumped % 100 == 0) {
70 std::cout << "Dumping frame " << frames_dumped << "/" << total << " ("
71 << (100 * frames_dumped / total) << "%)" << std::endl;
73 if(frames_dumped == total) {
74 //Rough way to end it.
75 dumper.end();
76 information_dispatch::do_dump_end();
77 exit(0);
81 void on_dump_end()
83 std::cout << "Finished!" << std::endl;
85 private:
86 uint64_t frames_dumped;
87 uint64_t total;
88 adv_dumper& dumper;
91 void dumper_startup(adv_dumper& dumper, const std::string& mode, const std::string& prefix, uint64_t length)
93 std::cout << "Invoking dumper" << std::endl;
94 try {
95 dumper.start(mode, prefix);
96 } catch(std::exception& e) {
97 std::cerr << "Can't start dumper: " << e.what() << std::endl;
98 exit(1);
100 if(information_dispatch::get_dumper_count()) {
101 std::cout << "Dumper attach confirmed" << std::endl;
102 } else {
103 std::cout << "Can't start dumper!" << std::endl;
104 exit(1);
106 new myavsnoop(dumper, length);
109 void startup_lua_scripts(const std::vector<std::string>& cmdline)
111 for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
112 std::string a = *i;
113 if(a.length() > 6 && a.substr(0, 6) == "--lua=") {
114 lua_add_startup_script(a.substr(6));
119 struct adv_dumper& locate_dumper(const std::string& name)
121 adv_dumper* _dumper = NULL;
122 std::set<adv_dumper*> dumpers = adv_dumper::get_dumper_set();
123 for(auto i : dumpers)
124 if(i->id() == name)
125 _dumper = i;
126 if(!_dumper) {
127 std::cerr << "No such dumper '" << name << "' found (try --dumper=list)" << std::endl;
128 exit(1);
130 return *_dumper;
133 std::string format_details(unsigned detail)
135 std::string r;
136 if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_file)
137 r = r + "TARGET_FILE";
138 else if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_prefix)
139 r = r + "TARGET_PREFIX";
140 else if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_special)
141 r = r + "TARGET_SPECIAL";
142 else
143 r = r + "TARGET_UNKNOWN";
144 return r;
147 adv_dumper& get_dumper(const std::vector<std::string>& cmdline, std::string& mode, std::string& prefix,
148 uint64_t& length, bool& overdump_mode, uint64_t& overdump_length)
150 bool dumper_given = false;
151 std::string dumper;
152 bool mode_given = false;
153 prefix = "avidump";
154 length = 0;
155 overdump_mode = false;
156 overdump_length = 0;
157 for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
158 std::string a = *i;
159 if(a == "--core=list") {
160 preferred_core_default = "list";
161 } else if(a.length() >= 9 && a.substr(0, 9) == "--dumper=") {
162 dumper_given = true;
163 dumper = a.substr(9);
164 } else if(a.length() >= 7 && a.substr(0, 7) == "--mode=") {
165 mode_given = true;
166 mode = a.substr(7);
167 } else if(a.length() >= 9 && a.substr(0, 9) == "--prefix=")
168 prefix = a.substr(9);
169 else if(a.length() >= 9 && a.substr(0, 9) == "--length=")
170 try {
171 length = boost::lexical_cast<uint64_t>(a.substr(9));
172 if(!length)
173 throw std::runtime_error("Length out of range (1-)");
174 if(overdump_mode)
175 throw std::runtime_error("--length and --overdump-length are "
176 "mutually exclusive.");
177 } catch(std::exception& e) {
178 std::cerr << "Bad --length: " << e.what() << std::endl;
179 exit(1);
181 else if(a.length() >= 18 && a.substr(0, 18) == "--overdump-length=")
182 try {
183 overdump_length = boost::lexical_cast<uint64_t>(a.substr(18));
184 overdump_mode = true;
185 if(length)
186 throw std::runtime_error("--length and --overdump-length are "
187 "mutually exclusive.");
188 } catch(std::exception& e) {
189 std::cerr << "Bad --overdump-length: " << e.what() << std::endl;
190 exit(1);
192 else if(a.length() >= 9 && a.substr(0, 9) == "--option=") {
193 std::string nameval = a.substr(9);
194 size_t s = nameval.find_first_of("=");
195 if(s >= nameval.length()) {
196 std::cerr << "Invalid option syntax (expected --option=foo=bar)" << std::endl;
197 exit(1);
199 std::string name = nameval.substr(0, s);
200 std::string val = nameval.substr(s + 1);
201 try {
202 lsnes_vset[name].str(val);
203 } catch(std::exception& e) {
204 std::cerr << "Can't set '" << name << "' to '" << val << "': " << e.what()
205 << std::endl;
206 exit(1);
208 } else if(a.length() >= 12 && a.substr(0, 15) == "--load-library=")
209 try {
210 with_loaded_library(*new loadlib::module(loadlib::library(a.substr(15))));
211 handle_post_loadlibrary();
212 } catch(std::runtime_error& e) {
213 std::cerr << "Can't load '" << a.substr(15) << "': " << e.what() << std::endl;
214 exit(1);
217 if(preferred_core_default == "list") {
218 //Help on cores.
219 std::set<std::pair<std::string, std::string>> cores;
220 for(auto i : core_type::get_core_types())
221 cores.insert(std::make_pair(i->get_core_shortname(), i->get_core_identifier()));
222 for(auto i : cores)
223 std::cout << i.first << " -> " << i.second << std::endl;
224 exit(0);
226 if(dumper == "list") {
227 //Help on dumpers.
228 std::set<adv_dumper*> dumpers = adv_dumper::get_dumper_set();
229 std::cout << "Dumpers available:" << std::endl;
230 for(auto i : dumpers)
231 std::cout << i->id() << "\t" << i->name() << std::endl;
232 exit(0);
234 if(!dumper_given) {
235 std::cerr << "Dumper required (--dumper=foo)" << std::endl;
236 exit(1);
238 if(mode == "list") {
239 //Help on modes.
240 adv_dumper& _dumper = locate_dumper(dumper);
241 std::set<std::string> modes = _dumper.list_submodes();
242 if(modes.empty()) {
243 unsigned d = _dumper.mode_details("");
244 std::cout << "No modes available for " << dumper << " (" << format_details(d) << ")"
245 << std::endl;
246 exit(0);
248 std::cout << "Modes available for " << dumper << ":" << std::endl;
249 for(auto i : modes) {
250 unsigned d = _dumper.mode_details(i);
251 std::cout << i << "\t" << _dumper.modename(i) << "\t(" << format_details(d) << ")"
252 << std::endl;
254 exit(0);
256 adv_dumper& _dumper = locate_dumper(dumper);
257 if(!mode_given && !_dumper.list_submodes().empty()) {
258 std::cerr << "Mode required for this dumper" << std::endl;
259 exit(1);
261 if(mode_given && _dumper.list_submodes().empty()) {
262 std::cerr << "This dumper does not have mode select" << std::endl;
263 exit(1);
265 if(mode_given && !_dumper.list_submodes().count(mode)) {
266 std::cerr << "'" << mode << "' is not a valid mode for '" << dumper << "'" << std::endl;
267 exit(1);
269 if(!length && !overdump_mode) {
270 std::cerr << "--length=<frames> or --overdump-length=<frames> has to be specified"
271 << std::endl;
272 exit(1);
274 return locate_dumper(dumper);
278 int main(int argc, char** argv)
280 reached_main();
281 std::vector<std::string> cmdline;
282 for(int i = 1; i < argc; i++)
283 cmdline.push_back(argv[i]);
284 uint64_t length, overdump_length;
285 bool overdump_mode;
286 std::string mode, prefix;
288 adv_dumper& dumper = get_dumper(cmdline, mode, prefix, length, overdump_mode, overdump_length);
290 set_random_seed();
291 platform::init();
292 init_lua();
294 messages << "lsnes version: lsnes rr" << lsnes_version << std::endl;
295 messages << "Command line is: ";
296 for(auto k = cmdline.begin(); k != cmdline.end(); k++)
297 messages << "\"" << *k << "\" ";
298 messages << std::endl;
300 std::string cfgpath = get_config_path();
301 autoload_libraries();
303 for(auto i : cmdline) {
304 regex_results r;
305 if(r = regex("--firmware-path=(.*)", i)) {
306 try {
307 lsnes_vsetc.set("firmwarepath", r[1]);
308 std::cerr << "Set firmware path to '" << r[1] << "'" << std::endl;
309 } catch(std::exception& e) {
310 std::cerr << "Can't set firmware path to '" << r[1] << "': " << e.what() << std::endl;
313 if(r = regex("--setting-(.*)=(.*)", i)) {
314 try {
315 lsnes_vset[r[1]].str(r[2]);
316 std::cerr << "Set " << r[1] << " to '" << r[2] << "'" << std::endl;
317 } catch(std::exception& e) {
318 std::cerr << "Can't set " << r[1] << " to '" << r[2] << "': " << e.what()
319 << std::endl;
324 set_hasher_callback(hash_callback);
326 std::string movfn;
327 for(auto i : cmdline) {
328 if(i.length() > 0 && i[0] != '-') {
329 movfn = i;
332 if(movfn == "") {
333 messages << "Movie filename required" << std::endl;
334 return 0;
337 init_main_callbacks();
338 messages << "--- Loading ROM ---" << std::endl;
339 struct loaded_rom r;
340 try {
341 std::map<std::string, std::string> tmp;
342 r = construct_rom(movfn, cmdline);
343 r.load(tmp, 1000000000, 0);
344 messages << "Using core: " << r.rtype->get_core_identifier() << std::endl;
345 } catch(std::bad_alloc& e) {
346 OOM_panic();
347 } catch(std::exception& e) {
348 messages << "FATAL: Can't load ROM: " << e.what() << std::endl;
349 quit_lua();
350 fatal_error();
351 exit(1);
353 messages << "Detected region: " << r.rtype->combine_region(*r.region).get_name() << std::endl;
354 set_nominal_framerate(r.region->approx_framerate());
356 messages << "--- End of Startup --- " << std::endl;
358 moviefile* movie;
359 try {
360 movie = new moviefile(movfn, *r.rtype);
361 //Load ROM before starting the dumper.
362 our_rom = r;
363 messages << "Using core: " << our_rom.rtype->get_core_identifier() << std::endl;
364 our_rom.region = &movie->gametype->get_region();
365 our_rom.load(movie->settings, movie->movie_rtc_second, movie->movie_rtc_subsecond);
366 startup_lua_scripts(cmdline);
367 if(overdump_mode)
368 length = overdump_length + movie->get_frame_count();
369 dumper_startup(dumper, mode, prefix, length);
370 main_loop(r, *movie, true);
371 } catch(std::bad_alloc& e) {
372 OOM_panic();
373 } catch(std::exception& e) {
374 messages << "FATAL: " << e.what() << std::endl;
375 quit_lua();
376 fatal_error();
377 return 1;
379 information_dispatch::do_dump_end();
380 quit_lua();
381 movb.release_memory();
382 kill_alias_binds();
383 cleanup_all_keys();
384 return 0;