gcsx_console.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. /* GCSx
  2. ** CONSOLE.CPP
  3. **
  4. ** Console command support
  5. */
  6. /*****************************************************************************
  7. ** Copyright (C) 2003-2006 Janson
  8. **
  9. ** This program is free software; you can redistribute it and/or modify
  10. ** it under the terms of the GNU General Public License as published by
  11. ** the Free Software Foundation; either version 2 of the License, or
  12. ** (at your option) any later version.
  13. **
  14. ** This program is distributed in the hope that it will be useful,
  15. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ** GNU General Public License for more details.
  18. **
  19. ** You should have received a copy of the GNU General Public License
  20. ** along with this program; if not, write to the Free Software
  21. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  22. *****************************************************************************/
  23. #include "all.h"
  24. // Command structures
  25. enum {
  26. PARAM_NONE = 0,
  27. PARAM_STRING,
  28. PARAM_INT,
  29. PARAM_LINE, // Remainder of line as a string
  30. };
  31. struct ConsoleParam {
  32. string valueS; // Always set
  33. int valueI;
  34. };
  35. #define MAX_PARAM 3
  36. struct ConsoleCommand {
  37. const char* cmd;
  38. int gameMode;
  39. int editorMode;
  40. int param[MAX_PARAM];
  41. void (*function)(ConsoleParam* params);
  42. };
  43. // Forward declarations
  44. void cWorldQuery(ConsoleParam* params);
  45. void cWorldSet(ConsoleParam* params);
  46. void cDebugLevelSet(ConsoleParam* params);
  47. void cDebugLevelQuery(ConsoleParam* params);
  48. void cUndoBufferSet(ConsoleParam* params);
  49. void cUndoBufferQuery(ConsoleParam* params);
  50. void cMemExcept(ConsoleParam* params);
  51. void cUndoExcept(ConsoleParam* params);
  52. void cHelpAll(ConsoleParam* params);
  53. void cHelpCommand(ConsoleParam* params);
  54. void cClear(ConsoleParam* params);
  55. void cOpenGLTest(ConsoleParam* params);
  56. void cConfigList(ConsoleParam* params);
  57. void cConfigQuery(ConsoleParam* params);
  58. void cConfigSet(ConsoleParam* params);
  59. void cTextureTest(ConsoleParam* params);
  60. void cCompile(ConsoleParam* params);
  61. void cDecompile(ConsoleParam* params);
  62. // Valid commands and parameters
  63. ConsoleCommand commands[] = {
  64. { "clear", 1, 1, { PARAM_NONE, }, cClear },
  65. { "compile", 0, 1, { PARAM_LINE, PARAM_NONE }, cCompile },
  66. { "config", 1, 1, { PARAM_NONE, }, cConfigList },
  67. { "config", 1, 1, { PARAM_STRING, PARAM_NONE }, cConfigQuery },
  68. { "config", 1, 1, { PARAM_STRING, PARAM_INT, PARAM_NONE }, cConfigSet },
  69. { "config", 1, 1, { PARAM_STRING, PARAM_LINE, PARAM_NONE }, cConfigSet },
  70. { "debuglevel", 1, 1, { PARAM_NONE, }, cDebugLevelQuery },
  71. { "debuglevel", 1, 1, { PARAM_INT, PARAM_NONE }, cDebugLevelSet },
  72. { "decompile", 0, 1, { PARAM_LINE, PARAM_NONE }, cDecompile },
  73. { "help", 1, 1, { PARAM_NONE, }, cHelpAll },
  74. { "help", 1, 1, { PARAM_STRING, PARAM_NONE }, cHelpCommand },
  75. { "opengltest", 1, 1, { PARAM_NONE, }, cOpenGLTest },
  76. { "undobuffer", 0, 1, { PARAM_NONE, }, cUndoBufferQuery },
  77. { "undobuffer", 0, 1, { PARAM_INT, PARAM_NONE }, cUndoBufferSet },
  78. { "world", 0, 1, { PARAM_NONE, }, cWorldQuery },
  79. { "world", 0, 1, { PARAM_INT, PARAM_NONE }, cWorldSet },
  80. #ifdef MEMDEBUG
  81. { "memexcept", 1, 1, { PARAM_INT, PARAM_NONE }, cMemExcept },
  82. #endif
  83. #ifndef NDEBUG
  84. { "texturetest",1, 1, { PARAM_INT, PARAM_INT, PARAM_INT }, cTextureTest },
  85. { "undoexcept", 0, 1, { PARAM_INT, PARAM_NONE }, cUndoExcept },
  86. #endif
  87. // @TODO: undo, undolist, redo, redolist, undoclear, redoclear
  88. // @TODO: fileexcept (debug only)
  89. // @TODO: exit, editor, frontend
  90. // @TODO: history
  91. { NULL, 0, 0, { PARAM_NONE }, NULL },
  92. };
  93. // Currently selected world, required for many functions
  94. WorldEdit* selectedWorld = NULL;
  95. WorldEdit* verifySelectedWorld() { start_func
  96. if (selectedWorld) {
  97. if (WorldEdit::verifyWorld(selectedWorld)) return selectedWorld;
  98. selectedWorld = NULL;
  99. debugWrite("(previously active world no longer available)");
  100. }
  101. if (WorldEdit::countWorlds() == 0) {
  102. debugWrite("ERROR: No worlds currently open.");
  103. return NULL;
  104. }
  105. if (WorldEdit::countWorlds() == 1) {
  106. selectedWorld = WorldEdit::listWorlds(0);
  107. debugWrite("(selecting world \"%s\" as active)", selectedWorld->getTitle().c_str());
  108. return selectedWorld;
  109. }
  110. debugWrite("ERROR: No active world- use 'world' to select an active world.");
  111. return NULL;
  112. }
  113. // Command functions
  114. void cCompile(ConsoleParam* params) { start_func
  115. WorldEdit* world = verifySelectedWorld();
  116. if (world) {
  117. toLower(params[0].valueS);
  118. ScriptEdit* script = dynamic_cast<ScriptEdit*>(world->findScriptCode(params[0].valueS));
  119. if (script == NULL) script = dynamic_cast<ScriptEdit*>(world->findScriptLib(params[0].valueS));
  120. if (script == NULL) {
  121. debugWrite("ERROR: No script '%s' found in active world", params[0].valueS.c_str());
  122. }
  123. else {
  124. // @TODO: throw_File; does this properly store the compiled form?
  125. script->markLock();
  126. script->compile();
  127. script->markUnlock();
  128. }
  129. }
  130. }
  131. // @TODO: Currently doesn't work (and disabled) during gameplay mode
  132. void cDecompile(ConsoleParam* params) { start_func
  133. WorldEdit* world = verifySelectedWorld();
  134. if (world) {
  135. toLower(params[0].valueS);
  136. ScriptEdit* script = dynamic_cast<ScriptEdit*>(world->findScriptCode(params[0].valueS));
  137. if (script == NULL) script = dynamic_cast<ScriptEdit*>(world->findScriptLib(params[0].valueS));
  138. if (script == NULL) {
  139. debugWrite("ERROR: No script '%s' found in active world", params[0].valueS.c_str());
  140. }
  141. else {
  142. // @TODO: throw_File; does this properly store the compiled form?
  143. script->markLock();
  144. script->decompile();
  145. script->markUnlock();
  146. }
  147. }
  148. }
  149. void cOpenGLTest(ConsoleParam* params) { start_func
  150. int maxSize = config->readNum(OGL_MAX_SIZE);
  151. int minSize = config->readNum(OGL_MIN_SIZE);
  152. selectVideoMode(-1, -1, -1, -1, 0, 1);
  153. debugWrite("OpenGL tests-");
  154. // For simplicity- ensure test size is a power of 2, max 512
  155. int testSize = 1;
  156. while ((testSize <= maxSize) && (testSize < 1024)) {
  157. testSize *= 2;
  158. }
  159. testSize /= 2;
  160. // Now test every size from 512 down to 16, limited by minsize
  161. if (minSize < 16) minSize = 16;
  162. int textureCount = 1000;
  163. while (testSize >= minSize) {
  164. textureCount = oglTestPossibleTextures(testSize, testSize, 1, textureCount);
  165. // abort if no limit detected
  166. if (textureCount < 0) break;
  167. testSize /= 2;
  168. textureCount *= 4;
  169. }
  170. initDefaultVideo();
  171. }
  172. void cClear(ConsoleParam* params) { start_func
  173. debugClear();
  174. }
  175. void cWorldQuery(ConsoleParam* params) { start_func
  176. if (WorldEdit::countWorlds() == 0) {
  177. debugWrite("No worlds currently open.");
  178. }
  179. else {
  180. debugWrite("%d world(s) currently open:", WorldEdit::countWorlds());
  181. int pos = 0;
  182. while (WorldEdit* nextWorld = WorldEdit::listWorlds(pos++)) {
  183. if (nextWorld == selectedWorld) debugWrite("%d: %s (active)", pos, nextWorld->getTitle().c_str());
  184. else debugWrite("%d: %s", pos, nextWorld->getTitle().c_str());
  185. }
  186. }
  187. }
  188. void cWorldSet(ConsoleParam* params) { start_func
  189. WorldEdit* nextWorld = WorldEdit::listWorlds(params[0].valueI - 1);
  190. if (nextWorld == NULL) debugWrite("ERROR: No world %d open- use 'world' to list open worlds.", params[0].valueI);
  191. else {
  192. selectedWorld = nextWorld;
  193. debugWrite("Selecting world \"%s\" as active.", selectedWorld->getTitle().c_str());
  194. }
  195. }
  196. void cDebugLevelSet(ConsoleParam* params) { start_func
  197. if ((params[0].valueI >= 0) && (params[0].valueI <= DEBUG_MAX)) {
  198. debugWrite("Debuglevel changed to %d.", debugLevel(params[0].valueI));
  199. }
  200. else {
  201. debugWrite("ERROR: Invalid debuglevel- Debuglevel ranges from 0 to %d.", DEBUG_MAX);
  202. }
  203. }
  204. void cDebugLevelQuery(ConsoleParam* params) { start_func
  205. debugWrite("Current debuglevel is %d.", debugLevel());
  206. }
  207. void cUndoBufferSet(ConsoleParam* params) { start_func
  208. WorldEdit* world = verifySelectedWorld();
  209. if (world) {
  210. if ((params[0].valueI >= 0) && (params[0].valueI <= UndoBuffer::UNDO_MAX_BUFFER)) {
  211. world->undo.bufferSize(params[0].valueI);
  212. if (params[0].valueI) debugWrite("Undo buffer resized to %dK.", params[0].valueI);
  213. else debugWrite("Undo buffer has been disabled.");
  214. }
  215. else debugWrite("ERROR: Undo buffer must be between 0K and %dK.", UndoBuffer::UNDO_MAX_BUFFER);
  216. }
  217. }
  218. void cUndoBufferQuery(ConsoleParam* params) { start_func
  219. WorldEdit* world = verifySelectedWorld();
  220. if (world) {
  221. if (world->undo.bufferSize()) debugWrite("Undo buffer is currently %dK.", world->undo.bufferSize());
  222. else debugWrite("Undo buffer is currently disabled.");
  223. }
  224. }
  225. #define MAX_CONFIG_HELP 5
  226. struct configLabel {
  227. const char* cmd;
  228. int isString;
  229. int code;
  230. const char* help;
  231. };
  232. configLabel configNames[] = {
  233. // @TODO: output help using a word-wrapping function
  234. { "front_alpha", 0, FRONTEND_ACTIVE_ALPHA,
  235. "Translucency (alpha) for front-end menus/windows when activated during gameplay. Ranges from 0 to 256. (recommended- 192 to 256)",
  236. },
  237. { "front_bkalpha", 0, FRONTEND_ACTIVE_BKALPHA,
  238. "Translucency (alpha) for front-end background/desktop when activated during gameplay. Ranges from 0 to 256. (recommended- 0 to 128)",
  239. },
  240. { "front_overlay", 0, FRONTEND_INACTIVE_ALPHA,
  241. "Translucency (alpha) for front-end menus/windows when overlaid during gameplay, while game is still active. Ranges from 0 to 256. (recommended- 128 to 192)",
  242. },
  243. { "front_overlaybk", 0, FRONTEND_INACTIVE_BKALPHA,
  244. "Translucency (alpha) for front-end background/desktop when overlaid during gameplay, while game is still active. Ranges from 0 to 256. (recommended- 0)",
  245. },
  246. { "texture_max", 0, OGL_MAX_SIZE,
  247. "Maximum height/width for OpenGL textures. This value is auto-detected and, if changed, may prevent graphics from displaying properly.",
  248. },
  249. { "texture_megagroup", 0, OGL_PREFER_GROUPING,
  250. "Group as many OpenGL textures as possible, even at the expense of texture memory. If off, textures are only grouped in an attempt to conserve memory. (0 = off, 1 = on)",
  251. },
  252. { "texture_min", 0, OGL_MIN_SIZE,
  253. "Minimum height/width for OpenGL textures. This value is auto-detected and, if changed, may prevent graphics from displaying properly.",
  254. },
  255. { "texture_powertwo", 0, OGL_POWER_OF_TWO,
  256. "OpenGL textures must be power-of-two sizes. This value is auto-detected and, if changed, may prevent graphics from displaying properly. (0 = off, 1 = on)",
  257. },
  258. { "texture_single", 0, OGL_FORCE_SINGLE,
  259. "Never group OpenGL textures together- always store single textures. If on, textures are never grouped- even when doing so would conserve texture memory. This setting overrides 'texture_megagroup'. (0 = off, 1 = on)",
  260. },
  261. { "linked_retain", 0, LINKED_RETAIN,
  262. "Retain linked version of bytecode in memory during gameplay. Increases memory usage while reducing scene-transition and script-loading times for scenes and scripts previously used. (0 = off, 1 = on)",
  263. },
  264. { "linked_pregen", 0, LINKED_PREGENERATE,
  265. "Pre-generate and retain linked version of bytecode for all scripts before gameplay. Increases game start time and memory usage while reducing all future scene-transition and script-loading times. This setting obsoletes 'linked_retain'. (0 = off, 1 = on)",
  266. },
  267. { NULL, 0, 0, NULL },
  268. };
  269. void cConfigList(ConsoleParam* params) { start_func
  270. for (int pos = 0; configNames[pos].cmd; ++pos) {
  271. if (configNames[pos].isString)
  272. debugWrite("%s: %s", configNames[pos].cmd, config->readStr(configNames[pos].code).c_str());
  273. else
  274. debugWrite("%s: %d", configNames[pos].cmd, config->readNum(configNames[pos].code));
  275. }
  276. }
  277. void cConfigQuery(ConsoleParam* params) { start_func
  278. for (int pos = 0; configNames[pos].cmd; ++pos) {
  279. if (myStricmp(params[0].valueS.c_str(), configNames[pos].cmd) == 0) {
  280. if (configNames[pos].isString)
  281. debugWrite("%s: %s", configNames[pos].cmd, config->readStr(configNames[pos].code).c_str());
  282. else
  283. debugWrite("%s: %d", configNames[pos].cmd, config->readNum(configNames[pos].code));
  284. debugWrite(" %s", configNames[pos].help);
  285. return;
  286. }
  287. }
  288. debugWrite("No configuration setting '%s' found. Type 'config' for a list.", params[0].valueS.c_str());
  289. }
  290. // @TODO: apply change for some settings (ex: front_*)
  291. void cConfigSet(ConsoleParam* params) { start_func
  292. for (int pos = 0; configNames[pos].cmd; ++pos) {
  293. if (myStricmp(params[0].valueS.c_str(), configNames[pos].cmd) == 0) {
  294. if (configNames[pos].isString) {
  295. debugWrite("%s: Previous setting: %s", configNames[pos].cmd, config->readStr(configNames[pos].code).c_str());
  296. config->write(configNames[pos].code, params[1].valueS);
  297. debugWrite("%s: New setting: %d", configNames[pos].cmd, config->readStr(configNames[pos].code).c_str());
  298. }
  299. else {
  300. debugWrite("%s: Previous setting: %d", configNames[pos].cmd, config->readNum(configNames[pos].code));
  301. config->write(configNames[pos].code, params[1].valueI);
  302. debugWrite("%s: New setting: %d", configNames[pos].cmd, config->readNum(configNames[pos].code));
  303. }
  304. return;
  305. }
  306. }
  307. debugWrite("No configuration setting '%s' found. Type 'config' for a list.", params[0].valueS.c_str());
  308. }
  309. #ifdef MEMDEBUG
  310. void cMemExcept(ConsoleParam* params) { start_func
  311. if (params[0].valueI >= 0) {
  312. // Turn off to debug write
  313. setMemThreshold(0);
  314. if (params[0].valueI) debugWrite("Memory fail threshold set to %d bytes.", params[0].valueI);
  315. else debugWrite("Memory fail threshold disabled.");
  316. setMemThreshold(params[0].valueI);
  317. }
  318. else debugWrite("ERROR: Memory fail threshold must be a positive value or zero.");
  319. }
  320. #endif
  321. #ifndef NDEBUG
  322. void cUndoExcept(ConsoleParam* params) { start_func
  323. if (params[0].valueI >= 0) {
  324. UndoBuffer::setUndoThreshold(params[0].valueI);
  325. if (params[0].valueI) debugWrite("Undo fail threshold set to %d item(s).", params[0].valueI);
  326. else debugWrite("Undo fail threshold disabled.");
  327. }
  328. else debugWrite("ERROR: Undo fail threshold must be a positive value or zero.");
  329. }
  330. void cTextureTest(ConsoleParam* params) { start_func
  331. int w = params[0].valueI;
  332. int h = params[1].valueI;
  333. int count = params[2].valueI;
  334. int max = config->readNum(OGL_MAX_SIZE);
  335. if ((w <= 0) || (h <= 0) || (count <= 0))
  336. debugWrite("ERROR: All parameters must be greater than zero.");
  337. else if ((w > max) || (h > max))
  338. debugWrite("ERROR: Size is larger than maximum texture size.");
  339. else {
  340. debugWrite("Storing %d graphics (%d x %d)", count, w, h);
  341. vector<TextureMap::tGroup> sizes;
  342. TextureMap::groupTextures(count, w, h, sizes);
  343. for (vector<TextureMap::tGroup>::iterator pos = sizes.begin(); pos != sizes.end(); ++pos)
  344. debugWrite(" %d of %d x %d textures", (*pos).qty, (*pos).texW, (*pos).texH);
  345. }
  346. }
  347. #endif
  348. const char* helpText[] = {
  349. "debuglevel",
  350. "Sets or views current debugging level. Each level outputs various details"
  351. "on current activities to the console window. Multiple levels may be added"
  352. "together. Available levels:",
  353. " 0 - No debugging output",
  354. " 1 - Undo",
  355. " 2 - Compilation",
  356. " 4 - Tokenization",
  357. " 8 - Bytecode",
  358. #ifdef NDEBUG
  359. #ifdef MEMDEBUG
  360. " 16- Memory (outputs to stdout.txt only)",
  361. " 32- Trace (outputs to stdout.txt only)",
  362. #endif
  363. #else
  364. #ifdef MEMDEBUG
  365. " 16- Interpreter",
  366. " 32- Memory (outputs to stdout.txt only)",
  367. " 64- Trace (outputs to stdout.txt only)",
  368. #else
  369. " 16- Interpreter",
  370. #endif
  371. #endif
  372. NULL,
  373. "clear",
  374. "Clears the console buffer.",
  375. NULL,
  376. "compile",
  377. "Recompiles the named script, displaying all warnings and errors to the "
  378. "console window.",
  379. NULL,
  380. "config",
  381. "Views or sets configuration settings. Type 'config' alone to list all "
  382. "possible settings, and their current values. Type 'config' followed by "
  383. "a setting name to view a single current setting. Type 'config' followed "
  384. "by a setting and a new value to change a setting.",
  385. NULL,
  386. "decompile",
  387. "Disassemble a script's bytecode and output to the console window.",
  388. NULL,
  389. "help",
  390. "Retrieves help on a specific command. Type 'help' alone to list all "
  391. "available commands. Type 'help' followed by a command name to get help "
  392. "on that specific command.",
  393. NULL,
  394. "opengltest",
  395. "Runs various OpenGL tests and reports on the results. "
  396. "(WARNING: GCSx MAY STOP RESPONDING FOR UP TO A MINUTE)",
  397. NULL,
  398. "undobuffer",
  399. "Sets or views the size of the undo buffer for the active world, in K. An "
  400. "undo buffer of 0K will disable undo functionality. Changing the buffer "
  401. "size clears all undo/redo for that world.",
  402. NULL,
  403. "world",
  404. "Sets or views active world. Many commands only operate on a single, open "
  405. "world. Type 'world' alone to list all available worlds and the currently "
  406. "active world. Type 'world' followed by a number to select the active world "
  407. "to use for commands.",
  408. NULL,
  409. #ifdef MEMDEBUG
  410. "memexcept",
  411. "Sets a level above which all memory allocations will fail, for testing "
  412. "purposes. Specify a number in bytes, or 0 to disable this feature. NOTE: "
  413. "most failed memory allocations will result in GCSx shutting down.",
  414. NULL,
  415. #endif
  416. #ifndef NDEBUG
  417. "texturetest",
  418. "Follow with a width and height in pixels, and a count. This will attempt "
  419. "to arrange the given number of graphics, of the given size, and return "
  420. "how many textures of what size will be used, based on current settings. "
  421. "Intented solely for debugging texture allocation algorithms.",
  422. NULL,
  423. "undoexcept",
  424. "Sets a number of actions at which all undo storage attempts will fail, "
  425. "for testing purposes. For example, 2 actions will cause an undo warning "
  426. "when the second action is attempted. Specify a number of actions, or 0 "
  427. "to disable this feature. NOTE: Some operations cause multiple undo "
  428. "actions.",
  429. NULL,
  430. #endif
  431. NULL,
  432. };
  433. vector<string>* allCommandsList = NULL;
  434. #define HELP_COLUMNS 4
  435. void cHelpAll(ConsoleParam* params) { start_func
  436. debugWrite("Available commands:");
  437. if (!allCommandsList) {
  438. set<string> allCommands;
  439. unsigned int maxCommandLength = 0;
  440. allCommandsList = new vector<string>;
  441. // Sort, count, and size all commands
  442. for (int cmdnum = 0; commands[cmdnum].cmd; ++cmdnum) {
  443. if (strlen(commands[cmdnum].cmd) > maxCommandLength) {
  444. maxCommandLength = strlen(commands[cmdnum].cmd);
  445. }
  446. allCommands.insert(commands[cmdnum].cmd);
  447. }
  448. // Number of rows?
  449. int rows = allCommands.size() / HELP_COLUMNS + ((allCommands.size() % HELP_COLUMNS) ? 1 : 0);
  450. allCommandsList->reserve(rows);
  451. // Generate columns (not particularly efficient, but who cares?)
  452. int spot = 0;
  453. int firstTrip = 1;
  454. for (set<string>::iterator pos = allCommands.begin(); pos != allCommands.end(); ++pos) {
  455. if (firstTrip) allCommandsList->push_back(*pos);
  456. else (*allCommandsList)[spot] += *pos;
  457. unsigned int pad = (*pos).length();
  458. while (pad++ <= maxCommandLength) {
  459. (*allCommandsList)[spot] += " ";
  460. }
  461. if (++spot >= rows) {
  462. firstTrip = 0;
  463. spot = 0;
  464. }
  465. }
  466. }
  467. for (vector<string>::iterator pos = allCommandsList->begin(); pos != allCommandsList->end(); ++pos) {
  468. debugWrite(" %s", (*pos).c_str());
  469. }
  470. debugWrite("Type 'help <command>' for details");
  471. }
  472. void cHelpCommand(ConsoleParam* params) { start_func
  473. // Lowercase
  474. toLower(params[0].valueS);
  475. for (int pos = 0; helpText[pos]; ++pos) {
  476. if (!strcmp(helpText[pos], params[0].valueS.c_str())) {
  477. for (++pos; helpText[pos]; ++pos) {
  478. debugWrite(helpText[pos]);
  479. }
  480. return;
  481. }
  482. for (++pos; helpText[pos]; ++pos) ;
  483. }
  484. debugWrite("No help available for '%s', type 'help' for a list of commands.", params[0].valueS.c_str());
  485. }
  486. // Console parser
  487. void consoleParse(const string& cmd) { start_func
  488. // Skip any leading spaces
  489. string::size_type pos = 0;
  490. pos = cmd.find_first_not_of(' ', pos);
  491. // (only spaces present)
  492. if (pos == string::npos) return;
  493. // Grab command- text up to next space or end of line
  494. string::size_type paramstart = cmd.find_first_of(' ', pos);
  495. string command = cmd.substr(pos, paramstart == string::npos ? string::npos : paramstart - pos);
  496. // (find start of parameters too)
  497. paramstart = cmd.find_first_not_of(' ', paramstart);
  498. // Lowercase
  499. toLower(command);
  500. // Find command (may be more than one match)
  501. int found = 0;
  502. ConsoleParam params[MAX_PARAM];
  503. for (int cmdnum = 0; commands[cmdnum].cmd; ++cmdnum) {
  504. if (!strcmp(command.c_str(), commands[cmdnum].cmd)) {
  505. found = 1;
  506. // Check parameters
  507. string::size_type parampos = paramstart;
  508. int invalid = 0;
  509. for (int paramnum = 0; paramnum < MAX_PARAM; ++paramnum) {
  510. if (commands[cmdnum].param[paramnum] == PARAM_NONE) {
  511. if (parampos != string::npos) invalid = 1;
  512. // (no more params)
  513. break;
  514. }
  515. else if (parampos == string::npos) {
  516. invalid = 1;
  517. }
  518. else {
  519. string::size_type paramend = cmd.find_first_of(' ', parampos);
  520. if (commands[cmdnum].param[paramnum] == PARAM_LINE) paramend = string::npos;
  521. params[paramnum].valueS = cmd.substr(parampos, paramend == string::npos ? string::npos : paramend - parampos);
  522. parampos = cmd.find_first_not_of(' ', paramend);
  523. params[paramnum].valueI = 0;
  524. // More specific types?
  525. if (commands[cmdnum].param[paramnum] == PARAM_INT) {
  526. try {
  527. params[paramnum].valueI = strToIntErr(params[paramnum].valueS);
  528. }
  529. catch (int) {
  530. invalid = 1;
  531. }
  532. }
  533. }
  534. }
  535. if (!invalid) {
  536. // Found match!
  537. if ((!commands[cmdnum].editorMode) && (getProgramMode() == MODE_EDITOR)) {
  538. debugWrite("Sorry, '%s' is only available in gameplay mode.", command.c_str());
  539. }
  540. else if ((!commands[cmdnum].gameMode) && (getProgramMode() != MODE_EDITOR)) {
  541. debugWrite("Sorry, '%s' is only available in editor mode.", command.c_str());
  542. }
  543. else {
  544. commands[cmdnum].function(params);
  545. }
  546. return;
  547. }
  548. }
  549. }
  550. // Not found or invalid parameters
  551. if (found) {
  552. // Invalid
  553. for (int cmdnum = 0; commands[cmdnum].cmd; ++cmdnum) {
  554. if (!strcmp(command.c_str(), commands[cmdnum].cmd)) {
  555. string usage;
  556. for (int paramnum = 0; paramnum < MAX_PARAM; ++paramnum) {
  557. switch (commands[cmdnum].param[paramnum]) {
  558. case PARAM_NONE:
  559. paramnum = MAX_PARAM;
  560. break;
  561. case PARAM_STRING:
  562. usage += " <string>";
  563. break;
  564. case PARAM_INT:
  565. usage += " <integer>";
  566. break;
  567. case PARAM_LINE:
  568. usage += " <string...>";
  569. break;
  570. }
  571. }
  572. debugWrite("Usage: %s%s", command.c_str(), usage.c_str());
  573. }
  574. }
  575. debugWrite("Type 'help %s' for details", command.c_str());
  576. }
  577. else {
  578. debugWrite("Unrecognized command '%s', type 'help' for help", command.c_str());
  579. }
  580. }
  581. // Cleanup
  582. void destroyConsoleGlobals() { start_func
  583. if (allCommandsList) {
  584. delete allCommandsList;
  585. allCommandsList = NULL;
  586. }
  587. }