Merge branch 'rr1-maint'
[lsnes.git] / src / util / lsnes-dumpavi.cpp
blob8466e764990a09e6258a8834ca56e024b90e9e89
1 #include "lsnes.hpp"
3 #include "core/advdumper.hpp"
4 #include "core/command.hpp"
5 #include "core/dispatch.hpp"
6 #include "core/mainloop.hpp"
7 #include "core/framerate.hpp"
8 #include "core/keymapper.hpp"
9 #include "interface/romtype.hpp"
10 #include "core/loadlib.hpp"
11 #include "lua/lua.hpp"
12 #include "core/mainloop.hpp"
13 #include "core/misc.hpp"
14 #include "core/moviedata.hpp"
15 #include "core/rom.hpp"
16 #include "core/rrdata.hpp"
17 #include "core/settings.hpp"
18 #include "core/window.hpp"
19 #include "library/string.hpp"
21 #include <sys/time.h>
22 #include <sstream>
24 namespace
26 class myavsnoop : public information_dispatch
28 public:
29 myavsnoop(adv_dumper& _dumper, uint64_t frames_to_dump)
30 : information_dispatch("myavsnoop-monitor"), dumper(_dumper)
32 frames_dumped = 0;
33 total = frames_to_dump;
36 ~myavsnoop() throw()
40 void on_frame(struct framebuffer_raw& _frame, uint32_t fps_n, uint32_t fps_d)
42 frames_dumped++;
43 if(frames_dumped % 100 == 0) {
44 std::cout << "Dumping frame " << frames_dumped << "/" << total << " ("
45 << (100 * frames_dumped / total) << "%)" << std::endl;
47 if(frames_dumped == total) {
48 //Rough way to end it.
49 dumper.end();
50 information_dispatch::do_dump_end();
51 exit(0);
55 void on_dump_end()
57 std::cout << "Finished!" << std::endl;
59 private:
60 uint64_t frames_dumped;
61 uint64_t total;
62 adv_dumper& dumper;
65 void dumper_startup(adv_dumper& dumper, const std::string& mode, const std::string& prefix, uint64_t length)
67 std::cout << "Invoking dumper" << std::endl;
68 try {
69 dumper.start(mode, prefix);
70 } catch(std::exception& e) {
71 std::cerr << "Can't start dumper: " << e.what() << std::endl;
72 exit(1);
74 if(information_dispatch::get_dumper_count()) {
75 std::cout << "Dumper attach confirmed" << std::endl;
76 } else {
77 std::cout << "Can't start dumper!" << std::endl;
78 exit(1);
80 new myavsnoop(dumper, length);
83 void startup_lua_scripts(const std::vector<std::string>& cmdline)
85 for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
86 std::string a = *i;
87 if(a.length() > 6 && a.substr(0, 6) == "--lua=") {
88 lsnes_cmd.invoke("run-lua " + a.substr(6));
93 struct adv_dumper& locate_dumper(const std::string& name)
95 adv_dumper* _dumper = NULL;
96 std::set<adv_dumper*> dumpers = adv_dumper::get_dumper_set();
97 for(auto i : dumpers)
98 if(i->id() == name)
99 _dumper = i;
100 if(!_dumper) {
101 std::cerr << "No such dumper '" << name << "' found (try --dumper=list)" << std::endl;
102 exit(1);
104 return *_dumper;
107 std::string format_details(unsigned detail)
109 std::string r;
110 if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_file)
111 r = r + "TARGET_FILE";
112 else if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_prefix)
113 r = r + "TARGET_PREFIX";
114 else if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_special)
115 r = r + "TARGET_SPECIAL";
116 else
117 r = r + "TARGET_UNKNOWN";
118 return r;
121 adv_dumper& get_dumper(const std::vector<std::string>& cmdline, std::string& mode, std::string& prefix,
122 uint64_t& length)
124 bool dumper_given = false;
125 std::string dumper;
126 bool mode_given = false;
127 prefix = "avidump";
128 length = 0;
129 for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
130 std::string a = *i;
131 if(a.length() >= 7 && a.substr(0, 7) == "--core=") {
132 preferred_core_default = a.substr(7);
133 } else if(a.length() >= 9 && a.substr(0, 9) == "--dumper=") {
134 dumper_given = true;
135 dumper = a.substr(9);
136 } else if(a.length() >= 7 && a.substr(0, 7) == "--mode=") {
137 mode_given = true;
138 mode = a.substr(7);
139 } else if(a.length() >= 9 && a.substr(0, 9) == "--prefix=")
140 prefix = a.substr(9);
141 else if(a.length() >= 9 && a.substr(0, 9) == "--length=")
142 try {
143 length = boost::lexical_cast<uint64_t>(a.substr(9));
144 if(!length)
145 throw std::runtime_error("Length out of range (1-)");
146 } catch(std::exception& e) {
147 std::cerr << "Bad --length: " << e.what() << std::endl;
148 exit(1);
150 else if(a.length() >= 9 && a.substr(0, 9) == "--option=") {
151 std::string nameval = a.substr(9);
152 size_t s = nameval.find_first_of("=");
153 if(s >= nameval.length()) {
154 std::cerr << "Invalid option syntax (expected --option=foo=bar)" << std::endl;
155 exit(1);
157 std::string name = nameval.substr(0, s);
158 std::string val = nameval.substr(s + 1);
159 try {
160 lsnes_vset[name].str(val);
161 } catch(std::exception& e) {
162 std::cerr << "Can't set '" << name << "' to '" << val << "': " << e.what()
163 << std::endl;
164 exit(1);
166 } else if(a.length() >= 12 && a.substr(0, 15) == "--load-library=")
167 try {
168 new loaded_library(a.substr(15));
169 handle_post_loadlibrary();
170 } catch(std::runtime_error& e) {
171 std::cerr << "Can't load '" << a.substr(15) << "': " << e.what() << std::endl;
172 exit(1);
175 if(preferred_core_default == "list") {
176 //Help on cores.
177 std::set<std::pair<std::string, std::string>> cores;
178 for(auto i : core_type::get_core_types())
179 cores.insert(std::make_pair(i->get_core_shortname(), i->get_core_identifier()));
180 for(auto i : cores)
181 std::cout << i.first << " -> " << i.second << std::endl;
182 exit(0);
184 if(dumper == "list") {
185 //Help on dumpers.
186 std::set<adv_dumper*> dumpers = adv_dumper::get_dumper_set();
187 std::cout << "Dumpers available:" << std::endl;
188 for(auto i : dumpers)
189 std::cout << i->id() << "\t" << i->name() << std::endl;
190 exit(0);
192 if(!dumper_given) {
193 std::cerr << "Dumper required (--dumper=foo)" << std::endl;
194 exit(1);
196 if(mode == "list") {
197 //Help on modes.
198 adv_dumper& _dumper = locate_dumper(dumper);
199 std::set<std::string> modes = _dumper.list_submodes();
200 if(modes.empty()) {
201 unsigned d = _dumper.mode_details("");
202 std::cout << "No modes available for " << dumper << " (" << format_details(d) << ")"
203 << std::endl;
204 exit(0);
206 std::cout << "Modes available for " << dumper << ":" << std::endl;
207 for(auto i : modes) {
208 unsigned d = _dumper.mode_details(i);
209 std::cout << i << "\t" << _dumper.modename(i) << "\t(" << format_details(d) << ")"
210 << std::endl;
212 exit(0);
214 adv_dumper& _dumper = locate_dumper(dumper);
215 if(!mode_given && !_dumper.list_submodes().empty()) {
216 std::cerr << "Mode required for this dumper" << std::endl;
217 exit(1);
219 if(mode_given && _dumper.list_submodes().empty()) {
220 std::cerr << "This dumper does not have mode select" << std::endl;
221 exit(1);
223 if(mode_given && !_dumper.list_submodes().count(mode)) {
224 std::cerr << "'" << mode << "' is not a valid mode for '" << dumper << "'" << std::endl;
225 exit(1);
227 if(!length) {
228 std::cerr << "--length=<frames> has to be specified" << std::endl;
229 exit(1);
231 return locate_dumper(dumper);
235 int main(int argc, char** argv)
237 reached_main();
238 std::vector<std::string> cmdline;
239 for(int i = 1; i < argc; i++)
240 cmdline.push_back(argv[i]);
241 uint64_t length;
242 std::string mode, prefix;
244 adv_dumper& dumper = get_dumper(cmdline, mode, prefix, length);
246 set_random_seed();
247 platform::init();
248 init_lua();
250 messages << "lsnes version: lsnes rr" << lsnes_version << std::endl;
251 messages << "Command line is: ";
252 for(auto k = cmdline.begin(); k != cmdline.end(); k++)
253 messages << "\"" << *k << "\" ";
254 messages << std::endl;
256 std::string cfgpath = get_config_path();
257 autoload_libraries();
259 for(auto i : cmdline) {
260 regex_results r;
261 if(r = regex("--firmware-path=(.*)", i)) {
262 try {
263 lsnes_vsetc.set("firmwarepath", r[1]);
264 std::cerr << "Set firmware path to '" << r[1] << "'" << std::endl;
265 } catch(std::exception& e) {
266 std::cerr << "Can't set firmware path to '" << r[1] << "': " << e.what() << std::endl;
269 if(r = regex("--setting-(.*)=(.*)", i)) {
270 try {
271 lsnes_vset[r[1]].str(r[2]);
272 std::cerr << "Set " << r[1] << " to '" << r[2] << "'" << std::endl;
273 } catch(std::exception& e) {
274 std::cerr << "Can't set " << r[1] << " to '" << r[2] << "': " << e.what() << std::endl;
279 messages << "--- Loading ROM ---" << std::endl;
280 struct loaded_rom r;
281 try {
282 std::map<std::string, std::string> tmp;
283 r = load_rom_from_commandline(cmdline);
284 r.load(tmp, 1000000000, 0);
285 messages << "Using core: " << r.rtype->get_core_identifier() << std::endl;
286 } catch(std::bad_alloc& e) {
287 OOM_panic();
288 } catch(std::exception& e) {
289 messages << "FATAL: Can't load ROM: " << e.what() << std::endl;
290 fatal_error();
291 exit(1);
293 messages << "Detected region: " << r.rtype->combine_region(*r.region).get_name() << std::endl;
294 set_nominal_framerate(r.region->approx_framerate());
296 messages << "--- Internal memory mappings ---" << std::endl;
297 dump_region_map();
298 messages << "--- End of Startup --- " << std::endl;
300 moviefile movie;
301 try {
302 bool tried = false;
303 bool loaded = false;
304 for(auto i = cmdline.begin(); i != cmdline.end(); i++)
305 if(i->length() > 0 && (*i)[0] != '-') {
306 try {
307 tried = true;
308 movie = moviefile(*i, *r.rtype);
309 loaded = true;
310 } catch(std::bad_alloc& e) {
311 OOM_panic();
312 } catch(std::exception& e) {
313 messages << "Error loading '" << *i << "': " << e.what() << std::endl;
316 if(!tried)
317 throw std::runtime_error("Specifying movie is required");
318 if(!loaded)
319 throw std::runtime_error("Can't load any of the movies specified");
320 //Load ROM before starting the dumper.
321 our_rom = &r;
322 messages << "Using core: " << our_rom->rtype->get_core_identifier() << std::endl;
323 our_rom->region = &movie.gametype->get_region();
324 our_rom->load(movie.settings, movie.movie_rtc_second, movie.movie_rtc_subsecond);
325 startup_lua_scripts(cmdline);
326 dumper_startup(dumper, mode, prefix, length);
327 main_loop(r, movie, true);
328 } catch(std::bad_alloc& e) {
329 OOM_panic();
330 } catch(std::exception& e) {
331 messages << "FATAL: " << e.what() << std::endl;
332 fatal_error();
333 return 1;
335 information_dispatch::do_dump_end();
336 rrdata::close();
337 quit_lua();
338 return 0;