scummvm/engines/ags/engine/main/config.cpp
Walter Agazzi 5d58384af2 AGS: remove "supersampling" setting, as not entirely supported
This setting comes from the original mobile port of AGS, but it's not clear whether it was
useful. More importantly, there are issues with this:
1. It was never implemented in another hardware accelerated driver (Direct3D), being
exclusive to OpenGL.
2. It was never added to standard setup, so not very visible.
3. It's not clear whether it was working properly all this time. There were multiple changes to graphic renderers, and some of them may assume that rendering at "native resolution" is done at exactly, well, native resolution.
2024-10-30 18:07:42 +02:00

447 lines
18 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
//
// Game configuration
//
#include "ags/engine/ac/game_setup.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/engine/ac/global_translation.h"
#include "ags/engine/ac/path_helper.h"
#include "ags/shared/ac/sprite_cache.h"
#include "ags/engine/ac/system.h"
#include "ags/shared/core/platform.h"
#include "ags/engine/debugging/debugger.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/engine/device/mouse_w32.h"
#include "ags/engine/main/config.h"
#include "ags/engine/media/audio/audio_system.h"
#include "ags/engine/platform/base/ags_platform_driver.h"
#include "ags/shared/util/directory.h"
#include "ags/shared/util/ini_util.h"
#include "ags/shared/util/text_stream_reader.h"
#include "ags/shared/util/path.h"
#include "ags/shared/util/string_utils.h"
#include "ags/metaengine.h"
#include "common/config-manager.h"
#include "common/language.h"
namespace AGS3 {
using namespace AGS::Shared;
using namespace AGS::Engine;
// Filename of the default config file, the one found in the game installation
const char *DefaultConfigFileName = "acsetup.cfg";
WindowSetup parse_window_mode(const String &option, bool as_windowed, WindowSetup def_value) {
// "full_window" option means pseudo fullscreen ("borderless fullscreen window")
if (!as_windowed && (option.CompareNoCase("full_window") == 0))
return WindowSetup(kWnd_FullDesktop);
// Check supported options for explicit resolution or scale factor,
// in which case we'll use either a resizing window or a REAL fullscreen mode
const WindowMode exp_wmode = as_windowed ? kWnd_Windowed : kWnd_Fullscreen;
// Note that for "desktop" we return "default" for windowed, this will result
// in referring to the desktop size but resizing in accordance to the scaling style
if (option.CompareNoCase("desktop") == 0)
return as_windowed ? WindowSetup(exp_wmode) : WindowSetup(get_desktop_size(), exp_wmode);
// "Native" means using game resolution as a window size
if (option.CompareNoCase("native") == 0)
return WindowSetup(_GP(game).GetGameRes(), exp_wmode);
// Try parse an explicit resolution type or game scale factor --
size_t at = option.FindChar('x');
if (at == 0) { // try parse as a scale (xN)
int scale = StrUtil::StringToInt(option.Mid(1));
if (scale > 0) return WindowSetup(scale, exp_wmode);
} else if (at != String::NoIndex) {
// else try parse as a "width x height"
Size sz = Size(StrUtil::StringToInt(option.Mid(0, at)),
StrUtil::StringToInt(option.Mid(at + 1)));
if (!sz.IsNull()) return WindowSetup(sz, exp_wmode);
}
// In case of "default" option, or any format mistake, return the default
return def_value;
}
// Legacy screen size definition
enum ScreenSizeDefinition {
kScreenDef_Undefined = -1,
kScreenDef_Explicit, // define by width & height
kScreenDef_ByGameScaling, // define by game scale factor
kScreenDef_MaxDisplay, // set to maximal supported (desktop/device screen size)
kNumScreenDef
};
static ScreenSizeDefinition parse_legacy_screendef(const String &option) {
const char *screen_sz_def_options[kNumScreenDef] = { "explicit", "scaling", "max" };
for (int i = 0; i < kNumScreenDef; ++i) {
if (option.CompareNoCase(screen_sz_def_options[i]) == 0) {
return (ScreenSizeDefinition)i;
}
}
return kScreenDef_Undefined;
}
FrameScaleDef parse_scaling_option(const String &option, FrameScaleDef def_value) {
if (option.CompareNoCase("round") == 0 || option.CompareNoCase("max_round") == 0)
return kFrame_Round;
if (option.CompareNoCase("stretch") == 0)
return kFrame_Stretch;
if (option.CompareNoCase("proportional") == 0)
return kFrame_Proportional;
return def_value;
}
static FrameScaleDef parse_legacy_scaling_option(const String &option, int &scale) {
FrameScaleDef frame = parse_scaling_option(option, kFrame_Undefined);
if (frame == kFrame_Undefined) {
scale = StrUtil::StringToInt(option);
return scale > 0 ? kFrame_Round : kFrame_Undefined;
}
return frame;
}
// Parses legacy filter ID and converts it into current scaling options
bool parse_legacy_frame_config(const String &scaling_option, String &filter_id,
FrameScaleDef &frame, int &scale_factor) {
struct {
String LegacyName;
String CurrentName;
int Scaling;
} legacy_filters[6] = { {"none", "none", -1}, {"max", "StdScale", 0}, {"StdScale", "StdScale", -1},
{"AAx", "Linear", -1}, {"Hq2x", "Hqx", 2}, {"Hq3x", "Hqx", 3} };
for (int i = 0; i < 6; i++) {
if (scaling_option.CompareLeftNoCase(legacy_filters[i].LegacyName) == 0) {
filter_id = legacy_filters[i].CurrentName;
frame = kFrame_Round;
scale_factor = legacy_filters[i].Scaling >= 0 ? legacy_filters[i].Scaling :
scaling_option.Mid(legacy_filters[i].LegacyName.GetLength()).ToInt();
return true;
}
}
return false;
}
String make_scaling_option(FrameScaleDef scale_def) {
switch (scale_def) {
case kFrame_Stretch:
return "stretch";
case kFrame_Proportional:
return "proportional";
default:
return "round";
}
}
uint32_t convert_scaling_to_fp(int scale_factor) {
if (scale_factor >= 0)
return scale_factor <<= kShift;
else
return kUnit / abs(scale_factor);
}
int convert_fp_to_scaling(uint32_t scaling) {
if (scaling == 0)
return 0;
return scaling >= kUnit ? (scaling >> kShift) : -kUnit / (int32_t)scaling;
}
String find_default_cfg_file() {
return Path::ConcatPaths(_GP(usetup).startup_dir, DefaultConfigFileName);
}
String find_user_global_cfg_file() {
return Path::ConcatPaths(GetGlobalUserConfigDir().FullDir, DefaultConfigFileName);
}
String find_user_cfg_file() {
return Path::ConcatPaths(GetGameUserConfigDir().FullDir, DefaultConfigFileName);
}
void config_defaults() {
#if AGS_PLATFORM_OS_WINDOWS
_GP(usetup).Screen.DriverID = "D3D9";
#else
_GP(usetup).Screen.DriverID = "OGL";
#endif
// Defaults for the window style are max resizing window and "fullscreen desktop"
_GP(usetup).Screen.FsSetup = WindowSetup(kWnd_FullDesktop);
_GP(usetup).Screen.WinSetup = WindowSetup(kWnd_Windowed);
}
static void read_legacy_graphics_config(const ConfigTree &cfg) {
// Pre-3.* game resolution setup
int default_res = CfgReadInt(cfg, "misc", "defaultres", kGameResolution_Default);
int screen_res = CfgReadInt(cfg, "misc", "screenres", 0);
if (screen_res > 0 &&
(default_res >= kGameResolution_Default && default_res <= kGameResolution_320x240)) {
_GP(usetup).override_upscale = true; // run low-res game in high-res mode
}
_GP(usetup).Screen.Windowed = CfgReadBoolInt(cfg, "misc", "windowed");
_GP(usetup).Screen.DriverID = CfgReadString(cfg, "misc", "gfxdriver", _GP(usetup).Screen.DriverID);
// Window setup: style and size definition, game frame style
{
String legacy_filter = CfgReadString(cfg, "misc", "gfxfilter");
if (!legacy_filter.IsEmpty()) {
// Legacy scaling config is applied only to windowed setting
int scale_factor = 0;
parse_legacy_frame_config(legacy_filter, _GP(usetup).Screen.Filter.ID, _GP(usetup).Screen.WinGameFrame,
scale_factor);
if (scale_factor > 0)
_GP(usetup).Screen.WinSetup = WindowSetup(scale_factor);
// AGS 3.2.1 and 3.3.0 aspect ratio preferences for fullscreen
if (!_GP(usetup).Screen.Windowed) {
bool allow_borders =
(CfgReadBoolInt(cfg, "misc", "sideborders") || CfgReadBoolInt(cfg, "misc", "forceletterbox") ||
CfgReadBoolInt(cfg, "misc", "prefer_sideborders") || CfgReadBoolInt(cfg, "misc", "prefer_letterbox"));
_GP(usetup).Screen.FsGameFrame = allow_borders ? kFrame_Proportional : kFrame_Stretch;
}
}
// AGS 3.4.0 - 3.4.1-rc uniform scaling option
String uniform_frame_scale = CfgReadString(cfg, "graphics", "game_scale");
if (!uniform_frame_scale.IsEmpty()) {
int src_scale = 1;
FrameScaleDef frame = parse_legacy_scaling_option(uniform_frame_scale, src_scale);
_GP(usetup).Screen.FsGameFrame = frame;
_GP(usetup).Screen.WinGameFrame = frame;
}
// AGS 3.5.* gfx mode with screen definition
const bool is_windowed = CfgReadBoolInt(cfg, "graphics", "windowed");
WindowSetup &ws = is_windowed ? _GP(usetup).Screen.WinSetup : _GP(usetup).Screen.FsSetup;
const WindowMode wm = is_windowed ? kWnd_Windowed : kWnd_Fullscreen;
ScreenSizeDefinition scr_def = parse_legacy_screendef(CfgReadString(cfg, "graphics", "screen_def"));
switch (scr_def) {
case kScreenDef_Explicit:
{
Size sz(
CfgReadInt(cfg, "graphics", "screen_width"),
CfgReadInt(cfg, "graphics", "screen_height"));
ws = WindowSetup(sz, wm);
}
break;
case kScreenDef_ByGameScaling:
{
int src_scale = 0;
is_windowed ?
parse_legacy_scaling_option(CfgReadString(cfg, "graphics", "game_scale_win"), src_scale) :
parse_legacy_scaling_option(CfgReadString(cfg, "graphics", "game_scale_fs"), src_scale);
ws = WindowSetup(src_scale, wm);
}
break;
case kScreenDef_MaxDisplay:
ws = is_windowed ? WindowSetup() : WindowSetup(kWnd_FullDesktop);
break;
default:
break;
}
}
_GP(usetup).Screen.Params.RefreshRate = CfgReadInt(cfg, "misc", "refresh");
_GP(usetup).enable_antialiasing = CfgReadBoolInt(cfg, "misc", "antialias");
}
static void read_legacy_config(const ConfigTree &cfg) {
read_legacy_graphics_config(cfg);
_GP(usetup).SpriteCacheSize = CfgReadInt(cfg, "misc", "cachemax", _GP(usetup).SpriteCacheSize);
}
void override_config_ext(ConfigTree &cfg) {
_G(platform)->ReadConfiguration(cfg);
}
void apply_config(const ConfigTree &cfg) {
// Legacy settings have to be translated into new options;
// they must be read first, to let newer options override them, if ones are present
read_legacy_config(cfg);
{
// Audio options
_GP(usetup).audio_enabled = CfgReadBoolInt(cfg, "sound", "enabled", _GP(usetup).audio_enabled);
_GP(usetup).audio_driver = CfgReadString(cfg, "sound", "driver");
// This option is backwards (usevox is 0 if no_speech_pack)
_GP(usetup).no_speech_pack = !CfgReadBoolInt(cfg, "sound", "usespeech", true);
// Graphics mode and options
_GP(usetup).Screen.DriverID = CfgReadString(cfg, "graphics", "driver", _GP(usetup).Screen.DriverID);
_GP(usetup).Screen.Windowed = CfgReadBoolInt(cfg, "graphics", "windowed", _GP(usetup).Screen.Windowed);
_GP(usetup).Screen.FsSetup =
parse_window_mode(CfgReadString(cfg, "graphics", "fullscreen", "default"), false, _GP(usetup).Screen.FsSetup);
_GP(usetup).Screen.WinSetup =
parse_window_mode(CfgReadString(cfg, "graphics", "window", "default"), true, _GP(usetup).Screen.WinSetup);
_GP(usetup).Screen.Filter.ID = CfgReadString(cfg, "graphics", "filter", "StdScale");
_GP(usetup).Screen.FsGameFrame =
parse_scaling_option(CfgReadString(cfg, "graphics", "game_scale_fs", "proportional"), _GP(usetup).Screen.FsGameFrame);
_GP(usetup).Screen.WinGameFrame =
parse_scaling_option(CfgReadString(cfg, "graphics", "game_scale_win", "round"), _GP(usetup).Screen.WinGameFrame);
_GP(usetup).Screen.Params.RefreshRate = CfgReadInt(cfg, "graphics", "refresh");
// Use ScummVM options to set the vsync flag, if available
if (ConfMan.hasKey("vsync"))
_GP(usetup).Screen.Params.VSync = ConfMan.getBool("vsync");
else
_GP(usetup).Screen.Params.VSync = CfgReadBoolInt(cfg, "graphics", "vsync");
_GP(usetup).RenderAtScreenRes = CfgReadBoolInt(cfg, "graphics", "render_at_screenres");
_GP(usetup).enable_antialiasing = CfgReadBoolInt(cfg, "graphics", "antialias");
_GP(usetup).software_render_driver = CfgReadString(cfg, "graphics", "software_driver");
#ifdef TODO
_GP(usetup).rotation = (ScreenRotation)CfgReadInt(cfg, "graphics", "rotation", _GP(usetup).rotation);
String rotation_str = CfgReadString(cfg, "graphics", "rotation", "unlocked");
_GP(usetup).rotation = StrUtil::ParseEnum<ScreenRotation>(
rotation_str, CstrArr<kNumScreenRotationOptions>{ "unlocked", "portrait", "landscape" },
_GP(usetup).rotation);
#endif
// Custom paths
_GP(usetup).load_latest_save = CfgReadBoolInt(cfg, "misc", "load_latest_save", _GP(usetup).load_latest_save);
_GP(usetup).user_data_dir = CfgReadString(cfg, "misc", "user_data_dir");
_GP(usetup).shared_data_dir = CfgReadString(cfg, "misc", "shared_data_dir");
_GP(usetup).show_fps = CfgReadBoolInt(cfg, "misc", "show_fps");
// Translation / localization
Common::String translation;
if (!ConfMan.get("language").empty() && ConfMan.isKeyTemporary("language")) {
// Map the language defined in the command-line "language" option to its description
Common::Language lang = Common::parseLanguage(ConfMan.get("language"));
if (lang != Common::Language::UNK_LANG) {
Common::String translationCode = Common::getLanguageCode(lang);
translationCode.toLowercase();
translation = Common::getLanguageDescription(lang);
translation.toLowercase();
// Check if the game actually has such a translation, and set it if it does
// The name of translation files can be anything, but in general they are one of:
// - English name of the language, for example French.tra or Spanish.tra (covered)
// - Translated name of the language, for example polsky.tra or francais.tra (not covered)
// - The language code, for example FR.tra or DE.tra (covered)
// - And these can be combined with a prefix or suffix, for example Nelly_Polish.tra, english2.tra (covered)
Common::StringArray traFileNames = AGSMetaEngine::getGameTranslations(ConfMan.getActiveDomainName());
for (Common::StringArray::iterator iter = traFileNames.begin(); iter != traFileNames.end(); ++iter) {
Common::String traFileName = *iter;
traFileName.toLowercase();
if (traFileName.contains(translation) || traFileName.equals(translationCode)) {
_GP(usetup).translation = *iter;
break;
}
}
}
} else if (ConfMan.getActiveDomain()->tryGetVal("translation", translation) && !translation.empty())
_GP(usetup).translation = translation;
else
_GP(usetup).translation = CfgReadString(cfg, "language", "translation");
// Resource caches and options
_GP(usetup).clear_cache_on_room_change = CfgReadBoolInt(cfg, "misc", "clear_cache_on_room_change", _GP(usetup).clear_cache_on_room_change);
_GP(usetup).SpriteCacheSize = CfgReadInt(cfg, "graphics", "sprite_cache_size", _GP(usetup).SpriteCacheSize);
_GP(usetup).TextureCacheSize = CfgReadInt(cfg, "graphics", "texture_cache_size", _GP(usetup).TextureCacheSize);
// Mouse options
_GP(usetup).mouse_auto_lock = CfgReadBoolInt(cfg, "mouse", "auto_lock");
_GP(usetup).mouse_speed = CfgReadFloat(cfg, "mouse", "speed", 1.f);
if (_GP(usetup).mouse_speed <= 0.f)
_GP(usetup).mouse_speed = 1.f;
const char *mouse_ctrl_options[kNumMouseCtrlOptions] = { "never", "fullscreen", "always" };
String mouse_str = CfgReadString(cfg, "mouse", "control_when", "fullscreen");
for (int i = 0; i < kNumMouseCtrlOptions; ++i) {
if (mouse_str.CompareNoCase(mouse_ctrl_options[i]) == 0) {
_GP(usetup).mouse_ctrl_when = (MouseControlWhen)i;
break;
}
}
_GP(usetup).mouse_ctrl_enabled = CfgReadBoolInt(cfg, "mouse", "control_enabled", _GP(usetup).mouse_ctrl_enabled);
const char *mouse_speed_options[kNumMouseSpeedDefs] = { "absolute", "current_display" };
mouse_str = CfgReadString(cfg, "mouse", "speed_def", "current_display");
for (int i = 0; i < kNumMouseSpeedDefs; ++i) {
if (mouse_str.CompareNoCase(mouse_speed_options[i]) == 0) {
_GP(usetup).mouse_speed_def = (MouseSpeedDef)i;
break;
}
}
// Various system options
_GP(usetup).multitasking = CfgReadInt(cfg, "misc", "background", 0) != 0;
// User's overrides and hacks
_GP(usetup).override_multitasking = CfgReadInt(cfg, "override", "multitasking", -1);
_GP(usetup).override_script_os = -1;
// Looks for the existence of the Linux executable
if (File::IsFile(Path::ConcatPaths(_GP(usetup).startup_dir, "ags64"))) {
_GP(usetup).override_script_os = eOS_Linux;
}
String override_os = CfgReadString(cfg, "override", "os");
if (override_os.CompareNoCase("dos") == 0) {
_GP(usetup).override_script_os = eOS_DOS;
} else if (override_os.CompareNoCase("win") == 0) {
_GP(usetup).override_script_os = eOS_Win;
} else if (override_os.CompareNoCase("linux") == 0) {
_GP(usetup).override_script_os = eOS_Linux;
} else if (override_os.CompareNoCase("mac") == 0) {
_GP(usetup).override_script_os = eOS_Mac;
}
_GP(usetup).override_upscale = CfgReadBoolInt(cfg, "override", "upscale", _GP(usetup).override_upscale);
_GP(usetup).legacysave_assume_dataver = static_cast<GameDataVersion>(CfgReadInt(cfg, "override", "legacysave_assume_dataver", kGameVersion_Undefined));
_GP(usetup).legacysave_let_gui_diff = CfgReadBoolInt(cfg, "override", "legacysave_let_gui_diff");
_GP(usetup).key_save_game = CfgReadInt(cfg, "override", "save_game_key", 0);
_GP(usetup).key_restore_game = CfgReadInt(cfg, "override", "restore_game_key", 0);
}
// Apply logging configuration
apply_debug_config(cfg);
}
void post_config() {
if (_GP(usetup).Screen.DriverID.IsEmpty() || _GP(usetup).Screen.DriverID.CompareNoCase("DX5") == 0)
_GP(usetup).Screen.DriverID = "Software";
// FIXME: this correction is needed at the moment because graphics driver
// implementation requires some filter to be created anyway
_GP(usetup).Screen.Filter.UserRequest = _GP(usetup).Screen.Filter.ID;
if (_GP(usetup).Screen.Filter.ID.IsEmpty() || _GP(usetup).Screen.Filter.ID.CompareNoCase("none") == 0) {
_GP(usetup).Screen.Filter.ID = "StdScale";
}
}
void save_config_file() {
// Translation / localization
if (!_GP(usetup).translation.IsEmpty()) {
ConfMan.getActiveDomain()->setVal("translation", _GP(usetup).translation.GetCStr());
ConfMan.flushToDisk();
} else if (ConfMan.getActiveDomain()->contains("translation")) {
ConfMan.getActiveDomain()->erase("translation");
ConfMan.flushToDisk();
}
// ScummVM doesn't write out other configuration changes
}
} // namespace AGS3