If errors are detected in INI settings display an info message for 5 seconds at core startup. (#696)
Added `cfg_error` function for reporting errors in the ini. Report errors when parsing video_mode information. Report out of bounds settings. Report unknown settings. Detect numeric parse failures.
This commit is contained in:
166
cfg.cpp
166
cfg.cpp
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
@@ -26,8 +27,8 @@ typedef struct
|
||||
const char* name;
|
||||
void* var;
|
||||
ini_vartypes_t type;
|
||||
int min;
|
||||
int max;
|
||||
int64_t min;
|
||||
int64_t max;
|
||||
} ini_var_t;
|
||||
|
||||
static const ini_var_t ini_vars[] =
|
||||
@@ -37,7 +38,7 @@ static const ini_var_t ini_vars[] =
|
||||
{ "FORCED_SCANDOUBLER", (void*)(&(cfg.forced_scandoubler)), UINT8, 0, 1 },
|
||||
{ "VGA_SCALER", (void*)(&(cfg.vga_scaler)), UINT8, 0, 1 },
|
||||
{ "VGA_SOG", (void*)(&(cfg.vga_sog)), UINT8, 0, 1 },
|
||||
{ "KEYRAH_MODE", (void*)(&(cfg.keyrah_mode)), UINT32, 0, (int)0xFFFFFFFF },
|
||||
{ "KEYRAH_MODE", (void*)(&(cfg.keyrah_mode)), UINT32, 0, 0xFFFFFFFF },
|
||||
{ "RESET_COMBO", (void*)(&(cfg.reset_combo)), UINT8, 0, 3 },
|
||||
{ "KEY_MENU_AS_RGUI", (void*)(&(cfg.key_menu_as_rgui)), UINT8, 0, 1 },
|
||||
{ "VIDEO_MODE", (void*)(cfg.video_conf), STRING, 0, sizeof(cfg.video_conf) - 1 },
|
||||
@@ -76,7 +77,7 @@ static const ini_var_t ini_vars[] =
|
||||
{ "SHARED_FOLDER", (void*)(&(cfg.shared_folder)), STRING, 0, sizeof(cfg.shared_folder) - 1 },
|
||||
{ "NO_MERGE_VID", (void*)(&(cfg.no_merge_vid)), UINT16, 0, 0xFFFF },
|
||||
{ "NO_MERGE_PID", (void*)(&(cfg.no_merge_pid)), UINT16, 0, 0xFFFF },
|
||||
{ "NO_MERGE_VIDPID", (void*)(cfg.no_merge_vidpid), UINT32ARR, 0, (int)0xFFFFFFFF },
|
||||
{ "NO_MERGE_VIDPID", (void*)(cfg.no_merge_vidpid), UINT32ARR, 0, 0xFFFFFFFF },
|
||||
{ "CUSTOM_ASPECT_RATIO_1", (void*)(&(cfg.custom_aspect_ratio[0])), STRING, 0, sizeof(cfg.custom_aspect_ratio[0]) - 1 },
|
||||
{ "CUSTOM_ASPECT_RATIO_2", (void*)(&(cfg.custom_aspect_ratio[1])), STRING, 0, sizeof(cfg.custom_aspect_ratio[1]) - 1 },
|
||||
{ "SPINNER_VID", (void*)(&(cfg.spinner_vid)), UINT16, 0, 0xFFFF },
|
||||
@@ -226,6 +227,65 @@ static int ini_get_section(char* buf, const char *vmode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ini_parse_numeric(const ini_var_t *var, const char *text, void *out)
|
||||
{
|
||||
uint32_t u32 = 0;
|
||||
int32_t i32 = 0;
|
||||
float f32 = 0.0f;
|
||||
char *endptr = nullptr;
|
||||
|
||||
bool out_of_range = true;
|
||||
|
||||
switch(var->type)
|
||||
{
|
||||
case UINT8:
|
||||
case UINT16:
|
||||
case UINT32:
|
||||
case UINT32ARR:
|
||||
u32 = strtoul(text, &endptr, 0);
|
||||
if (u32 < var->min) u32 = var->min;
|
||||
else if (u32 > var->max) u32 = var->max;
|
||||
else out_of_range = false;
|
||||
break;
|
||||
|
||||
case INT8:
|
||||
case INT16:
|
||||
case INT32:
|
||||
i32 = strtol(text, &endptr, 0);
|
||||
if (i32 < var->min) i32 = var->min;
|
||||
else if (i32 > var->max) i32 = var->max;
|
||||
else out_of_range = false;
|
||||
break;
|
||||
|
||||
case FLOAT:
|
||||
f32 = strtof(text, &endptr);
|
||||
if (f32 < var->min) f32 = var->min;
|
||||
else if (f32 > var->max) f32 = var->max;
|
||||
else out_of_range = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
out_of_range = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*endptr) cfg_error("%s: \'%s\' not a number", var->name, text);
|
||||
else if (out_of_range) cfg_error("%s: \'%s\' out of range", var->name, text);
|
||||
|
||||
switch (var->type)
|
||||
{
|
||||
case UINT8: *(uint8_t*)out = u32; break;
|
||||
case INT8: *(int8_t*)out = u32; break;
|
||||
case UINT16: *(uint16_t*)out = u32; break;
|
||||
case UINT32ARR: *(uint32_t*)out = u32; break;
|
||||
case INT16: *(int16_t*)out = u32; break;
|
||||
case UINT32: *(uint32_t*)out = u32; break;
|
||||
case INT32: *(int32_t*)out = u32; break;
|
||||
case FLOAT: *(float*)out = f32; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ini_parse_var(char* buf)
|
||||
{
|
||||
// find var
|
||||
@@ -248,62 +308,35 @@ static void ini_parse_var(char* buf)
|
||||
if (!strcasecmp(buf, ini_vars[j].name)) var_id = j;
|
||||
}
|
||||
|
||||
// get data
|
||||
if (var_id != -1)
|
||||
if (var_id == -1)
|
||||
{
|
||||
cfg_error("%s: unknown option", buf);
|
||||
}
|
||||
else // get data
|
||||
{
|
||||
i++;
|
||||
while (buf[i] == '=' || CHAR_IS_SPACE(buf[i])) i++;
|
||||
ini_parser_debugf("Got VAR '%s' with VALUE %s", buf, buf+i);
|
||||
|
||||
switch (ini_vars[var_id].type)
|
||||
const ini_var_t *var = &ini_vars[var_id];
|
||||
|
||||
switch (var->type)
|
||||
{
|
||||
case UINT8:
|
||||
*(uint8_t*)(ini_vars[var_id].var) = strtoul(&(buf[i]), NULL, 0);
|
||||
if (*(uint8_t*)(ini_vars[var_id].var) > ini_vars[var_id].max) *(uint8_t*)(ini_vars[var_id].var) = ini_vars[var_id].max;
|
||||
if (*(uint8_t*)(ini_vars[var_id].var) < ini_vars[var_id].min) *(uint8_t*)(ini_vars[var_id].var) = ini_vars[var_id].min;
|
||||
break;
|
||||
case INT8:
|
||||
*(int8_t*)(ini_vars[var_id].var) = strtol(&(buf[i]), NULL, 0);
|
||||
if (*(int8_t*)(ini_vars[var_id].var) > ini_vars[var_id].max) *(int8_t*)(ini_vars[var_id].var) = ini_vars[var_id].max;
|
||||
if (*(int8_t*)(ini_vars[var_id].var) < ini_vars[var_id].min) *(int8_t*)(ini_vars[var_id].var) = ini_vars[var_id].min;
|
||||
break;
|
||||
case UINT16:
|
||||
*(uint16_t*)(ini_vars[var_id].var) = strtoul(&(buf[i]), NULL, 0);
|
||||
if (*(uint16_t*)(ini_vars[var_id].var) > ini_vars[var_id].max) *(uint16_t*)(ini_vars[var_id].var) = ini_vars[var_id].max;
|
||||
if (*(uint16_t*)(ini_vars[var_id].var) < ini_vars[var_id].min) *(uint16_t*)(ini_vars[var_id].var) = ini_vars[var_id].min;
|
||||
case STRING:
|
||||
memset(var->var, 0, var->max);
|
||||
strncpy((char*)(var->var), &(buf[i]), var->max);
|
||||
break;
|
||||
|
||||
case UINT32ARR:
|
||||
{
|
||||
uint32_t *arr = (uint32_t*)ini_vars[var_id].var;
|
||||
uint32_t *arr = (uint32_t*)var->var;
|
||||
uint32_t pos = ++arr[0];
|
||||
arr[pos] = strtoul(&(buf[i]), NULL, 0);
|
||||
if (arr[pos] > (uint32_t)ini_vars[var_id].max) arr[pos] = (uint32_t)ini_vars[var_id].max;
|
||||
if (arr[pos] < (uint32_t)ini_vars[var_id].min) arr[pos] = (uint32_t)ini_vars[var_id].min;
|
||||
ini_parse_numeric(var, &buf[i], &arr[pos]);
|
||||
}
|
||||
break;
|
||||
case INT16:
|
||||
*(int16_t*)(ini_vars[var_id].var) = strtol(&(buf[i]), NULL, 0);
|
||||
if (*(int16_t*)(ini_vars[var_id].var) > ini_vars[var_id].max) *(int16_t*)(ini_vars[var_id].var) = ini_vars[var_id].max;
|
||||
if (*(int16_t*)(ini_vars[var_id].var) < ini_vars[var_id].min) *(int16_t*)(ini_vars[var_id].var) = ini_vars[var_id].min;
|
||||
break;
|
||||
case UINT32:
|
||||
*(uint32_t*)(ini_vars[var_id].var) = strtoul(&(buf[i]), NULL, 0);
|
||||
if (*(uint32_t*)(ini_vars[var_id].var) > (uint32_t)ini_vars[var_id].max) *(uint32_t*)(ini_vars[var_id].var) = ini_vars[var_id].max;
|
||||
if (*(uint32_t*)(ini_vars[var_id].var) < (uint32_t)ini_vars[var_id].min) *(uint32_t*)(ini_vars[var_id].var) = ini_vars[var_id].min;
|
||||
break;
|
||||
case INT32:
|
||||
*(int32_t*)(ini_vars[var_id].var) = strtol(&(buf[i]), NULL, 0);
|
||||
if (*(int32_t*)(ini_vars[var_id].var) > ini_vars[var_id].max) *(int32_t*)(ini_vars[var_id].var) = ini_vars[var_id].max;
|
||||
if (*(int32_t*)(ini_vars[var_id].var) < ini_vars[var_id].min) *(int32_t*)(ini_vars[var_id].var) = ini_vars[var_id].min;
|
||||
break;
|
||||
case FLOAT:
|
||||
*(float*)(ini_vars[var_id].var) = strtof(&(buf[i]), NULL);
|
||||
if (*(float*)(ini_vars[var_id].var) > ini_vars[var_id].max) *(float*)(ini_vars[var_id].var) = ini_vars[var_id].max;
|
||||
if (*(float*)(ini_vars[var_id].var) < ini_vars[var_id].min) *(float*)(ini_vars[var_id].var) = ini_vars[var_id].min;
|
||||
break;
|
||||
case STRING:
|
||||
memset(ini_vars[var_id].var, 0, ini_vars[var_id].max);
|
||||
strncpy((char*)(ini_vars[var_id].var), &(buf[i]), ini_vars[var_id].max);
|
||||
|
||||
default:
|
||||
ini_parse_numeric(var, &buf[i], var->var);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -356,6 +389,11 @@ static void ini_parse(int alt, const char *vmode)
|
||||
FileClose(&ini_file);
|
||||
}
|
||||
|
||||
static constexpr int CFG_ERRORS_MAX = 4;
|
||||
static constexpr int CFG_ERRORS_STRLEN = 128;
|
||||
static char cfg_errors[CFG_ERRORS_MAX][CFG_ERRORS_STRLEN];
|
||||
static int cfg_error_count = 0;
|
||||
|
||||
const char* cfg_get_name(uint8_t alt)
|
||||
{
|
||||
static char name[64];
|
||||
@@ -385,6 +423,7 @@ void cfg_parse()
|
||||
cfg.dvi_mode = 2;
|
||||
has_video_sections = false;
|
||||
using_video_section = false;
|
||||
cfg_error_count = 0;
|
||||
ini_parse(altcfg(), video_get_core_mode_name(1));
|
||||
if (has_video_sections && !using_video_section)
|
||||
{
|
||||
@@ -397,3 +436,34 @@ bool cfg_has_video_sections()
|
||||
{
|
||||
return has_video_sections;
|
||||
}
|
||||
|
||||
|
||||
void cfg_error(const char *fmt, ...)
|
||||
{
|
||||
if (cfg_error_count >= CFG_ERRORS_MAX) return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(cfg_errors[cfg_error_count], CFG_ERRORS_STRLEN, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
printf("ERROR CFG: %s\n", cfg_errors[cfg_error_count]);
|
||||
|
||||
cfg_error_count += 1;
|
||||
}
|
||||
|
||||
bool cfg_check_errors(char *msg, size_t max_len)
|
||||
{
|
||||
msg[0] = '\0';
|
||||
|
||||
if (cfg_error_count == 0) return false;
|
||||
|
||||
int pos = snprintf(msg, max_len, "%d INI Error%s\n---", cfg_error_count, cfg_error_count > 1 ? "s" : "");
|
||||
|
||||
for (int i = 0; i < cfg_error_count; i++)
|
||||
{
|
||||
pos += snprintf(msg + pos, max_len - pos, "\n%s\n", cfg_errors[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
3
cfg.h
3
cfg.h
@@ -87,4 +87,7 @@ void cfg_parse();
|
||||
const char* cfg_get_name(uint8_t alt);
|
||||
bool cfg_has_video_sections();
|
||||
|
||||
void cfg_error(const char *fmt, ...);
|
||||
bool cfg_check_errors(char *msg, size_t max_len);
|
||||
|
||||
#endif // __CFG_H__
|
||||
|
||||
@@ -1505,6 +1505,13 @@ void user_io_init(const char *path, const char *xml)
|
||||
// release reset
|
||||
if (!is_minimig() && !is_st()) user_io_status_set("[0]", 0);
|
||||
if (xml && isXmlName(xml) == 1) arcade_check_error();
|
||||
|
||||
char cfg_errs[512];
|
||||
if (cfg_check_errors(cfg_errs, sizeof(cfg_errs)))
|
||||
{
|
||||
Info(cfg_errs, 5000);
|
||||
sleep(5);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1851,6 +1851,8 @@ static int parse_custom_video_mode(char* vcfg, vmode_custom_t *v)
|
||||
char work[1024];
|
||||
char *next;
|
||||
|
||||
if (!vcfg[0]) return -1;
|
||||
|
||||
memset(v, 0, sizeof(vmode_custom_t));
|
||||
v->param.rb = 1; // default reduced blanking to true
|
||||
|
||||
@@ -1866,7 +1868,7 @@ static int parse_custom_video_mode(char* vcfg, vmode_custom_t *v)
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt == 2)
|
||||
if (cnt == 2 && token_cnt > 2)
|
||||
{
|
||||
valf = strtod(tokens[cnt], &next);
|
||||
if (!*next) cnt++;
|
||||
@@ -1885,6 +1887,7 @@ static int parse_custom_video_mode(char* vcfg, vmode_custom_t *v)
|
||||
else
|
||||
{
|
||||
printf("Error parsing video_mode parameter %d \"%s\": \"%s\"\n", i, flag, vcfg);
|
||||
cfg_error("Invalid video_mode\n> %s", vcfg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -1921,6 +1924,7 @@ static int parse_custom_video_mode(char* vcfg, vmode_custom_t *v)
|
||||
else
|
||||
{
|
||||
printf("Error parsing video_mode parameter: ""%s""\n", vcfg);
|
||||
cfg_error("Invalid video_mode\n> %s", vcfg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user