Files
Main_MiSTer/cfg.cpp
tonytoon b897349052 screenshot optimizations to remove stalls in inputs / cd based cores (#1126)
* increase allowed autofire rates from 5 to 30. add autofire_on_directions option to mister.ini (defaults to 0/off)

* simd rewrite of scaler memcopy

* optimize screenshots / scaler copies

* optimize screenshots; simd memcopy for scaler_read, add frame timer callback handler

* tweaks

* restore .png save functionality.

* screenshot optimizations

* fix double free, guard against NULL

* fixing merge

* add mister.ini for screenshot_image_format

* correct file extension handling.

* slightly tweak bmp routine

* consolide screenshot code back into scaler.cpp, remove custom resize/bmp code.
2026-03-24 14:55:00 +08:00

811 lines
24 KiB
C++

// cfg.c
// 2015, rok.krajnc@gmail.com
// 2017+, Sorgelig
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <ctype.h>
#include "cfg.h"
#include "debug.h"
#include "file_io.h"
#include "user_io.h"
#include "video.h"
#include "support/arcade/mra_loader.h"
cfg_t cfg;
static FILE *orig_stdout = NULL;
static FILE *dev_null = NULL;
typedef enum
{
UINT8 = 0, INT8, UINT16, INT16, UINT32, INT32, HEX8, HEX16, HEX32, FLOAT, STRING, UINT32ARR, HEX32ARR, STRINGARR
} ini_vartypes_t;
typedef struct
{
const char* name;
void* var;
ini_vartypes_t type;
int64_t min;
int64_t max;
} ini_var_t;
static const ini_var_t ini_vars[] =
{
{ "YPBPR", (void*)(&(cfg.vga_mode_int)), UINT8, 0, 1 },
{ "COMPOSITE_SYNC", (void*)(&(cfg.csync)), UINT8, 0, 1 },
{ "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)), HEX32, 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 },
{ "VIDEO_MODE_PAL", (void*)(cfg.video_conf_pal), STRING, 0, sizeof(cfg.video_conf_pal) - 1 },
{ "VIDEO_MODE_NTSC", (void*)(cfg.video_conf_ntsc), STRING, 0, sizeof(cfg.video_conf_ntsc) - 1 },
{ "VIDEO_INFO", (void*)(&(cfg.video_info)), UINT8, 0, 10 },
{ "VSYNC_ADJUST", (void*)(&(cfg.vsync_adjust)), UINT8, 0, 2 },
{ "HDMI_AUDIO_96K", (void*)(&(cfg.hdmi_audio_96k)), UINT8, 0, 1 },
{ "DVI_MODE", (void*)(&(cfg.dvi_mode)), UINT8, 0, 1 },
{ "HDMI_LIMITED", (void*)(&(cfg.hdmi_limited)), UINT8, 0, 2 },
{ "KBD_NOMOUSE", (void*)(&(cfg.kbd_nomouse)), UINT8, 0, 1 },
{ "MOUSE_THROTTLE", (void*)(&(cfg.mouse_throttle)), UINT8, 1, 100 },
{ "BOOTSCREEN", (void*)(&(cfg.bootscreen)), UINT8, 0, 1 },
{ "VSCALE_MODE", (void*)(&(cfg.vscale_mode)), UINT8, 0, 5 },
{ "VSCALE_BORDER", (void*)(&(cfg.vscale_border)), UINT16, 0, 399 },
{ "RBF_HIDE_DATECODE", (void*)(&(cfg.rbf_hide_datecode)), UINT8, 0, 1 },
{ "MENU_PAL", (void*)(&(cfg.menu_pal)), UINT8, 0, 1 },
{ "BOOTCORE", (void*)(&(cfg.bootcore)), STRING, 0, sizeof(cfg.bootcore) - 1 },
{ "BOOTCORE_TIMEOUT", (void*)(&(cfg.bootcore_timeout)), INT16, 2, 30 },
{ "FONT", (void*)(&(cfg.font)), STRING, 0, sizeof(cfg.font) - 1 },
{ "FB_SIZE", (void*)(&(cfg.fb_size)), UINT8, 0, 4 },
{ "FB_TERMINAL", (void*)(&(cfg.fb_terminal)), UINT8, 0, 1 },
{ "OSD_TIMEOUT", (void*)(&(cfg.osd_timeout)), INT16, 0, 3600 },
{ "DIRECT_VIDEO", (void*)(&(cfg.direct_video)), UINT8, 0, 2 },
{ "OSD_ROTATE", (void*)(&(cfg.osd_rotate)), UINT8, 0, 2 },
{ "DEADZONE", (void*)(&(cfg.controller_deadzone)), STRINGARR, sizeof(cfg.controller_deadzone) / sizeof(*cfg.controller_deadzone), sizeof(*cfg.controller_deadzone) },
{ "GAMEPAD_DEFAULTS", (void*)(&(cfg.gamepad_defaults)), UINT8, 0, 1 },
{ "RECENTS", (void*)(&(cfg.recents)), UINT8, 0, 1 },
{ "CONTROLLER_INFO", (void*)(&(cfg.controller_info)), UINT8, 0, 10 },
{ "REFRESH_MIN", (void*)(&(cfg.refresh_min)), FLOAT, 0, 150 },
{ "REFRESH_MAX", (void*)(&(cfg.refresh_max)), FLOAT, 0, 150 },
{ "JAMMA_VID", (void*)(&(cfg.jamma_vid)), HEX16, 0, 0xFFFF },
{ "JAMMA_PID", (void*)(&(cfg.jamma_pid)), HEX16, 0, 0xFFFF },
{ "JAMMA2_VID", (void*)(&(cfg.jamma2_vid)), HEX16, 0, 0xFFFF },
{ "JAMMA2_PID", (void*)(&(cfg.jamma2_pid)), HEX16, 0, 0xFFFF },
{ "SNIPER_MODE", (void*)(&(cfg.sniper_mode)), UINT8, 0, 1 },
{ "BROWSE_EXPAND", (void*)(&(cfg.browse_expand)), UINT8, 0, 1 },
{ "LOGO", (void*)(&(cfg.logo)), UINT8, 0, 1 },
{ "SHARED_FOLDER", (void*)(&(cfg.shared_folder)), STRING, 0, sizeof(cfg.shared_folder) - 1 },
{ "NO_MERGE_VID", (void*)(&(cfg.no_merge_vid)), HEX16, 0, 0xFFFF },
{ "NO_MERGE_PID", (void*)(&(cfg.no_merge_pid)), HEX16, 0, 0xFFFF },
{ "NO_MERGE_VIDPID", (void*)(cfg.no_merge_vidpid), HEX32ARR, 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)), HEX16, 0, 0xFFFF },
{ "SPINNER_PID", (void*)(&(cfg.spinner_pid)), HEX16, 0, 0xFFFF },
{ "SPINNER_AXIS", (void*)(&(cfg.spinner_axis)), UINT8, 0, 2 },
{ "SPINNER_THROTTLE", (void*)(&(cfg.spinner_throttle)), INT32, -10000, 10000 },
{ "AFILTER_DEFAULT", (void*)(&(cfg.afilter_default)), STRING, 0, sizeof(cfg.afilter_default) - 1 },
{ "VFILTER_DEFAULT", (void*)(&(cfg.vfilter_default)), STRING, 0, sizeof(cfg.vfilter_default) - 1 },
{ "VFILTER_VERTICAL_DEFAULT", (void*)(&(cfg.vfilter_vertical_default)), STRING, 0, sizeof(cfg.vfilter_vertical_default) - 1 },
{ "VFILTER_SCANLINES_DEFAULT", (void*)(&(cfg.vfilter_scanlines_default)), STRING, 0, sizeof(cfg.vfilter_scanlines_default) - 1 },
{ "SHMASK_DEFAULT", (void*)(&(cfg.shmask_default)), STRING, 0, sizeof(cfg.shmask_default) - 1 },
{ "SHMASK_MODE_DEFAULT", (void*)(&(cfg.shmask_mode_default)), UINT8, 0, 255 },
{ "PRESET_DEFAULT", (void*)(&(cfg.preset_default)), STRING, 0, sizeof(cfg.preset_default) - 1 },
{ "LOG_FILE_ENTRY", (void*)(&(cfg.log_file_entry)), UINT8, 0, 1 },
{ "BT_AUTO_DISCONNECT", (void*)(&(cfg.bt_auto_disconnect)), UINT32, 0, 180 },
{ "BT_RESET_BEFORE_PAIR", (void*)(&(cfg.bt_reset_before_pair)), UINT8, 0, 1 },
{ "WAITMOUNT", (void*)(&(cfg.waitmount)), STRING, 0, sizeof(cfg.waitmount) - 1 },
{ "RUMBLE", (void *)(&(cfg.rumble)), UINT8, 0, 1 },
{ "WHEEL_FORCE", (void*)(&(cfg.wheel_force)), UINT8, 0, 100 },
{ "WHEEL_RANGE", (void*)(&(cfg.wheel_range)), UINT16, 0, 1000 },
{ "HDMI_GAME_MODE", (void *)(&(cfg.hdmi_game_mode)), UINT8, 0, 1 },
{ "VRR_MODE", (void *)(&(cfg.vrr_mode)), UINT8, 0, 3 },
{ "VRR_MIN_FRAMERATE", (void *)(&(cfg.vrr_min_framerate)), UINT8, 0, 255 },
{ "VRR_MAX_FRAMERATE", (void *)(&(cfg.vrr_max_framerate)), UINT8, 0, 255 },
{ "VRR_VESA_FRAMERATE", (void *)(&(cfg.vrr_vesa_framerate)), UINT8, 0, 255 },
{ "VIDEO_OFF", (void*)(&(cfg.video_off)), INT16, 0, 3600 },
{ "VIDEO_OFF_HDMI", (void*)(&(cfg.video_off_hdmi)), UINT8, 0, 1 },
{ "PLAYER_1_CONTROLLER", (void*)(&(cfg.player_controller[0])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0]) },
{ "PLAYER_2_CONTROLLER", (void*)(&(cfg.player_controller[1])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0]) },
{ "PLAYER_3_CONTROLLER", (void*)(&(cfg.player_controller[2])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0]) },
{ "PLAYER_4_CONTROLLER", (void*)(&(cfg.player_controller[3])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0]) },
{ "PLAYER_5_CONTROLLER", (void*)(&(cfg.player_controller[4])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0]) },
{ "PLAYER_6_CONTROLLER", (void*)(&(cfg.player_controller[5])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0]) },
{ "DISABLE_AUTOFIRE", (void *)(&(cfg.disable_autofire)), UINT8, 0, 1 },
{ "VIDEO_BRIGHTNESS", (void *)(&(cfg.video_brightness)), UINT8, 0, 100 },
{ "VIDEO_CONTRAST", (void *)(&(cfg.video_contrast)), UINT8, 0, 100 },
{ "VIDEO_SATURATION", (void *)(&(cfg.video_saturation)), UINT8, 0, 100 },
{ "VIDEO_HUE", (void *)(&(cfg.video_hue)), UINT16, 0, 360 },
{ "VIDEO_GAIN_OFFSET", (void *)(&(cfg.video_gain_offset)), STRING, 0, sizeof(cfg.video_gain_offset) },
{ "HDR", (void*)(&cfg.hdr), UINT8, 0, 2 },
{ "HDR_MAX_NITS", (void*)(&(cfg.hdr_max_nits)), UINT16, 100, 10000 },
{ "HDR_AVG_NITS", (void*)(&(cfg.hdr_avg_nits)), UINT16, 100, 10000 },
{ "VGA_MODE", (void*)(&(cfg.vga_mode)), STRING, 0, sizeof(cfg.vga_mode) - 1 },
{ "NTSC_MODE", (void *)(&(cfg.ntsc_mode)), UINT8, 0, 2 },
{ "CONTROLLER_UNIQUE_MAPPING", (void *)(cfg.controller_unique_mapping), UINT32ARR, 0, 0xFFFFFFFF },
{ "OSD_LOCK", (void*)(&(cfg.osd_lock)), STRING, 0, sizeof(cfg.osd_lock) - 1 },
{ "OSD_LOCK_TIME", (void*)(&(cfg.osd_lock_time)), UINT16, 0, 60 },
{ "DEBUG", (void *)(&(cfg.debug)), UINT8, 0, 1 },
{ "LOOKAHEAD", (void *)(&(cfg.lookahead)), UINT8, 0, 3 },
{ "MAIN", (void*)(&(cfg.main)), STRING, 0, sizeof(cfg.main) - 1 },
{ "VFILTER_INTERLACE_DEFAULT", (void*)(&(cfg.vfilter_interlace_default)), STRING, 0, sizeof(cfg.vfilter_interlace_default) - 1 },
{ "AUTOFIRE_RATES", (void *)(&(cfg.autofire_rates)), STRING, 0, sizeof(cfg.autofire_rates) - 1 },
{ "AUTOFIRE_ON_DIRECTIONS", (void *)(&(cfg.autofire_on_directions)), UINT8, 0, 1 },
{ "SCREENSHOT_IMAGE_FORMAT", (void *)(&(cfg.screenshot_image_format)), STRING, 0, sizeof(cfg.screenshot_image_format) - 1 },
};
static const int nvars = (int)(sizeof(ini_vars) / sizeof(ini_var_t));
#define INI_LINE_SIZE 1024
#define INI_SECTION_START '['
#define INI_SECTION_END ']'
#define INCL_SECTION '+'
#define CHAR_IS_NUM(c) (((c) >= '0') && ((c) <= '9'))
#define CHAR_IS_ALPHA_LOWER(c) (((c) >= 'a') && ((c) <= 'z'))
#define CHAR_IS_ALPHA_UPPER(c) (((c) >= 'A') && ((c) <= 'Z'))
#define CHAR_IS_ALPHANUM(c) (CHAR_IS_ALPHA_LOWER(c) || CHAR_IS_ALPHA_UPPER(c) || CHAR_IS_NUM(c))
#define CHAR_IS_SPECIAL(c) (((c) == '[') || ((c) == ']') || ((c) == '(') || ((c) == ')') || \
((c) == '-') || ((c) == '+') || ((c) == '/') || ((c) == '=') || \
((c) == '#') || ((c) == '$') || ((c) == '@') || ((c) == '_') || \
((c) == ',') || ((c) == '.') || ((c) == '!') || ((c) == '*') || \
((c) == ':') || ((c) == '~'))
#define CHAR_IS_VALID(c) (CHAR_IS_ALPHANUM(c) || CHAR_IS_SPECIAL(c))
#define CHAR_IS_SPACE(c) (((c) == ' ') || ((c) == '\t'))
#define CHAR_IS_LINEEND(c) (((c) == '\n'))
#define CHAR_IS_COMMENT(c) (((c) == ';'))
#define CHAR_IS_QUOTE(c) (((c) == '"'))
fileTYPE ini_file;
static bool has_video_sections = false;
static bool using_video_section = false;
int ini_pt = 0;
static char ini_getch()
{
static uint8_t buf[512];
if (!(ini_pt & 0x1ff)) FileReadSec(&ini_file, buf);
if (ini_pt >= ini_file.size) return 0;
return buf[(ini_pt++) & 0x1ff];
}
static int ini_getline(char* line)
{
char c, ignore = 0, skip = 1;
int i = 0;
while ((c = ini_getch()))
{
if (!CHAR_IS_SPACE(c)) skip = 0;
if (i >= (INI_LINE_SIZE - 1) || CHAR_IS_COMMENT(c)) ignore = 1;
if (CHAR_IS_LINEEND(c)) break;
if ((CHAR_IS_SPACE(c) || CHAR_IS_VALID(c)) && !ignore && !skip) line[i++] = c;
}
line[i] = 0;
while (i > 0 && CHAR_IS_SPACE(line[i - 1])) line[--i] = 0;
return c == 0;
}
static int ini_get_section(char* buf, const char *vmode)
{
int i = 0;
int incl = (buf[0] == INCL_SECTION);
// get section start marker
if (buf[0] != INI_SECTION_START && buf[0] != INCL_SECTION)
{
return 0;
}
else buf++;
int wc_pos = -1;
int eq_pos = -1;
// get section stop marker
while (buf[i])
{
if (buf[i] == INI_SECTION_END)
{
buf[i] = 0;
break;
}
if (buf[i] == '*') wc_pos = i;
if (buf[i] == '=') eq_pos = i;
i++;
if (i >= INI_LINE_SIZE) return 0;
}
if (!strcasecmp(buf, "MiSTer") ||
(is_arcade() && !strcasecmp(buf, "arcade")) ||
(arcade_is_vertical() && !strcasecmp(buf, "arcade_vertical")) ||
((wc_pos >= 0) ? !strncasecmp(buf, user_io_get_core_name(1), wc_pos) : !strcasecmp(buf, user_io_get_core_name(1))) ||
((wc_pos >= 0) ? !strncasecmp(buf, user_io_get_core_name(0), wc_pos) : !strcasecmp(buf, user_io_get_core_name(0))))
{
if (incl)
{
ini_parser_debugf("included '%s'", buf);
}
else
{
ini_parser_debugf("Got SECTION '%s'", buf);
}
return 1;
}
else if ((eq_pos >= 0) && !strncasecmp(buf, "video", eq_pos))
{
has_video_sections = true;
if(!strcasecmp(&buf[eq_pos+1], vmode))
{
using_video_section = true;
ini_parser_debugf("Got SECTION '%s'", buf);
return 1;
}
else
{
return 0;
}
}
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;
bool invalid_format = false;
switch(var->type)
{
case HEX8:
case HEX16:
case HEX32:
case HEX32ARR:
invalid_format = strncasecmp(text, "0x", 2);
// fall through
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);
else if (invalid_format) cfg_error("%s: \'%s\' invalid format", var->name, text);
switch (var->type)
{
case HEX8:
case UINT8: *(uint8_t*)out = u32; break;
case INT8: *(int8_t*)out = i32; break;
case HEX16:
case UINT16: *(uint16_t*)out = u32; break;
case HEX32ARR:
case UINT32ARR: *(uint32_t*)out = u32; break;
case INT16: *(int16_t*)out = i32; break;
case HEX32:
case UINT32: *(uint32_t*)out = u32; break;
case INT32: *(int32_t*)out = i32; break;
case FLOAT: *(float*)out = f32; break;
default: break;
}
}
// Used to determine if an array variable should be appended or restarted.
static bool var_array_append[sizeof(ini_vars) / sizeof(ini_var_t)] = {};
static void ini_parse_var(char* buf)
{
// find var
int i = 0;
while (1)
{
if (buf[i] == '=' || CHAR_IS_SPACE(buf[i]))
{
buf[i] = 0;
break;
}
else if (!buf[i]) return;
i++;
}
// parse var
int var_id = -1;
for (int j = 0; j < (int)(sizeof(ini_vars) / sizeof(ini_var_t)); j++)
{
if (!strcasecmp(buf, ini_vars[j].name)) var_id = j;
}
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);
const ini_var_t *var = &ini_vars[var_id];
switch (var->type)
{
case STRING:
memset(var->var, 0, var->max);
snprintf((char*)(var->var), var->max, "%s", buf+i);
break;
case STRINGARR:
{
int item_sz = var->max;
if (!var_array_append[var_id])
{
var_array_append[var_id] = true;
for (int n = 0; n < var->min; n++)
{
char *str = ((char*)var->var) + (n * item_sz);
str[0] = 0;
}
}
for (int n = 0; n < var->min; n++)
{
char *str = ((char*)var->var) + (n * item_sz);
if (!strlen(str))
{
snprintf(str, item_sz, "%s", buf + i);
break;
}
}
}
break;
case HEX32ARR:
case UINT32ARR:
{
if (!var_array_append[var_id])
{
var_array_append[var_id] = true;
uint32_t *arr = (uint32_t*)var->var;
arr[0] = 0;
}
uint32_t *arr = (uint32_t*)var->var;
uint32_t pos = ++arr[0];
ini_parse_numeric(var, &buf[i], &arr[pos]);
}
break;
default:
ini_parse_numeric(var, &buf[i], var->var);
if (!strcasecmp(var->name, "DEBUG"))
{
stdout = cfg.debug ? orig_stdout : dev_null;
}
break;
}
}
}
static void ini_parse(int alt, const char *vmode)
{
static char line[INI_LINE_SIZE];
int section = 0;
int eof;
if (!orig_stdout) orig_stdout = stdout;
if (!dev_null)
{
dev_null = fopen("/dev/null", "w");
if (dev_null)
{
int null_fd = fileno(dev_null);
if (null_fd >= 0) fcntl(null_fd, F_SETFD, FD_CLOEXEC);
stdout = dev_null;
}
}
ini_parser_debugf("Start INI parser for core \"%s\"(%s), video mode \"%s\".", user_io_get_core_name(0), user_io_get_core_name(1), vmode);
memset(line, 0, sizeof(line));
memset(&ini_file, 0, sizeof(ini_file));
const char *name = cfg_get_name(alt);
if (!FileOpen(&ini_file, name)) return;
ini_parser_debugf("Opened file %s with size %llu bytes.", name, ini_file.size);
ini_pt = 0;
// parse ini
while (1)
{
// get line
eof = ini_getline(line);
ini_parser_debugf("line(%d): \"%s\".", section, line);
if (line[0] == INI_SECTION_START)
{
// if first char in line is INI_SECTION_START, get section
section = ini_get_section(line, vmode);
if (section)
{
memset(var_array_append, 0, sizeof(var_array_append));
}
}
else if (line[0] == INCL_SECTION && !section)
{
section = ini_get_section(line, vmode);
if (section)
{
memset(var_array_append, 0, sizeof(var_array_append));
}
}
else if(section)
{
// otherwise this is a variable, get it
ini_parse_var(line);
}
// if end of file, stop
if (eof) break;
}
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 int done = 0;
static char names[3][64] = {};
static char name[64];
if (!done)
{
done = 1;
DIR *d = opendir(getRootDir());
if (!d)
{
printf("Couldn't open dir: %s\n", getRootDir());
}
else
{
struct dirent *de;
int i = 0;
while ((de = readdir(d)) && i < 3)
{
int len = strlen(de->d_name);
if (!strncasecmp(de->d_name, "MiSTer_", 7) && !strcasecmp(de->d_name + len - 4, ".ini"))
{
snprintf(names[i], sizeof(names[0]), "%s", de->d_name);
i++;
}
}
closedir(d);
}
for (int i = 1; i < 3; i++)
{
for (int j = 1; j < 3; j++)
{
if ((!names[j - 1][0] && names[j][0]) || (names[j - 1][0] && names[j][0] && strcasecmp(names[j - 1], names[j]) > 0))
{
strcpy(name, names[j - 1]);
strcpy(names[j - 1], names[j]);
strcpy(names[j], name);
}
}
}
}
strcpy(name, "MiSTer.ini");
if (alt && alt < 4) strcpy(name, names[alt-1]);
return name;
}
const char* cfg_get_label(uint8_t alt)
{
if (!alt) return "Main";
const char *name = cfg_get_name(alt);
if (!name[0]) return " -- ";
static char label[6];
snprintf(label, sizeof(label), "%s", name + 7);
char *p = strrchr(label, '.');
if (p) *p = 0;
if (!strcasecmp(label, "alt")) return "Alt1";
if (!strcasecmp(label, "alt_1")) return "Alt1";
if (!strcasecmp(label, "alt_2")) return "Alt2";
if (!strcasecmp(label, "alt_3")) return "Alt3";
for (int i = 0; i < 4; i++) if (!label[i]) label[i] = ' ';
label[4] = 0;
return label;
}
void cfg_parse()
{
memset(&cfg, 0, sizeof(cfg));
cfg.csync = 1;
cfg.bootscreen = 1;
cfg.fb_terminal = 1;
cfg.controller_info = 6;
cfg.browse_expand = 1;
cfg.logo = 1;
cfg.rumble = 1;
cfg.wheel_force = 50;
cfg.dvi_mode = 2;
cfg.lookahead = 2;
cfg.hdr = 0;
cfg.hdr_max_nits = 1000;
cfg.hdr_avg_nits = 250;
cfg.video_brightness = 50;
cfg.video_contrast = 50;
cfg.video_saturation = 100;
cfg.video_hue = 0;
strcpy(cfg.video_gain_offset, "1, 0, 1, 0, 1, 0");
strcpy(cfg.main, "MiSTer");
has_video_sections = false;
using_video_section = false;
cfg_error_count = 0;
strcpy(cfg.autofire_rates, "10,15,30");
strcpy(cfg.screenshot_image_format, "png");
ini_parse(altcfg(), video_get_core_mode_name(1));
if (has_video_sections && !using_video_section)
{
// second pass to look for section without vrefresh
ini_parse(altcfg(), video_get_core_mode_name(0));
}
if (strlen(cfg.vga_mode))
{
if (!strcasecmp(cfg.vga_mode, "rgb")) cfg.vga_mode_int = 0;
if (!strcasecmp(cfg.vga_mode, "ypbpr")) cfg.vga_mode_int = 1;
if (!strcasecmp(cfg.vga_mode, "svideo")) cfg.vga_mode_int = 2;
if (!strcasecmp(cfg.vga_mode, "cvbs")) cfg.vga_mode_int = 3;
if (!strcasecmp(cfg.vga_mode, "subcarrier"))
{
cfg.vga_mode_int = 4;
cfg.csync = 1;
cfg.forced_scandoubler = 0;
}
}
}
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;
}
void cfg_print()
{
printf("Loaded config:\n--------------\n");
for (uint i = 0; i < (sizeof(ini_vars) / sizeof(ini_vars[0])); i++)
{
switch (ini_vars[i].type)
{
case UINT8:
printf(" %s=%u\n", ini_vars[i].name, *(uint8_t*)ini_vars[i].var);
break;
case UINT16:
printf(" %s=%u\n", ini_vars[i].name, *(uint16_t*)ini_vars[i].var);
break;
case UINT32:
printf(" %s=%u\n", ini_vars[i].name, *(uint32_t*)ini_vars[i].var);
break;
case UINT32ARR:
if (*(uint32_t*)ini_vars[i].var)
{
uint32_t* arr = (uint32_t*)ini_vars[i].var;
for (uint32_t n = 0; n < arr[0]; n++) printf(" %s=%u\n", ini_vars[i].name, arr[n + 1]);
}
break;
case HEX8:
printf(" %s=0x%02X\n", ini_vars[i].name, *(uint8_t*)ini_vars[i].var);
break;
case HEX16:
printf(" %s=0x%04X\n", ini_vars[i].name, *(uint16_t*)ini_vars[i].var);
break;
case HEX32:
printf(" %s=0x%08X\n", ini_vars[i].name, *(uint32_t*)ini_vars[i].var);
break;
case HEX32ARR:
if (*(uint32_t*)ini_vars[i].var)
{
uint32_t* arr = (uint32_t*)ini_vars[i].var;
for (uint32_t n = 0; n < arr[0]; n++) printf(" %s=0x%08X\n", ini_vars[i].name, arr[n + 1]);
}
break;
case INT8:
printf(" %s=%d\n", ini_vars[i].name, *(int8_t*)ini_vars[i].var);
break;
case INT16:
printf(" %s=%d\n", ini_vars[i].name, *(int16_t*)ini_vars[i].var);
break;
case INT32:
printf(" %s=%d\n", ini_vars[i].name, *(int32_t*)ini_vars[i].var);
break;
case FLOAT:
printf(" %s=%f\n", ini_vars[i].name, *(float*)ini_vars[i].var);
break;
case STRING:
if (*(uint32_t*)ini_vars[i].var) printf(" %s=%s\n", ini_vars[i].name, (char*)ini_vars[i].var);
break;
case STRINGARR:
for (int n = 0; n < ini_vars[i].min; n++)
{
char *str = ((char*)ini_vars[i].var) + (n*ini_vars[i].max);
if (!strlen(str)) break;
printf(" %s=%s\n", ini_vars[i].name, str);
}
break;
}
}
printf("--------------\n");
}
static int yc_parse_mode(char* buf, yc_mode *mode)
{
int i = 0;
while (1)
{
if (buf[i] == '=' || CHAR_IS_LINEEND(buf[i]))
{
buf[i] = 0;
break;
}
else if (!buf[i]) return 0;
i++;
}
i++;
while (buf[i] == '=' || CHAR_IS_SPACE(buf[i])) i++;
ini_parser_debugf("Got yc_mode '%s' with VALUE %s", buf, buf + i);
snprintf(mode->key, sizeof(mode->key), "%s", buf);
mode->phase_inc = strtoull(buf + i, 0, 0);
if (!mode->phase_inc)
{
printf("ERROR: cannot parse YC phase_inc: '%s'\n", buf + i);
return 0;
}
return 1;
}
void yc_parse(yc_mode *yc_table, int max)
{
memset(yc_table, 0, max * sizeof(yc_mode));
static char line[INI_LINE_SIZE];
int eof;
memset(line, 0, sizeof(line));
memset(&ini_file, 0, sizeof(ini_file));
const char *corename = user_io_get_core_name(1);
int corename_len = strlen(corename);
const char *name = "yc.txt";
if (!FileOpen(&ini_file, name)) return;
ini_parser_debugf("Opened file %s with size %llu bytes.", name, ini_file.size);
ini_pt = 0;
int n = 0;
while (n < max)
{
// get line
eof = ini_getline(line);
if (!strncasecmp(line, corename, corename_len))
{
int res = yc_parse_mode(line, &yc_table[n]);
if (res) n++;
}
// if end of file, stop
if (eof) break;
}
FileClose(&ini_file);
}