diff options
Diffstat (limited to 'saga/cvar.cpp')
-rw-r--r-- | saga/cvar.cpp | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/saga/cvar.cpp b/saga/cvar.cpp new file mode 100644 index 0000000000..5f69faf3a9 --- /dev/null +++ b/saga/cvar.cpp @@ -0,0 +1,612 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2004 The ScummVM project + * + * The ReInherit Engine is (C)2000-2003 by Daniel Balsom. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ +/* + Description: + + Configuration Variable Module + + Notes: +*/ + +#include "reinherit.h" + +#include <limits.h> +#include <stddef.h> + +/* + Uses the following modules: +\*--------------------------------------------------------------------------*/ +#include "console_mod.h" + +/* + Begin module +\*--------------------------------------------------------------------------*/ +#include "cvar_mod.h" +#include "cvar.h" + +namespace Saga { + +R_CVAR *CVHashTbl[R_CVAR_HASHLEN]; + +char *CVAR_ErrMsg[] = { + + "No Error", + "Not implememented.", + "Memory allocation failed", + "Value overflowed while parsing", + "Invalid numeric constant", + "Value overflows destination type", + "Assignment of negative value to unsigned variable", + "Value outside of specified bounds", + "Invalid string literal", + "Invalid type for assignment", + "Variable is read-only", + "Not a valid function" +}; + +enum CVAR_Errors { + + CVERR_NONE, + CVERR_NOTIMPL, + CVERR_MEM, + CVERR_PARSEOVERFLOW, + CVERR_INVALID, + CVERR_DESTOVERFLOW, + CVERR_SIGN, + CVERR_BOUND, + CVERR_STRING, + CVERR_TYPE, + CVERR_READONLY, + CVERR_NOTFUNC +}; + +static enum CVAR_Errors CVAR_ErrorState; + +int CVAR_GetError(char **err_str) +/****************************************************************************\ + Returns the appropriate cvar error string +\****************************************************************************/ +{ + + *err_str = CVAR_ErrMsg[CVAR_ErrorState]; + + return CVAR_ErrorState; +} + +int CVAR_Shutdown(void) +/****************************************************************************\ + Frees the cvar hash table +\****************************************************************************/ +{ + + R_CVAR *walk_ptr; + R_CVAR *temp_ptr; + int i; + + R_printf(R_STDOUT, "CVAR_Shutdown(): Deleting cvar hash table.\n"); + + for (i = 0; i < R_CVAR_HASHLEN; i++) { + + for (walk_ptr = CVHashTbl[i]; walk_ptr; walk_ptr = temp_ptr) { + + temp_ptr = walk_ptr->next; + free(walk_ptr); + } + } + + return R_SUCCESS; +} + +unsigned int CVAR_HashString(const char *str) +/****************************************************************************\ + Returns hash index for string 'str'. + Cannot fail. +\****************************************************************************/ +{ + + unsigned int index; + + for (index = 0; *str != '\0'; str++) { + index = *str + 31 * index; + } + + return index % R_CVAR_HASHLEN; +} + +int CVAR_Add(int index, R_CVAR * cvar) +/****************************************************************************\ + Adds a copy of the given cvar into the hash table. + Returns R_SUCCESS if cvar was added, R_MEM if allocation failed. +\****************************************************************************/ +{ + + R_CVAR *new_cvar; + R_CVAR *temp_ptr; + + new_cvar = (R_CVAR *)malloc(sizeof(R_CVAR)); + + if (new_cvar == NULL) { + CVAR_ErrorState = CVERR_MEM; + return R_MEM; + } + + memcpy(new_cvar, cvar, sizeof(R_CVAR)); + + if (CVHashTbl[index] == NULL) { + CVHashTbl[index] = new_cvar; + new_cvar->next = NULL; + } else { + temp_ptr = CVHashTbl[index]; + CVHashTbl[index] = new_cvar; + new_cvar->next = temp_ptr; + } + + CVAR_ErrorState = CVERR_NONE; + return R_SUCCESS; +} + +int CVAR_Exec(R_CVAR_P cvar_func, char *r_value) +/****************************************************************************\ + Attempts to execute the specified console function with the given argument + string. + Returns R_FAILURE if cvar_func is not a valid console function +\****************************************************************************/ +{ + + int cf_argc = 0; + char **cf_argv = NULL; + int max_args; + + if (cvar_func->type != R_CVAR_FUNC) { + CVAR_ErrorState = CVERR_NOTFUNC; + return R_FAILURE; + } + + cf_argc = EXPR_GetArgs(r_value, &cf_argv); + + if (cf_argc < cvar_func->t.func.min_args) { + CON_Print("Too few arguments to function."); + if (cf_argv) + free(cf_argv); + return R_FAILURE; + } + + max_args = cvar_func->t.func.max_args; + if ((max_args > -1) && (cf_argc > max_args)) { + CON_Print("Too many arguments to function."); + if (cf_argv) + free(cf_argv); + return R_FAILURE; + } + + /* Call function */ + (cvar_func->t.func.func_p) (cf_argc, cf_argv); + + if (cf_argv) + free(cf_argv); + + return R_SUCCESS; +} + +int CVAR_SetValue(R_CVAR_P cvar, char *r_value) +/****************************************************************************\ + Attempts to assign the value contained in the string 'r_value' to cvar. + Returns R_FAILURE if there was an error parsing 'r_value' +\****************************************************************************/ +{ + + long int int_param; + unsigned long uint_param; + + char *end_p; + ptrdiff_t scan_len; + int r_value_len; + + r_value_len = strlen(r_value); + + if (cvar->flags & R_CVAR_READONLY) { + CVAR_ErrorState = CVERR_READONLY; + return R_FAILURE; + } + + switch (cvar->type) { + + case R_CVAR_INT: + + int_param = strtol(r_value, &end_p, 10); + + if ((int_param == LONG_MIN) || (int_param == LONG_MAX)) { + CVAR_ErrorState = CVERR_PARSEOVERFLOW; + return R_FAILURE; + } + + scan_len = end_p - r_value; + + if (int_param == 0) { + if (!scan_len || r_value[scan_len - 1] != '0') { + /* strtol() returned 0, but string isn't "0". Invalid. */ + CVAR_ErrorState = CVERR_INVALID; + return R_FAILURE; + } + } + + if (scan_len != r_value_len) { + /* Entire string wasn't converted...Invalid */ + CVAR_ErrorState = CVERR_INVALID; + return R_FAILURE; + } + + if ((int_param < CV_INTMIN) || (int_param > CV_INTMAX)) { + /* Overflows destination type */ + CVAR_ErrorState = CVERR_DESTOVERFLOW; + return R_FAILURE; + } + + /* Ignore bounds if equal */ + if (cvar->t.i.lbound != cvar->t.i.ubound) { + + if ((int_param < cvar->t.i.lbound) || + (int_param > cvar->t.i.ubound)) { + /* Value is outside of cvar bounds */ + CVAR_ErrorState = CVERR_BOUND; + return R_FAILURE; + } + } + + *(cvar->t.i.var_p) = (cv_int_t) int_param; + +#ifdef R_CVAR_TRACE + printf("Set cvar to value %ld.\n", int_param); +#endif + + break; + + case R_CVAR_UINT: + + if (*r_value == '-') { + CVAR_ErrorState = CVERR_SIGN; + return R_FAILURE; + } + + uint_param = strtoul(r_value, &end_p, 10); + + if (uint_param == ULONG_MAX) { + CVAR_ErrorState = CVERR_PARSEOVERFLOW; + return R_FAILURE; + } + + scan_len = end_p - r_value; + + if (uint_param == 0) { + + if (!scan_len || r_value[scan_len - 1] != '0') { + /* strtol() returned 0, but string isn't "0". Invalid. */ + CVAR_ErrorState = CVERR_INVALID; + return R_FAILURE; + } + } + + if (scan_len != r_value_len) { + /* Entire string wasn't converted...Invalid */ + CVAR_ErrorState = CVERR_INVALID; + return R_FAILURE; + } + + if (uint_param > CV_UINTMAX) { + /* Overflows destination type */ + CVAR_ErrorState = CVERR_DESTOVERFLOW; + return R_FAILURE; + } + + /* Ignore bounds if equal */ + if (cvar->t.ui.lbound != cvar->t.ui.ubound) { + + if ((uint_param < cvar->t.ui.lbound) || + (uint_param > cvar->t.ui.ubound)) { + /* Value is outside cvar bounds */ + CVAR_ErrorState = CVERR_BOUND; + return R_FAILURE; + } + } + + *(cvar->t.ui.var_p) = (cv_uint_t) uint_param; + +#ifdef R_CVAR_TRACE + printf("Set cvar to value %lu.\n", uint_param); +#endif + + break; + + case R_CVAR_FLOAT: + + CVAR_ErrorState = CVERR_NOTIMPL; + return R_FAILURE; + break; + + case R_CVAR_STRING: + + if (strrchr(r_value, '\"') != NULL) { + CVAR_ErrorState = CVERR_STRING; + return R_FAILURE; + } + + strncpy(cvar->t.s.var_str, r_value, cvar->t.s.ubound); + if (cvar->t.s.ubound < r_value_len) { + cvar->t.s.var_str[cvar->t.s.ubound] = 0; + } +#ifdef R_CVAR_TRACE + printf("Set cvar to value \"%s\".\n", cvar->t.s.var_str); +#endif + + break; + + default: + + CVAR_ErrorState = CVERR_TYPE; + return R_FAILURE; + break; + } + + CVAR_ErrorState = CVERR_NONE; + return R_SUCCESS; +} + +R_CVAR_P CVAR_Find(const char *var_str) +/****************************************************************************\ + Given a cvar name this function returns a pointer to the appropriate + cvar structure or NULL if no match was found. +\****************************************************************************/ +{ + + R_CVAR *walk_ptr; + int hash; + + hash = CVAR_HashString(var_str); + +#ifdef R_CVAR_TRACE + printf("Performing lookup on hash bucket %d.\n", hash); +#endif + + walk_ptr = CVHashTbl[hash]; + + while (walk_ptr != NULL) { + + if (strcmp(var_str, walk_ptr->name) == 0) { + return walk_ptr; + } + + walk_ptr = walk_ptr->next; + } + + return NULL; +} + +int CVAR_IsFunc(R_CVAR_P cvar_func) +{ + + if (cvar_func->type == R_CVAR_FUNC) + return 1; + else + return 0; +} + +int +CVAR_RegisterFunc(cv_func_t func, + const char *func_name, + const char *func_argstr, uint flags, int min_args, int max_args) +/****************************************************************************\ + Registers a console function 'cvar' + (could think of a better place to put these...?) +\****************************************************************************/ +{ + + R_CVAR new_cvar; + int hash; + + new_cvar.name = func_name; + new_cvar.type = R_CVAR_FUNC; + new_cvar.section = NULL; + new_cvar.flags = flags; + + new_cvar.t.func.func_p = func; + new_cvar.t.func.func_argstr = func_argstr; + new_cvar.t.func.min_args = min_args; + new_cvar.t.func.max_args = max_args; + hash = CVAR_HashString(func_name); + +#ifdef R_CVAR_TRACE + printf("Added FUNC cvar to hash bucket %d.\n", hash); +#endif + + return CVAR_Add(hash, &new_cvar); +} + +int +CVAR_Register_I(cv_int_t * var_p, + const char *var_name, + const char *section, uint flags, cv_int_t lbound, cv_int_t ubound) +/****************************************************************************\ + Registers an integer type cvar. +\****************************************************************************/ +{ + + R_CVAR new_cvar; + int hash; + + new_cvar.name = var_name; + new_cvar.type = R_CVAR_INT; + new_cvar.section = section; + new_cvar.flags = flags; + + new_cvar.t.i.var_p = var_p; + + new_cvar.t.i.lbound = lbound; + new_cvar.t.i.ubound = ubound; + + hash = CVAR_HashString(var_name); + +#ifdef R_CVAR_TRACE + printf("Added INT cvar to hash bucket %d.\n", hash); +#endif + + return CVAR_Add(hash, &new_cvar); +} + +int +CVAR_Register_UI(cv_uint_t * var_p, + const char *var_name, + const char *section, uint flags, cv_uint_t lbound, cv_uint_t ubound) +/****************************************************************************\ + Registers an unsigned integer type cvar. +\****************************************************************************/ +{ + + R_CVAR new_cvar; + int hash; + + new_cvar.name = var_name; + new_cvar.type = R_CVAR_UINT; + new_cvar.section = section; + new_cvar.flags = flags; + + new_cvar.t.ui.var_p = var_p; + + new_cvar.t.ui.lbound = lbound; + new_cvar.t.ui.ubound = ubound; + + hash = CVAR_HashString(var_name); + +#ifdef R_CVAR_TRACE + printf("Added UNSIGNED INT ccvar to hash bucket %d.\n", hash); +#endif + + return CVAR_Add(hash, &new_cvar); +} + +int +CVAR_Register_F(cv_float_t * var_p, + const char *var_name, + const char *section, uint flags, cv_float_t lbound, cv_float_t ubound) +/****************************************************************************\ + Registers a floating point type cvar. +\****************************************************************************/ +{ + + R_CVAR new_cvar; + int hash; + + new_cvar.name = var_name; + new_cvar.type = R_CVAR_FLOAT; + new_cvar.section = section; + new_cvar.flags = flags; + + new_cvar.t.f.var_p = var_p; + + new_cvar.t.f.lbound = lbound; + new_cvar.t.f.ubound = ubound; + + hash = CVAR_HashString(var_name); + +#ifdef R_CVAR_TRACE + printf("Added FLOAT cvar to hash bucket %d.\n", hash); +#endif + + return CVAR_Add(hash, &new_cvar); +} + +int +CVAR_Register_S(cv_char_t * var_str, + const char *var_name, const char *section, uint flags, int ubound) +/****************************************************************************\ + Registers a string type cvar. Storage must be provided in var_p for 'ubound' + characters plus 1 for NUL char. +\****************************************************************************/ +{ + + R_CVAR new_cvar; + int hash; + + new_cvar.name = var_name; + new_cvar.type = R_CVAR_STRING; + new_cvar.section = section; + new_cvar.flags = flags; + + new_cvar.t.s.var_str = var_str; + new_cvar.t.s.ubound = ubound; + + hash = CVAR_HashString(var_name); + +#ifdef R_CVAR_TRACE + printf("Added UNSIGNED INT var to hash bucket %d.\n", hash); +#endif + + return CVAR_Add(hash, &new_cvar); +} + +int CVAR_Print(R_CVAR_P con_cvar) +/****************************************************************************\ + Displays the value and type of the given cvar to the console. +\****************************************************************************/ +{ + + switch (con_cvar->type) { + + case R_CVAR_INT: + CON_Print("\"%s\"(i) = %d", + con_cvar->name, *(con_cvar->t.i.var_p)); + break; + + case R_CVAR_UINT: + CON_Print("\"%s\"(ui) = %u", + con_cvar->name, *(con_cvar->t.ui.var_p)); + break; + + case R_CVAR_FLOAT: + CON_Print("\"%s\"(ui) = %f", + con_cvar->name, *(con_cvar->t.f.var_p)); + break; + + case R_CVAR_STRING: + CON_Print("\"%s\"(s) = \"%s\"", con_cvar->name, + con_cvar->t.s.var_str); + break; + + case R_CVAR_FUNC: + if (con_cvar->t.func.func_argstr) { + CON_Print("\"%s\"(func) Args: %s", con_cvar->name, + con_cvar->t.func.func_argstr); + } else { + CON_Print("\"%s\"(func) No arguments.", + con_cvar->name); + } + break; + + default: + CON_Print("Invalid variable type.\n"); + break; + } + + return R_SUCCESS; +} + +} // End of namespace Saga |