3
0
mirror of https://github.com/CrazyRedMachine/popnhax.git synced 2024-11-27 23:40:50 +01:00

config file advanced diagnostics

This commit is contained in:
CrazyRedMachine 2024-06-24 22:07:40 +02:00
parent 4c92b4fda7
commit c775320a07
5 changed files with 320 additions and 52 deletions

View File

@ -3,6 +3,7 @@
#include "util/crc32.h" #include "util/crc32.h"
#include "util/log.h" #include "util/log.h"
#include "util/psmap.h"
#include "config.h" #include "config.h"
#define F_OK 0 #define F_OK 0
@ -220,6 +221,272 @@ static bool config_make_opt(const char *xml_filename){
return true; return true;
} }
static bool type_to_str(enum psmap_property_type type, char* expected_type)
{
bool res = true;
switch (type)
{
case PSMAP_PROPERTY_TYPE_S8:
strcpy(expected_type, "s8");
break;
case PSMAP_PROPERTY_TYPE_U8:
strcpy(expected_type, "u8");
break;
case PSMAP_PROPERTY_TYPE_S16:
strcpy(expected_type, "s16");
break;
case PSMAP_PROPERTY_TYPE_U16:
strcpy(expected_type, "u16");
break;
case PSMAP_PROPERTY_TYPE_S32:
strcpy(expected_type, "s32");
break;
case PSMAP_PROPERTY_TYPE_U32:
strcpy(expected_type, "u32");
break;
case PSMAP_PROPERTY_TYPE_S64:
strcpy(expected_type, "s64");
break;
case PSMAP_PROPERTY_TYPE_U64:
strcpy(expected_type, "u64");
break;
case PSMAP_PROPERTY_TYPE_STR:
strcpy(expected_type, "str");
break;
case PSMAP_PROPERTY_TYPE_FLOAT:
strcpy(expected_type, "float");
break;
case PSMAP_PROPERTY_TYPE_ATTR:
strcpy(expected_type, "attr");
break;
case PSMAP_PROPERTY_TYPE_BOOL:
strcpy(expected_type, "bool");
break;
default:
strcpy(expected_type, "INVALID");
res = false;
break;
}
return res;
}
static int64_t get_min(enum psmap_property_type type)
{
switch (type)
{
case PSMAP_PROPERTY_TYPE_S8:
return -128;
case PSMAP_PROPERTY_TYPE_S16:
return -32768;
case PSMAP_PROPERTY_TYPE_S32:
return -2147483648;
case PSMAP_PROPERTY_TYPE_S64:
return LLONG_MIN;
case PSMAP_PROPERTY_TYPE_U8:
case PSMAP_PROPERTY_TYPE_U16:
case PSMAP_PROPERTY_TYPE_U32:
case PSMAP_PROPERTY_TYPE_U64:
return 0;
default:
return -1;
}
}
static int64_t get_max(enum psmap_property_type type)
{
switch (type)
{
case PSMAP_PROPERTY_TYPE_S8:
return 127;
case PSMAP_PROPERTY_TYPE_S16:
return 32767;
case PSMAP_PROPERTY_TYPE_S32:
return 2147483647;
case PSMAP_PROPERTY_TYPE_S64:
return 9223372036854775807;
case PSMAP_PROPERTY_TYPE_U8:
return 255;
case PSMAP_PROPERTY_TYPE_U16:
return 65535;
case PSMAP_PROPERTY_TYPE_U32:
return 4294967295;
default:
return -1;
}
}
static bool is_valid(const char *option_line, uint8_t type, uint32_t member_width, char **reason)
{
char tmp_reason[256] = {0};
char open[64] = {0};
char expected_type[8] = {0};
char option_type[64] = {0};
char value[64] = {0};
char close[64] = {0};
if (!type_to_str((enum psmap_property_type) type, expected_type))
{
sprintf(tmp_reason, "bad type value"); // shouldn't happen as it's psmap struct type
*reason = strdup(tmp_reason);
return false;
}
/* attempt to scan line */
int num = sscanf(option_line, " %s __type=\"%[^<>\"]\">%[^><\"]%[^\"\r\n] ", open, option_type, value, close);
if ( num == 2 && strcmp(option_type, "str")==0 )
{
num = sscanf(option_line, " %s __type=\"%[^<>\"]\">%[^\"\r\n] ", open, option_type, close);
if (num != 3)
{
sprintf(tmp_reason, "malformed line (should be <option_name __type=\"option_type\">option_value</option_name>)");
*reason = strdup(tmp_reason);
return false;
}
}
else if ( num != 4 )
{
sprintf(tmp_reason, "malformed line (should be <option_name __type=\"option_type\">option_value</option_name>)");
*reason = strdup(tmp_reason);
return false;
}
/* tag errors */
if ( open[0] != '<' )
{
sprintf(tmp_reason, "malformed opening tag (should be <%s __type\"option_type\">)", open);
*reason = strdup(tmp_reason);
return false;
}
if ( close[0] != '<' || close[1] != '/' || close[strlen(close)-1] != '>' )
{
sprintf(tmp_reason, "malformed closing tag (should be </%s>)", open+1);
*reason = strdup(tmp_reason);
return false;
}
if ( close[strlen(open)+1] != '>' || strncmp(open+1, close+2, strlen(open+1)) != 0 )
{
sprintf(tmp_reason, "opening tag doesn't match closing tag (%s> vs %s)", open, close);
*reason = strdup(tmp_reason);
return false;
}
/* type errors */
if ( strcmp(option_type, expected_type) != 0 )
{
sprintf(tmp_reason, "unexpected type (expected %s, got %s)", expected_type, option_type);
*reason = strdup(tmp_reason);
return false;
}
if ( type == PSMAP_PROPERTY_TYPE_STR )
{
if ( strlen(value) > member_width-1 )
{
sprintf(tmp_reason, "value %s too long (max length is %d, got %d)", value, member_width-1, strlen(value));
*reason = strdup(tmp_reason);
return false;
}
return true;
}
if ( type == PSMAP_PROPERTY_TYPE_ATTR )
return true; // can't do further checks
if ( type == PSMAP_PROPERTY_TYPE_FLOAT )
{
char *endPtr;
(void)strtof(value, &endPtr);
if ( endPtr != (value+strlen(value)) || errno == ERANGE )
{
sprintf(tmp_reason, "invalid %s value %s (should be a valid IEEE 754 floating point number)", option_type, value);
*reason = strdup(tmp_reason);
return false;
}
}
if ( type == PSMAP_PROPERTY_TYPE_BOOL )
{
if ( strlen(value) != 1 || (value[0] != '0' && value[0] != '1') )
{
sprintf(tmp_reason, "invalid bool value (should be 0 or 1, got %s)", value);
*reason = strdup(tmp_reason);
return false;
}
return true;
}
if ( type == PSMAP_PROPERTY_TYPE_U64 )
{
char *endPtr;
(void)strtoull(value, &endPtr, 10);
if ( endPtr != (value+strlen(value)) || errno == ERANGE )
{
sprintf(tmp_reason, "invalid %s value %s (should be an integer between 0 and 18446744073709551615)", option_type, value);
*reason = strdup(tmp_reason);
return false;
}
}
else // only numeric types up to S64 remain
{
char *endPtr;
int64_t parsed = strtoull(value, &endPtr, 10);
if ( type == PSMAP_PROPERTY_TYPE_S64 && errno == ERANGE )
{
sprintf(tmp_reason, "invalid %s value (should be an integer between -9223372036854775808 and 9223372036854775807)", option_type);
*reason = strdup(tmp_reason);
return false;
}
else if ( endPtr != (value+strlen(value)) )
{
sprintf(tmp_reason, "invalid value %s (should be an integer and nothing else)", value);
*reason = strdup(tmp_reason);
return false;
}
int64_t min = get_min((enum psmap_property_type)type);
int64_t max = get_max((enum psmap_property_type)type);
if ( parsed < min || parsed > max )
{
sprintf(tmp_reason, "invalid value (should be an integer between %lld and %lld, got %s)", min, max, value);
*reason = strdup(tmp_reason);
return false;
}
}
return true;
}
void config_diag(const char *opt_filepath, const struct property_psmap *psmap) {
int i = 0;
int shift = strlen("/popnhax/");
FILE *opt = fopen(opt_filepath, "r");
if ( opt == NULL )
{
LOG("FATAL ERROR: cannot open %s\n", opt_filepath);
}
while ( psmap[i].type != 0xFF )
{
bool required = !psmap[i].has_default;
const char *option_name = psmap[i].path+shift;
//LOG("checking %s option %s\n", required ? "REQUIRED" : "OPTIONAL", option_name);
char *option_line = get_option(opt, (char*)option_name); //will return NULL if option_name is NULL
if (option_line)
{
char *reason;
if (!is_valid(option_line, psmap[i].type, psmap[i].member_width, &reason))
{
LOG("\t%s", option_line);
LOG("ERROR: %s: %s\n", option_name, reason);
}
}
else if (required)
{
LOG("ERROR: required option %s not found\n", option_name);
}
free(option_line);
i++;
}
}
// take care of updating .xml/.opt files if needed // take care of updating .xml/.opt files if needed
bool config_process(const char *xml_filepath){ bool config_process(const char *xml_filepath){
// look for _update marker file // look for _update marker file

View File

@ -73,5 +73,6 @@ struct popnhax_config {
}; };
bool config_process(const char *filepath); // take care of updating .xml/.opt files if needed bool config_process(const char *filepath); // take care of updating .xml/.opt files if needed
void config_diag(const char *xml_filepath, const struct property_psmap *psmap);
#endif #endif

View File

@ -7917,7 +7917,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
if (!_load_config(g_config_fn, &config, config_psmap)) if (!_load_config(g_config_fn, &config, config_psmap))
{ {
LOG("FATAL ERROR: Could not parse %s\n", g_config_fn); LOG("popnhax: FATAL ERROR: failed to load %s. Running advanced diagnostic...\n", g_config_fn);
config_diag(g_config_fn, config_psmap);
exit(1); exit(1);
} }

47
util/psmap.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef __PSMAP_H__
#define __PSMAP_H__
struct property_psmap {
uint8_t type;
uint8_t has_default;
uint16_t field_offset;
uint32_t member_width;
const char *path;
uintptr_t default_value;
} __attribute__((packed));
enum psmap_property_type {
PSMAP_PROPERTY_TYPE_S8 = 0x02,
PSMAP_PROPERTY_TYPE_U8 = 0x03,
PSMAP_PROPERTY_TYPE_S16 = 0x04,
PSMAP_PROPERTY_TYPE_U16 = 0x05,
PSMAP_PROPERTY_TYPE_S32 = 0x06,
PSMAP_PROPERTY_TYPE_U32 = 0x07,
PSMAP_PROPERTY_TYPE_S64 = 0x08,
PSMAP_PROPERTY_TYPE_U64 = 0x09,
PSMAP_PROPERTY_TYPE_STR = 0x0A,
PSMAP_PROPERTY_TYPE_FLOAT = 0x0D,
PSMAP_PROPERTY_TYPE_ATTR = 0x2D,
PSMAP_PROPERTY_TYPE_BOOL = 0x32,
};
#ifndef __offsetof
#define __offsetof(_s, _f) __builtin_offsetof(_s, _f)
#endif
#ifndef __fieldwidth
#define __fieldwidth(_s, _f) sizeof((((_s *)NULL)->_f))
#endif
#define PSMAP_BEGIN(_name, _type) const _type struct property_psmap _name[] = {
#define PSMAP_MEMBER_REQ(_type, _struct, _member, _path) \
{_type, false, __offsetof(_struct, _member), __fieldwidth(_struct, _member), _path, 0},
#define PSMAP_MEMBER_OPT(_type, _struct, _member, _path, _def) \
{_type, true, __offsetof(_struct, _member), __fieldwidth(_struct, _member), \
_path, (uintptr_t)_def},
#define PSMAP_END \
{ 0xff, false, 0, 0, "NULL", 0 } \
} \
;
#endif

