/* * OpenClonk, http://www.openclonk.org * Copyright (c) 1998-2000, Matthes Bender * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ * Copyright (c) 2009-2016, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ /* Game configuration as stored in registry */ #include "C4Include.h" #include "config/C4Config.h" #include "C4Version.h" #include "lib/C4Log.h" #include "c4group/C4Components.h" #include "network/C4Network2.h" #include "c4group/C4Language.h" #include #include "platform/StdFile.h" #include "platform/C4Window.h" #include "platform/StdRegistry.h" #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_LOCALE_H #include #endif #include "game/C4Application.h" void C4ConfigGeneral::CompileFunc(StdCompiler *pComp) { // For those without the ability to intuitively guess what the falses and trues mean: // its mkNamingAdapt(field, name, default, fPrefillDefault, fStoreDefault) // where fStoreDefault writes out the value to the config even if it's the same as the default. #define s mkStringAdaptM pComp->Value(mkNamingAdapt(s(Name), "Name", "" )); pComp->Value(mkNamingAdapt(s(Language), "Language", "", false, true)); pComp->Value(mkNamingAdapt(s(LanguageEx), "LanguageEx", "", false, true)); pComp->Value(mkNamingAdapt(s(Participants), "Participants", "" )); // deliberately not grandfathering UserPath setting, since it was written to config by default pComp->Value(mkNamingAdapt(s(ConfigUserPath), "UserDataPath", "", false, true)); // assimilate old data pComp->Value(mkNamingAdapt(s(Adopt.PlayerPath), "PlayerPath", "")); // temporary path only set during updates pComp->Value(mkNamingAdapt(s(TempUpdatePath), "TempUpdatePath", "")); pComp->Value(mkNamingAdapt(s(MissionAccess), "MissionAccess", "", false, true)); pComp->Value(mkNamingAdapt(FPS, "FPS", 0 )); pComp->Value(mkNamingAdapt(DefRec, "DefRec", 0 )); pComp->Value(mkNamingAdapt(ScreenshotFolder, "ScreenshotFolder", "Screenshots", false, true)); pComp->Value(mkNamingAdapt(ScrollSmooth, "ScrollSmooth", 4 )); pComp->Value(mkNamingAdapt(AlwaysDebug, "DebugMode", 0 )); pComp->Value(mkNamingAdapt(OpenScenarioInGameMode, "OpenScenarioInGameMode", 0 )); #ifdef _WIN32 pComp->Value(mkNamingAdapt(MMTimer, "MMTimer", 1 )); #endif pComp->Value(mkNamingAdapt(s(RXFontName), "FontName", C4DEFAULT_FONT_NAME, false, true)); pComp->Value(mkNamingAdapt(RXFontSize, "FontSize", 14, false, true)); pComp->Value(mkNamingAdapt(GamepadEnabled, "GamepadEnabled", true )); pComp->Value(mkNamingAdapt(FirstStart, "FirstStart", true )); pComp->Value(mkNamingAdapt(ConfigResetSafety, "ConfigResetSafety", static_cast(ConfigResetSafetyVal) )); } void C4ConfigDeveloper::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(AutoFileReload, "AutoFileReload", 1 , false, true)); pComp->Value(mkNamingAdapt(ExtraWarnings, "ExtraWarnings", 0 , false, true)); pComp->Value(mkNamingAdapt(s(TodoFilename), "TodoFilename", "{SCENARIO}/TODO.txt", false, true)); pComp->Value(mkNamingAdapt(s(AltTodoFilename), "AltTodoFilename2", "{USERPATH}/TODO.txt", false, true)); pComp->Value(mkNamingAdapt(MaxScriptMRU, "MaxScriptMRU", 30 , false, false)); pComp->Value(mkNamingAdapt(DebugShapeTextures, "DebugShapeTextures", 0 , false, true)); } void C4ConfigGraphics::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(ResX, "ResolutionX", -1 ,false, true)); pComp->Value(mkNamingAdapt(ResY, "ResolutionY", -1 ,false, true)); pComp->Value(mkNamingAdapt(WindowX, "WindowX", 800 ,false, true)); pComp->Value(mkNamingAdapt(WindowY, "WindowY", 600 ,false, true)); pComp->Value(mkNamingAdapt(RefreshRate, "RefreshRate", 0 )); pComp->Value(mkNamingAdapt(SplitscreenDividers, "SplitscreenDividers", 1 )); pComp->Value(mkNamingAdapt(ShowStartupMessages, "ShowStartupMessages", 1 ,false, true)); pComp->Value(mkNamingAdapt(VerboseObjectLoading, "VerboseObjectLoading", 0 )); pComp->Value(mkNamingAdapt(MenuTransparency, "MenuTransparency", 1 ,false, true)); pComp->Value(mkNamingAdapt(UpperBoard, "UpperBoard", 1 ,false, true)); pComp->Value(mkNamingAdapt(ShowClock, "ShowClock", 0 ,false, true)); pComp->Value(mkNamingAdapt(ShowCrewNames, "ShowCrewNames", 1 ,false, true)); pComp->Value(mkNamingAdapt(ShowCrewCNames, "ShowCrewCNames", 0 ,false, true)); pComp->Value(mkNamingAdapt(Windowed, "Windowed", 0 ,false, true)); pComp->Value(mkNamingAdapt(PXSGfx, "PXSGfx" , 1 )); pComp->Value(mkNamingAdapt(Gamma, "Gamma" , 100 )); pComp->Value(mkNamingAdapt(Currency, "Currency" , 0 )); pComp->Value(mkNamingAdapt(Monitor, "Monitor", 0 )); // 0 = D3DADAPTER_DEFAULT pComp->Value(mkNamingAdapt(FireParticles, "FireParticles", 1 )); pComp->Value(mkNamingAdapt(MaxRefreshDelay, "MaxRefreshDelay", 30 )); pComp->Value(mkNamingAdapt(NoOffscreenBlits, "NoOffscreenBlits", 1 )); pComp->Value(mkNamingAdapt(MultiSampling, "MultiSampling", 4 )); pComp->Value(mkNamingAdapt(AutoFrameSkip, "AutoFrameSkip", 1 )); } void C4ConfigSound::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(RXSound, "Sound", 1 ,false, true)); pComp->Value(mkNamingAdapt(RXMusic, "Music", 1 ,false, true)); pComp->Value(mkNamingAdapt(FEMusic, "MenuMusic", 1 ,false, true)); pComp->Value(mkNamingAdapt(FESamples, "MenuSound", 1 ,false, true)); pComp->Value(mkNamingAdapt(FMMode, "FMMode", 1 )); pComp->Value(mkNamingAdapt(Verbose, "Verbose", 0 )); pComp->Value(mkNamingAdapt(MusicVolume, "MusicVolume2", 40 ,false, true)); pComp->Value(mkNamingAdapt(SoundVolume, "SoundVolume", 100 ,false, true)); } void C4ConfigNetwork::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(ControlRate, "ControlRate", 3 ,false, true)); pComp->Value(mkNamingAdapt(ControlPreSend, "ControlPreSend", -1 )); pComp->Value(mkNamingAdapt(s(WorkPath), "WorkPath", "Network" ,false, true)); pComp->Value(mkNamingAdapt(Lobby, "Lobby", 0 )); pComp->Value(mkNamingAdapt(NoRuntimeJoin, "NoRuntimeJoin", 1 ,false, true)); pComp->Value(mkNamingAdapt(NoReferenceRequest, "NoReferenceRequest", 0 )); pComp->Value(mkNamingAdapt(MaxResSearchRecursion, "MaxResSearchRecursion",1 ,false, true)); pComp->Value(mkNamingAdapt(Comment, "Comment", "" ,false, true)); pComp->Value(mkNamingAdapt(PortTCP, "PortTCP", C4NetStdPortTCP ,false, true)); pComp->Value(mkNamingAdapt(PortUDP, "PortUDP", C4NetStdPortUDP ,false, true)); pComp->Value(mkNamingAdapt(EnableUPnP, "EnableUPnP", 1 , false, true)); pComp->Value(mkNamingAdapt(PortDiscovery, "PortDiscovery", C4NetStdPortDiscovery ,false, true)); pComp->Value(mkNamingAdapt(PortRefServer, "PortRefServer", C4NetStdPortRefServer ,false, true)); pComp->Value(mkNamingAdapt(ControlMode, "ControlMode", 0 )); pComp->Value(mkNamingAdapt(Nick, "Nick", "" ,false, true)); pComp->Value(mkNamingAdapt(MaxLoadFileSize, "MaxLoadFileSize", 5*1024*1024 ,false, true)); pComp->Value(mkNamingAdapt(MasterServerSignUp, "MasterServerSignUp", 1 )); pComp->Value(mkNamingAdapt(MasterServerActive, "MasterServerActive", 0 )); pComp->Value(mkNamingAdapt(MasterKeepPeriod, "MasterKeepPeriod", 60 )); pComp->Value(mkNamingAdapt(MasterReferencePeriod, "MasterReferencePeriod",120 )); pComp->Value(mkNamingAdapt(LeagueServerSignUp, "LeagueServerSignUp", 0 )); pComp->Value(mkNamingAdapt(UseAlternateServer, "UseAlternateServer", 0 )); pComp->Value(mkNamingAdapt(s(AlternateServerAddress),"AlternateServerAddress", "league.openclonk.org:80/league.php")); pComp->Value(mkNamingAdapt(s(LastPassword), "LastPassword", "Wipf" )); #ifdef WITH_AUTOMATIC_UPDATE pComp->Value(mkNamingAdapt(s(UpdateServerAddress), "UpdateServerAddress", "www.openclonk.org:80/update/")); pComp->Value(mkNamingAdapt(AutomaticUpdate, "AutomaticUpdate", 0 ,false, true)); pComp->Value(mkNamingAdapt(LastUpdateTime, "LastUpdateTime", 0 )); #endif pComp->Value(mkNamingAdapt(AsyncMaxWait, "AsyncMaxWait", 2 )); pComp->Value(mkNamingAdapt(PacketLogging, "PacketLogging", 0 )); pComp->Value(mkNamingAdapt(mkParAdapt(LastLeagueServer, StdCompiler::RCT_All), "LastLeagueServer", "" )); pComp->Value(mkNamingAdapt(mkParAdapt(LastLeaguePlayerName, StdCompiler::RCT_All), "LastLeaguePlayerName", "" )); pComp->Value(mkNamingAdapt(mkParAdapt(LastLeagueAccount, StdCompiler::RCT_All), "LastLeagueAccount", "" )); pComp->Value(mkNamingAdapt(mkParAdapt(LastLeagueLoginToken, StdCompiler::RCT_All), "LastLeagueLoginToken", "" )); } void C4ConfigLobby::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(AllowPlayerSave, "AllowPlayerSave", 0 ,false, false)); pComp->Value(mkNamingAdapt(CountdownTime, "CountdownTime", 5 ,false, false)); } void C4ConfigIRC::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(s(Server), "Server", "irc.euirc.net", false, true)); pComp->Value(mkNamingAdapt(s(Nick), "Nick", "" , false, true)); pComp->Value(mkNamingAdapt(s(RealName), "RealName", "" , false, true)); pComp->Value(mkNamingAdapt(s(Channel), "Channel", "#openclonk" , false, true)); pComp->Value(mkNamingAdapt(AllowAllChannels, "AllowAllChannels", 0 , false, true)); } void C4ConfigSecurity::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(WasRegistered, "WasRegistered", 0 )); #ifdef _WIN32 pComp->Value(mkNamingAdapt(s(KeyPath), "KeyPath", "%APPDATA%\\" C4ENGINENAME, false, true)); #elif defined(__linux__) pComp->Value(mkNamingAdapt(s(KeyPath), "KeyPath", "$HOME/.clonk/" C4ENGINENICK, false, true)); #elif defined(__APPLE__) pComp->Value(mkNamingAdapt(s(KeyPath), "KeyPath", "$HOME/Library/Application Support/" C4ENGINENAME, false, true)); #endif } void C4ConfigGamepad::CompileFunc(StdCompiler *pComp, bool fButtonsOnly) { /* The defaults here are for a Logitech Dual Action under Linux-SDL. Better than nothing, I guess. */ if (!fButtonsOnly) { for (int i=0; i<6; ++i) { pComp->Value(mkNamingAdapt(AxisMin[i], FormatString("Axis%dMin", i).getData(), 0u)); pComp->Value(mkNamingAdapt(AxisMax[i], FormatString("Axis%dMax", i).getData(), 0u)); pComp->Value(mkNamingAdapt(AxisCalibrated[i], FormatString("Axis%dCalibrated", i).getData(), false)); } } pComp->Value(mkNamingAdapt(Button[0], "Button1", -1 )); pComp->Value(mkNamingAdapt(Button[1], "Button2", -1 )); pComp->Value(mkNamingAdapt(Button[2], "Button3", -1 )); pComp->Value(mkNamingAdapt(Button[3], "Button4", -1 )); pComp->Value(mkNamingAdapt(Button[4], "Button5", -1 )); pComp->Value(mkNamingAdapt(Button[5], "Button6", -1 )); pComp->Value(mkNamingAdapt(Button[6], "Button7", -1 )); pComp->Value(mkNamingAdapt(Button[7], "Button8", -1 )); pComp->Value(mkNamingAdapt(Button[8], "Button9", -1 )); pComp->Value(mkNamingAdapt(Button[9], "Button10", -1 )); pComp->Value(mkNamingAdapt(Button[10], "Button11", -1 )); pComp->Value(mkNamingAdapt(Button[11], "Button12", -1 )); } void C4ConfigGamepad::ResetButtons() { // loads an empty config for the buttons StdCompilerNull Comp; Comp.Compile(mkParAdapt(*this, true)); } void C4ConfigGamepad::Reset() { // loads an empty config for the gamepad config StdCompilerNull Comp; Comp.Compile(mkParAdapt(*this, false)); } void C4ConfigControls::CompileFunc(StdCompiler *pComp) { #ifndef USE_CONSOLE pComp->Value(mkNamingAdapt(UserSets, "UserSets", C4PlayerControlAssignmentSets())); pComp->Value(mkNamingAdapt(MouseAutoScroll, "MouseAutoScroll", 0 /* change default 33 to enable */ )); pComp->Value(mkNamingAdapt(GamepadGuiControl, "GamepadGuiControl", 0, false, true)); #endif } C4Config::C4Config() { Default(); } C4Config::~C4Config() { fConfigLoaded = false; } void C4Config::Default() { // force default values StdCompilerNull Comp; Comp.Compile(*this); fConfigLoaded=false; } void C4Config::GetConfigFileName(StdStrBuf &filename, const char *szConfigFile) { if (szConfigFile) { // Config filename is specified filename.Ref(szConfigFile); } else { // Config filename from home StdStrBuf home(getenv("HOME")); if (home) { home += "/"; } filename.Copy(home); #ifdef __APPLE__ filename += "Library/Preferences/" C4ENGINEID ".config"; #else filename += ".clonk/" C4ENGINENICK "/config"; #endif } } bool C4Config::Load(const char *szConfigFile) { try { #ifdef _WIN32 // Windows: Default load from registry, if no explicit config file is specified if (!szConfigFile) { StdCompilerConfigRead CfgRead(HKEY_CURRENT_USER, "Software\\" C4CFG_Company "\\" C4ENGINENAME); CfgRead.Compile(*this); } else #endif { // Nonwindows or explicit config file: Determine filename to load config from StdStrBuf filename; GetConfigFileName(filename, szConfigFile); // Load config file into buf StdStrBuf buf; buf.LoadFromFile(filename.getData()); if (buf.isNull()) { // Config file not present? #ifdef __linux__ if (!szConfigFile) { StdStrBuf filename(getenv("HOME")); if (filename) { filename += "/"; } filename += ".clonk/" C4ENGINENICK; CreatePath(filename.getData()); } #endif // Buggy StdCompiler crashes when compiling a Null-StdStrBuf buf.Ref(" "); } // Read config from buffer StdCompilerINIRead IniRead; IniRead.setInput(buf); IniRead.Compile(*this); } } catch (StdCompiler::Exception *pExc) { // Configuration file syntax error? LogF("Error loading configuration: %s"/*LoadResStr("IDS_ERR_CONFREAD") - restbl not yet loaded*/, pExc->Msg.getData()); delete pExc; return false; } // Config postinit General.DeterminePaths(); #ifdef HAVE_WINSOCK // Setup WS manually, so c4group doesn't depend on C4NetIO WSADATA wsadata; bool fWinSock = !WSAStartup(WINSOCK_VERSION, &wsadata); #endif if (SEqual(Network.Nick.getData(), "Unknown")) { char LocalName[25+1]; *LocalName = 0; gethostname(LocalName, 25); if (*LocalName) Network.Nick.Copy(LocalName); } #ifdef HAVE_WINSOCK if (fWinSock) WSACleanup(); #endif General.DefaultLanguage(); // Warning against invalid ports if (Config.Network.PortTCP>0 && Config.Network.PortTCP == Config.Network.PortRefServer) { Log("Warning: Network TCP port and reference server port both set to same value - increasing reference server port!"); ++Config.Network.PortRefServer; if (Config.Network.PortRefServer>=65536) Config.Network.PortRefServer = C4NetStdPortRefServer; } if (Config.Network.PortUDP>0 && Config.Network.PortUDP == Config.Network.PortDiscovery) { Log("Warning: Network UDP port and LAN game discovery port both set to same value - increasing discovery port!"); ++Config.Network.PortDiscovery; if (Config.Network.PortDiscovery>=65536) Config.Network.PortDiscovery = C4NetStdPortDiscovery; } // Empty nick already defaults to GetRegistrationData("Nick") or // Network.LocalName at relevant places. fConfigLoaded = true; if (szConfigFile) ConfigFilename.Copy(szConfigFile); else ConfigFilename.Clear(); return true; } bool C4Config::Save() { try { #ifdef _WIN32 if (!ConfigFilename.getLength()) { // Windows: Default save to registry, if it wasn't loaded from file StdCompilerConfigWrite CfgWrite(HKEY_CURRENT_USER, "Software\\" C4CFG_Company "\\" C4ENGINENAME); CfgWrite.Decompile(*this); } else #endif { StdStrBuf filename; GetConfigFileName(filename, ConfigFilename.getLength() ? ConfigFilename.getData() : NULL); StdCompilerINIWrite IniWrite; IniWrite.Decompile(*this); IniWrite.getOutput().SaveToFile(filename.getData()); } } catch (StdCompiler::Exception *pExc) { LogF(LoadResStr("IDS_ERR_CONFSAVE"), pExc->Msg.getData()); delete pExc; return false; } return true; } void C4ConfigGeneral::DeterminePaths() { #ifdef _WIN32 // Exe path wchar_t apath[CFG_MaxString]; if (GetModuleFileNameW(NULL,apath,CFG_MaxString)) { ExePath = StdStrBuf(apath); TruncatePath(ExePath.getMData()); ExePath.SetLength(SLen(ExePath.getMData())); ExePath.AppendBackslash(); } // Temp path GetTempPathW(CFG_MaxString,apath); TempPath = StdStrBuf(apath); if (TempPath[0]) TempPath.AppendBackslash(); #elif defined(__linux__) ExePath.SetLength(1024); ssize_t l = readlink("/proc/self/exe", ExePath.getMData(), 1024); if (l < -1) { ExePath.Ref("."); } else { ExePath.SetLength(l); GetParentPath(ExePath.getData(), &ExePath); ExePath.AppendBackslash(); } const char * t = getenv("TMPDIR"); if (t) { TempPath = t; TempPath.AppendBackslash(); } else TempPath = "/tmp/"; #else // Mac: Just use the working directory as ExePath. ExePath = GetWorkingDirectory(); ExePath.AppendBackslash(); TempPath = "/tmp/"; #endif // Find system-wide data path #if defined(_WIN32) // Use ExePath: on windows, everything is installed to one directory SCopy(ExePath.getMData(),SystemDataPath); #elif defined(__APPLE__) SCopy(::Application.GetGameDataPath().getData(),SystemDataPath); #elif defined(WITH_AUTOMATIC_UPDATE) // WITH_AUTOMATIC_UPDATE builds are our tarball releases and // development snapshots, i.e. where the game data is at the // same location as the executable. SCopy(ExePath.getMData(),SystemDataPath); #elif defined(OC_SYSTEM_DATA_DIR) SCopy(OC_SYSTEM_DATA_DIR, SystemDataPath); #else #error Please define OC_SYSTEM_DATA_DIR! #endif AppendBackslash(SystemDataPath); // Find user-specific data path if (ConfigUserPath[0]) SCopy(ConfigUserPath, UserDataPath); else #if defined(_WIN32) SCopy("%APPDATA%\\" C4ENGINENAME, UserDataPath); #elif defined(__APPLE__) SCopy("$HOME/Library/Application Support/" C4ENGINENAME, UserDataPath); #else SCopy("$HOME/.clonk/" C4ENGINENICK, UserDataPath); #endif C4Config::ExpandEnvironmentVariables(UserDataPath, CFG_MaxString); AppendBackslash(UserDataPath); // Screenshot path SCopy(UserDataPath, ScreenshotPath, CFG_MaxString-1); if (ScreenshotFolder.getLength()+std::strlen(ScreenshotPath)+1<=CFG_MaxString) { SAppend(ScreenshotFolder.getData(), ScreenshotPath); AppendBackslash(ScreenshotPath); } // Create user path if it doesn't already exist CreatePath(UserDataPath); } static char AtPathFilename[_MAX_PATH+1]; const char* C4Config::AtExePath(const char *szFilename) { SCopy(General.ExePath.getData(),AtPathFilename,_MAX_PATH); SAppend(szFilename,AtPathFilename,_MAX_PATH); return AtPathFilename; } const char* C4Config::AtUserDataPath(const char *szFilename) { SCopy(General.UserDataPath, AtPathFilename, _MAX_PATH); SAppend(szFilename, AtPathFilename, _MAX_PATH); return AtPathFilename; } const char* C4Config::AtSystemDataPath(const char *szFilename) { SCopy(General.SystemDataPath, AtPathFilename, _MAX_PATH); SAppend(szFilename, AtPathFilename, _MAX_PATH); return AtPathFilename; } const char* C4Config::AtTempPath(const char *szFilename) { SCopy(General.TempPath.getData(),AtPathFilename,_MAX_PATH); SAppend(szFilename,AtPathFilename,_MAX_PATH); return AtPathFilename; } const char* C4Config::AtNetworkPath(const char *szFilename) { SCopy(General.UserDataPath,AtPathFilename,_MAX_PATH); SAppend(Network.WorkPath,AtPathFilename,_MAX_PATH); SAppend(szFilename,AtPathFilename,_MAX_PATH); return AtPathFilename; } const char *C4Config::AtScreenshotPath(const char *szFilename) { int len; SCopy(General.ScreenshotPath,AtPathFilename,_MAX_PATH); if ((len = SLen(AtPathFilename))) if (AtPathFilename[len-1] == DirectorySeparator) AtPathFilename[len-1] = '\0'; if (!CreatePath(AtPathFilename)) { SCopy(General.UserDataPath,AtPathFilename,_MAX_PATH); } AppendBackslash(AtPathFilename); SAppend(szFilename,AtPathFilename,_MAX_PATH); return AtPathFilename; } bool C4ConfigGeneral::CreateSaveFolder(const char *strDirectory, const char *strLanguageTitle) { // Create directory if needed if (!CreatePath(strDirectory)) return false; // Create title component if needed char lang[3]; SCopy(Config.General.Language, lang, 2); StdStrBuf strTitleFile; strTitleFile.Format("%s%c%s", strDirectory, DirectorySeparator, C4CFN_WriteTitle); StdStrBuf strTitleData; strTitleData.Format("%s:%s", lang, strLanguageTitle); CStdFile hFile; if (!FileExists(strTitleFile.getData())) if (!hFile.Create(strTitleFile.getData()) || !hFile.WriteString(strTitleData.getData()) || !hFile.Close()) return false; // Save folder seems okay return true; } const char* C4ConfigNetwork::GetLeagueServerAddress() { // Alternate (configurable) league server if (UseAlternateServer) return AlternateServerAddress; // Standard (hardcoded) official league server else return "league.openclonk.org:80/league.php"; } void C4ConfigNetwork::CheckPortsForCollisions() { // check for port collisions if (PortTCP != -1 && PortTCP == PortRefServer) { LogSilentF("Network: TCP Port collision, setting defaults"); PortTCP = C4NetStdPortTCP; PortRefServer = C4NetStdPortRefServer; } if (PortUDP != -1 && PortUDP == PortDiscovery) { LogSilentF("Network: UDP Port collision, setting defaults"); PortUDP = C4NetStdPortUDP; PortDiscovery = C4NetStdPortDiscovery; } } void C4ConfigNetwork::SetLeagueLoginData(const char *szServer, const char *szPlayerName, const char *szAccount, const char *szLoginToken) { // ideally, there would be a list to store multiple logins // however, we don't really support multiplayer at one computer at the moment anyway LastLeagueServer.Copy(szServer); LastLeaguePlayerName.Copy(szPlayerName); LastLeagueAccount.Copy(szAccount); LastLeagueLoginToken.Copy(szLoginToken); } bool C4ConfigNetwork::GetLeagueLoginData(const char *szServer, const char *szPlayerName, StdStrBuf *pAccount, StdStrBuf *pLoginToken) const { // check if last login matches and store if desired if (LastLeagueServer == szServer && LastLeaguePlayerName == szPlayerName) { pAccount->Copy(LastLeagueAccount); pLoginToken->Copy(LastLeagueLoginToken); return true; } return false; } void C4ConfigControls::ResetKeys() { UserSets.Clear(); } const char* C4Config::AtUserDataRelativePath(const char *szFilename) { // Specified file is located in UserDataPath: return relative path return GetRelativePathS(szFilename, General.UserDataPath); } const char* C4Config::AtSystemDataRelativePath(const char *szFilename) { // Specified file is located in SystemDataPath: return relative path return GetRelativePathS(szFilename, General.SystemDataPath); } const char* C4Config::AtRelativePath(const char *szFilename) { const char *szPath = GetRelativePathS(szFilename, General.UserDataPath); if (szPath == szFilename) return GetRelativePathS(szFilename, General.SystemDataPath); return szPath; } void C4Config::ForceRelativePath(StdStrBuf *sFilename) { assert(sFilename); // Specified file is located in SystemDataPath? const char *szRelative = GetRelativePathS(sFilename->getData(), General.SystemDataPath); if (szRelative != sFilename->getData()) { // return relative path StdStrBuf sTemp; sTemp.Copy(szRelative); sFilename->Take(std::move(sTemp)); } else { // not in ExePath: Is it a global path? if (IsGlobalPath(sFilename->getData())) { // then shorten it (e.g. C:\Temp\Missions.ocf\Goldmine.ocs to Missions.ocf\Goldmine.ocs) StdStrBuf sTemp; sTemp.Copy(GetC4Filename(sFilename->getData())); sFilename->Take(std::move(sTemp)); } } } void C4ConfigGeneral::DefaultLanguage() { // No language defined: default to German or English by system language if (!Language[0]) { if (IsGermanSystem()) SCopy("DE - Deutsch", Language); else SCopy("US - English", Language); } // No fallback sequence defined: use primary language list if (!LanguageEx[0]) GetLanguageSequence(Language, LanguageEx); } bool C4Config::Registered() { // Dummy function: to be overloaded in C4Config return true; } bool C4Config::Init() { return true; } const char* C4Config::GetSubkeyPath(const char *strSubkey) { static char key[1024 + 1]; #ifdef _WIN32 sprintf(key, "Software\\%s\\%s\\%s", C4CFG_Company, C4ENGINENAME, strSubkey); #else sprintf(key, "%s", strSubkey); #endif return key; } int C4ConfigGeneral::GetLanguageSequence(const char *strSource, char *strTarget) { // Copy a condensed list of language codes from the source list to the target string, // skipping any whitespace or long language descriptions. Language sequences are // comma separated. int iCount = 0; char strLang[2 + 1]; for (int i = 0; SCopySegment(strSource, i, strLang, ',', 2, true); i++) if (strLang[0]) { if (strTarget[0]) SAppendChar(',', strTarget); SAppend(strLang, strTarget); iCount++; } return iCount; } void C4ConfigStartup::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(HideMsgGfxEngineChange, "HideMsgGfxEngineChange", 0)); pComp->Value(mkNamingAdapt(HideMsgGfxBitDepthChange, "HideMsgGfxBitDepthChange", 0)); pComp->Value(mkNamingAdapt(HideMsgMMTimerChange, "HideMsgMMTimerChange", 0)); pComp->Value(mkNamingAdapt(HideMsgStartDedicated, "HideMsgStartDedicated", 0)); pComp->Value(mkNamingAdapt(HideMsgPlrTakeOver, "HideMsgPlrTakeOver", 0)); pComp->Value(mkNamingAdapt(HideMsgPlrNoTakeOver, "HideMsgPlrNoTakeOver", 0)); pComp->Value(mkNamingAdapt(HideMsgNoOfficialLeague, "HideMsgNoOfficialLeague", 0)); pComp->Value(mkNamingAdapt(HideMsgIRCDangerous, "HideMsgIRCDangerous", 0)); pComp->Value(mkNamingAdapt(AlphabeticalSorting, "AlphabeticalSorting", 0)); pComp->Value(mkNamingAdapt(LastPortraitFolderIdx, "LastPortraitFolderIdx", 0)); } void C4Config::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(General, "General" )); pComp->Value(mkNamingAdapt(Controls, "Controls" )); for (int i=0; iValue(mkNamingAdapt(Gamepads[i], FormatString("Gamepad%d", i).getData())); pComp->Value(mkNamingAdapt(Graphics, "Graphics" )); pComp->Value(mkNamingAdapt(Sound, "Sound" )); pComp->Value(mkNamingAdapt(Network, "Network" )); pComp->Value(mkNamingAdapt(Lobby, "Lobby" )); pComp->Value(mkNamingAdapt(IRC, "IRC" )); pComp->Value(mkNamingAdapt(Developer, "Developer" )); pComp->Value(mkNamingAdapt(Startup, "Startup" )); pComp->Value(mkNamingAdapt(Security, "Security" )); } bool C4Config::AddModule(const char *szPath, char *szModules) { return SAddModule(szModules,szPath); } bool C4Config::IsModule(const char *szPath, char *szModules) { return SIsModule(szModules,szPath); } bool C4Config::RemoveModule(const char *szPath, char *szModules) { return SRemoveModule(szModules,szPath); } void C4Config::ExpandEnvironmentVariables(char *strPath, size_t iMaxLen) { #ifdef _WIN32 wchar_t buf[_MAX_PATH + 1]; ExpandEnvironmentStringsW(GetWideChar(strPath), buf, _MAX_PATH); SCopy(StdStrBuf(buf).getData(), strPath, iMaxLen); #else // __linux__ or __APPLE___ StdStrBuf home(getenv("HOME")); char* rest; if (home && (rest = const_cast(SSearch(strPath, "$HOME"))) && (std::strlen(strPath) - 5 + home.getLength() <= iMaxLen)) { // String replace... there might be a more elegant way to do this. memmove(rest + home.getLength() - SLen("$HOME"), rest, SLen(rest) + 1); strncpy(rest - SLen("$HOME"), home.getData(), home.getLength()); } #endif } void C4Config::CleanupTempUpdateFolder() { // Get rid of update path present from before update if (*General.TempUpdatePath) { EraseItem(General.TempUpdatePath); *General.TempUpdatePath = '\0'; } } const char *C4Config::MakeTempUpdateFolder() { // just pick a temp name StdStrBuf sTempName; sTempName.Copy(AtTempPath("update")); MakeTempFilename(&sTempName); SCopy(sTempName.getData(), General.TempUpdatePath); CreatePath(General.TempUpdatePath); return General.TempUpdatePath; } const char *C4Config::AtTempUpdatePath(const char *szFilename) { SCopy(General.TempUpdatePath,AtPathFilename,_MAX_PATH-1); AppendBackslash(AtPathFilename); SAppend(szFilename,AtPathFilename,_MAX_PATH); return AtPathFilename; } C4Config Config;