RetroArch/deps/switchres/switchres.cpp
Ben Templeman 197203d09b Fixed monitor index corruption on Windows and added correct fractal scalling. only used when required.
Updated log defines to match SR upstream.

Added new SR_CONFIG_PATHS for non Winddows and Linux systems.
Not that SR works on them but to fix RA compile issues

Updated SR2 code base to latest. Added supprt for windows monitor indexing.
Fixed monitor index bug where index 1 was not being used corretly
and "auto" was not being sent.

Updated swithres for x86 windows fix
fixed SR2 auto issue

Fixed auto monitor bug

Fixed monitor index corruption on Windows

Fixxed buffer size bug

Added correct fractal scalling. only used when required.
2021-07-07 18:06:47 +01:00

382 lines
10 KiB
C++

/**************************************************************
switchres.cpp - Swichres manager
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <fstream>
#include <string.h>
#include <algorithm>
#include "switchres.h"
#include "log.h"
using namespace std;
const string WHITESPACE = " \n\r\t\f\v";
#if defined(_WIN32)
#define SR_CONFIG_PATHS ";.\\;.\\ini\\;"
#elif defined(__linux__)
#define SR_CONFIG_PATHS ";./;./ini/;/etc/;"
#else
#define SR_CONFIG_PATHS ";./"
#endif
//============================================================
// logging
//============================================================
void switchres_manager::set_log_level(int log_level) { set_log_verbosity(log_level); }
void switchres_manager::set_log_verbose_fn(void *func_ptr) { set_log_verbose((void *)func_ptr); }
void switchres_manager::set_log_info_fn(void *func_ptr) { set_log_info((void *)func_ptr); }
void switchres_manager::set_log_error_fn(void *func_ptr) { set_log_error((void *)func_ptr); }
//============================================================
// File parsing helpers
//============================================================
string ltrim(const string& s)
{
size_t start = s.find_first_not_of(WHITESPACE);
return (start == string::npos) ? "" : s.substr(start);
}
string rtrim(const string& s)
{
size_t end = s.find_last_not_of(WHITESPACE);
return (end == string::npos) ? "" : s.substr(0, end + 1);
}
string trim(const string& s)
{
return rtrim(ltrim(s));
}
bool get_value(const string& line, string& key, string& value)
{
size_t key_end = line.find_first_of(WHITESPACE);
key = line.substr(0, key_end);
value = ltrim(line.substr(key_end + 1));
if (key.length() > 0 && value.length() > 0)
return true;
return false;
}
constexpr unsigned int s2i(const char* str, int h = 0)
{
return !str[h] ? 5381 : (s2i(str, h+1)*33) ^ str[h];
}
//============================================================
// switchres_manager::switchres_manager
//============================================================
switchres_manager::switchres_manager()
{
// Set Switchres default config options
set_monitor("generic_15");
set_modeline("auto");
set_lcd_range("auto");
for (int i = 0; i++ < MAX_RANGES;) set_crt_range(i, "auto");
// Set display manager default options
set_screen("auto");
set_modeline_generation(true);
set_lock_unsupported_modes(true);
set_lock_system_modes(true);
set_refresh_dont_care(false);
// Set modeline generator default options
set_interlace(true);
set_doublescan(true);
set_dotclock_min(0.0f);
set_rotation(false);
set_monitor_aspect(STANDARD_CRT_ASPECT);
set_refresh_tolerance(2.0f);
set_super_width(2560);
set_v_shift_correct(0);
set_pixel_precision(1);
set_interlace_force_even(0);
// Create our display manager
m_display_factory = new display_manager();
// Set logger properties
set_log_info_fn((void*)printf);
set_log_error_fn((void*)printf);
set_log_verbose_fn((void*)printf);
set_log_level(2);
}
//============================================================
// switchres_manager::~switchres_manager
//============================================================
switchres_manager::~switchres_manager()
{
if (m_display_factory) delete m_display_factory;
for (auto &display : displays)
delete display;
};
//============================================================
// switchres_manager::add_display
//============================================================
display_manager* switchres_manager::add_display()
{
// Parse display specific ini, if it exists
display_settings base_ds = ds;
char file_name[32] = {0};
sprintf(file_name, "display%d.ini", (int)displays.size());
parse_config(file_name);
// Create new display
display_manager *display = m_display_factory->make(&ds);
display->set_index(displays.size());
displays.push_back(display);
log_verbose("Switchres(v%s) display[%d]: monitor[%s] generation[%s]\n",
SWITCHRES_VERSION, display->index(), ds.monitor, ds.modeline_generation?"on":"off");
display->parse_options();
// restore base display settings
ds = base_ds;
return display;
}
//============================================================
// switchres_manager::parse_config
//============================================================
bool switchres_manager::parse_config(const char *file_name)
{
ifstream config_file;
// Search for ini file in our config paths
auto start = 0U;
while (true)
{
char full_path[256] = "";
string paths = SR_CONFIG_PATHS;
auto end = paths.find(";", start);
if (end == string::npos) return false;
snprintf(full_path, sizeof(full_path), "%s%s", paths.substr(start, end - start).c_str(), file_name);
config_file.open(full_path);
if (config_file.is_open())
{
log_verbose("parsing %s\n", full_path);
break;
}
start = end + 1;
}
// Ini file found, parse it
string line;
while (getline(config_file, line))
{
line = trim(line);
if (line.length() == 0 || line.at(0) == '#')
continue;
string key, value;
if(get_value(line, key, value))
{
switch (s2i(key.c_str()))
{
// Switchres options
case s2i("verbose"):
if (atoi(value.c_str())) set_log_verbose_fn((void*)printf);
break;
case s2i("monitor"):
transform(value.begin(), value.end(), value.begin(), ::tolower);
set_monitor(value.c_str());
break;
case s2i("crt_range0"):
set_crt_range(0, value.c_str());
break;
case s2i("crt_range1"):
set_crt_range(1, value.c_str());
break;
case s2i("crt_range2"):
set_crt_range(2, value.c_str());
break;
case s2i("crt_range3"):
set_crt_range(3, value.c_str());
break;
case s2i("crt_range4"):
set_crt_range(4, value.c_str());
break;
case s2i("crt_range5"):
set_crt_range(5, value.c_str());
break;
case s2i("crt_range6"):
set_crt_range(6, value.c_str());
break;
case s2i("crt_range7"):
set_crt_range(7, value.c_str());
break;
case s2i("crt_range8"):
set_crt_range(8, value.c_str());
break;
case s2i("crt_range9"):
set_crt_range(9, value.c_str());
break;
case s2i("lcd_range"):
set_lcd_range(value.c_str());
break;
case s2i("modeline"):
set_modeline(value.c_str());
break;
case s2i("user_mode"):
{
modeline user_mode = {};
if (strcmp(value.c_str(), "auto"))
{
if (sscanf(value.c_str(), "%dx%d@%d", &user_mode.width, &user_mode.height, &user_mode.refresh) < 1)
{
log_error("Error: use format resolution <w>x<h>@<r>\n");
break;
}
}
set_user_mode(&user_mode);
break;
}
// Display options
case s2i("display"):
set_screen(value.c_str());
break;
case s2i("api"):
set_api(value.c_str());
break;
case s2i("modeline_generation"):
set_modeline_generation(atoi(value.c_str()));
break;
case s2i("lock_unsupported_modes"):
set_lock_unsupported_modes(atoi(value.c_str()));
break;
case s2i("lock_system_modes"):
set_lock_system_modes(atoi(value.c_str()));
break;
case s2i("refresh_dont_care"):
set_refresh_dont_care(atoi(value.c_str()));
break;
case s2i("keep_changes"):
set_keep_changes(atoi(value.c_str()));
break;
// Modeline generation options
case s2i("interlace"):
set_interlace(atoi(value.c_str()));
break;
case s2i("doublescan"):
set_doublescan(atoi(value.c_str()));
break;
case s2i("dotclock_min"):
{
double pclock_min = 0.0f;
sscanf(value.c_str(), "%lf", &pclock_min);
set_dotclock_min(pclock_min);
break;
}
case s2i("sync_refresh_tolerance"):
{
double refresh_tolerance = 0.0f;
sscanf(value.c_str(), "%lf", &refresh_tolerance);
set_refresh_tolerance(refresh_tolerance);
break;
}
case s2i("super_width"):
{
int super_width = 0;
sscanf(value.c_str(), "%d", &super_width);
set_super_width(super_width);
break;
}
case s2i("aspect"):
set_monitor_aspect(get_aspect(value.c_str()));
break;
case s2i("v_shift_correct"):
set_v_shift_correct(atoi(value.c_str()));
break;
case s2i("pixel_precision"):
set_pixel_precision(atoi(value.c_str()));
break;
case s2i("interlace_force_even"):
set_interlace_force_even(atoi(value.c_str()));
break;
// Custom video backend options
case s2i("screen_compositing"):
set_screen_compositing(atoi(value.c_str()));
break;
case s2i("screen_reordering"):
set_screen_reordering(atoi(value.c_str()));
break;
case s2i("allow_hardware_refresh"):
set_allow_hardware_refresh(atoi(value.c_str()));
break;
case s2i("custom_timing"):
set_custom_timing(value.c_str());
break;
// Various
case s2i("verbosity"):
{
int verbosity_level = 1;
sscanf(value.c_str(), "%d", &verbosity_level);
set_log_level(verbosity_level);
break;
}
default:
log_error("Invalid option %s\n", key.c_str());
break;
}
}
}
config_file.close();
return true;
}
//============================================================
// switchres_manager::get_aspect
//============================================================
double switchres_manager::get_aspect(const char* aspect)
{
int num, den;
if (sscanf(aspect, "%d:%d", &num, &den) == 2)
{
if (den == 0)
{
log_error("Error: denominator can't be zero\n");
return STANDARD_CRT_ASPECT;
}
return (double(num)/double(den));
}
log_error("Error: use format --aspect <num:den>\n");
return STANDARD_CRT_ASPECT;
}