mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-24 00:20:01 +00:00
197203d09b
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.
382 lines
10 KiB
C++
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;
|
|
}
|