View File

@ -5,49 +5,7 @@
#include "imports/avs.h" #include "imports/avs.h"
#include "util/membuf.h" #include "util/membuf.h"
#include "util/psmap.h"
struct property_psmap {
uint8_t type;
uint8_t has_default;
uint16_t field_offset;
uint32_t member_width;
const char *path;
uintptr_t default_value;
} __attribute__((packed));
enum psmap_property_type {
PSMAP_PROPERTY_TYPE_S8 = 0x02,
PSMAP_PROPERTY_TYPE_U8 = 0x03,
PSMAP_PROPERTY_TYPE_S16 = 0x04,
PSMAP_PROPERTY_TYPE_U16 = 0x05,
PSMAP_PROPERTY_TYPE_S32 = 0x06,
PSMAP_PROPERTY_TYPE_U32 = 0x07,
PSMAP_PROPERTY_TYPE_S64 = 0x08,
PSMAP_PROPERTY_TYPE_U64 = 0x09,
PSMAP_PROPERTY_TYPE_STR = 0x0A,
PSMAP_PROPERTY_TYPE_FLOAT = 0x0D,
PSMAP_PROPERTY_TYPE_ATTR = 0x2D,
PSMAP_PROPERTY_TYPE_BOOL = 0x32,
};
#ifndef __offsetof
#define __offsetof(_s, _f) __builtin_offsetof(_s, _f)
#endif
#ifndef __fieldwidth
#define __fieldwidth(_s, _f) sizeof((((_s *)NULL)->_f))
#endif
#define PSMAP_BEGIN(_name, _type) const _type struct property_psmap _name[] = {
#define PSMAP_MEMBER_REQ(_type, _struct, _member, _path) \
{_type, false, __offsetof(_struct, _member), __fieldwidth(_struct, _member), _path, 0},
#define PSMAP_MEMBER_OPT(_type, _struct, _member, _path, _def) \
{_type, true, __offsetof(_struct, _member), __fieldwidth(_struct, _member), \
_path, (uintptr_t)_def},
#define PSMAP_END \
{ 0xff, false, 0, 0, "NULL", 0 } \
} \
;
int reader_callback(uint32_t context, void *bytes, size_t nbytes) { int reader_callback(uint32_t context, void *bytes, size_t nbytes) {
return fread(bytes, 1, nbytes, (FILE *)TlsGetValue(context)); return fread(bytes, 1, nbytes, (FILE *)TlsGetValue(context));
@ -127,13 +85,7 @@ struct property *load_prop_file(const char *filename) {
bool _load_config(const char *filename, void *dest, const struct property_psmap *psmap) { bool _load_config(const char *filename, void *dest, const struct property_psmap *psmap) {
struct property *config_xml = load_prop_file(filename); struct property *config_xml = load_prop_file(filename);
if (!config_xml) { if (!config_xml || !(property_psmap_import(config_xml, nullptr, dest, psmap))) {
printf("Couldn't load xml file: %s\n", filename);
return false;
}
if (!(property_psmap_import(config_xml, nullptr, dest, psmap))) {
printf("Couldn't parse psmap\n");
return false; return false;
} }