1 // Hyperbolic Rogue -- commandline options
2 // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
3 
4 /** \file commandline.cpp
5  *  \brief Commandline options support
6  */
7 
8 #include "hyper.h"
9 namespace hr {
10 
11 #ifdef RESOURCEDESTDIR
12 EX string rsrcdir = RESOURCEDESTDIR;
13 #endif
14 
15 #ifndef RESOURCEDESTDIR
16 EX string rsrcdir = "";
17 #endif
18 
19 #if CAP_COMMANDLINE
20 EX const char *scorefile = "hyperrogue.log";
21 
22 EX namespace arg {
readland(const string & ss)23 EX eLand readland(const string& ss) {
24   if(ss == "II") return laCrossroads2;
25   if(ss == "III") return laCrossroads3;
26   if(ss == "IV") return laCrossroads4;
27   if(ss == "V") return laCrossroads5;
28   for(int l=0; l<landtypes; l++) if(appears(linf[l].name, ss)) {
29     return eLand(l);
30     break;
31     }
32   return laNone;
33   }
34 
readItem(const string & ss)35 EX eItem readItem(const string& ss) {
36   for(int i=0; i<ittypes; i++) if(appears(iinf[i].name, ss)) {
37     return eItem(i);
38     break;
39     }
40   return itNone;
41   }
42 
readMonster(const string & ss)43 EX eMonster readMonster(const string& ss) {
44   if(ss == "Pike") return moPike;
45   for(int i=0; i<motypes; i++) if(appears(minf[i].name, ss)) {
46     return eMonster(i);
47     break;
48     }
49   return moNone;
50   }
51 EX }
52 
initializeCLI()53 EX void initializeCLI() {
54   printf("HyperRogue by Zeno Rogue <zeno@attnam.com>, version " VER "\n");
55 
56 #if !NOLICENSE
57   printf("released under GNU General Public License version 2 and thus\n");
58   printf("comes with absolutely no warranty; see COPYING for details\n");
59 #endif
60 
61   #ifdef FHS
62   static string sbuf, cbuf;
63   if(getenv("HOME")) {
64     sbuf = getenv("HOME"); sbuf += "/."; sbuf += scorefile;
65     cbuf = getenv("HOME"); cbuf += "/."; cbuf += conffile;
66     scorefile = sbuf.c_str();
67     conffile = cbuf.c_str();
68     }
69   #endif
70   }
71 
72 EX namespace arg {
73   EX int curphase;
74 
75   EX vector<string> argument;
76   EX int pos;
77 
lshift()78   EX void lshift() { pos++; }
unshift()79   EX void unshift() { pos--; }
80 
shift()81   EX void shift() {
82     lshift(); if(pos >= isize(argument)) { printf("Missing parameter\n"); exit(1); }
83     }
84 
nomore()85   EX bool nomore() { return pos >= isize(argument); }
86 
args()87   EX const string& args() { return argument[pos]; }
argcs()88   EX const char* argcs() { return args().c_str(); }
argi()89   EX int argi() { return atoi(argcs()); }
90 
shift_argi()91   EX int shift_argi() { shift(); return argi(); }
shift_args()92   EX const string& shift_args() { shift(); return args(); }
93 
arghex()94   EX unsigned arghex() { return strtoll(argcs(), NULL, 16); }
argf()95   EX ld argf() {
96     try {
97       return parseld(args());
98       }
99     catch(hr_parse_exception& ex) {
100       println(hlog, "error parsing commandline parameters: ", ex.s);
101       exit(1);
102       }
103     }
argis(const string & s)104   EX bool argis(const string& s) { if(args()[0] == '-' && args()[1] == '-') return args().substr(1) == s; return args() == s; }
105 
shift_arg_formula(ld & x,const reaction_t & r IS (reaction_t ()))106   EX void shift_arg_formula(ld& x, const reaction_t& r IS(reaction_t())) {
107     shift(); ld old = x; x = argf();
108     #if CAP_ANIMATIONS
109     anims::animate_parameter(x, args(), r);
110     #endif
111     if(old != x && r) r();
112     }
113 
114   #if HDR
115 
116   // an useful macro
117   #define PHASE(x) { if(arg::curphase > x) arg::phaseerror(x); else if(arg::curphase < x) return 2; }
118   #define PHASEFROM(x) { if(arg::curphase < x) return 2; }
119 
120   #define TOGGLE(x, param, act) \
121   else if(args()[0] == '-' && args()[1] == x && !args()[2]) { PHASEFROM(2); showstartmenu = false; act; } \
122   else if(args()[0] == '-' && args()[1] == x && args()[2] == '1') { PHASEFROM(2); showstartmenu = false; if(!param) act; } \
123   else if(args()[0] == '-' && args()[1] == x && args()[2] == '0') { PHASEFROM(2); showstartmenu = false; if(param) act; }
124 
125   #endif
126 
cheat()127   EX void cheat() { autocheat = true; cheater++; timerghost = false; }
128 
init(int argc,char ** argv)129   EX void init(int argc, char **argv) { for(int i=0; i<argc; i++) argument.push_back(argv[i]); lshift(); }
130 
phaseerror(int x)131   EX void phaseerror(int x) {
132     printf("Command line error: cannot read command '%s' from phase %d in phase %d\n", args().c_str(), x, curphase);
133     exit(1);
134     }
135 
136   bool dialog_launched = false;
137 
launch_dialog(const reaction_t & r IS (reaction_t ()))138   EX void launch_dialog(const reaction_t& r IS(reaction_t())) {
139     if(!dialog_launched) {
140       popScreenAll();
141       showstartmenu = false;
142       dialog_launched = true;
143       }
144     if(r) pushScreen(r);
145     }
146 
147   EX int readCommon();
148   EX int readLocal();
149   EX void read(int phase);
150 EX }
151 
152 
readCommon()153 int arg::readCommon() {
154 
155 // first phase options
156 
157   if(argis("-s")) { PHASE(1); shift(); scorefile = argcs(); }
158   else if(argis("-rsrc")) { PHASE(1); shift(); rsrcdir = args(); }
159   else if(argis("-nogui")) { PHASE(1); noGUI = true; }
160 #ifndef EMSCRIPTEN
161 #if CAP_SDL
162   else if(argis("-font")) { PHASE(1); shift(); fontpath = args(); }
163 #endif
164 #endif
165 
166   else if(argis("-test-ach"))
167     test_achievements = true;
168 
169   else if(argis("-test"))
170     callhooks(hooks_tests);
171   else if(argis("-offline")) {
172     PHASE(1);
173     offlineMode = true;
174     }
175   else if(argis("-no-stamp")) {
176     debugflags &=~ DF_TIME;
177     }
178   else if(argis("-debf")) {
179     shift();
180     string s = args();
181     for(char c: s) {
182       for(int i=0; i<int(strlen(DF_KEYS)); i++) {
183         if(DF_KEYS[i] == c) debugflags |= (1<<i);
184         else if(DF_KEYS[i] == (c ^ 32)) debugflags &= ~(1<<i);
185         }
186       if(c >= '0' && c <= '9') {
187         debugflags &= DF_TIME;
188         if(c >= '1')
189           debugflags |= DF_INIT | DF_WARN | DF_MSG | DF_ERROR;
190         if(c >= '2')
191           debugflags |= DF_GEOM | DF_GP | DF_LOG | DF_FIELD | DF_POLY;
192         if(c >= '3')
193           debugflags |= DF_TURN | DF_STEAM;
194         if(c >= '4')
195           debugflags |= DF_GRAPH | DF_MEMORY;
196         }
197       else if(c == '+') {
198         if(debugfile) fclose(debugfile);
199         shift();
200         println(hlog, "writing to ", argcs());
201         debugfile = fopen(argcs(), "at");
202         }
203       else if(c == '@') {
204         if(debugfile) fclose(debugfile);
205         shift();
206         println(hlog, "writing to ", argcs());
207         debugfile = fopen(argcs(), "wt");
208         }
209       }
210     }
211   else if(argis("-run")) {
212     PHASE(3);
213     start_game();
214     mainloop(); quitmainloop = false;
215     }
216   else if(argis("-msg")) {
217     shift(); addMessage(args());
218     printf("%s\n", args().c_str());
219     }
220   else if(argis("-msg0")) {
221     clearMessages();
222     }
223 #if CAP_TOUR
224   else if(argis("-tour")) {
225     showstartmenu = false;
226     PHASEFROM(2); start_game(); tour::start();
227     }
228   else if(argis("-presentation")) {
229     PHASEFROM(2); tour::texts = false;
230     start_game(); tour::start();
231     }
232   else if(argis("-print-tour")) {
233     tour::print();
234     }
235 #endif
236   else if(argis("-draw")) {
237     PHASE(3); start_game(); drawscreen();
238     }
239   else if(argis("-rotate")) {
240     PHASE(3);  start_game();
241     shift(); ld a = argf();
242     shift(); ld b = argf();
243     View = View * spin(M_PI * 2 * a / b);
244     }
245   else if(argis("-rotate3")) {
246     PHASE(3);  start_game();
247     shift(); ld a = argf();
248     shift(); ld b = argf();
249     View = View * cspin(1, 2, M_PI * 2 * a / b);
250     }
251   else if(argis("-face-vertex")) {
252     PHASE(3);  start_game();
253     auto &ss = currentmap->get_cellshape(cwt.at);
254     View = cspin(0, 2, M_PI/2) * spintox(ss.vertices_only_local[0]);
255     }
256   else if(argis("-face-face")) {
257     PHASE(3);  start_game();
258     View = cspin(0, 2, M_PI/2);
259     }
260   else if(argis("-grotate")) {
261     PHASE(3);  start_game();
262     shift(); int i = argi();
263     shift(); int j = argi();
264     shift(); View = View * cspin(i, j, argf());
265     }
266   else if(argis("-exit")) {
267     PHASE(3); printf("Success.\n");
268     exit(0);
269     }
270 
271 // graphical options
272   else if(argis("-noscr")) {
273     PHASE(3);
274     popScreenAll();
275     showstartmenu = false;
276     }
277 
278   else if(argis("-viz")) {
279     PHASE(3);
280     showstartmenu = false;
281     start_game();
282     popScreenAll();
283     clearMessages();
284     nohud = true;
285     mapeditor::drawplayer = false;
286     }
287 
288   else if(argis("-vizhr")) {
289     PHASE(3);
290     showstartmenu = false;
291     popScreenAll();
292     clearMessages();
293     }
294 
295 // informational
296   else if(argis("-version") || argis("-v")) {
297     printf("HyperRogue version " VER "\n");
298     exit(0);
299     }
300   else if(argis("-L")) {
301     printf("Treasures:\n");
302     for(int i=1; i<ittypes; i++)
303       if(itemclass(eItem(i)) == IC_TREASURE)
304         printf("    %s\n", iinf[i].name);
305     printf("\n");
306     printf("Orbs:\n");
307     for(int i=1; i<ittypes; i++)
308       if(itemclass(eItem(i)) == IC_ORB)
309         printf("    %s\n", iinf[i].name);
310     printf("\n");
311     printf("Other items:\n");
312     for(int i=1; i<ittypes; i++)
313       if(itemclass(eItem(i)) == IC_OTHER)
314         printf("    %s\n", iinf[i].name);
315     printf("\n");
316     printf("Monsters:\n");
317     for(int i=1; i<motypes; i++)
318       printf("    %s\n", minf[i].name);
319     printf("\n");
320     printf("Lands:\n");
321     for(int i=1; i<landtypes; i++)
322       printf("    %s\n", linf[i].name);
323     printf("\n");
324     printf("Walls:\n");
325     for(int i=0; i<walltypes; i++)
326       printf("    %s\n", winf[i].name);
327     printf("\n");
328     exit(0);
329     }
330   else if(argis("")) {}
331   else if(argis("-help") || argis("-h")) {
332     printf("Press F1 while playing to get ingame options.\n\n");
333     printf("HyperRogue accepts the following command line options:\n");
334     printf("  -c FILE        - use the specified configuration file\n");
335     printf("  -s FILE        - use the specified highscore file\n");
336     printf("  -m FILE        - use the specified soundtrack (music)\n");
337     printf("  -se DIR        - the directory containing sound effects\n");
338     printf("  -lev FILE      - use the specified filename for the map editor (without loading)\n");
339     printf("  -load FILE     - use the specified filename for the map editor\n");
340     printf("  -canvas COLOR  - set background color or pattern code for the canvas\n");
341     printf("  --version, -v  - show the version number\n");
342     printf("  --help, -h     - show the commandline options\n");
343     printf("  -f*            - toggle fullscreen mode\n");
344     printf("  -wm n, -mm n   - start in the given wallmode or monmode\n");
345     printf("  -r WxHxF       - use the given resolution and font size\n");
346     printf("  -o*            - toggle the OpenGL mode\n");
347     printf("  -W LAND        - start in the given land (cheat)\n");
348     printf("  -W2 LAND       - make the given land easy to find (also turns on autocheat)\n");
349     printf("  -ch            - auto-enable cheat mode\n");
350     printf("  -geo n         - switch geometry (1=Euclidean, 2=spherical, 3=elliptic, 4/5=quotient)\n");
351     printf("  -qs <desc>     - fieldpattern: quotient by the given <desc> (must be followed by qpar)\n");
352     printf("  -qpar <prime>  - fieldpattern: use the given prime instead of 43\n");
353     printf("  -cs <desc>     - fieldpattern: set subpath to the given <desc> (cannot be followed by qpar)\n");
354     printf("  -csp           - fieldpattern: find the subpath of order <prime> (cannot be followed by qpar)\n");
355     printf("  -S*            - toggle Shmup\n");
356     printf("  -P n           - switch Shmup number of players (n=1..7)\n");
357     printf("  -PM            - switch the model index\n");
358     printf("  -H*            - toggle Hardcore\n");
359     printf("  -T*            - toggle Tactical\n");
360     printf("  -7*            - toggle heptagonal mode\n");
361     printf("  -C*            - toggle Chaos mode\n");
362     printf("  -R*            - toggle Random Pattern\n");
363     printf("  -Y id          - enable Yendor, level id\n");
364     printf("  -D             - disable all the special game modes\n");
365     printf("  -L             - list of features\n");
366     printf("  -debugf 7      - output debugging information to hyperrogue-debug.txt\n");
367     printf("  -debuge 7      - output debugging information to stderr\n");
368     printf("  -offline       - don't connect to Steam (for Steam versions)\n");
369     printf("  -I ITEM n      - start with n of ITEM (activates cheat and disables ghosts)\n");
370     printf("  -fix           - fix the seed\n");
371     printf("Toggles: -o0 disables, -o1 enables, -o switches\n");
372     printf("Not all options are documented, see hyper.cpp\n");
373     exit(0);
374     }
375   else return 1;
376   return 0;
377   }
378 
379 EX purehookset hooks_config;
380 
381 EX hookset<int()> hooks_args;
382 
383 EX map<string, pair<int, reaction_t>> *added_commands;
384 
385 EX namespace arg {
386 
read_added_commands()387   int read_added_commands() {
388     if(!added_commands) return 1;
389     if(added_commands->count(args())) {
390       auto& ac = (*added_commands)[args()];
391       if(ac.first == 2)
392         PHASEFROM(2);
393       if(ac.first == 3)
394         PHASE(3);
395       ac.second();
396       return 0;
397       }
398     return 1;
399     }
400 
add_at(const string & s,int at,const reaction_t & r)401   EX int add_at(const string& s, int at, const reaction_t& r) {
402     if(!added_commands) added_commands = new map<string, pair<int, reaction_t>> ();
403     if(added_commands->count(s)) throw hr_exception("arg::add conflict");
404     (*added_commands)[s] = {at, r};
405     return 1;
406     }
407 
add1(const string & s,const reaction_t & r)408   EX int add1(const string& s, const reaction_t& r) { return add_at(s, 1, r); }
add2(const string & s,const reaction_t & r)409   EX int add2(const string& s, const reaction_t& r) { return add_at(s, 2, r); }
add3(const string & s,const reaction_t & r)410   EX int add3(const string& s, const reaction_t& r) { return add_at(s, 3, r); }
411 
412   auto ah = addHook(hooks_args, 0, readCommon) + addHook(hooks_args, 200, read_added_commands);
413 
read(int phase)414   void read(int phase) {
415     curphase = phase;
416     callhooks(hooks_config);
417     while(pos < isize(argument)) {
418       int r = callhandlers(1, hooks_args);
419       switch (r) {
420         case 0: lshift(); break;
421         case 1: printf("Unknown option: %s\n", argcs()); exit(3); break;
422         case 2: return;
423         default: assert(false);
424         }
425       }
426     }
427 EX }
428 #endif
429 
430 #if !CAP_COMMANDLINE
431 EX namespace arg {
add1(const string & s,const reaction_t & r)432   EX int add1(const string& s, const reaction_t& r) { return 0; }
add2(const string & s,const reaction_t & r)433   EX int add2(const string& s, const reaction_t& r) { return 0; }
add3(const string & s,const reaction_t & r)434   EX int add3(const string& s, const reaction_t& r) { return 0; }
435 EX }
436 #endif
437 }
438