1 |
#include <windows.h> |
2 |
#include <string.h> |
3 |
#include <time.h> |
4 |
|
5 |
#include "Daodan_Config.h" |
6 |
#include "Daodan_Patch.h" |
7 |
#include "Patches/Utility.h" |
8 |
|
9 |
#include "Oni/Oni.h" |
10 |
#include "_Version.h" |
11 |
|
12 |
#include "Inifile_Reader.h" |
13 |
|
14 |
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) |
15 |
|
16 |
static const char* iniName = "daodan.ini"; |
17 |
static const char* helpFile = "daodan_help.txt"; |
18 |
|
19 |
static const char* defaultSection = "options"; |
20 |
|
21 |
void DDrConfig_PrintHelp(); |
22 |
|
23 |
|
24 |
ConfigSection_t config[] = { |
25 |
{ "patches", "Patches", { |
26 |
{ "alttab", |
27 |
"Allows user to switch applications while in Oni (Alt-Tab) and use Windows key, however it may enable the screensaver as well.", |
28 |
C_BOOL, |
29 |
{.intBoolVal = true}, |
30 |
{.intBoolVal = true} }, |
31 |
{ "argb8888", |
32 |
"Textures using ARGB8888 can be used.", |
33 |
C_BOOL, |
34 |
{.intBoolVal = true}, |
35 |
{.intBoolVal = true} }, |
36 |
{ "binkplay", |
37 |
"Fix binkplay calls to use GDI and outro same mode as intro.", |
38 |
C_BOOL, |
39 |
{.intBoolVal = true}, |
40 |
{.intBoolVal = true} }, |
41 |
{ "bsl", |
42 |
"Enables d_regen (unfinished) and prevents fly-in portraits from being stretched when playing in widescreen resolutions.", |
43 |
C_BOOL, |
44 |
{.intBoolVal = true}, |
45 |
{.intBoolVal = true} }, |
46 |
{ "cheater", |
47 |
"Adds new cheat codes (see section below).", |
48 |
C_BOOL, |
49 |
{.intBoolVal = true}, |
50 |
{.intBoolVal = true} }, |
51 |
{ "cheatsenabled", |
52 |
"Enables cheats without having to beat the game first.", |
53 |
C_BOOL, |
54 |
{.intBoolVal = true}, |
55 |
{.intBoolVal = true} }, |
56 |
{ "cheattable", |
57 |
"Replaces Oni's cheat table with table that includes new cheats (see section below).", |
58 |
C_BOOL, |
59 |
{.intBoolVal = true}, |
60 |
{.intBoolVal = true} }, |
61 |
{ "chinese", |
62 |
"Allow for chinese fonts to be shown.", |
63 |
C_BOOL, |
64 |
{.intBoolVal = true}, |
65 |
{.intBoolVal = true} }, |
66 |
{ "clipcursor", |
67 |
"Limit cursor to Oni's window.", |
68 |
C_BOOL, |
69 |
{.intBoolVal = true}, |
70 |
{.intBoolVal = true} }, |
71 |
{ "cooldowntimer", |
72 |
"Disables weapon cooldown exploit.", |
73 |
C_BOOL, |
74 |
{.intBoolVal = true}, |
75 |
{.intBoolVal = true} }, |
76 |
{ "daodandisplayenum", |
77 |
"Offers more display modes in the Options menu.", |
78 |
C_BOOL, |
79 |
{.intBoolVal = true}, |
80 |
{.intBoolVal = true} }, |
81 |
{ "directinput", |
82 |
"Forces on DirectInput.", |
83 |
C_BOOL, |
84 |
{.intBoolVal = true}, |
85 |
{.intBoolVal = true} }, |
86 |
{ "disablecmdline", |
87 |
"Replaces existing command line parser with Daodan's in order to add new commands. Meant to be used with getcmdline.", |
88 |
C_BOOL, |
89 |
{.intBoolVal = true}, |
90 |
{.intBoolVal = true} }, |
91 |
{ "fonttexturecache", |
92 |
"Doubles size of font texture cache.", |
93 |
C_BOOL, |
94 |
{.intBoolVal = true}, |
95 |
{.intBoolVal = true} }, |
96 |
{ "getcmdline", |
97 |
"Replaces existing command line parser with Daodan's in order to add new commands. Meant to be used with disablecmdline.", |
98 |
C_BOOL, |
99 |
{.intBoolVal = true}, |
100 |
{.intBoolVal = true} }, |
101 |
{ "hdscreens_lowres", |
102 |
"Allow HD screens with resolution < 1024*768.", |
103 |
C_BOOL, |
104 |
{.intBoolVal = true}, |
105 |
{.intBoolVal = true} }, |
106 |
{ "highres_console", |
107 |
"Fixes bug where console line becomes invisible at higher resolutions.", |
108 |
C_BOOL, |
109 |
{.intBoolVal = true}, |
110 |
{.intBoolVal = true} }, |
111 |
{ "kickguns", |
112 |
"Unfinished, do not use.", |
113 |
C_BOOL, |
114 |
{.intBoolVal = false}, |
115 |
{.intBoolVal = false} }, |
116 |
{ "largetextures", |
117 |
"Textures up to 512x512 can be used.", |
118 |
C_BOOL, |
119 |
{.intBoolVal = true}, |
120 |
{.intBoolVal = true} }, |
121 |
{ "levelplugins", |
122 |
"Allows level files to be loaded from the GDF which do not end in \"_Final\".", |
123 |
C_BOOL, |
124 |
{.intBoolVal = true}, |
125 |
{.intBoolVal = true} }, |
126 |
{ "newweap", |
127 |
"Picking up a weapon displays a message containing the weapon name and amount of ammo.", |
128 |
C_BOOL, |
129 |
{.intBoolVal = true}, |
130 |
{.intBoolVal = true} }, |
131 |
{ "nomultibyte", |
132 |
"Enables languages which use multibyte coding (such as Chinese).", |
133 |
C_BOOL, |
134 |
{.intBoolVal = true}, |
135 |
{.intBoolVal = true} }, |
136 |
{ "optionsvisible", |
137 |
"Always show options button in main menu, even when pausing from a game.", |
138 |
C_BOOL, |
139 |
{.intBoolVal = true}, |
140 |
{.intBoolVal = true} }, |
141 |
{ "particledisablebit", |
142 |
"Unlocks particle action disabling/enabling bits for all events so that a particle event can occur multiple times.", |
143 |
C_BOOL, |
144 |
{.intBoolVal = false}, |
145 |
{.intBoolVal = false} }, |
146 |
{ "pathfinding", |
147 |
"Multiplies size of pathfinding grid cache by eight in order to prevent crashes in large levels.", |
148 |
C_BOOL, |
149 |
{.intBoolVal = true}, |
150 |
{.intBoolVal = true} }, |
151 |
{ "projaware", |
152 |
"Allows AI to dodge incoming gunfire properly.", |
153 |
C_BOOL, |
154 |
{.intBoolVal = true}, |
155 |
{.intBoolVal = true} }, |
156 |
{ "safeprintf", |
157 |
"Replaces Oni's function that prints to startup.txt with a safer one.", |
158 |
C_BOOL, |
159 |
{.intBoolVal = true}, |
160 |
{.intBoolVal = true} }, |
161 |
{ "showalllasersights", |
162 |
"Show all (also enemies') weapon lasersights.", |
163 |
C_BOOL, |
164 |
{.intBoolVal = false}, |
165 |
{.intBoolVal = false} }, |
166 |
{ "showtriggervolumes", |
167 |
"Allows BSL variable \"show_triggervolumes\" to work when set to 1.", |
168 |
C_BOOL, |
169 |
{.intBoolVal = true}, |
170 |
{.intBoolVal = true} }, |
171 |
{ "throwtest", |
172 |
"Not recommended for use; experiment with allowing enemies to be thrown over railings.", |
173 |
C_BOOL, |
174 |
{.intBoolVal = false}, |
175 |
{.intBoolVal = false} }, |
176 |
{ "usedaodangl", |
177 |
"Provides an improved windowed mode (-noswitch); this patch is known to break the hiding of the Windows taskbar in fullscreen mode.", |
178 |
C_BOOL, |
179 |
{.intBoolVal = true}, |
180 |
{.intBoolVal = true} }, |
181 |
{ "usegettickcount", |
182 |
"Replaces Oni's timing functions with more accurate ones.", |
183 |
C_BOOL, |
184 |
{.intBoolVal = true}, |
185 |
{.intBoolVal = true} }, |
186 |
{ "wpfadetime", |
187 |
"Adds working function for existing BSL command wp_fadetime, sets fade time to 4800.", |
188 |
C_BOOL, |
189 |
{.intBoolVal = true}, |
190 |
{.intBoolVal = true} }, |
191 |
{ 0, 0, 0, {0}, {0} } |
192 |
} }, |
193 |
{ "options", "Options", { |
194 |
{ "border", |
195 |
"If \"windowhack\" patch is active, make sure game window has border in windowed mode.", |
196 |
C_BOOL, |
197 |
{.intBoolVal = true}, |
198 |
{.intBoolVal = true} }, |
199 |
{ "debug", |
200 |
"???", |
201 |
EXT_BOOL, |
202 |
{.intBoolVal = false }, |
203 |
{.extBoolVal = &AKgDebug_DebugMaps } }, |
204 |
{ "debugfiles", |
205 |
"???", |
206 |
EXT_BOOL, |
207 |
{.intBoolVal = false }, |
208 |
{.extBoolVal = &BFgDebugFileEnable } }, |
209 |
{ "findsounds", |
210 |
"???", |
211 |
EXT_BOOL, |
212 |
{.intBoolVal = false }, |
213 |
{.extBoolVal = &SSgSearchOnDisk } }, |
214 |
{ "gamma", |
215 |
"Enable gamma slider in fullscreen.", |
216 |
C_BOOL, |
217 |
{.intBoolVal = true}, |
218 |
{.intBoolVal = true} }, |
219 |
{ "help", |
220 |
"Generates this help file.", |
221 |
C_CMD, |
222 |
{.intBoolVal = 0}, |
223 |
{.callback = DDrConfig_PrintHelp} }, |
224 |
{ "ignore_private_data", |
225 |
"? No effect ?", |
226 |
EXT_BOOL, |
227 |
{.intBoolVal = false }, |
228 |
{.extBoolVal = &opt_ignore_private_data } }, |
229 |
{ "sound", |
230 |
"???", |
231 |
EXT_BOOL, |
232 |
{.intBoolVal = true }, |
233 |
{.extBoolVal = &opt_sound } }, |
234 |
{ "switch", |
235 |
"Always switch screen to resolution on Oni's Options screen, making the game fullscreen; opposite of Oni's built-in argument \"noswitch\".", |
236 |
EXT_BOOL, |
237 |
{.intBoolVal = true}, |
238 |
{.extBoolVal = &M3gResolutionSwitch} }, |
239 |
{ "topmost", |
240 |
"Keep game window on top in windowed mode, even when switching applications.", |
241 |
C_BOOL, |
242 |
{.intBoolVal = false}, |
243 |
{.intBoolVal = false} }, |
244 |
{ "usedaodanbsl", |
245 |
"Adds new BSL commands (see below).", |
246 |
C_BOOL, |
247 |
{.intBoolVal = true}, |
248 |
{.intBoolVal = true} }, |
249 |
{ "language", |
250 |
"Localization for hardcoded strings (e.g. \"Savepoints\").", |
251 |
C_STRING, |
252 |
{.stringVal = "en"}, |
253 |
{.stringVal = "en"} }, |
254 |
{ 0, 0, 0, {0}, {0} } |
255 |
} } |
256 |
}; |
257 |
|
258 |
|
259 |
void DDrConfig_Print() |
260 |
{ |
261 |
for (unsigned int s = 0; s < ARRAY_SIZE(config); s++) { |
262 |
for (ConfigOption_t* co = config[s].options; co->name != 0; co++) { |
263 |
switch (co->type) { |
264 |
case C_STRING: |
265 |
STARTUPMESSAGE("Option %s.%s = %s (def %s)", config[s].name, co->name, co->value.stringVal, co->defaultValue.stringVal); |
266 |
break; |
267 |
case EXT_BOOL: |
268 |
STARTUPMESSAGE("Option %s.%s = %d (def %d)", config[s].name, co->name, *co->value.extBoolVal, co->defaultValue.intBoolVal); |
269 |
break; |
270 |
case C_CMD: |
271 |
break; |
272 |
default: |
273 |
STARTUPMESSAGE("Option %s.%s = %d (def %d)", config[s].name, co->name, co->value.intBoolVal, co->defaultValue.intBoolVal); |
274 |
} |
275 |
} |
276 |
} |
277 |
} |
278 |
|
279 |
void DDrConfig_PrintHelp() |
280 |
{ |
281 |
STARTUPMESSAGE("Writing Daodan help file (%s)", helpFile); |
282 |
|
283 |
FILE* fp; |
284 |
remove(helpFile); |
285 |
fp = fopen(helpFile, "w"); |
286 |
if (fp) |
287 |
{ |
288 |
time_t rawtime; |
289 |
struct tm* timeinfo; |
290 |
char buffer[80]; |
291 |
time(&rawtime); |
292 |
timeinfo = localtime(&rawtime); |
293 |
strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", timeinfo); |
294 |
|
295 |
fprintf(fp, "Daodan help - generated on %s for Daodan v."DAODAN_VERSION_STRING"\n\n", buffer); |
296 |
fprintf(fp, "List of Daodan configuration parameters:\n"); |
297 |
for (unsigned int s = 0; s < ARRAY_SIZE(config); s++) { |
298 |
fprintf(fp, " %s - %s:\n", config[s].name, config[s].description); |
299 |
for (ConfigOption_t* co = config[s].options; co->name != 0; co++) { |
300 |
char* name = co->name; |
301 |
char* desc = co->description; |
302 |
const char* tName = DDrConfig_GetOptionTypeName(co->type); |
303 |
int boolV = co->defaultValue.intBoolVal; |
304 |
char* val; |
305 |
switch (co->type) { |
306 |
case C_STRING: |
307 |
val = co->defaultValue.stringVal; |
308 |
break; |
309 |
case EXT_BOOL: |
310 |
val = (boolV ? "true" : "false"); |
311 |
break; |
312 |
case C_BOOL: |
313 |
val = (boolV ? "true" : "false"); |
314 |
break; |
315 |
case C_CMD: |
316 |
val = ""; |
317 |
break; |
318 |
default: |
319 |
val = malloc(20); |
320 |
sprintf(val, "%d", boolV); |
321 |
} |
322 |
fprintf(fp, " %-22s %6s=%-5s %s\n", name, tName, val, desc); |
323 |
} |
324 |
fprintf(fp, "\n"); |
325 |
} |
326 |
fprintf(fp, "\nConfiguration parameters can be either set in daodan.ini or passed on command line.\n\n"); |
327 |
fprintf(fp, "In daodan.ini each section of parameters has its own section in the ini file started by [section name]. Parameters are given within that section by their name only, followed by an equals sign and the desired value. Example:\n"); |
328 |
fprintf(fp, " [sectionX]\n parameterName = false\n"); |
329 |
fprintf(fp, "\nTo pass the parameter on the command line:\n"); |
330 |
fprintf(fp, " Oni.exe -sectionX.parameterName false\n"); |
331 |
fprintf(fp, "For bool parameters the value can be ommitted so it is regarded as \"true\":\n"); |
332 |
fprintf(fp, " Oni.exe -sectionX.parameterName\n"); |
333 |
fprintf(fp, "To disable a bool parameter you can prefix \"no\" to the parameter name like this:\n"); |
334 |
fprintf(fp, " Oni.exe -sectionX.noparameterName\n"); |
335 |
fprintf(fp, "If no section is given it is assumed to be \"%s\", e.g.\n", defaultSection); |
336 |
fprintf(fp, " Oni.exe -%s.parametername\n", defaultSection); |
337 |
fprintf(fp, "can simply be written as\n"); |
338 |
fprintf(fp, " Oni.exe -parametername\n"); |
339 |
|
340 |
fclose(fp); |
341 |
} |
342 |
else |
343 |
{ |
344 |
STARTUPMESSAGE("Writing Daodan help file failed", 0); |
345 |
} |
346 |
} |
347 |
|
348 |
const char* DDrConfig_GetOptionTypeName(OptionType_t type) |
349 |
{ |
350 |
switch (type) { |
351 |
case C_INT: |
352 |
return "Int"; |
353 |
case C_BOOL: |
354 |
return "Bool"; |
355 |
case C_STRING: |
356 |
return "String"; |
357 |
case C_CMD: |
358 |
return "Cmd"; |
359 |
case EXT_BOOL: |
360 |
return "pBool"; |
361 |
default: |
362 |
return "unknown"; |
363 |
} |
364 |
} |
365 |
|
366 |
static ConfigOption_t* DDrConfig_GetOption(const char* fullOptName) |
367 |
{ |
368 |
char section[50]; |
369 |
strcpy(section, fullOptName); |
370 |
|
371 |
char* option = strchr(section, '.'); |
372 |
if (option == 0) { |
373 |
STARTUPMESSAGE("Could not find option separator in \"%s\"", fullOptName); |
374 |
return 0; |
375 |
} |
376 |
*option++ = 0; |
377 |
|
378 |
for (unsigned int s = 0; s < ARRAY_SIZE(config); s++) { |
379 |
if (!_stricmp(config[s].name, section)) { |
380 |
for (ConfigOption_t* co = config[s].options; co->name != 0; co++) { |
381 |
if (!_stricmp(co->name, option)) { |
382 |
return co; |
383 |
} |
384 |
} |
385 |
STARTUPMESSAGE("Could not find option \"%s\" in section \"%s\"", option, section); |
386 |
return 0; |
387 |
} |
388 |
} |
389 |
STARTUPMESSAGE("Could not find section \"%s\" for option \"%s\"", section, option); |
390 |
return 0; |
391 |
} |
392 |
|
393 |
|
394 |
|
395 |
ConfigOption_t* DDrConfig_GetOptOfType(const char* fullOptName, OptionType_t type) |
396 |
{ |
397 |
ConfigOption_t* co = DDrConfig_GetOption(fullOptName); |
398 |
if (co == 0) |
399 |
return 0; |
400 |
|
401 |
if (co->type != type) { |
402 |
STARTUPMESSAGE("Option \"%s\" is not of type %s", fullOptName, DDrConfig_GetOptionTypeName(type)); |
403 |
return 0; |
404 |
} |
405 |
return co; |
406 |
} |
407 |
|
408 |
|
409 |
|
410 |
void DDrConfig_InitExtBools() |
411 |
{ |
412 |
for (unsigned int s = 0; s < ARRAY_SIZE(config); s++) { |
413 |
for (ConfigOption_t* co = config[s].options; co->name != 0; co++) { |
414 |
if (co->type == EXT_BOOL) { |
415 |
*co->value.extBoolVal = co->defaultValue.intBoolVal; |
416 |
} |
417 |
} |
418 |
} |
419 |
} |
420 |
|
421 |
|
422 |
|
423 |
void DDrConfig_WriteTemplateIni() |
424 |
{ |
425 |
FILE* fp; |
426 |
STARTUPMESSAGE("%s doesn't exist, creating", iniName); |
427 |
fp = fopen(iniName, "w"); |
428 |
if (fp) |
429 |
{ |
430 |
for (unsigned int s = 0; s < ARRAY_SIZE(config); s++) { |
431 |
fprintf(fp, "[%s]\n", config[s].name); |
432 |
} |
433 |
fclose(fp); |
434 |
} |
435 |
else |
436 |
{ |
437 |
STARTUPMESSAGE("Writing %s template file failed", iniName); |
438 |
} |
439 |
} |
440 |
|
441 |
|
442 |
void DDrIniCallback(const char* section, const char* name, const char* value) |
443 |
{ |
444 |
static char curSection[20]; |
445 |
char fullOptName[50]; |
446 |
|
447 |
if (!_stricmp(section, "patch")) |
448 |
section = "patches"; |
449 |
|
450 |
strcpy(curSection, section); |
451 |
|
452 |
strcpy(fullOptName, curSection); |
453 |
fullOptName[strlen(curSection)] = '.'; |
454 |
strcpy(fullOptName+strlen(curSection)+1, name); |
455 |
|
456 |
ConfigOption_t* co = DDrConfig_GetOption(fullOptName); |
457 |
|
458 |
if (co) |
459 |
{ |
460 |
char* buf = 0; |
461 |
switch (co->type) { |
462 |
case C_INT: |
463 |
co->value.intBoolVal = strtol(value, NULL, 0); |
464 |
break; |
465 |
case C_BOOL: |
466 |
co->value.intBoolVal = !_stricmp(value, "true"); |
467 |
break; |
468 |
case C_STRING: |
469 |
buf = malloc(strlen(value)+1); |
470 |
strcpy(buf, value); |
471 |
co->value.stringVal = buf; |
472 |
break; |
473 |
case C_CMD: |
474 |
co->value.callback(); |
475 |
break; |
476 |
case EXT_BOOL: |
477 |
*(co->value.extBoolVal) = !_stricmp(value, "true"); |
478 |
break; |
479 |
default: |
480 |
STARTUPMESSAGE("Config value type unknown: %d", co->type); |
481 |
} |
482 |
} |
483 |
} |
484 |
|
485 |
|
486 |
bool DDrConfig_ParseCommandLine(int argc, char* argv[]) |
487 |
{ |
488 |
for (int i = 1; i < argc; i ++) |
489 |
{ |
490 |
if (argv[i][0] == '-') |
491 |
{ |
492 |
const char* section; |
493 |
char* optionsep; |
494 |
char* option; |
495 |
bool invertedOption; |
496 |
|
497 |
if ((optionsep = strchr(argv[i], '.'))) |
498 |
// Is "section.option" |
499 |
{ |
500 |
*optionsep = 0; |
501 |
option = optionsep+1; |
502 |
section = argv[i]+1; |
503 |
} |
504 |
else |
505 |
// Is just "option" |
506 |
{ |
507 |
section = defaultSection; |
508 |
option = argv[i]+1; |
509 |
} |
510 |
|
511 |
invertedOption = (option[0] == 'n' || option[0] == 'N') && (option[1] == 'o' || option[1] == 'O'); |
512 |
if (invertedOption) |
513 |
option += 2; |
514 |
|
515 |
if (i < (argc - 1) && argv[i+1][0] != '-') |
516 |
// Has value in next field |
517 |
{ |
518 |
DDrIniCallback(section, option, argv[++i]); |
519 |
} |
520 |
else |
521 |
// Implicit value |
522 |
{ |
523 |
DDrIniCallback(section, option, (invertedOption ? "false" : "true")); |
524 |
} |
525 |
|
526 |
if (optionsep) |
527 |
*optionsep = '.'; |
528 |
} |
529 |
else |
530 |
{ |
531 |
STARTUPMESSAGE("Parse error \"%s\"", argv[i]); |
532 |
return false; |
533 |
} |
534 |
} |
535 |
return true; |
536 |
} |
537 |
|
538 |
void DDrConfig(int argc, char* argv[]) |
539 |
{ |
540 |
STARTUPMESSAGE("Initializing standard booleans", 0); |
541 |
DDrConfig_InitExtBools(); |
542 |
|
543 |
if (GetFileAttributes(iniName) == INVALID_FILE_ATTRIBUTES) |
544 |
DDrConfig_WriteTemplateIni(); |
545 |
|
546 |
STARTUPMESSAGE("Parsing daodan.ini...", 0); |
547 |
if (!Inifile_Read(iniName, DDrIniCallback)) |
548 |
STARTUPMESSAGE("Error reading daodan.ini, check your syntax!", 0); |
549 |
STARTUPMESSAGE("Finished parsing", 0); |
550 |
|
551 |
|
552 |
|
553 |
STARTUPMESSAGE("Parsing command line...", 0); |
554 |
DDrConfig_ParseCommandLine(argc, argv); |
555 |
STARTUPMESSAGE("Finished parsing", 0); |
556 |
|
557 |
// DDrConfig_Print(); |
558 |
} |
559 |
|