/* 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 #include /* 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]; static const 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(const 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