forked from Mirrors/openclonk
829 lines
32 KiB
C++
829 lines
32 KiB
C++
/*
|
|
* 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 "C4ForbidLibraryCompilation.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 <utility>
|
|
#include "platform/StdFile.h"
|
|
#include "platform/C4Window.h"
|
|
#include "platform/StdRegistry.h"
|
|
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
#ifdef HAVE_LOCALE_H
|
|
#include <locale.h>
|
|
#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<int32_t>(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));
|
|
pComp->Value(mkNamingAdapt(ShowHelp, "ShowHelp", true , false, false));
|
|
for (int32_t i = 0; i < CFG_MaxEditorMRU; ++i)
|
|
pComp->Value(mkNamingAdapt(s(RecentlyEditedSzenarios[i]), FormatString("EditorMRU%02d", (int)i).getData(), "", false, false));
|
|
}
|
|
|
|
void C4ConfigDeveloper::AddRecentlyEditedScenario(const char *fn)
|
|
{
|
|
if (!fn || !*fn) return;
|
|
// Put given scenario first in list by moving all other scenarios down
|
|
// Check how many scenarios to move down the list. Stop moving down when the given scenario is in the list
|
|
int32_t move_down_num;
|
|
for (move_down_num = 0; move_down_num < CFG_MaxEditorMRU - 1; ++move_down_num)
|
|
if (!strncmp(fn, RecentlyEditedSzenarios[move_down_num], CFG_MaxString))
|
|
break;
|
|
// Move them down
|
|
for (int32_t i = move_down_num; i > 0; --i)
|
|
strcpy(RecentlyEditedSzenarios[i], RecentlyEditedSzenarios[i - 1]);
|
|
// Put current scenario in
|
|
strncpy(RecentlyEditedSzenarios[0], fn, CFG_MaxString);
|
|
}
|
|
|
|
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 ));
|
|
pComp->Value(mkNamingAdapt(MouseCursorSize, "MouseCursorSize", 50 ));
|
|
}
|
|
|
|
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(s(PuncherAddress), "PuncherAddress", "netpuncher.openclonk.org:11115"));
|
|
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; i<C4ConfigMaxGamepads; ++i)
|
|
pComp->Value(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<char *>(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;
|