RetroArch/deps/switchres/switchres.cpp
2021-06-07 19:15:37 +02:00

380 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\\;"
#else
#define SR_CONFIG_PATHS ";./;./ini/;/etc/;"
#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;
}