#include #include #include #include "Daodan_Config.h" #include "Daodan_Patch.h" #include "Patches/Utility.h" #include "Oni/Oni.h" #include "_Version.h" #include "Inifile_Reader.h" #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) static const char* iniName = "daodan.ini"; static const char* helpFile = "daodan_help.txt"; static const char* defaultSection = "options"; static char invalidCurParamaters[2000] = ""; static char invalidTotalParamaters[4000] = ""; void DDrConfig_PrintHelp(); ConfigSection_t config[] = { { "", "Command line only", { { "help", "Generates this help file.", C_CMD, {.intBoolVal = 0}, {.callback = DDrConfig_PrintHelp} }, { 0, 0, 0, {0}, {0} } } }, { "devmode", "Developer Mode", { { "highres_console", "Fixes bug where console line becomes invisible at higher resolutions.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "showtriggervolumes", "Allows BSL variable \"show_triggervolumes\" and Ctrl+Shift+X (in devmode) to work.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { 0, 0, 0, {0}, {0} } } }, { "gameplay", "Gameplay", { { "characterawareness", "Makes AI remember the player.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "cheatsenabled", "Enables cheats without having to beat the game first.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "cheattable", "Replaces Oni's cheat table with table that includes new cheats including devmode.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "cooldowntimer", "Disables weapon cooldown exploit.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "kickguns", "EXPERIMENTAL! Unfinished, do not use.", C_BOOL, {.intBoolVal = false}, {.intBoolVal = false} }, { "pathfinding", "Size of pathfinding grid cache increased by eight times in order to prevent crashes in large levels.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "projaware", "Allows AI to dodge incoming gunfire properly.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "throwtest", "EXPERIMENTAL! Experiment with allowing enemies to be thrown over railings.", C_BOOL, {.intBoolVal = false}, {.intBoolVal = false} }, { "wpfadetime", "Adds working function for existing BSL command wp_fadetime, sets fade time to 4800.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { 0, 0, 0, {0}, {0} } } }, { "graphics", "Graphics", { { "binkplay", "Fix binkplay calls to use GDI and outro same mode as intro.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "daodangl", "Provides an improved windowed mode (-noswitch).", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "displayenum", "Offers a more accurate list of available display modes in the Options menu.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "gamma", "Enable gamma slider in fullscreen, disable in windowed mode.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "newweap", "Standing above a weapon displays a message containing the weapon name and amount of ammo.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "optionsvisible", "Always show options button in main menu, even when pausing from a game.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "showalllasersights", "Show all (also enemies') weapon lasersights.", C_BOOL, {.intBoolVal = false}, {.intBoolVal = false} }, { "widescreenportraits", "Prevents fly-in portraits from being stretched when playing in widescreen resolutions.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { 0, 0, 0, {0}, {0} } } }, { "language", "Language", { { "chinese", "Allow for chinese fonts to be shown if the required DLL is available.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "fonttexturecache", "Doubles size of font texture cache.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "language", "Localization for hardcoded strings (e.g. \"Savepoints\").", C_STRING, {.stringVal = "en"}, {.stringVal = "en"} }, { "nomultibyte", "Enables languages which use multibyte coding (such as Chinese).", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { 0, 0, 0, {0}, {0} } } }, { "modding", "Modding", { { "argb8888", "Allows using textures with ARGB8888.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "d_regen", "Enables script command d_regen (query/set regeneration for any character).", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "daodanbsl", "Adds new BSL commands.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "hdscreens_lowres", "Allow HD intro/ending screens on game resolutions smaller than 1024x768.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "largetextures", "Textures up to 512x512 can be used.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "levelplugins", "Allows level files to be loaded which do not end in \"_Final\".", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { 0, 0, 0, {0}, {0} } } }, { "oni", "Original Oni Options", { { "debug", "Not useful, probably does nothing.", EXT_BOOL, {.intBoolVal = false }, {.extBoolVal = &AKgDebug_DebugMaps } }, { "debugfiles", "Logs called BSL functions to script_debug.txt.", EXT_BOOL, {.intBoolVal = false }, {.extBoolVal = &BFgDebugFileEnable } }, { "findsounds", "Not useful, extends output of sound_list_broken_links.", EXT_BOOL, {.intBoolVal = false }, {.extBoolVal = &SSgSearchOnDisk } }, { "ignore_private_data", "Not useful, probably does nothing.", EXT_BOOL, {.intBoolVal = false }, {.extBoolVal = &opt_ignore_private_data } }, { "sound", "Enable sound.", EXT_BOOL, {.intBoolVal = true }, {.extBoolVal = &opt_sound } }, { "switch", "Switch to fullscreen instead of staying in a window.", EXT_BOOL, {.intBoolVal = true}, {.extBoolVal = &M3gResolutionSwitch} }, { 0, 0, 0, {0}, {0} } } }, { "windows", "Windows", { { "alttab", "Allows to Alt-Tab out of Oni and use Windows key. May enable the screensaver as well.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "border", "Add a border if in windowed mode and \"usedaodangl\" patch is active.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "clipcursor", "Limit cursor to Oni's window.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "directinput", "Enforces the usage of DirectInput on every system. Should be off for Linux/Wine.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "disablecmdline", "Disables Oni's existing command line parser as Daodan has its own.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "killvtune", "Prevent loading of vtuneapi.dll.", C_BOOL, {.intBoolVal = false}, {.intBoolVal = false} }, { "safeprintf", "Replaces Oni's function that prints to startup.txt with a safer one.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "topmost", "Keep game window on top in windowed mode, even when switching applications.", C_BOOL, {.intBoolVal = false}, {.intBoolVal = false} }, { "usegettickcount", "Replaces Oni's timing functions with more accurate ones.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { 0, 0, 0, {0}, {0} } } } }; void DDrConfig_Print() { for (unsigned int s = 0; s < ARRAY_SIZE(config); s++) { for (ConfigOption_t* co = config[s].options; co->name != 0; co++) { switch (co->type) { case C_STRING: STARTUPMESSAGE("Option %s.%s = %s (def %s)", config[s].name, co->name, co->value.stringVal, co->defaultValue.stringVal); break; case EXT_BOOL: STARTUPMESSAGE("Option %s.%s = %d (def %d)", config[s].name, co->name, *co->value.extBoolVal, co->defaultValue.intBoolVal); break; case C_CMD: break; default: STARTUPMESSAGE("Option %s.%s = %d (def %d)", config[s].name, co->name, co->value.intBoolVal, co->defaultValue.intBoolVal); } } } } void DDrConfig_PrintHelp() { STARTUPMESSAGE("Writing Daodan help file (%s)", helpFile); FILE* fp; remove(helpFile); fp = fopen(helpFile, "w"); if (fp) { time_t rawtime; struct tm* timeinfo; char buffer[80]; time(&rawtime); timeinfo = localtime(&rawtime); strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", timeinfo); fprintf(fp, "Daodan help - generated on %s for Daodan v."DAODAN_VERSION_STRING"\n\n", buffer); fprintf(fp, "List of Daodan configuration parameters:\n"); for (unsigned int s = 0; s < ARRAY_SIZE(config); s++) { fprintf(fp, " %s - %s:\n", config[s].name, config[s].description); for (ConfigOption_t* co = config[s].options; co->name != 0; co++) { char* name = co->name; char* desc = co->description; const char* tName = DDrConfig_GetOptionTypeName(co->type); const char* val = DDrConfig_GetOptionValueString(co, 1); if (!val) val = ""; fprintf(fp, " %-22s %6s=%-5s %s\n", name, tName, val, desc); } fprintf(fp, "\n"); } fprintf(fp, "\nConfiguration parameters can be either set in daodan.ini or passed on command line.\n\n"); 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"); fprintf(fp, " [sectionX]\n parameterName = false\n"); fprintf(fp, "\nTo pass the parameter on the command line:\n"); fprintf(fp, " Oni.exe -parameterName false\n"); fprintf(fp, "For bool parameters the value can be ommitted so it is regarded as \"true\":\n"); fprintf(fp, " Oni.exe -parameterName\n"); fprintf(fp, "To disable a bool parameter you can prefix \"no\" to the parameter name like this:\n"); fprintf(fp, " Oni.exe -noparameterName\n"); fclose(fp); } else { STARTUPMESSAGE("Writing Daodan help file failed", 0); } } const char* DDrConfig_GetOptionTypeName(OptionType_t type) { switch (type) { case C_INT: return "Int"; case C_BOOL: return "Bool"; case C_STRING: return "String"; case C_CMD: return "Cmd"; case EXT_BOOL: return "pBool"; default: return "unknown"; } } const char* DDrConfig_GetOptionValueString(ConfigOption_t* opt, char printdefault) { OptionValue_t* optVal = (printdefault ? &opt->defaultValue : &opt->value); int boolV = optVal->intBoolVal; char* val = 0; switch (opt->type) { case C_STRING: return optVal->stringVal; case EXT_BOOL: if (printdefault) return (boolV ? "true" : "false"); else return (*optVal->extBoolVal ? "true" : "false"); case C_BOOL: return (boolV ? "true" : "false"); case C_CMD: return 0; default: val = malloc(20); sprintf(val, "%d", boolV); return val; } } char DDrConfig_NonDefaultOptionValue(ConfigOption_t* opt) { switch (opt->type) { case C_STRING: return _stricmp(opt->defaultValue.stringVal, opt->value.stringVal); case EXT_BOOL: return !opt->defaultValue.intBoolVal != !*opt->value.extBoolVal; case C_BOOL: return !opt->defaultValue.intBoolVal != !opt->value.intBoolVal; case C_CMD: return 0; case C_INT: return opt->defaultValue.intBoolVal != opt->value.intBoolVal; } return 0; } static ConfigOption_t* DDrConfig_GetOption(const char* fullOptName) { char section[50]; strcpy(section, fullOptName); char* option = strchr(section, '.'); if (option == 0) { STARTUPMESSAGE("Could not find option separator in \"%s\"", fullOptName); return 0; } *option++ = 0; char isWildcardSection = !_stricmp(section, "*"); for (unsigned int s = 0; s < ARRAY_SIZE(config); s++) { if (isWildcardSection || !_stricmp(config[s].name, section)) { for (ConfigOption_t* co = config[s].options; co->name != 0; co++) { if (!_stricmp(co->name, option)) { return co; } } if (!isWildcardSection) { STARTUPMESSAGE("Could not find option \"%s\" in section \"%s\"", option, section); return 0; } } } if (!isWildcardSection) STARTUPMESSAGE("Could not find section \"%s\" for option \"%s\"", section, option); else STARTUPMESSAGE("Could not find option \"%s\"", option); return 0; } ConfigOption_t* DDrConfig_GetOptOfType(const char* fullOptName, OptionType_t type) { ConfigOption_t* co = DDrConfig_GetOption(fullOptName); if (co == 0) return 0; if (co->type != type) { STARTUPMESSAGE("Option \"%s\" is not of type %s", fullOptName, DDrConfig_GetOptionTypeName(type)); return 0; } return co; } void DDrConfig_InitExtBools() { for (unsigned int s = 0; s < ARRAY_SIZE(config); s++) { for (ConfigOption_t* co = config[s].options; co->name != 0; co++) { if (co->type == EXT_BOOL) { *co->value.extBoolVal = co->defaultValue.intBoolVal; } } } } void DDrConfig_WriteIni() { FILE* fp; STARTUPMESSAGE("%s doesn't exist, creating", iniName); fp = fopen(iniName, "w"); if (fp) { for (unsigned int s = 0; s < ARRAY_SIZE(config); s++) { if (strlen(config[s].name)) { fprintf(fp, "[%s]\n", config[s].name); for (ConfigOption_t* co = config[s].options; co->name != 0; co++) { char* name = co->name; const char* val = DDrConfig_GetOptionValueString(co, 0); if (val && DDrConfig_NonDefaultOptionValue(co)) fprintf(fp, "%s = %s\n", name, val); } fprintf(fp, "\n"); } } fclose(fp); } else { STARTUPMESSAGE("Writing %s template file failed", iniName); } } void DDrIniCallback(const char* section, const char* name, const char* value) { char fullOptName[50]; if (!_stricmp(section, "patch")) section = "patches"; strcpy(fullOptName, section); fullOptName[strlen(section)] = '.'; strcpy(fullOptName+strlen(section)+1, name); ConfigOption_t* co = DDrConfig_GetOption(fullOptName); if (co) { char* buf = 0; switch (co->type) { case C_INT: co->value.intBoolVal = strtol(value, NULL, 0); break; case C_BOOL: co->value.intBoolVal = !_stricmp(value, "true"); break; case C_STRING: buf = malloc(strlen(value)+1); strcpy(buf, value); co->value.stringVal = buf; break; case C_CMD: co->value.callback(); break; case EXT_BOOL: *(co->value.extBoolVal) = !_stricmp(value, "true"); break; default: STARTUPMESSAGE("Config value type unknown: %d", co->type); } } else { char buf[100]; if (!_stricmp(section, "*")) sprintf(buf, " %s\n", name); else sprintf(buf, " %s.%s\n", section, name); if (strlen(buf) + strlen(invalidCurParamaters) < sizeof(invalidCurParamaters) - 1) { strcpy(invalidCurParamaters + strlen(invalidCurParamaters), buf); } } } bool DDrConfig_ParseCommandLine(int argc, char* argv[]) { for (int i = 1; i < argc; i ++) { if (argv[i][0] == '-') { char* option; bool invertedOption; option = argv[i]+1; invertedOption = (option[0] == 'n' || option[0] == 'N') && (option[1] == 'o' || option[1] == 'O'); if (invertedOption) option += 2; if (i < (argc - 1) && argv[i+1][0] != '-') // Has value in next field { DDrIniCallback("*", option, argv[++i]); } else // Implicit value { DDrIniCallback("*", option, (invertedOption ? "false" : "true")); } } else { STARTUPMESSAGE("Parse error \"%s\"", argv[i]); return false; } } return true; } void DDrConfig(int argc, char* argv[]) { STARTUPMESSAGE("Initializing standard booleans", 0); DDrConfig_InitExtBools(); if (GetFileAttributes(iniName) == INVALID_FILE_ATTRIBUTES) DDrConfig_WriteIni(); STARTUPMESSAGE("Parsing daodan.ini...", 0); if (!Inifile_Read(iniName, DDrIniCallback)) STARTUPMESSAGE("Error reading daodan.ini, check your syntax!", 0); STARTUPMESSAGE("Finished parsing", 0); if (strlen(invalidCurParamaters) > 0) { sprintf(invalidTotalParamaters, "In %s:\n%s\n", iniName, invalidCurParamaters); invalidCurParamaters[0] = 0; } STARTUPMESSAGE("Parsing command line...", 0); DDrConfig_ParseCommandLine(argc, argv); STARTUPMESSAGE("Finished parsing", 0); if (strlen(invalidCurParamaters) > 0) { sprintf(invalidTotalParamaters, "%sOn command line:\n%s\n", invalidTotalParamaters, invalidCurParamaters); } if (strlen(invalidTotalParamaters) > 0) { char msg[4096]; sprintf(msg, "Invalid parameters given:\n%sContinue launching Oni?", invalidTotalParamaters); int res = MessageBox(NULL, msg, "Parameters invalid", MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON1); if (res == IDNO) { exit(0); } } DDrConfig_WriteIni(); // DDrConfig_Print(); }