Handle conflicting sysregions
[lsnes.git] / src / util / lsnes-dumpavi.cpp
blobed20468f218097b100a74519cc673fb3edc1e8ed
1 #include "lsnes.hpp"
3 #include "core/advdumper.hpp"
4 #include "core/command.hpp"
5 #include "core/dispatch.hpp"
6 #include "core/framerate.hpp"
7 #include "core/keymapper.hpp"
8 #include "interface/romtype.hpp"
9 #include "core/loadlib.hpp"
10 #include "lua/lua.hpp"
11 #include "core/mainloop.hpp"
12 #include "core/misc.hpp"
13 #include "core/moviedata.hpp"
14 #include "core/rom.hpp"
15 #include "core/rrdata.hpp"
16 #include "core/settings.hpp"
17 #include "core/window.hpp"
18 #include "library/string.hpp"
20 #include <sys/time.h>
21 #include <sstream>
23 namespace
25 class myavsnoop : public information_dispatch
27 public:
28 myavsnoop(adv_dumper& _dumper, uint64_t frames_to_dump)
29 : information_dispatch("myavsnoop-monitor"), dumper(_dumper)
31 frames_dumped = 0;
32 total = frames_to_dump;
35 ~myavsnoop()
39 void on_frame(struct framebuffer_raw& _frame, uint32_t fps_n, uint32_t fps_d)
41 frames_dumped++;
42 if(frames_dumped % 100 == 0) {
43 std::cout << "Dumping frame " << frames_dumped << "/" << total << " ("
44 << (100 * frames_dumped / total) << "%)" << std::endl;
46 if(frames_dumped == total) {
47 //Rough way to end it.
48 dumper.end();
49 information_dispatch::do_dump_end();
50 exit(0);
54 void on_dump_end()
56 std::cout << "Finished!" << std::endl;
58 private:
59 uint64_t frames_dumped;
60 uint64_t total;
61 adv_dumper& dumper;
64 void dumper_startup(adv_dumper& dumper, const std::string& mode, const std::string& prefix, uint64_t length)
66 std::cout << "Invoking dumper" << std::endl;
67 try {
68 dumper.start(mode, prefix);
69 } catch(std::exception& e) {
70 std::cerr << "Can't start dumper: " << e.what() << std::endl;
71 exit(1);
73 if(information_dispatch::get_dumper_count()) {
74 std::cout << "Dumper attach confirmed" << std::endl;
75 } else {
76 std::cout << "Can't start dumper!" << std::endl;
77 exit(1);
79 myavsnoop* s = new myavsnoop(dumper, length);
82 void startup_lua_scripts(const std::vector<std::string>& cmdline)
84 for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
85 std::string a = *i;
86 if(a.length() > 6 && a.substr(0, 6) == "--lua=") {
87 lsnes_cmd.invoke("run-lua " + a.substr(6));
92 struct adv_dumper& locate_dumper(const std::string& name)
94 adv_dumper* _dumper = NULL;
95 std::set<adv_dumper*> dumpers = adv_dumper::get_dumper_set();
96 for(auto i : dumpers)
97 if(i->id() == name)
98 _dumper = i;
99 if(!_dumper) {
100 std::cerr << "No such dumper '" << name << "' found (try --dumper=list)" << std::endl;
101 exit(1);
103 return *_dumper;
106 std::string format_details(unsigned detail)
108 std::string r;
109 if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_file)
110 r = r + "TARGET_FILE";
111 else if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_prefix)
112 r = r + "TARGET_PREFIX";
113 else if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_special)
114 r = r + "TARGET_SPECIAL";
115 else
116 r = r + "TARGET_UNKNOWN";
117 return r;
120 adv_dumper& get_dumper(const std::vector<std::string>& cmdline, std::string& mode, std::string& prefix,
121 uint64_t& length)
123 bool dumper_given = false;
124 std::string dumper;
125 bool mode_given = false;
126 bool length_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() >= 9 && a.substr(0, 9) == "--dumper=") {
132 dumper_given = true;
133 dumper = a.substr(9);
134 } else if(a.length() >= 7 && a.substr(0, 7) == "--mode=") {
135 mode_given = true;
136 mode = a.substr(7);
137 } else if(a.length() >= 9 && a.substr(0, 9) == "--prefix=")
138 prefix = a.substr(9);
139 else if(a.length() >= 9 && a.substr(0, 9) == "--length=")
140 try {
141 length = boost::lexical_cast<uint64_t>(a.substr(9));
142 if(!length)
143 throw std::runtime_error("Length out of range (1-)");
144 } catch(std::exception& e) {
145 std::cerr << "Bad --length: " << e.what() << std::endl;
146 exit(1);
148 else if(a.length() >= 9 && a.substr(0, 9) == "--option=") {
149 std::string nameval = a.substr(9);
150 size_t s = nameval.find_first_of("=");
151 if(s >= nameval.length()) {
152 std::cerr << "Invalid option syntax (expected --option=foo=bar)" << std::endl;
153 exit(1);
155 std::string name = nameval.substr(0, s);
156 std::string val = nameval.substr(s + 1);
157 try {
158 lsnes_set.set(name, val);
159 } catch(std::exception& e) {
160 std::cerr << "Can't set '" << name << "' to '" << val << "': " << e.what()
161 << std::endl;
162 exit(1);
164 } else if(a.length() >= 12 && a.substr(0, 15) == "--load-library=")
165 try {
166 new loaded_library(a.substr(15));
167 handle_post_loadlibrary();
168 } catch(std::runtime_error& e) {
169 std::cerr << "Can't load '" << a.substr(15) << "': " << e.what() << std::endl;
170 exit(1);
173 if(dumper == "list") {
174 //Help on dumpers.
175 std::set<adv_dumper*> dumpers = adv_dumper::get_dumper_set();
176 std::cout << "Dumpers available:" << std::endl;
177 for(auto i : dumpers)
178 std::cout << i->id() << "\t" << i->name() << std::endl;
179 exit(0);
181 if(!dumper_given) {
182 std::cerr << "Dumper required (--dumper=foo)" << std::endl;
183 exit(1);
185 if(mode == "list") {
186 //Help on modes.
187 adv_dumper& _dumper = locate_dumper(dumper);
188 std::set<std::string> modes = _dumper.list_submodes();
189 if(modes.empty()) {
190 unsigned d = _dumper.mode_details("");
191 std::cout << "No modes available for " << dumper << " (" << format_details(d) << ")"
192 << std::endl;
193 exit(0);
195 std::cout << "Modes available for " << dumper << ":" << std::endl;
196 for(auto i : modes) {
197 unsigned d = _dumper.mode_details(i);
198 std::cout << i << "\t" << _dumper.modename(i) << "\t(" << format_details(d) << ")"
199 << std::endl;
201 exit(0);
203 adv_dumper& _dumper = locate_dumper(dumper);
204 if(!mode_given && !_dumper.list_submodes().empty()) {
205 std::cerr << "Mode required for this dumper" << std::endl;
206 exit(1);
208 if(mode_given && _dumper.list_submodes().empty()) {
209 std::cerr << "This dumper does not have mode select" << std::endl;
210 exit(1);
212 if(mode_given && !_dumper.list_submodes().count(mode)) {
213 std::cerr << "'" << mode << "' is not a valid mode for '" << dumper << "'" << std::endl;
214 exit(1);
216 if(!length) {
217 std::cerr << "--length=<frames> has to be specified" << std::endl;
218 exit(1);
220 return locate_dumper(dumper);
224 int main(int argc, char** argv)
226 reached_main();
227 std::vector<std::string> cmdline;
228 for(int i = 1; i < argc; i++)
229 cmdline.push_back(argv[i]);
230 uint64_t length;
231 std::string mode, prefix;
233 adv_dumper& dumper = get_dumper(cmdline, mode, prefix, length);
235 set_random_seed();
236 platform::init();
237 init_lua();
239 messages << "lsnes version: lsnes rr" << lsnes_version << std::endl;
240 messages << "Command line is: ";
241 for(auto k = cmdline.begin(); k != cmdline.end(); k++)
242 messages << "\"" << *k << "\" ";
243 messages << std::endl;
245 std::string cfgpath = get_config_path();
246 autoload_libraries();
248 for(auto i : cmdline) {
249 regex_results r;
250 if(r = regex("--setting-(.*)=(.*)", i)) {
251 try {
252 lsnes_set.set(r[1], r[2]);
253 std::cerr << "Set " << r[1] << " to '" << r[2] << "'" << std::endl;
254 } catch(std::exception& e) {
255 std::cerr << "Can't set " << r[1] << " to '" << r[2] << "': " << e.what() << std::endl;
258 if(r = regex("--clear-setting-(.*)", i)) {
259 try {
260 lsnes_set.blank(r[1]);
261 std::cerr << "Blanked " << r[1] << std::endl;
262 } catch(std::exception& e) {
263 std::cerr << "Can't blank " << r[1] << ": " << e.what() << std::endl;
268 messages << "--- Loading ROM ---" << std::endl;
269 struct loaded_rom r;
270 try {
271 std::map<std::string, std::string> tmp;
272 r = load_rom_from_commandline(cmdline);
273 r.load(tmp, 1000000000, 0);
274 messages << "Using core: " << r.rtype->get_core_identifier() << std::endl;
275 } catch(std::bad_alloc& e) {
276 OOM_panic();
277 } catch(std::exception& e) {
278 messages << "FATAL: Can't load ROM: " << e.what() << std::endl;
279 fatal_error();
280 exit(1);
282 messages << "Detected region: " << r.rtype->combine_region(*r.region).get_name() << std::endl;
283 set_nominal_framerate(r.region->approx_framerate());
285 messages << "--- Internal memory mappings ---" << std::endl;
286 dump_region_map();
287 messages << "--- End of Startup --- " << std::endl;
289 moviefile movie;
290 try {
291 bool tried = false;
292 bool loaded = false;
293 for(auto i = cmdline.begin(); i != cmdline.end(); i++)
294 if(i->length() > 0 && (*i)[0] != '-') {
295 try {
296 tried = true;
297 movie = moviefile(*i, *r.rtype);
298 loaded = true;
299 } catch(std::bad_alloc& e) {
300 OOM_panic();
301 } catch(std::exception& e) {
302 messages << "Error loading '" << *i << "': " << e.what() << std::endl;
305 if(!tried)
306 throw std::runtime_error("Specifying movie is required");
307 if(!loaded)
308 throw std::runtime_error("Can't load any of the movies specified");
309 //Load ROM before starting the dumper.
310 our_rom = &r;
311 messages << "Using core: " << our_rom->rtype->get_core_identifier() << std::endl;
312 our_rom->region = &movie.gametype->get_region();
313 our_rom->load(movie.settings, movie.movie_rtc_second, movie.movie_rtc_subsecond);
314 startup_lua_scripts(cmdline);
315 dumper_startup(dumper, mode, prefix, length);
316 main_loop(r, movie, true);
317 } catch(std::bad_alloc& e) {
318 OOM_panic();
319 } catch(std::exception& e) {
320 messages << "FATAL: " << e.what() << std::endl;
321 fatal_error();
322 return 1;
324 information_dispatch::do_dump_end();
325 rrdata::close();
326 quit_lua();
327 return 0;