#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"; void DDrConfig_PrintHelp(); ConfigSection_t config[] = { { "patches", "Patches", { { "alttab", "Allows user to switch applications while in Oni (Alt-Tab) and use Windows key, however it may enable the screensaver as well.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "argb8888", "Textures using ARGB8888 can be used.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "binkplay", "Fix binkplay calls to use GDI and outro same mode as intro.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "bsl", "Enables d_regen (unfinished) and prevents fly-in portraits from being stretched when playing in widescreen resolutions.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "cheater", "Adds new cheat codes (see section below).", 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 (see section below).", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "chinese", "Allow for chinese fonts to be shown.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "clipcursor", "Limit cursor to Oni's window.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "cooldowntimer", "Disables weapon cooldown exploit.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "daodandisplayenum", "Offers more display modes in the Options menu.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "directinput", "Forces on DirectInput.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "disablecmdline", "Replaces existing command line parser with Daodan's in order to add new commands. Meant to be used with getcmdline.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "fonttexturecache", "Doubles size of font texture cache.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "getcmdline", "Replaces existing command line parser with Daodan's in order to add new commands. Meant to be used with disablecmdline.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "hdscreens_lowres", "Allow HD screens with resolution < 1024*768.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "highres_console", "Fixes bug where console line becomes invisible at higher resolutions.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "kickguns", "Unfinished, do not use.", C_BOOL, {.intBoolVal = false}, {.intBoolVal = false} }, { "largetextures", "Textures up to 512x512 can be used.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "levelplugins", "Allows level files to be loaded from the GDF which do not end in \"_Final\".", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "newweap", "Picking up a weapon displays a message containing the weapon name and amount of ammo.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "nomultibyte", "Enables languages which use multibyte coding (such as Chinese).", 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} }, { "particledisablebit", "Unlocks particle action disabling/enabling bits for all events so that a particle event can occur multiple times.", C_BOOL, {.intBoolVal = false}, {.intBoolVal = false} }, { "pathfinding", "Multiplies size of pathfinding grid cache by eight 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} }, { "safeprintf", "Replaces Oni's function that prints to startup.txt with a safer one.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "showalllasersights", "Show all (also enemies') weapon lasersights.", C_BOOL, {.intBoolVal = false}, {.intBoolVal = false} }, { "showtriggervolumes", "Allows BSL variable \"show_triggervolumes\" to work when set to 1.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "throwtest", "Not recommended for use; experiment with allowing enemies to be thrown over railings.", C_BOOL, {.intBoolVal = false}, {.intBoolVal = false} }, { "usedaodangl", "Provides an improved windowed mode (-noswitch); this patch is known to break the hiding of the Windows taskbar in fullscreen mode.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "usegettickcount", "Replaces Oni's timing functions with more accurate ones.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "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} } } }, { "options", "Options", { { "border", "If \"windowhack\" patch is active, make sure game window has border in windowed mode.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "debug", "???", EXT_BOOL, {.intBoolVal = false }, {.extBoolVal = &AKgDebug_DebugMaps } }, { "debugfiles", "???", EXT_BOOL, {.intBoolVal = false }, {.extBoolVal = &BFgDebugFileEnable } }, { "findsounds", "???", EXT_BOOL, {.intBoolVal = false }, {.extBoolVal = &SSgSearchOnDisk } }, { "gamma", "Enable gamma slider in fullscreen.", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "help", "Generates this help file.", C_CMD, {.intBoolVal = 0}, {.callback = DDrConfig_PrintHelp} }, { "ignore_private_data", "? No effect ?", EXT_BOOL, {.intBoolVal = false }, {.extBoolVal = &opt_ignore_private_data } }, { "sound", "???", EXT_BOOL, {.intBoolVal = true }, {.extBoolVal = &opt_sound } }, { "switch", "Always switch screen to resolution on Oni's Options screen, making the game fullscreen; opposite of Oni's built-in argument \"noswitch\".", EXT_BOOL, {.intBoolVal = true}, {.extBoolVal = &M3gResolutionSwitch} }, { "topmost", "Keep game window on top in windowed mode, even when switching applications.", C_BOOL, {.intBoolVal = false}, {.intBoolVal = false} }, { "usedaodanbsl", "Adds new BSL commands (see below).", C_BOOL, {.intBoolVal = true}, {.intBoolVal = true} }, { "language", "Localization for hardcoded strings (e.g. \"Savepoints\").", C_STRING, {.stringVal = "en"}, {.stringVal = "en"} }, { 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); int boolV = co->defaultValue.intBoolVal; char* val; switch (co->type) { case C_STRING: val = co->defaultValue.stringVal; break; case EXT_BOOL: val = (boolV ? "true" : "false"); break; case C_BOOL: val = (boolV ? "true" : "false"); break; case C_CMD: val = ""; break; default: val = malloc(20); sprintf(val, "%d", boolV); } 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 -sectionX.parameterName false\n"); fprintf(fp, "For bool parameters the value can be ommitted so it is regarded as \"true\":\n"); fprintf(fp, " Oni.exe -sectionX.parameterName\n"); fprintf(fp, "To disable a bool parameter you can prefix \"no\" to the parameter name like this:\n"); fprintf(fp, " Oni.exe -sectionX.noparameterName\n"); fprintf(fp, "If no section is given it is assumed to be \"%s\", e.g.\n", defaultSection); fprintf(fp, " Oni.exe -%s.parametername\n", defaultSection); fprintf(fp, "can simply be written as\n"); fprintf(fp, " Oni.exe -parametername\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"; } } 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; for (unsigned int s = 0; s < ARRAY_SIZE(config); s++) { if (!_stricmp(config[s].name, section)) { for (ConfigOption_t* co = config[s].options; co->name != 0; co++) { if (!_stricmp(co->name, option)) { return co; } } STARTUPMESSAGE("Could not find option \"%s\" in section \"%s\"", option, section); return 0; } } STARTUPMESSAGE("Could not find section \"%s\" for option \"%s\"", section, 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_WriteTemplateIni() { 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++) { fprintf(fp, "[%s]\n", config[s].name); } fclose(fp); } else { STARTUPMESSAGE("Writing %s template file failed", iniName); } } void DDrIniCallback(const char* section, const char* name, const char* value) { static char curSection[20]; char fullOptName[50]; if (!_stricmp(section, "patch")) section = "patches"; strcpy(curSection, section); strcpy(fullOptName, curSection); fullOptName[strlen(curSection)] = '.'; strcpy(fullOptName+strlen(curSection)+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); } } } bool DDrConfig_ParseCommandLine(int argc, char* argv[]) { for (int i = 1; i < argc; i ++) { if (argv[i][0] == '-') { const char* section; char* optionsep; char* option; bool invertedOption; if ((optionsep = strchr(argv[i], '.'))) // Is "section.option" { *optionsep = 0; option = optionsep+1; section = argv[i]+1; } else // Is just "option" { section = defaultSection; 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(section, option, argv[++i]); } else // Implicit value { DDrIniCallback(section, option, (invertedOption ? "false" : "true")); } if (optionsep) *optionsep = '.'; } 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_WriteTemplateIni(); STARTUPMESSAGE("Parsing daodan.ini...", 0); if (!Inifile_Read(iniName, DDrIniCallback)) STARTUPMESSAGE("Error reading daodan.ini, check your syntax!", 0); STARTUPMESSAGE("Finished parsing", 0); STARTUPMESSAGE("Parsing command line...", 0); DDrConfig_ParseCommandLine(argc, argv); STARTUPMESSAGE("Finished parsing", 0); // DDrConfig_Print(); }