1 
2 #include "console.h"
3 
4 #include "ObjManager.h"
5 #include "game.h"
6 #include "graphics/Renderer.h"
7 #include "nx.h"
8 #include "settings.h"
9 #include "statusbar.h"
10 #include "tsc.h"
11 
12 using namespace NXE::Graphics;
13 
14 #include "common/misc.h"
15 #include "Utils/Logger.h"
16 #include "debug.h"
17 #include "map.h"
18 #include "p_arms.h"
19 #include "player.h"
20 #include "playerstats.h"
21 #include "sound/SoundManager.h"
22 
23 #include <cstdarg>
24 #include <string>
25 #include <vector>
26 #include <SDL.h>
27 
28 #define Respond console.Print
29 
30 #if defined(_WIN32)
31 #define strcasecmp _stricmp
32 #endif
33 
__god(std::vector<std::string> * args,int num)34 static void __god(std::vector<std::string> *args, int num)
35 {
36   game.debug.god = !game.debug.god;
37   Respond("God mode:  {}", game.debug.god ? "enabled" : "disabled");
38 }
39 
__script(std::vector<std::string> * args,int num)40 static void __script(std::vector<std::string> *args, int num)
41 {
42   // release any focus a current script may have on us
43   if (player->movementmode == MOVEMODE_NORMAL)
44     map_focus(NULL);
45 
46   if (game.tsc->StartScript(num))
47   {
48     Respond("Script {:04d} started.", num);
49   }
50   else
51   {
52     Respond("No such script {:04d}", num);
53   }
54 }
55 
__warp(std::vector<std::string> * args,int num)56 static void __warp(std::vector<std::string> *args, int num)
57 {
58   if (num == 0)
59   {
60     std::string stagename;
61     for (unsigned int i = 0; i < args->size(); i++)
62     {
63       if (i != 0)
64         stagename += ' ';
65       stagename += (args->at(i));
66     }
67 
68     LOG_DEBUG("Looking for '{}'", stagename.c_str());
69     for (num = 0;; num++)
70     {
71       if (num >= num_stages)
72       {
73         if (!strcasecmp(stagename.c_str(), "village"))
74         {
75           num = 11;
76         }
77         else
78         {
79           Respond("Could determine stage number from your description.");
80           return;
81         }
82 
83         break;
84       }
85 
86       if (strcasebegin(stages[num].stagename, stagename.c_str()))
87         break;
88       if (strcasebegin(stages[num].filename, stagename.c_str()))
89         break;
90     }
91   }
92 
93   game.switchstage.mapno   = num;
94   game.switchstage.playerx = 16;
95   game.switchstage.playery = 16;
96 }
97 
__sound(std::vector<std::string> * args,int num)98 static void __sound(std::vector<std::string> *args, int num)
99 {
100   NXE::Sound::SoundManager::getInstance()->playSfx((NXE::Sound::SFX)num);
101   console.SetVisible(true); // keep console up
102 }
103 
__music(std::vector<std::string> * args,int num)104 static void __music(std::vector<std::string> *args, int num)
105 {
106   /*  extern std::vector<std::string> org_names;
107     bool ok = true;
108     std::vector<std::string>::size_type i;
109 
110     const char *name = args->at(0).c_str();
111     if (num == 0 && strcmp(name, "0") != 0)
112     {
113       for (i = 1; i < org_names.size(); i++)
114       {
115         if (strcasebegin(org_names[i].c_str(), name))
116         {
117           num = i;
118           break;
119         }
120       }
121 
122       if (num == 0)
123       {
124         Respond("Don't know that song.");
125         return;
126       }
127     }
128 
129     if (num < 0)
130       ok = false;
131     else if (num >= (int)org_names.size())
132       ok = false;
133 
134     if (!ok)
135     {
136       Respond("track out of range");
137       music(0);
138     }
139     else
140     {
141       music(0);
142       music(num);
143       if (num > 0)
144         Respond("%s started", org_names[num].c_str());
145       else
146         Respond("ZERO MUZAK");
147     }*/
148 }
149 
__giveweapon(std::vector<std::string> * args,int num)150 static void __giveweapon(std::vector<std::string> *args, int num)
151 {
152   if (num >= 0 && num < WPN_COUNT)
153   {
154     GetWeapon(num, 100);
155   }
156 }
157 
__dropweapon(std::vector<std::string> * args,int num)158 static void __dropweapon(std::vector<std::string> *args, int num)
159 {
160   if (args->size() == 0)
161     num = player->curWeapon;
162 
163   player->weapons[num].hasWeapon = 0;
164   player->weapons[num].maxammo   = 0;
165   player->weapons[num].ammo      = 0;
166 
167   if (num == player->curWeapon)
168     stat_NextWeapon();
169 }
170 
171 // set weapon level
__level(std::vector<std::string> * args,int num)172 static void __level(std::vector<std::string> *args, int num)
173 {
174   num--;
175   if (num < 0)
176     num = 0;
177   if (num > 2)
178     num = 2;
179 
180   if (player->weapons[player->curWeapon].xp < 5)
181     player->weapons[player->curWeapon].xp = 5;
182 
183   for (int timeout = 0; timeout < 500; timeout++)
184   {
185     if (player->weapons[player->curWeapon].level == num)
186     {
187       return;
188     }
189     else if (player->weapons[player->curWeapon].level < num)
190     {
191       AddXP(1);
192     }
193     else
194     {
195       SubXP(1);
196     }
197   }
198 
199 //  Respond("Timeout");
200 }
201 
__ammo(std::vector<std::string> * args,int num)202 static void __ammo(std::vector<std::string> *args, int num)
203 {
204   player->weapons[player->curWeapon].ammo = num;
205   if (player->weapons[player->curWeapon].ammo > player->weapons[player->curWeapon].maxammo)
206     player->weapons[player->curWeapon].maxammo = player->weapons[player->curWeapon].ammo;
207 }
208 
__maxammo(std::vector<std::string> * args,int num)209 static void __maxammo(std::vector<std::string> *args, int num)
210 {
211   player->weapons[player->curWeapon].maxammo = num;
212   if (player->weapons[player->curWeapon].ammo > player->weapons[player->curWeapon].maxammo)
213     player->weapons[player->curWeapon].ammo = player->weapons[player->curWeapon].maxammo;
214 }
215 
__hp(std::vector<std::string> * args,int num)216 static void __hp(std::vector<std::string> *args, int num)
217 {
218   player->hp = num;
219   if (player->hp > player->maxHealth)
220     player->maxHealth = player->hp;
221 }
222 
__maxhp(std::vector<std::string> * args,int num)223 static void __maxhp(std::vector<std::string> *args, int num)
224 {
225   player->maxHealth = num;
226   if (player->hp > player->maxHealth)
227   {
228     player->hp = player->maxHealth;
229     // PHealthBar.displayed_value = player->hp;
230   }
231 }
232 
__xp(std::vector<std::string> * args,int num)233 static void __xp(std::vector<std::string> *args, int num)
234 {
235   player->weapons[player->curWeapon].xp = num;
236 }
237 
238 /*
239 static void __spawn(std::vector<std::string> *args, int num)
240 {
241   unsigned int i = 0;
242 
243   // if first argument is a number interpret it as a count of
244   // objects to spawn.
245   unsigned int count;
246   if (isdigit(args->at(0).c_str()[0]))
247   {
248     count = num;
249     i++;
250   }
251   else
252   {
253     count = 1;
254   }
255 
256   // reconstitute the arguments into the name of the object
257   // to be spawned.
258   std::string objName;
259   unsigned int starti = i;
260   for (;; i++)
261   {
262     if (i >= args->size())
263       break;
264 
265     if (i > starti)
266       objName += ' ';
267     objName += (args->at(i));
268   }
269 
270   // try and get object type from the provided name
271   int type = ObjectNameToType(objName.c_str());
272   if (type == -1)
273   {
274     Respond("Unknown object. See object.h for definitions.");
275     return;
276   }
277 
278   // reset console animate flags on any previously spawned objects
279   Object *o;
280   FOREACH_OBJECT(o)
281   {
282     o->nxflags &= ~NXFLAG_CONSOLE_ANIMATE;
283   }
284 
285   // get starting spawn position and spacing
286   int x = player->x + ((player->dir == RIGHT) ? (24 * CSFI) : -(24 * CSFI));
287   int y = player->y - (16 * CSFI);
288   int w = (Renderer::getInstance()->sprites.sprites[objprop[type].sprite].w + 4) * CSFI;
289 
290   // create 'em
291   for (i = 0; i < count; i++)
292   {
293     Object *o = CreateObject(x, y, type);
294 
295     o->dir = player->dir;
296     o->nxflags |= NXFLAG_CONSOLE_ANIMATE;
297     x += w;
298   }
299 
300   if (count != 1)
301     Respond("{} x{}", DescribeObjectType(type), count);
302   else
303     Respond("{}", DescribeObjectType(type));
304 }
305 
306 static void __animate(std::vector<std::string> *args, int num)
307 {
308   Object *o;
309 
310   if (args->size() == 2)
311   { // specifying explicitly by id2
312     o = FindObjectByID2(atoi(args->at(0).c_str()));
313     if (o)
314       o->state = atoi(args->at(1).c_str());
315     else
316       Respond("Object not found.");
317 
318     return;
319   }
320 
321   // animating implicitly from last spawn command
322   bool found = false;
323   FOREACH_OBJECT(o)
324   {
325     if (o->nxflags & NXFLAG_CONSOLE_ANIMATE)
326     {
327       o->state = num;
328       found    = true;
329     }
330   }
331 
332   if (!found)
333     Respond("No objects found.");
334 }
335 */
__infinitedamage(std::vector<std::string> * args,int num)336 static void __infinitedamage(std::vector<std::string> *args, int num)
337 {
338   game.debug.infinite_damage = !game.debug.infinite_damage;
339   Respond(game.debug.infinite_damage ? "My, oh my..." : "Back to normal.");
340 }
341 
__killall(std::vector<std::string> * args,int num)342 static void __killall(std::vector<std::string> *args, int num)
343 {
344   for (int i = 0; i < nOnscreenObjects; i++)
345   {
346     Object *o = onscreen_objects[i];
347     if (o->flags & FLAG_SHOOTABLE)
348     {
349       o->flags &= ~FLAG_INVULNERABLE;
350       o->DealDamage(999);
351     }
352   }
353 }
354 
__movemode(std::vector<std::string> * args,int num)355 static void __movemode(std::vector<std::string> *args, int num)
356 {
357   player->movementmode = num;
358 }
359 
__flag(std::vector<std::string> * args,int num)360 static void __flag(std::vector<std::string> *args, int num)
361 {
362   game.flags[num] ^= 1;
363   Respond("Flag {:04d}: {}", num, game.flags[num] ? "SET" : "CLEARED");
364 }
365 
__clearflags(std::vector<std::string> * args,int num)366 static void __clearflags(std::vector<std::string> *args, int num)
367 {
368   memset(game.flags, 0, sizeof(game.flags));
369   Respond("Warning- all game flags cleared");
370 }
371 /*
372 static void __equip(std::vector<std::string> *args, int num)
373 {
374   static const char *equiplist[] = {"booster08", "map",        "armsbarrier", "turbocharge", "airtank",
375                                     "booster20", "mimigamask", "whimstar",    "nikumaru",    NULL};
376   int i, mask;
377 
378   const char *item = args->at(0).c_str();
379   bool enable      = args->size() > 1 ? atoi(args->at(1).c_str()) : true;
380 
381   mask = 0x01;
382   for (i = 0; equiplist[i]; i++)
383   {
384     if (!strcasecmp(equiplist[i], item))
385     {
386       // allow only booster 08 or booster 20 at a time
387       if (mask & (EQUIP_BOOSTER08 | EQUIP_BOOSTER20))
388         player->equipmask &= ~(EQUIP_BOOSTER08 | EQUIP_BOOSTER20);
389 
390       if (enable)
391         player->equipmask |= mask;
392       else
393         player->equipmask &= ~mask;
394 
395       Respond("Item {} ({:#04x}) %sequipped.", equiplist[i], mask, enable ? "" : "un-");
396       return;
397     }
398 
399     mask <<= 1;
400   }
401 
402   Respond("Unknown item");
403 }
404 */
__giveitem(std::vector<std::string> * args,int num)405 static void __giveitem(std::vector<std::string> *args, int num)
406 {
407   if (FindInventory(num) == -1)
408   {
409     AddInventory(num);
410     Respond("Added item {} to your inventory.", num);
411   }
412   else
413   {
414     Respond("You already have item {} in your inventory.", num);
415   }
416 }
417 
__takeitem(std::vector<std::string> * args,int num)418 static void __takeitem(std::vector<std::string> *args, int num)
419 {
420   if (FindInventory(num) != -1)
421   {
422     DelInventory(num);
423     Respond("Removed item {} from your inventory.", num);
424   }
425   else
426   {
427     Respond("You don't have item {} in your inventory.", num);
428   }
429 }
430 /*
431 static void __quake(std::vector<std::string> *args, int num)
432 {
433   if (args->size() > 0)
434     megaquake(50);
435   else
436     quake(50);
437 }
438 
439 static void __boa(std::vector<std::string> *args, int num)
440 {
441   game.stageboss.SetState(num);
442 }
443 */
444 // skip to normal ending sequence
__ending_normal(std::vector<std::string> * args,int num)445 static void __ending_normal(std::vector<std::string> *args, int num)
446 {
447   game.reset();
448   game.pause(0);
449   game.setmode(GM_NORMAL);
450 
451   game.flags[1341]              = true;
452   game.switchstage.mapno        = 70;
453   game.switchstage.playerx      = 16;
454   game.switchstage.playery      = 16;
455   game.switchstage.eventonentry = 400;
456 }
457 
458 // skip to good ending sequence
__ending_good(std::vector<std::string> * args,int num)459 static void __ending_good(std::vector<std::string> *args, int num)
460 {
461   game.reset();
462   game.pause(0);
463   game.setmode(GM_NORMAL);
464 
465   game.flags[1341]              = true;
466   game.flags[2000]              = true;
467   game.switchstage.mapno        = 71;
468   game.switchstage.playerx      = 16;
469   game.switchstage.playery      = 16;
470   game.switchstage.eventonentry = 120;
471 }
472 
473 // skip to credits
__cre(std::vector<std::string> * args,int num)474 static void __cre(std::vector<std::string> *args, int num)
475 {
476   game.reset();
477   game.pause(0);
478   game.setmode(GM_NORMAL);
479 
480   game.flags[1341]              = true;
481   game.flags[162]              = true;
482   game.switchstage.mapno        = 0;
483   game.switchstage.playerx      = 16;
484   game.switchstage.playery      = 16;
485   game.switchstage.eventonentry = 100;
486 }
487 
__cre_good(std::vector<std::string> * args,int num)488 static void __cre_good(std::vector<std::string> *args, int num)
489 {
490   game.reset();
491   game.pause(0);
492   game.setmode(GM_NORMAL);
493 
494   game.flags[1341]              = true;
495   game.flags[162]              = true;
496   game.flags[2000]              = true;
497   game.switchstage.mapno        = 0;
498   game.switchstage.playerx      = 16;
499   game.switchstage.playery      = 16;
500   game.switchstage.eventonentry = 100;
501 }
502 /*
503 static void __reset(std::vector<std::string> *args, int num)
504 {
505   game.reset();
506 }
507 */
__fps(std::vector<std::string> * args,int num)508 static void __fps(std::vector<std::string> *args, int num)
509 {
510   extern int fps;
511 
512   settings->show_fps ^= 1;
513   settings_save();
514   fps = 0;
515 }
516 
517 /*
518 void c------------------------------() {}
519 */
520 
__skip_intro(std::vector<std::string> * args,int num)521 static void __skip_intro(std::vector<std::string> *args, int num)
522 {
523   settings->skip_intro = !settings->skip_intro;
524   settings_save();
525   Respond("skip_intro: {}", settings->skip_intro ? "enabled" : "disabled");
526 }
527 
528 /*
529 void c------------------------------() {}
530 */
531 
__help(std::vector<std::string> * args,int num)532 static void __help(std::vector<std::string> *args, int num)
533 {
534     Respond("Available commands:");
535     for (auto& command: console.getCommands())
536     {
537       Respond(command.name + " - " + command.help);
538     }
539 }
540 
__quit(std::vector<std::string> * args,int num)541 static void __quit(std::vector<std::string> *args, int num)
542 {
543   Respond("Bye-bye!");
544   lastinputs[ESCKEY] = true;
545   game.running       = false;
546 }
547 
548 /*
549 void c------------------------------() {}
550 */
551 /*
552 static void __hide_player(std::vector<std::string> *args, int num)
553 {
554   player->hide = num;
555 }
556 
557 static void __lock_inputs(std::vector<std::string> *args, int num)
558 {
559   player->inputs_locked = num;
560 }
561 
562 static void __freeze_game(std::vector<std::string> *args, int num)
563 {
564   game.frozen = num;
565 }
566 
567 static void __show_textbox(std::vector<std::string> *args, int num)
568 {
569   textbox.SetVisible(num);
570 }
571 */
DebugConsole()572 DebugConsole::DebugConsole()
573 {
574     commands = {{"god",            __god,            0, 0,   "Toggle god-mode" },
575                 {"script",         __script,         1, 1,   "Execute script <num>" },
576                 {"warp",           __warp,           1, 999, "Warp to level <name|num>" },
577                 {"sound",          __sound,          1, 1,   "Make a sound <num>" },
578                 {"music",          __music,          1, 1,   "Play music <name>" },
579                 {"giveweapon",     __giveweapon,     1, 1,   "Give veapon <num>" },
580                 {"dropweapon",     __dropweapon,     0, 1,   "Drop weapon [num] (or current, without parameters)" },
581 
582                 {"level",          __level,          1, 1,   "Set current weapon level to <num>" },
583                 {"ammo",           __ammo,           1, 1,   "Give ammo <num>" },
584                 {"maxammo",        __maxammo,        1, 1,   "Set maximum ammo to <num>" },
585                 {"hp",             __hp,             1, 1,   "Give HP <num>" },
586                 {"maxhp",          __maxhp,          1, 1,   "Set maximum HP to <num>" },
587                 {"xp",             __xp,             1, 1,   "Give XP <num>" },
588 //                {"spawn",          __spawn,          1, 999, "Spawn object <num>" },
589 //                {"animate",        __animate,        1, 2,   "Set object <id> state to <num>" },
590                 {"infinitedamage", __infinitedamage, 0, 0,   "Infinite damage" },
591                 {"killall",        __killall,        0, 0,   "Kill all on-screen objects" },
592                 {"movemode",       __movemode,       1, 1,   "Set move mode <num>. 0 - normal, 1 - waterway, 2 - no-clip" },
593                 {"flag",           __flag,           1, 1,   "Toggle game flag <num>" },
594                 {"clearflags",     __clearflags,     0, 0,   "Clear all game flags" },
595 
596                 {"giveitem",       __giveitem,       1, 1,   "Give item <num>" },
597                 {"takeitem",       __takeitem,       1, 1,   "Remove item <num>" },
598 //                {"boss_state",     __boa,            1, 1,   "Set stageboss state to <num>" },
599                 {"ending_normal",  __ending_normal,  0, 0,   "Run normal ending" },
600                 {"ending_good",    __ending_good,    0, 0,   "Run good ending" },
601                 {"credits_normal", __cre,            0, 0,   "Run credits(normal)" },
602                 {"credits_good",   __cre_good,       0, 0,   "Run credits(good)" },
603                 {"fps",            __fps,            0, 0,   "Toggle fps display" },
604                 {"skip-intro",     __skip_intro,     0, 0,   "Toggle intro skip" },
605                 {"quit",           __quit,           0, 0,   "Quit game" },
606 
607                 {"help",          __help,            0, 0,   "Show this help" }
608                };
609 }
610 
611 /*
612 void c------------------------------() {}
613 */
614 
SetVisible(bool newstate)615 void DebugConsole::SetVisible(bool newstate)
616 {
617   LOG_DEBUG("DebugConsole::SetVisible({})", newstate ? "true" : "false");
618 
619   if (fVisible != newstate)
620   {
621     fVisible     = newstate;
622     fKeyDown     = 0;
623     fRepeatTimer = 0;
624 
625     if (newstate)
626     {
627       fLine           = "";
628       fBrowsingExpansion = false;
629       fBackIndex         = fBackBuffer.size() - 1;
630 
631       fResponse.clear();
632       fCursorTimer = 0;
633     }
634   }
635 }
636 
IsVisible()637 bool DebugConsole::IsVisible()
638 {
639   return fVisible;
640 }
641 
HandleKey(int key)642 bool DebugConsole::HandleKey(int key)
643 {
644   if (!fVisible)
645     return 0;
646   if (key != 9)
647     fBrowsingExpansion = false;
648 
649   if (key != fKeyDown)
650   {
651     fKeyDown     = key;
652     fRepeatTimer = 25;
653   }
654   else
655   {
656     fRepeatTimer = 1;
657   }
658 
659   fCursorTimer = 0;
660 
661   switch (key)
662   {
663     case 27:
664     case '`':
665     {
666       SetVisible(false);
667     }
668     break;
669 
670     case 13:
671     case 271: // numeric enter
672     {
673       SetVisible(false);
674 
675       Execute(fLine);
676     }
677     break;
678 
679     case 10:
680       break;
681 
682     case 8:
683     {
684       if (fLine.size() > 0)
685         fLine.pop_back();
686     }
687     break;
688 
689     case 9: // command completion
690     {
691       ExpandCommand();
692 
693       fBrowsingExpansion = true;
694       fExpandIndex++;
695     }
696     break;
697 
698     // command backbuffer
699     case SDLK_UP:
700     case SDLK_DOWN:
701     {
702       if (fBackBuffer.size() > 0)
703       {
704         fBackIndex += (key == SDLK_UP) ? -1 : 1;
705         if (fBackIndex < 0)
706           fBackIndex = (fBackBuffer.size() - 1);
707         else
708           fBackIndex %= fBackBuffer.size();
709 
710         fLine = fBackBuffer.at(fBackIndex);
711       }
712     }
713     break;
714 
715     default:
716     {
717       fLine.append(1,(char)key);
718     }
719     break;
720   }
721 
722   return 1;
723 }
724 
HandleKeyRelease(int key)725 void DebugConsole::HandleKeyRelease(int key)
726 {
727   if (key == fKeyDown)
728   {
729     fKeyDown     = 0;
730     fRepeatTimer = 0;
731   }
732 }
733 
734 /*
735 void c------------------------------() {}
736 */
737 
Draw()738 void DebugConsole::Draw()
739 {
740   if (fResponse.size() > 0)
741   {
742     int y = 8 + 8 * fResponse.size();
743     for (auto& str: fResponse)
744     {
745       this->DrawDebugText(str, y);
746       y -= 8;
747     }
748 
749     if (--fResponseTimer <= 0)
750     {
751       fResponse.erase(fResponse.begin());
752       if (fResponse.size() > 0)
753       {
754         fResponseTimer = 30;
755       }
756     }
757   }
758   else if (fVisible)
759   {
760     // key-repeat
761     if (fKeyDown)
762     {
763       if (--fRepeatTimer < 0)
764         HandleKey(fKeyDown);
765     }
766 
767     std::string buffer;
768     buffer = "-> " + fLine + ((fCursorTimer < 20) ? "_" : " ");
769     this->DrawDebugText(buffer);
770 
771     if (++fCursorTimer > 30)
772       fCursorTimer = 0;
773   }
774 }
775 
DrawDebugText(const std::string & text,int y)776 void DebugConsole::DrawDebugText(const std::string& text, int y)
777 {
778   Renderer::getInstance()->font.drawLTR(4, (Renderer::getInstance()->screenHeight - y), text, 0xFFAAAA, true);
779 }
780 
Execute(std::string & line)781 bool DebugConsole::Execute(std::string& line)
782 {
783   LOG_DEBUG("DebugConsole::Execute('{}')", line);
784 
785   // record command in backbuffer
786   if (!fBackBuffer.empty())
787     fBackBuffer.erase(fBackBuffer.end());
788   if (fBackBuffer.size() >= CONSOLE_MAX_BACK)
789     fBackBuffer.erase(fBackBuffer.begin());
790 
791   trim(line);
792 
793   fBackBuffer.push_back(line);
794   fBackBuffer.push_back(std::string(""));
795 
796   // split command into arguments
797   std::vector<std::string> args;
798   std::string cmd = SplitCommand(line, args);
799 
800   if (cmd.size())
801   {
802     std::vector<CommandEntry> matches;
803     MatchCommand(cmd, matches);
804 
805     if (matches.size() == 1)
806     {
807       CommandEntry command = matches.at(0);
808 
809       if (args.size() < command.minArgs || args.size() > command.maxArgs)
810       {
811         if (command.minArgs == command.maxArgs)
812         {
813           Print("'{}' requires {} argument{}", command.name, command.minArgs, (command.minArgs == 1) ? "" : "s");
814         }
815         else if (args.size() < command.minArgs)
816         {
817           Print("'{}' requires at least {} argument{}", command.name, command.minArgs,
818                 (command.minArgs == 1) ? "" : "s");
819         }
820         else
821         {
822           Print("'{}' requires no more than {} arguments", command.name, command.maxArgs);
823         }
824       }
825       else
826       {
827         void (*handler)(std::vector<std::string> *, int) = command.handler;
828         int num                                          = (args.size() > 0) ? atoi(args.at(0).c_str()) : 0;
829 
830         (*handler)(&args, num);
831         return 1;
832       }
833     }
834     else if (matches.size() == 0)
835     {
836       Print("I don't understand");
837     }
838     else
839     {
840       Print("Ambiguous command");
841     }
842   }
843 
844   return 0;
845 }
846 
MatchCommand(const std::string & cmd,std::vector<CommandEntry> & matches)847 void DebugConsole::MatchCommand(const std::string& cmd, std::vector<CommandEntry>& matches)
848 {
849   for (auto& command: commands)
850   {
851     size_t pos = command.name.find(cmd);
852     if (pos == 0 && pos != std::string::npos)
853       matches.push_back(command);
854   }
855 }
856 
857 // split an input line into command and arguments
858 // returns the command portion of the line
859 
SplitCommand(const std::string & line_in,std::vector<std::string> & args)860 std::string DebugConsole::SplitCommand(const std::string& line_in, std::vector<std::string>& args)
861 {
862     std::size_t start = 0;
863     std::string first;
864     std::size_t idx = 0;
865 
866     std::size_t end = line_in.find_first_of(" \t");
867 
868     while (end != std::string::npos) {
869         if (idx == 0)
870           first = line_in.substr(start, end - start);
871         else
872           args.push_back(line_in.substr(start, end - start));
873         start = end + 1;
874         end = line_in.find_first_of(" \t", start);
875         idx++;
876     }
877 
878     if (idx == 0)
879       first = line_in.substr(start, end - start);
880     else
881       args.push_back(line_in.substr(start));
882 
883     return first;
884 }
885 
886 // tab-expand the current command
ExpandCommand()887 void DebugConsole::ExpandCommand()
888 {
889   std::vector<std::string> args;
890   std::vector<CommandEntry> matches;
891 
892   if (!fBrowsingExpansion)
893   {
894     trim(fLine);
895     fLineToExpand = fLine;
896     fExpandIndex = 0;
897   }
898 
899   std::string cmd = SplitCommand(fLineToExpand, args);
900   if (cmd.size())
901   {
902     MatchCommand(cmd, matches);
903 
904     if (matches.size() > 0)
905     {
906       if (fExpandIndex >= matches.size())
907         fExpandIndex = 0;
908 
909       CommandEntry command = matches.at(fExpandIndex);
910       std::string newCommand(command.name);
911 
912       for (unsigned int i = 0; i < args.size(); i++)
913       {
914         newCommand.append(" ");
915         newCommand.append(args.at(i));
916       }
917 
918       if (args.size() < command.minArgs)
919         newCommand.append(" ");
920 
921       fLine = newCommand;
922     }
923   }
924 
925   if (matches.size() != 1)
926     NXE::Sound::SoundManager::getInstance()->playSfx(NXE::Sound::SFX::SND_TINK);
927 }
928