| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) 1998-2000, Matthes Bender (RedWolf Design) |
| 5 | * Copyright (c) 2017-2022, The LegacyClonk Team and contributors |
| 6 | * |
| 7 | * Distributed under the terms of the ISC license; see accompanying file |
| 8 | * "COPYING" for details. |
| 9 | * |
| 10 | * "Clonk" is a registered trademark of Matthes Bender, used with permission. |
| 11 | * See accompanying file "TRADEMARK" for details. |
| 12 | * |
| 13 | * To redistribute this file separately, substitute the full license texts |
| 14 | * for the above references. |
| 15 | */ |
| 16 | |
| 17 | /* Game configuration as stored in registry */ |
| 18 | |
| 19 | #include <C4Config.h> |
| 20 | |
| 21 | #include "C4Version.h" |
| 22 | #ifdef C4ENGINE |
| 23 | #include <C4Application.h> |
| 24 | #include "C4GameControl.h" |
| 25 | #include <C4Log.h> |
| 26 | #include <C4Network2.h> |
| 27 | #include "C4Network2IO.h" |
| 28 | #include "C4Network2Reference.h" |
| 29 | #include "C4Network2UPnP.h" |
| 30 | #include "C4Record.h" |
| 31 | #include "C4ResStrTable.h" |
| 32 | #include <C4UpperBoard.h> |
| 33 | #include "StdPNG.h" |
| 34 | #endif |
| 35 | |
| 36 | #include <StdFile.h> |
| 37 | |
| 38 | #ifdef _WIN32 |
| 39 | #include "StdRegistry.h" |
| 40 | #elif defined(__linux__) |
| 41 | #include <clocale> |
| 42 | #endif |
| 43 | |
| 44 | #include <format> |
| 45 | |
| 46 | bool isGermanSystem() |
| 47 | { |
| 48 | #ifdef _WIN32 |
| 49 | if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_GERMAN) return true; |
| 50 | #elif defined(__APPLE__) and defined(C4ENGINE) |
| 51 | extern bool isGerman(); |
| 52 | if (isGerman()) return true; |
| 53 | #elif defined(__linux__) |
| 54 | if (strstr(haystack: std::setlocale(LC_MESSAGES, locale: nullptr), needle: "de" )) return true; |
| 55 | #endif |
| 56 | return false; |
| 57 | } |
| 58 | |
| 59 | void C4ConfigGeneral::CompileFunc(StdCompiler *pComp) |
| 60 | { |
| 61 | // For those without the ability to intuitively guess what the falses and trues mean: |
| 62 | // its mkNamingAdapt(field, name, default, fPrefillDefault, fStoreDefault) |
| 63 | // where fStoreDefault writes out the value to the config even if it's the same as the default. |
| 64 | #define s mkStringAdaptM |
| 65 | // Version got introduced in 348, so any config without it is assumed to be created by 347 |
| 66 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Version, szName: "Version" , rDefault: 347)); |
| 67 | pComp->Value(rStruct: mkNamingAdapt(s(Name), szName: "Name" , rDefault: "" )); |
| 68 | pComp->Value(rStruct: mkNamingAdapt(s(Language), szName: "Language" , rDefault: "" , fPrefillDefault: false, fStoreDefault: true)); |
| 69 | pComp->Value(rStruct: mkNamingAdapt(s(LanguageEx), szName: "LanguageEx" , rDefault: "" , fPrefillDefault: false, fStoreDefault: true)); |
| 70 | pComp->Value(rStruct: mkNamingAdapt(s(LanguageCharset), szName: "LanguageCharset" , rDefault: "" , fPrefillDefault: false, fStoreDefault: true)); |
| 71 | fUTF8 = SEqual(szStr1: LanguageCharset, szStr2: "UTF-8" ); |
| 72 | pComp->Value(rStruct: mkNamingAdapt(s(Definitions), szName: "Definitions" , rDefault: "" )); |
| 73 | pComp->Value(rStruct: mkNamingAdapt(s(Participants), szName: "Participants" , rDefault: "" )); |
| 74 | pComp->Value(rStruct: mkNamingAdapt(s(LogPath), szName: "LogPath" , rDefault: "" , fPrefillDefault: false, fStoreDefault: true)); |
| 75 | pComp->Value(rStruct: mkNamingAdapt(s(PlayerPath), szName: "PlayerPath" , rDefault: "" , fPrefillDefault: false, fStoreDefault: true)); |
| 76 | pComp->Value(rStruct: mkNamingAdapt(s(DefinitionPath), szName: "DefinitionPath" , rDefault: "" , fPrefillDefault: false, fStoreDefault: true)); |
| 77 | #ifdef _WIN32 |
| 78 | pComp->Value(mkNamingAdapt(s(UserPath), "UserPath" , "%APPDATA%\\LegacyClonk" , false, true)); |
| 79 | #elif defined(__linux__) |
| 80 | pComp->Value(rStruct: mkNamingAdapt(s(UserPath), szName: "UserPath" , rDefault: "$HOME/.legacyclonk" , fPrefillDefault: false, fStoreDefault: true)); |
| 81 | #elif defined(__APPLE__) |
| 82 | pComp->Value(mkNamingAdapt(s(UserPath), "UserPath" , "$HOME/Library/Application Support/LegacyClonk" , false, true)); |
| 83 | #endif |
| 84 | pComp->Value(rStruct: mkNamingAdapt(rValue&: SaveGameFolder, szName: "SaveGameFolder" , rDefault: "Savegames.c4f" , fPrefillDefault: false, fStoreDefault: true)); |
| 85 | pComp->Value(rStruct: mkNamingAdapt(rValue&: SaveDemoFolder, szName: "SaveDemoFolder" , rDefault: "Records.c4f" , fPrefillDefault: false, fStoreDefault: true)); |
| 86 | #ifdef C4ENGINE |
| 87 | pComp->Value(rStruct: mkNamingAdapt(s(MissionAccess), szName: "MissionAccess" , rDefault: "" , fPrefillDefault: false, fStoreDefault: true)); |
| 88 | #endif |
| 89 | pComp->Value(rStruct: mkNamingAdapt(rValue&: FPS, szName: "FPS" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 90 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Record, szName: "Record" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 91 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ScreenshotFolder, szName: "ScreenshotFolder" , rDefault: "Screenshots" , fPrefillDefault: false, fStoreDefault: true)); |
| 92 | pComp->Value(rStruct: mkNamingAdapt(rValue&: FairCrew, szName: "NoCrew" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 93 | pComp->Value(rStruct: mkNamingAdapt(rValue&: FairCrewStrength, szName: "DefCrewStrength" , rDefault: 1000, fPrefillDefault: false, fStoreDefault: true)); |
| 94 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ScrollSmooth, szName: "ScrollSmooth" , rDefault: 4)); |
| 95 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AlwaysDebug, szName: "DebugMode" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 96 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AllowScriptingInReplays, szName: "AllowScriptingInReplays" , rDefault: false)); |
| 97 | |
| 98 | pComp->Value(rStruct: mkNamingAdapt(s(RXFontName), szName: "FontName" , rDefault: "Endeavour" , fPrefillDefault: false, fStoreDefault: true)); |
| 99 | pComp->Value(rStruct: mkNamingAdapt(rValue&: RXFontSize, szName: "FontSize" , rDefault: 14, fPrefillDefault: false, fStoreDefault: true)); |
| 100 | pComp->Value(rStruct: mkNamingAdapt(rValue&: GamepadEnabled, szName: "GamepadEnabled" , rDefault: true)); |
| 101 | pComp->Value(rStruct: mkNamingAdapt(rValue&: FirstStart, szName: "FirstStart" , rDefault: true)); |
| 102 | pComp->Value(rStruct: mkNamingAdapt(rValue&: UserPortraitsWritten, szName: "UserPortraitsWritten" , rDefault: false)); |
| 103 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ConfigResetSafety, szName: "ConfigResetSafety" , rDefault: static_cast<int32_t>(ConfigResetSafetyVal))); |
| 104 | pComp->Value(rStruct: mkNamingAdapt(rValue&: UseWhiteIngameChat, szName: "UseWhiteIngameChat" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 105 | pComp->Value(rStruct: mkNamingAdapt(rValue&: UseWhiteLobbyChat, szName: "UseWhiteLobbyChat" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 106 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ShowLogTimestamps, szName: "ShowLogTimestamps" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 107 | |
| 108 | #ifdef __APPLE__ |
| 109 | pComp->Value(mkNamingAdapt(Preloading, "Preloading" , false)); |
| 110 | #else |
| 111 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Preloading, szName: "Preloading" , rDefault: true)); |
| 112 | #endif |
| 113 | |
| 114 | #ifndef _WIN32 |
| 115 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ThreadPoolThreadCount, szName: "ThreadPoolThreadCount" , rDefault: 8)); |
| 116 | #endif |
| 117 | } |
| 118 | |
| 119 | #ifdef C4ENGINE |
| 120 | |
| 121 | void C4ConfigDeveloper::ConsoleScriptStrictnessWrapper::CompileFunc(StdCompiler *const comp) |
| 122 | { |
| 123 | StdEnumEntry<C4AulScriptStrict> ConsoleScriptStrictnessValues[] = |
| 124 | { |
| 125 | {.Name: "NonStrict" , .Val: C4AulScriptStrict::NONSTRICT}, |
| 126 | {.Name: "Strict1" , .Val: C4AulScriptStrict::STRICT1}, |
| 127 | {.Name: "Strict2" , .Val: C4AulScriptStrict::STRICT2}, |
| 128 | {.Name: "Strict3" , .Val: C4AulScriptStrict::STRICT3}, |
| 129 | {.Name: "MaxStrict" , .Val: MaxStrictSentinel} |
| 130 | }; |
| 131 | |
| 132 | comp->Value(rStruct: mkEnumAdaptT<C4AulScriptStrict>(rVal&: Strictness, pNames: ConsoleScriptStrictnessValues)); |
| 133 | |
| 134 | if (comp->isCompiler() && Strictness != MaxStrictSentinel) |
| 135 | { |
| 136 | Strictness = static_cast<C4AulScriptStrict>(std::clamp(val: std::to_underlying(value: Strictness), lo: std::to_underlying(value: C4AulScriptStrict::NONSTRICT), hi: std::to_underlying(value: C4AulScriptStrict::MAXSTRICT))); |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | void C4ConfigDeveloper::CompileFunc(StdCompiler *pComp) |
| 141 | { |
| 142 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AutoFileReload, szName: "AutoFileReload" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 143 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ConsoleScriptStrictness, szName: "ConsoleScriptStrictness" , rDefault: ConsoleScriptStrictnessWrapper{.Strictness: ConsoleScriptStrictnessWrapper::MaxStrictSentinel})); |
| 144 | } |
| 145 | |
| 146 | void C4ConfigGraphics::CompileFunc(StdCompiler *pComp) |
| 147 | { |
| 148 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ResX, szName: "ResolutionX" , rDefault: 800, fPrefillDefault: false, fStoreDefault: true)); |
| 149 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ResY, szName: "ResolutionY" , rDefault: 600, fPrefillDefault: false, fStoreDefault: true)); |
| 150 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Scale, szName: "Scale" , rDefault: 100, fPrefillDefault: false, fStoreDefault: true)); |
| 151 | pComp->Default(szName: "ShowAllResolutions" ); |
| 152 | pComp->Value(rStruct: mkNamingAdapt(rValue&: SplitscreenDividers, szName: "SplitscreenDividers" , rDefault: 1)); |
| 153 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ShowPlayerHUDAlways, szName: "ShowPlayerHUDAlways" , rDefault: true)); |
| 154 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ShowPortraits, szName: "ShowPortraits" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 155 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AddNewCrewPortraits, szName: "AddNewCrewPortraits" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 156 | pComp->Value(rStruct: mkNamingAdapt(rValue&: SaveDefaultPortraits, szName: "SaveDefaultPortraits" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 157 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ShowCommands, szName: "ShowCommands" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 158 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ShowCommandKeys, szName: "ShowCommandKeys" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 159 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ColorAnimation, szName: "ColorAnimation" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 160 | pComp->Value(rStruct: mkNamingAdapt(rValue&: SmokeLevel, szName: "SmokeLevel" , rDefault: 200, fPrefillDefault: false, fStoreDefault: true)); |
| 161 | pComp->Value(rStruct: mkNamingAdapt(rValue&: VerboseObjectLoading, szName: "VerboseObjectLoading" , rDefault: 0, fPrefillDefault: false, fStoreDefault: true)); |
| 162 | |
| 163 | StdEnumEntry<int32_t> UpperBoardDisplayModes[] = |
| 164 | { |
| 165 | {.Name: "Hide" , .Val: C4UpperBoard::Hide}, |
| 166 | {.Name: "Full" , .Val: C4UpperBoard::Full}, |
| 167 | {.Name: "Small" , .Val: C4UpperBoard::Small}, |
| 168 | {.Name: "Mini" , .Val: C4UpperBoard::Mini} |
| 169 | }; |
| 170 | pComp->Value(rStruct: mkNamingAdapt(rValue: mkEnumAdaptT<int32_t>(rVal&: UpperBoard, pNames: UpperBoardDisplayModes), szName: "UpperBoard" , rDefault: C4UpperBoard::Full, fPrefillDefault: false, fStoreDefault: true)); |
| 171 | |
| 172 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ShowClock, szName: "ShowClock" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 173 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ShowCrewNames, szName: "ShowCrewNames" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 174 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ShowCrewCNames, szName: "ShowCrewCNames" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 175 | pComp->Value(rStruct: mkNamingAdapt(rValue&: MsgBoard, szName: "MsgBoard" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 176 | pComp->Value(rStruct: mkNamingAdapt(rValue&: PXSGfx, szName: "PXSGfx" , rDefault: true)); |
| 177 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Engine, szName: "Engine" , GFXENGN_OPENGL, fPrefillDefault: false, fStoreDefault: true)); |
| 178 | pComp->Value(rStruct: mkNamingAdapt(rValue&: NoAlphaAdd, szName: "NoAlphaAdd" , rDefault: false)); |
| 179 | pComp->Value(rStruct: mkNamingAdapt(rValue&: PointFiltering, szName: "PointFiltering" , rDefault: false)); |
| 180 | pComp->Value(rStruct: mkNamingAdapt(rValue&: NoBoxFades, szName: "NoBoxFades" , rDefault: false)); |
| 181 | pComp->Value(rStruct: mkNamingAdapt(rValue&: NoAcceleration, szName: "NoAcceleration" , rDefault: false)); |
| 182 | pComp->Value(rStruct: mkNamingAdapt(rValue&: TexIndent, szName: "TexIndent" , rDefault: 0)); |
| 183 | pComp->Value(rStruct: mkNamingAdapt(rValue&: BlitOffset, szName: "BlitOffset" , rDefault: 0)); |
| 184 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AllowedBlitModes, szName: "AllowedBlitModes" , C4GFXBLIT_ALL)); |
| 185 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Gamma1, szName: "Gamma1" , rDefault: 0)); |
| 186 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Gamma2, szName: "Gamma2" , rDefault: 0x808080)); |
| 187 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Gamma3, szName: "Gamma3" , rDefault: 0xffffff)); |
| 188 | pComp->Default(szName: "Currency" ); |
| 189 | pComp->Value(rStruct: mkNamingAdapt(rValue&: RenderInactive, szName: "RenderInactive" , rDefault: Console)); |
| 190 | pComp->Value(rStruct: mkNamingAdapt(rValue&: DisableGamma, szName: "DisableGamma" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 191 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Monitor, szName: "Monitor" , rDefault: 0)); // 0 = D3DADAPTER_DEFAULT |
| 192 | pComp->Value(rStruct: mkNamingAdapt(rValue&: FireParticles, szName: "FireParticles" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 193 | pComp->Value(rStruct: mkNamingAdapt(rValue&: MaxRefreshDelay, szName: "MaxRefreshDelay" , rDefault: 30)); |
| 194 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Shader, szName: "Shader" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 195 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AutoFrameSkip, szName: "AutoFrameSkip" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 196 | pComp->Value(rStruct: mkNamingAdapt(rValue&: CacheTexturesInRAM, szName: "CacheTexturesInRAM" , rDefault: 100)); |
| 197 | |
| 198 | StdEnumEntry<DisplayMode> DisplayModes[] = |
| 199 | { |
| 200 | {.Name: "Fullscreen" , .Val: DisplayMode::Fullscreen}, |
| 201 | {.Name: "Window" , .Val: DisplayMode::Window} |
| 202 | }; |
| 203 | pComp->Value(rStruct: mkNamingAdapt(rValue: mkEnumAdaptT<int>(rVal&: UseDisplayMode, pNames: DisplayModes), szName: "DisplayMode" , rDefault: DisplayMode::Fullscreen, fPrefillDefault: false, fStoreDefault: true)); |
| 204 | |
| 205 | #ifdef _WIN32 |
| 206 | pComp->Value(mkNamingAdapt(Maximized, "Maximized" , false, false, true)); |
| 207 | pComp->Value(mkNamingAdapt(PositionX, "PositionX" , 0, false, true)); |
| 208 | pComp->Value(mkNamingAdapt(PositionY, "PositionY" , 0, false, true)); |
| 209 | #endif |
| 210 | |
| 211 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ShowFolderMaps, szName: "ShowFolderMaps" , rDefault: true)); |
| 212 | pComp->Value(rStruct: mkNamingAdapt(rValue&: UseShaderGamma, szName: "UseShaderGamma" , rDefault: true)); |
| 213 | } |
| 214 | |
| 215 | void C4ConfigSound::CompileFunc(StdCompiler *pComp) |
| 216 | { |
| 217 | pComp->Value(rStruct: mkNamingAdapt(rValue&: RXSound, szName: "Sound" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 218 | pComp->Value(rStruct: mkNamingAdapt(rValue&: RXMusic, szName: "Music" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 219 | pComp->Value(rStruct: mkNamingAdapt(rValue&: FEMusic, szName: "MenuMusic" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 220 | pComp->Value(rStruct: mkNamingAdapt(rValue&: FESamples, szName: "MenuSound" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 221 | pComp->Default(szName: "Verbose" ); |
| 222 | pComp->Value(rStruct: mkNamingAdapt(rValue&: MusicVolume, szName: "MusicVolume" , rDefault: 100, fPrefillDefault: false, fStoreDefault: true)); |
| 223 | pComp->Value(rStruct: mkNamingAdapt(rValue&: SoundVolume, szName: "SoundVolume" , rDefault: 100, fPrefillDefault: false, fStoreDefault: true)); |
| 224 | pComp->Value(rStruct: mkNamingAdapt(rValue&: MaxChannels, szName: "MaxChannels" , rDefault: C4AudioSystem::MaxChannels)); |
| 225 | pComp->Value(rStruct: mkNamingAdapt(rValue&: PreferLinearResampling, szName: "PreferLinearResampling" , rDefault: false)); |
| 226 | |
| 227 | if (pComp->isCompiler()) |
| 228 | { |
| 229 | MaxChannels = std::clamp(val: MaxChannels, lo: 1, hi: C4AudioSystem::MaxChannels); |
| 230 | } |
| 231 | |
| 232 | pComp->Value(rStruct: mkNamingAdapt(rValue&: MuteSoundCommand, szName: "MuteSoundCommand" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 233 | } |
| 234 | |
| 235 | void C4ConfigNetwork::CompileFunc(StdCompiler *pComp) |
| 236 | { |
| 237 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ControlRate, szName: "ControlRate" , rDefault: 2, fPrefillDefault: false, fStoreDefault: true)); |
| 238 | pComp->Value(rStruct: mkNamingAdapt(s(WorkPath), szName: "WorkPath" , rDefault: "Network" , fPrefillDefault: false, fStoreDefault: true)); |
| 239 | pComp->Value(rStruct: mkNamingAdapt(rValue&: NoRuntimeJoin, szName: "NoRuntimeJoin" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 240 | pComp->Value(rStruct: mkNamingAdapt(rValue&: MaxResSearchRecursion, szName: "MaxResSearchRecursion" , rDefault: 1, fPrefillDefault: false, fStoreDefault: true)); |
| 241 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Comment, szName: "Comment" , rDefault: "" , fPrefillDefault: false, fStoreDefault: true)); |
| 242 | |
| 243 | pComp->Value(rStruct: mkNamingAdapt(rValue&: PortTCP, szName: "PortTCP" , rDefault: C4NetStdPortTCP, fPrefillDefault: false, fStoreDefault: true)); |
| 244 | pComp->Value(rStruct: mkNamingAdapt(rValue&: PortUDP, szName: "PortUDP" , rDefault: C4NetStdPortUDP, fPrefillDefault: false, fStoreDefault: true)); |
| 245 | pComp->Value(rStruct: mkNamingAdapt(rValue&: PortDiscovery, szName: "PortDiscovery" , rDefault: C4NetStdPortDiscovery, fPrefillDefault: false, fStoreDefault: true)); |
| 246 | pComp->Value(rStruct: mkNamingAdapt(rValue&: PortRefServer, szName: "PortRefServer" , rDefault: C4NetStdPortRefServer, fPrefillDefault: false, fStoreDefault: true)); |
| 247 | |
| 248 | pComp->Value(rStruct: mkNamingAdapt(rValue&: ControlMode, szName: "ControlMode" , rDefault: 0, fPrefillDefault: false, fStoreDefault: true)); |
| 249 | pComp->Value(rStruct: mkNamingAdapt(rValue&: LocalName, szName: "LocalName" , rDefault: "Unknown" , fPrefillDefault: false, fStoreDefault: true)); |
| 250 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Nick, szName: "Nick" , rDefault: "" , fPrefillDefault: false, fStoreDefault: true)); |
| 251 | pComp->Value(rStruct: mkNamingAdapt(rValue&: MaxLoadFileSize, szName: "MaxLoadFileSize" , rDefault: 100 * 1024 * 1024, fPrefillDefault: false, fStoreDefault: true)); |
| 252 | |
| 253 | pComp->Value(rStruct: mkNamingAdapt(rValue&: MasterServerSignUp, szName: "MasterServerSignUp" , rDefault: true, fPrefillDefault: false, fStoreDefault: true)); |
| 254 | pComp->Value(rStruct: mkNamingAdapt(rValue&: MasterReferencePeriod, szName: "MasterReferencePeriod" , rDefault: 120, fPrefillDefault: false, fStoreDefault: true)); |
| 255 | pComp->Value(rStruct: mkNamingAdapt(rValue&: LeagueServerSignUp, szName: "LeagueServerSignUp" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 256 | pComp->Value(rStruct: mkNamingAdapt(s(ServerAddress), szName: "ServerAddress" , C4CFG_LeagueServer, fPrefillDefault: false, fStoreDefault: true)); |
| 257 | pComp->Value(rStruct: mkNamingAdapt(rValue&: UseAlternateServer, szName: "UseAlternateServer" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 258 | pComp->Value(rStruct: mkNamingAdapt(s(AlternateServerAddress), szName: "AlternateServerAddress" , C4CFG_LeagueServer, fPrefillDefault: false, fStoreDefault: true)); |
| 259 | pComp->Value(rStruct: mkNamingAdapt(s(UpdateServerAddress), szName: "UpdateServerAddress" , C4CFG_UpdateServer)); |
| 260 | pComp->Value(rStruct: mkNamingAdapt(s(LastPassword), szName: "LastPassword" , rDefault: "Wipf" , fPrefillDefault: false, fStoreDefault: true)); |
| 261 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AutomaticUpdate, szName: "EnableAutomaticUpdate" , rDefault: true)); |
| 262 | pComp->Value(rStruct: mkNamingAdapt(rValue&: LastUpdateTime, szName: "LastUpdateTime" , rDefault: 0, fPrefillDefault: false, fStoreDefault: true)); |
| 263 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AsyncMaxWait, szName: "AsyncMaxWait" , rDefault: 2, fPrefillDefault: false, fStoreDefault: true)); |
| 264 | |
| 265 | pComp->Value(rStruct: mkNamingAdapt(s(PuncherAddress), szName: "PuncherAddress" , rDefault: DefaultPuncherServer, fPrefillDefault: false, fStoreDefault: true)); |
| 266 | |
| 267 | pComp->Value(rStruct: mkNamingAdapt(rValue&: LeagueAccount, szName: "LeagueNick" , rDefault: "" , fPrefillDefault: false, fStoreDefault: false)); |
| 268 | pComp->Value(rStruct: mkNamingAdapt(rValue&: LeagueAutoLogin, szName: "LeagueAutoLogin" , rDefault: true, fPrefillDefault: false, fStoreDefault: false)); |
| 269 | pComp->Value(rStruct: mkNamingAdapt(rValue&: UseCurl, szName: "UseCurl" , rDefault: true)); |
| 270 | pComp->Value(rStruct: mkNamingAdapt(rValue&: EnableUPnP, szName: "EnableUPnP" , rDefault: true)); |
| 271 | } |
| 272 | |
| 273 | void C4ConfigLobby::CompileFunc(StdCompiler *pComp) |
| 274 | { |
| 275 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AllowPlayerSave, szName: "AllowPlayerSave" , rDefault: false, fPrefillDefault: false, fStoreDefault: true)); |
| 276 | pComp->Value(rStruct: mkNamingAdapt(rValue&: CountdownTime, szName: "CountdownTime" , rDefault: 5, fPrefillDefault: false, fStoreDefault: true)); |
| 277 | } |
| 278 | |
| 279 | void C4ConfigIRC::CompileFunc(StdCompiler *pComp) |
| 280 | { |
| 281 | pComp->Value(rStruct: mkNamingAdapt(s(Server), szName: "Server2" , rDefault: "irc.euirc.net" , fPrefillDefault: false, fStoreDefault: true)); |
| 282 | pComp->Value(rStruct: mkNamingAdapt(s(Nick), szName: "Nick" , rDefault: "" , fPrefillDefault: false, fStoreDefault: true)); |
| 283 | pComp->Value(rStruct: mkNamingAdapt(s(RealName), szName: "RealName" , rDefault: "" , fPrefillDefault: false, fStoreDefault: true)); |
| 284 | pComp->Value(rStruct: mkNamingAdapt(s(Channel), szName: "Channel" , rDefault: "#clonken,#legacyclonk" , fPrefillDefault: false, fStoreDefault: true)); |
| 285 | } |
| 286 | |
| 287 | void C4ConfigGamepad::CompileFunc(StdCompiler *pComp, bool fButtonsOnly) |
| 288 | { |
| 289 | /* The defaults here are for a Logitech Dual Action under Linux-SDL. Better than nothing, I guess. */ |
| 290 | if (!fButtonsOnly) |
| 291 | { |
| 292 | for (int i = 0; i < 6; ++i) |
| 293 | { |
| 294 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AxisMin[i], szName: std::format(fmt: "Axis{}Min" , args&: i).c_str(), rDefault: 0u)); |
| 295 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AxisMax[i], szName: std::format(fmt: "Axis{}Max" , args&: i).c_str(), rDefault: 0u)); |
| 296 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AxisCalibrated[i], szName: std::format(fmt: "Axis{}Calibrated" , args&: i).c_str(), rDefault: false)); |
| 297 | } |
| 298 | } |
| 299 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Button[0], szName: "Button1" , rDefault: -1)); |
| 300 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Button[1], szName: "Button2" , rDefault: -1)); |
| 301 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Button[2], szName: "Button3" , rDefault: -1)); |
| 302 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Button[3], szName: "Button4" , rDefault: -1)); |
| 303 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Button[4], szName: "Button5" , rDefault: -1)); |
| 304 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Button[5], szName: "Button6" , rDefault: -1)); |
| 305 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Button[6], szName: "Button7" , rDefault: -1)); |
| 306 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Button[7], szName: "Button8" , rDefault: -1)); |
| 307 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Button[8], szName: "Button9" , rDefault: -1)); |
| 308 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Button[9], szName: "Button10" , rDefault: -1)); |
| 309 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Button[10], szName: "Button11" , rDefault: -1)); |
| 310 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Button[11], szName: "Button12" , rDefault: -1)); |
| 311 | } |
| 312 | |
| 313 | void C4ConfigGamepad::Reset() |
| 314 | { |
| 315 | // loads an empty config for the gamepad config |
| 316 | StdCompilerNull Comp; Comp.Compile(rStruct: mkParAdapt(rObj&: *this, rPar: false)); |
| 317 | } |
| 318 | |
| 319 | void C4ConfigControls::CompileFunc(StdCompiler *pComp, bool fKeysOnly) |
| 320 | { |
| 321 | #ifndef USE_CONSOLE |
| 322 | #ifdef _WIN32 |
| 323 | #define KEY(win, x, sdl) win |
| 324 | #elif defined(USE_X11) |
| 325 | #define KEY(win, x, sdl) x |
| 326 | #else |
| 327 | #define KEY(win, x, sdl) sdl |
| 328 | #endif |
| 329 | |
| 330 | bool fGer = isGermanSystem(); |
| 331 | |
| 332 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[0][ 0], szName: "Kbd1Key1" , KEY('Q', XK_q, SDL_SCANCODE_Q))); |
| 333 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[0][ 1], szName: "Kbd1Key2" , KEY('W', XK_w, SDL_SCANCODE_W))); |
| 334 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[0][ 2], szName: "Kbd1Key3" , KEY('E', XK_e, SDL_SCANCODE_E))); |
| 335 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[0][ 3], szName: "Kbd1Key4" , KEY('A', XK_a, SDL_SCANCODE_A))); |
| 336 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[0][ 4], szName: "Kbd1Key5" , KEY('S', XK_s, SDL_SCANCODE_S))); |
| 337 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[0][ 5], szName: "Kbd1Key6" , KEY('D', XK_d, SDL_SCANCODE_D))); |
| 338 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[0][ 6], szName: "Kbd1Key7" , rDefault: fGer ? KEY('Y', XK_y, SDL_SCANCODE_Z) : KEY('Z', XK_z, SDL_SCANCODE_Z))); |
| 339 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[0][ 7], szName: "Kbd1Key8" , KEY('X', XK_x, SDL_SCANCODE_X))); |
| 340 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[0][ 8], szName: "Kbd1Key9" , KEY('C', XK_c, SDL_SCANCODE_C))); |
| 341 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[0][ 9], szName: "Kbd1Key10" , rDefault: fGer ? KEY(226, XK_less, SDL_SCANCODE_NONUSBACKSLASH) : KEY('R', XK_r, SDL_SCANCODE_R))); |
| 342 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[0][10], szName: "Kbd1Key11" , KEY('V', XK_v, SDL_SCANCODE_V))); |
| 343 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[0][11], szName: "Kbd1Key12" , KEY('F', XK_f, SDL_SCANCODE_F))); |
| 344 | |
| 345 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[1][ 0], szName: "Kbd2Key1" , KEY(103, XK_KP_Home, SDL_SCANCODE_KP_7))); |
| 346 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[1][ 1], szName: "Kbd2Key2" , KEY(104, XK_KP_Up, SDL_SCANCODE_KP_8))); |
| 347 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[1][ 2], szName: "Kbd2Key3" , KEY(105, XK_KP_Page_Up, SDL_SCANCODE_KP_9))); |
| 348 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[1][ 3], szName: "Kbd2Key4" , KEY(100, XK_KP_Left, SDL_SCANCODE_KP_4))); |
| 349 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[1][ 4], szName: "Kbd2Key5" , KEY(101, XK_KP_Begin, SDL_SCANCODE_KP_5))); |
| 350 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[1][ 5], szName: "Kbd2Key6" , KEY(102, XK_KP_Right, SDL_SCANCODE_KP_6))); |
| 351 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[1][ 6], szName: "Kbd2Key7" , KEY( 97, XK_KP_End, SDL_SCANCODE_KP_1))); |
| 352 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[1][ 7], szName: "Kbd2Key8" , KEY( 98, XK_KP_Down, SDL_SCANCODE_KP_2))); |
| 353 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[1][ 8], szName: "Kbd2Key9" , KEY( 99, XK_KP_Page_Down, SDL_SCANCODE_KP_3))); |
| 354 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[1][ 9], szName: "Kbd2Key10" , KEY( 96, XK_KP_Insert, SDL_SCANCODE_KP_0))); |
| 355 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[1][10], szName: "Kbd2Key11" , KEY(110, XK_KP_Delete, SDL_SCANCODE_KP_PERIOD))); |
| 356 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[1][11], szName: "Kbd2Key12" , KEY(107, XK_KP_Add, SDL_SCANCODE_KP_PLUS))); |
| 357 | |
| 358 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[2][ 0], szName: "Kbd3Key1" , KEY('I', XK_i, SDL_SCANCODE_I))); |
| 359 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[2][ 1], szName: "Kbd3Key2" , KEY('O', XK_o, SDL_SCANCODE_O))); |
| 360 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[2][ 2], szName: "Kbd3Key3" , KEY('P', XK_p, SDL_SCANCODE_P))); |
| 361 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[2][ 3], szName: "Kbd3Key4" , KEY('K', XK_k, SDL_SCANCODE_K))); |
| 362 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[2][ 4], szName: "Kbd3Key5" , KEY('L', XK_l, SDL_SCANCODE_L))); |
| 363 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[2][ 5], szName: "Kbd3Key6" , rDefault: fGer ? KEY(192, XK_odiaeresis, SDL_SCANCODE_SEMICOLON) : KEY(0xBA, XK_semicolon, SDL_SCANCODE_SEMICOLON))); |
| 364 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[2][ 6], szName: "Kbd3Key7" , KEY(188, XK_comma, SDL_SCANCODE_COMMA))); |
| 365 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[2][ 7], szName: "Kbd3Key8" , KEY(190, XK_period, SDL_SCANCODE_PERIOD))); |
| 366 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[2][ 8], szName: "Kbd3Key9" , rDefault: fGer ? KEY(189, XK_minus, SDL_SCANCODE_SLASH) : KEY(0xBF, XK_slash, SDL_SCANCODE_SLASH))); |
| 367 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[2][ 9], szName: "Kbd3Key10" , KEY('M', XK_m, SDL_SCANCODE_M))); |
| 368 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[2][10], szName: "Kbd3Key11" , KEY(222, XK_adiaeresis, SDL_SCANCODE_APOSTROPHE))); |
| 369 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[2][11], szName: "Kbd3Key12" , KEY(186, XK_udiaeresis, SDL_SCANCODE_LEFTBRACKET))); |
| 370 | |
| 371 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[3][ 0], szName: "Kbd4Key1" , KEY(VK_INSERT, XK_Insert, SDL_SCANCODE_INSERT))); |
| 372 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[3][ 1], szName: "Kbd4Key2" , KEY(VK_HOME, XK_Home, SDL_SCANCODE_HOME))); |
| 373 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[3][ 2], szName: "Kbd4Key3" , KEY(VK_PRIOR, XK_Page_Up, SDL_SCANCODE_PAGEUP))); |
| 374 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[3][ 3], szName: "Kbd4Key4" , KEY(VK_DELETE, XK_Delete, SDL_SCANCODE_DELETE))); |
| 375 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[3][ 4], szName: "Kbd4Key5" , KEY(VK_UP, XK_Up, SDL_SCANCODE_UP))); |
| 376 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[3][ 5], szName: "Kbd4Key6" , KEY(VK_NEXT, XK_Page_Down, SDL_SCANCODE_PAGEDOWN))); |
| 377 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[3][ 6], szName: "Kbd4Key7" , KEY(VK_LEFT, XK_Left, SDL_SCANCODE_LEFT))); |
| 378 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[3][ 7], szName: "Kbd4Key8" , KEY(VK_DOWN, XK_Down, SDL_SCANCODE_DOWN))); |
| 379 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[3][ 8], szName: "Kbd4Key9" , KEY(VK_RIGHT, XK_Right, SDL_SCANCODE_RIGHT))); |
| 380 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[3][ 9], szName: "Kbd4Key10" , KEY(VK_END, XK_End, SDL_SCANCODE_END))); |
| 381 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[3][10], szName: "Kbd4Key11" , KEY(VK_RETURN, XK_Return, SDL_SCANCODE_RETURN))); |
| 382 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Keyboard[3][11], szName: "Kbd4Key12" , KEY(VK_BACK, XK_BackSpace, SDL_SCANCODE_BACKSPACE))); |
| 383 | |
| 384 | if (fKeysOnly) return; |
| 385 | |
| 386 | pComp->Value(rStruct: mkNamingAdapt(rValue&: MouseAScroll, szName: "MouseAutoScroll" , rDefault: 0)); |
| 387 | pComp->Value(rStruct: mkNamingAdapt(rValue&: GamepadGuiControl, szName: "GamepadGuiControl" , rDefault: 0, fPrefillDefault: false, fStoreDefault: true)); |
| 388 | |
| 389 | #undef KEY |
| 390 | #undef s |
| 391 | #endif // USE_CONSOLE |
| 392 | } |
| 393 | |
| 394 | void C4ConfigCooldowns::CompileFunc(StdCompiler *comp) |
| 395 | { |
| 396 | using namespace std::chrono_literals; |
| 397 | |
| 398 | comp->Value(rStruct: mkNamingAdapt(rValue&: SoundCommand, szName: "SoundCommand" , rDefault: 0s)); |
| 399 | comp->Value(rStruct: mkNamingAdapt(rValue: mkParAdapt(rObj&: ReadyCheck, rPar: 5s), szName: "ReadyCheck" , rDefault: 10s)); |
| 400 | } |
| 401 | |
| 402 | void C4ConfigToasts::CompileFunc(StdCompiler *comp) |
| 403 | { |
| 404 | comp->Value(rStruct: mkNamingAdapt(rValue&: ReadyCheck, szName: "ReadyCheck" , rDefault: true)); |
| 405 | } |
| 406 | |
| 407 | void C4ConfigLogging::CompileFunc(StdCompiler *const comp) |
| 408 | { |
| 409 | comp->Value(rStruct: mkNamingAdapt(rValue&: LogLevelStdout, szName: "LogLevelStdout" , rDefault: spdlog::level::info)); |
| 410 | |
| 411 | comp->Value(rStruct&: AudioSystem); |
| 412 | comp->Value(rStruct&: AulExec); |
| 413 | comp->Value(rStruct&: AulProfiler); |
| 414 | comp->Value(rStruct&: DDraw); |
| 415 | comp->Value(rStruct&: GameControl); |
| 416 | comp->Value(rStruct&: Network); |
| 417 | comp->Value(rStruct&: Network2IO); |
| 418 | comp->Value(rStruct&: Network2HTTPClient); |
| 419 | comp->Value(rStruct&: Network2UPnP); |
| 420 | comp->Value(rStruct&: Playback); |
| 421 | comp->Value(rStruct&: PNGFile); |
| 422 | |
| 423 | #ifdef WITH_GLIB |
| 424 | comp->Value(rStruct&: GLib); |
| 425 | #endif |
| 426 | } |
| 427 | #endif |
| 428 | |
| 429 | C4Config::C4Config() |
| 430 | { |
| 431 | Default(); |
| 432 | } |
| 433 | |
| 434 | C4Config::~C4Config() |
| 435 | { |
| 436 | fConfigLoaded = false; |
| 437 | } |
| 438 | |
| 439 | void C4Config::Default() |
| 440 | { |
| 441 | // force default values |
| 442 | StdCompilerNull Comp; Comp.Compile(rStruct&: *this); |
| 443 | fConfigLoaded = false; |
| 444 | } |
| 445 | |
| 446 | bool C4Config::Load(bool forceWorkingDirectory, const char *szConfigFile) |
| 447 | { |
| 448 | try |
| 449 | { |
| 450 | #ifdef _WIN32 |
| 451 | // Windows: Default load from registry, if no explicit config file is specified |
| 452 | if (!szConfigFile) |
| 453 | { |
| 454 | StdCompilerConfigRead CfgRead(HKEY_CURRENT_USER, "Software\\" C4CFG_Company "\\" C4CFG_Product); |
| 455 | CfgRead.Compile(*this); |
| 456 | } |
| 457 | else |
| 458 | #endif |
| 459 | { |
| 460 | // Nonwindows or explicit config file: Determine filename to load config from |
| 461 | StdStrBuf filename; |
| 462 | if (szConfigFile) |
| 463 | { |
| 464 | // Config filename is specified |
| 465 | filename.Ref(pnData: szConfigFile); |
| 466 | // make sure we're at the correct path to load it |
| 467 | if (forceWorkingDirectory) General.DeterminePaths(forceWorkingDirectory: true); |
| 468 | } |
| 469 | else |
| 470 | { |
| 471 | // Config filename from home |
| 472 | StdStrBuf home(getenv(name: "HOME" ), false); |
| 473 | if (home) { home += "/" ; } |
| 474 | filename.Copy(Buf2: home); |
| 475 | #ifdef __APPLE__ |
| 476 | filename += "Library/Preferences/legacyclonk.config" ; |
| 477 | #else |
| 478 | filename += ".legacyclonk/config" ; |
| 479 | #endif |
| 480 | } |
| 481 | |
| 482 | // Load config file into buf |
| 483 | StdStrBuf buf; |
| 484 | buf.LoadFromFile(szFile: filename.getData()); |
| 485 | |
| 486 | if (buf.isNull()) |
| 487 | { |
| 488 | // Config file not present? |
| 489 | #ifdef __linux__ |
| 490 | if (!szConfigFile) |
| 491 | { |
| 492 | StdStrBuf filename(getenv(name: "HOME" ), false); |
| 493 | if (filename) { filename += "/" ; } |
| 494 | filename += ".legacyclonk" ; |
| 495 | MakeDirectory(pathname: filename.getData()); |
| 496 | } |
| 497 | #endif |
| 498 | // Buggy StdCompiler crashes when compiling a Null-StdStrBuf |
| 499 | buf.Ref(pnData: " " ); |
| 500 | } |
| 501 | |
| 502 | // Read config from buffer |
| 503 | StdCompilerINIRead IniRead; |
| 504 | IniRead.setInput(buf); |
| 505 | IniRead.Compile(rStruct&: *this); |
| 506 | } |
| 507 | } |
| 508 | catch ([[maybe_unused]] const StdCompiler::Exception &e) |
| 509 | { |
| 510 | // Configuration file syntax error? |
| 511 | #ifdef C4ENGINE |
| 512 | spdlog::critical(fmt: "Error loading configuration: {}" , args: e.what()); |
| 513 | #endif |
| 514 | return false; |
| 515 | } |
| 516 | |
| 517 | // Config postinit |
| 518 | General.DeterminePaths(forceWorkingDirectory); |
| 519 | #ifdef C4ENGINE |
| 520 | AdaptToCurrentVersion(); |
| 521 | #ifdef _WIN32 |
| 522 | bool fWinSock = AcquireWinSock(); |
| 523 | #endif |
| 524 | if (SEqual(szStr1: Network.LocalName.getData(), szStr2: "Unknown" )) |
| 525 | { |
| 526 | char LocalName[25 + 1]; *LocalName = 0; |
| 527 | gethostname(name: LocalName, len: 25); |
| 528 | if (*LocalName) Network.LocalName.Copy(pnData: LocalName); |
| 529 | } |
| 530 | #ifdef _WIN32 |
| 531 | if (fWinSock) ReleaseWinSock(); |
| 532 | #endif |
| 533 | #endif |
| 534 | General.DefaultLanguage(); |
| 535 | #ifdef C4ENGINE |
| 536 | #ifndef USE_CONSOLE |
| 537 | if (Graphics.Engine != GFXENGN_NOGFX) Graphics.Engine = GFXENGN_OPENGL; |
| 538 | #endif |
| 539 | // Warning against invalid ports |
| 540 | for (const auto &port : |
| 541 | { |
| 542 | &Config.Network.PortTCP, |
| 543 | &Config.Network.PortUDP, |
| 544 | &Config.Network.PortDiscovery, |
| 545 | &Config.Network.PortRefServer |
| 546 | } |
| 547 | ) |
| 548 | { |
| 549 | if (*port < 0 || *port > 65535) *port = 0; |
| 550 | } |
| 551 | if (Config.Network.PortTCP > 0 && Config.Network.PortTCP == Config.Network.PortRefServer) |
| 552 | { |
| 553 | spdlog::warn(msg: "Network TCP port and reference server port both set to same value - increasing reference server port!" ); |
| 554 | ++Config.Network.PortRefServer; |
| 555 | if (Config.Network.PortRefServer >= 65536) Config.Network.PortRefServer = C4NetStdPortRefServer; |
| 556 | } |
| 557 | if (Config.Network.PortUDP > 0 && Config.Network.PortUDP == Config.Network.PortDiscovery) |
| 558 | { |
| 559 | spdlog::warn(msg: "Network UDP port and LAN game discovery port both set to same value - increasing discovery port!" ); |
| 560 | ++Config.Network.PortDiscovery; |
| 561 | if (Config.Network.PortDiscovery >= 65536) Config.Network.PortDiscovery = C4NetStdPortDiscovery; |
| 562 | } |
| 563 | #endif |
| 564 | fConfigLoaded = true; |
| 565 | if (szConfigFile) ConfigFilename.Copy(pnData: szConfigFile); else ConfigFilename.Clear(); |
| 566 | return true; |
| 567 | } |
| 568 | |
| 569 | bool C4Config::Save() |
| 570 | { |
| 571 | try |
| 572 | { |
| 573 | #ifdef _WIN32 |
| 574 | if (!ConfigFilename.getLength()) |
| 575 | { |
| 576 | // Windows: Default save to registry, if it wasn't loaded from file |
| 577 | StdCompilerConfigWrite CfgWrite(HKEY_CURRENT_USER, "Software\\" C4CFG_Company "\\" C4CFG_Product); |
| 578 | CfgWrite.Decompile(*this); |
| 579 | } |
| 580 | else |
| 581 | #endif |
| 582 | { |
| 583 | StdStrBuf filename; |
| 584 | if (ConfigFilename.getLength()) |
| 585 | { |
| 586 | filename.Ref(Buf2: ConfigFilename); |
| 587 | } |
| 588 | else |
| 589 | { |
| 590 | filename.Copy(pnData: getenv(name: "HOME" )); |
| 591 | if (filename) { filename += "/" ; } |
| 592 | #ifdef __APPLE__ |
| 593 | filename += "Library/Preferences/legacyclonk.config" ; |
| 594 | #else |
| 595 | filename += ".legacyclonk/config" ; |
| 596 | #endif |
| 597 | } |
| 598 | StdCompilerINIWrite IniWrite; |
| 599 | IniWrite.Decompile(rStruct: *this); |
| 600 | const std::string output{IniWrite.getOutput()}; |
| 601 | StdStrBuf{output.c_str(), output.size(), false}.SaveToFile(szFile: filename.getData()); |
| 602 | } |
| 603 | } |
| 604 | catch ([[maybe_unused]] const StdCompiler::Exception &e) |
| 605 | { |
| 606 | #ifdef C4ENGINE |
| 607 | Log(id: C4ResStrTableKey::IDS_ERR_CONFSAVE, args: e.what()); |
| 608 | #endif |
| 609 | return false; |
| 610 | } |
| 611 | return true; |
| 612 | } |
| 613 | |
| 614 | void C4ConfigGeneral::DeterminePaths(bool forceWorkingDirectory) |
| 615 | { |
| 616 | #ifdef _WIN32 |
| 617 | // Exe path |
| 618 | if (GetModuleFileNameA(nullptr, ExePath, CFG_MaxString)) |
| 619 | { |
| 620 | TruncatePath(ExePath); AppendBackslash(ExePath); |
| 621 | } |
| 622 | // Temp path |
| 623 | GetTempPathA(CFG_MaxString, TempPath); |
| 624 | if (TempPath[0]) AppendBackslash(TempPath); |
| 625 | #elif defined(__linux__) |
| 626 | #ifdef C4ENGINE |
| 627 | GetParentPath(szFilename: Application.Location, szBuffer: ExePath); |
| 628 | #else |
| 629 | ExePath[0] = '.'; ExePath[1] = 0; |
| 630 | #endif |
| 631 | AppendBackslash(szFileName: ExePath); |
| 632 | const char *t = getenv(name: "TMPDIR" ); |
| 633 | if (t) |
| 634 | { |
| 635 | SCopy(szSource: t, sTarget: TempPath, iMaxL: sizeof(TempPath) - 2); |
| 636 | AppendBackslash(szFileName: TempPath); |
| 637 | } |
| 638 | else |
| 639 | SCopy(szSource: "/tmp/" , sTarget: TempPath); |
| 640 | #else |
| 641 | // Mac: Just use the working directory as ExePath. |
| 642 | SCopy(GetWorkingDirectory(), ExePath); |
| 643 | AppendBackslash(ExePath); |
| 644 | SCopy("/tmp/" , TempPath); |
| 645 | #endif |
| 646 | // Force working directory to exe path if desired |
| 647 | if (forceWorkingDirectory) |
| 648 | SetWorkingDirectory(ExePath); |
| 649 | // Log path |
| 650 | SCopy(szSource: ExePath, sTarget: LogPath); |
| 651 | if (LogPath[0]) AppendBackslash(szFileName: LogPath); |
| 652 | else SCopy(szSource: ExePath, sTarget: LogPath); |
| 653 | // Screenshot path |
| 654 | SCopy(szSource: ExePath, sTarget: ScreenshotPath, iMaxL: CFG_MaxString - 1); |
| 655 | if (ScreenshotFolder.getLength() + SLen(sptr: ScreenshotPath) + 1 <= CFG_MaxString) |
| 656 | { |
| 657 | SAppend(szSource: ScreenshotFolder.getData(), szTarget: ScreenshotPath); |
| 658 | AppendBackslash(szFileName: ScreenshotPath); |
| 659 | } |
| 660 | // Player path |
| 661 | if (PlayerPath[0]) AppendBackslash(szFileName: PlayerPath); |
| 662 | #ifdef C4ENGINE |
| 663 | // Create user path if it doesn't already exist |
| 664 | if (!DirectoryExists(szFileName: Config.AtUserPath(szFilename: "" ))) |
| 665 | MakeDirectory(pathname: Config.AtUserPath(szFilename: "" ), nullptr); // currently no error handling here; also: no recursive directory creation |
| 666 | #endif |
| 667 | } |
| 668 | |
| 669 | char AtPathFilename[_MAX_PATH + 1]; |
| 670 | |
| 671 | const char *C4Config::AtExePath(const char *szFilename) |
| 672 | { |
| 673 | SCopy(szSource: General.ExePath, sTarget: AtPathFilename, _MAX_PATH); |
| 674 | SAppend(szSource: szFilename, szTarget: AtPathFilename, _MAX_PATH); |
| 675 | return AtPathFilename; |
| 676 | } |
| 677 | |
| 678 | const char *C4Config::AtUserPath(const char *szFilename) |
| 679 | { |
| 680 | SCopy(szSource: General.UserPath, sTarget: AtPathFilename, _MAX_PATH); |
| 681 | ExpandEnvironmentVariables(strPath: AtPathFilename, _MAX_PATH); |
| 682 | AppendBackslash(szFileName: AtPathFilename); |
| 683 | SAppend(szSource: szFilename, szTarget: AtPathFilename, _MAX_PATH); |
| 684 | return AtPathFilename; |
| 685 | } |
| 686 | |
| 687 | const char *C4Config::AtTempPath(const char *szFilename) |
| 688 | { |
| 689 | SCopy(szSource: General.TempPath, sTarget: AtPathFilename, _MAX_PATH); |
| 690 | SAppend(szSource: szFilename, szTarget: AtPathFilename, _MAX_PATH); |
| 691 | return AtPathFilename; |
| 692 | } |
| 693 | |
| 694 | #ifdef C4ENGINE |
| 695 | |
| 696 | const char *C4Config::AtNetworkPath(const char *szFilename) |
| 697 | { |
| 698 | SCopy(szSource: Network.WorkPath, sTarget: AtPathFilename, _MAX_PATH); |
| 699 | SAppend(szSource: szFilename, szTarget: AtPathFilename, _MAX_PATH); |
| 700 | return AtPathFilename; |
| 701 | } |
| 702 | |
| 703 | #endif |
| 704 | |
| 705 | const char *C4Config::AtScreenshotPath(const char *szFilename) |
| 706 | { |
| 707 | SCopy(szSource: General.ScreenshotPath, sTarget: AtPathFilename, _MAX_PATH); |
| 708 | if (const auto len = SLen(sptr: AtPathFilename); len > 0) |
| 709 | if (AtPathFilename[len - 1] == DirectorySeparator) |
| 710 | AtPathFilename[len - 1] = '\0'; |
| 711 | if (!DirectoryExists(szFileName: AtPathFilename) && !MakeDirectory(pathname: AtPathFilename, nullptr)) |
| 712 | { |
| 713 | SCopy(szSource: General.ExePath, sTarget: General.ScreenshotPath, iMaxL: CFG_MaxString - 1); |
| 714 | SCopy(szSource: General.ScreenshotPath, sTarget: AtPathFilename, _MAX_PATH); |
| 715 | } |
| 716 | else |
| 717 | AppendBackslash(szFileName: AtPathFilename); |
| 718 | SAppend(szSource: szFilename, szTarget: AtPathFilename, _MAX_PATH); |
| 719 | return AtPathFilename; |
| 720 | } |
| 721 | |
| 722 | #ifdef C4ENGINE |
| 723 | |
| 724 | bool C4ConfigGeneral::CreateSaveFolder(const char *strDirectory, const char *strLanguageTitle) |
| 725 | { |
| 726 | // Create directory if needed |
| 727 | if (!DirectoryExists(szFileName: strDirectory)) |
| 728 | if (!MakeDirectory(pathname: strDirectory, nullptr)) |
| 729 | return false; |
| 730 | // Create title component if needed |
| 731 | char lang[3]; SCopy(szSource: Config.General.Language, sTarget: lang, iMaxL: 2); |
| 732 | const std::string titleFile{std::format(fmt: "{}" DirSep C4CFN_WriteTitle, args&: strDirectory)}; |
| 733 | const std::string titleData{std::format(fmt: "{}:{}" , args: +lang, args&: strLanguageTitle)}; |
| 734 | CStdFile hFile; |
| 735 | if (!FileExists(szFileName: titleFile.c_str())) |
| 736 | if (!hFile.Create(szFileName: titleFile.c_str()) || !hFile.WriteString(szStr: titleData.c_str()) || !hFile.Close()) |
| 737 | return false; |
| 738 | // Save folder seems okay |
| 739 | return true; |
| 740 | } |
| 741 | |
| 742 | const char *C4ConfigNetwork::GetLeagueServerAddress() |
| 743 | { |
| 744 | // Alternate (GUI configurable) league server |
| 745 | if (UseAlternateServer) |
| 746 | return AlternateServerAddress; |
| 747 | // Standard (registry/config file configurable) official league server |
| 748 | else |
| 749 | return ServerAddress; |
| 750 | } |
| 751 | |
| 752 | void C4ConfigControls::ResetKeys() |
| 753 | { |
| 754 | StdCompilerNull Comp; Comp.Compile(rStruct: mkParAdapt(rObj&: *this, rPar: true)); |
| 755 | } |
| 756 | |
| 757 | #endif |
| 758 | |
| 759 | const char *C4Config::AtExeRelativePath(const char *szFilename) |
| 760 | { |
| 761 | // Specified file is located in ExePath: return relative path |
| 762 | return GetRelativePathS(strPath: szFilename, strRelativeTo: General.ExePath); |
| 763 | } |
| 764 | |
| 765 | void C4Config::ForceRelativePath(StdStrBuf *sFilename) |
| 766 | { |
| 767 | assert(sFilename); |
| 768 | // Specified file is located in ExePath? |
| 769 | const char *szRelative = GetRelativePathS(strPath: sFilename->getData(), strRelativeTo: General.ExePath); |
| 770 | if (szRelative != sFilename->getData()) |
| 771 | { |
| 772 | // return relative path |
| 773 | StdStrBuf sTemp; sTemp.Copy(pnData: szRelative); |
| 774 | sFilename->Take(Buf2&: sTemp); |
| 775 | } |
| 776 | else |
| 777 | { |
| 778 | // not in ExePath: Is it a global path? |
| 779 | if (IsGlobalPath(szPath: sFilename->getData())) |
| 780 | { |
| 781 | // then shorten it (e.g. C:\Temp\Missions.c4f\Goldmine.c4s to Missions.c4f\Goldmine.c4s) |
| 782 | StdStrBuf sTemp; sTemp.Copy(pnData: GetC4Filename(szPath: sFilename->getData())); |
| 783 | sFilename->Take(Buf2&: sTemp); |
| 784 | } |
| 785 | } |
| 786 | } |
| 787 | |
| 788 | void C4ConfigGeneral::DefaultLanguage() |
| 789 | { |
| 790 | // No language defined: default to German or English by system language |
| 791 | if (!Language[0]) |
| 792 | { |
| 793 | if (isGermanSystem()) |
| 794 | SCopy(szSource: "DE - Deutsch" , sTarget: Language); |
| 795 | else |
| 796 | SCopy(szSource: "US - English" , sTarget: Language); |
| 797 | } |
| 798 | // No fallback sequence defined: use primary language list |
| 799 | if (!LanguageEx[0]) |
| 800 | GetLanguageSequence(strSource: Language, strTarget: LanguageEx); |
| 801 | } |
| 802 | |
| 803 | bool C4Config::Init() |
| 804 | { |
| 805 | return true; |
| 806 | } |
| 807 | |
| 808 | const char *C4Config::GetSubkeyPath(const char *strSubkey) |
| 809 | { |
| 810 | static char key[1024 + 1]; |
| 811 | #ifdef _WIN32 |
| 812 | FormatWithNull(key, "Software\\{}\\{}\\{}" , +C4CFG_Company, +C4CFG_Product, strSubkey); |
| 813 | #else |
| 814 | SCopy(szSource: strSubkey, sTarget: key, iMaxL: 1024); |
| 815 | #endif |
| 816 | return key; |
| 817 | } |
| 818 | |
| 819 | int C4ConfigGeneral::GetLanguageSequence(const char *strSource, char *strTarget) |
| 820 | { |
| 821 | // Copy a condensed list of language codes from the source list to the target string, |
| 822 | // skipping any whitespace or long language descriptions. Language sequences are |
| 823 | // comma separated. |
| 824 | int iCount = 0; |
| 825 | char strLang[2 + 1]; |
| 826 | for (int i = 0; SCopySegment(fstr: strSource, segn: i, tstr: strLang, sepa: ',', iMaxL: 2, fSkipWhitespace: true); i++) |
| 827 | if (strLang[0]) |
| 828 | { |
| 829 | if (strTarget[0]) SAppendChar(cChar: ',', szStr: strTarget); |
| 830 | SAppend(szSource: strLang, szTarget: strTarget); |
| 831 | iCount++; |
| 832 | } |
| 833 | return iCount; |
| 834 | } |
| 835 | |
| 836 | #ifdef C4ENGINE |
| 837 | |
| 838 | void C4ConfigStartup::CompileFunc(StdCompiler *pComp) |
| 839 | { |
| 840 | pComp->Value(rStruct: mkNamingAdapt(rValue&: HideMsgStartDedicated, szName: "HideMsgStartDedicated" , rDefault: false)); |
| 841 | pComp->Value(rStruct: mkNamingAdapt(rValue&: HideMsgPlrTakeOver, szName: "HideMsgPlrTakeOver" , rDefault: false)); |
| 842 | pComp->Value(rStruct: mkNamingAdapt(rValue&: HideMsgPlrNoTakeOver, szName: "HideMsgPlrNoTakeOver" , rDefault: false)); |
| 843 | pComp->Value(rStruct: mkNamingAdapt(rValue&: HideMsgNoOfficialLeague, szName: "HideMsgNoOfficialLeague" , rDefault: false)); |
| 844 | pComp->Value(rStruct: mkNamingAdapt(rValue&: HideMsgIRCDangerous, szName: "HideMsgIRCDangerous" , rDefault: false)); |
| 845 | pComp->Value(rStruct: mkNamingAdapt(rValue&: AlphabeticalSorting, szName: "AlphabeticalSorting" , rDefault: false)); |
| 846 | pComp->Value(rStruct: mkNamingAdapt(rValue&: LastPortraitFolderIdx, szName: "LastPortraitFolderIdx" , rDefault: 0)); |
| 847 | } |
| 848 | |
| 849 | #endif |
| 850 | |
| 851 | void C4Config::CompileFunc(StdCompiler *pComp) |
| 852 | { |
| 853 | pComp->Value(rStruct: mkNamingAdapt(rValue&: General, szName: "General" )); |
| 854 | #ifdef C4ENGINE |
| 855 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Controls, szName: "Controls" )); |
| 856 | for (int i = 0; i < C4ConfigMaxGamepads; ++i) |
| 857 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Gamepads[i], szName: std::format(fmt: "Gamepad{}" , args&: i).c_str())); |
| 858 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Graphics, szName: "Graphics" )); |
| 859 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Sound, szName: "Sound" )); |
| 860 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Network, szName: "Network" )); |
| 861 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Lobby, szName: "Lobby" )); |
| 862 | pComp->Value(rStruct: mkNamingAdapt(rValue&: IRC, szName: "IRC" )); |
| 863 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Developer, szName: "Developer" )); |
| 864 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Startup, szName: "Startup" )); |
| 865 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Cooldowns, szName: "Cooldowns" )); |
| 866 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Toasts, szName: "Toasts" )); |
| 867 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Logging, szName: "Logging" )); |
| 868 | #endif |
| 869 | } |
| 870 | |
| 871 | // The internal clonk charset is one of the windows charsets |
| 872 | // But to save the used one to the configuration, a string is used |
| 873 | // So we need to convert this string to the windows number for windows |
| 874 | // and RTF, and to the iconv name for iconv |
| 875 | const char *C4Config::GetCharsetCodeName(const char *const charset) noexcept |
| 876 | { |
| 877 | // Match charset name to WinGDI codes |
| 878 | if (SEqualNoCase(szStr1: charset, szStr2: "SHIFTJIS" )) return "CP932" ; |
| 879 | if (SEqualNoCase(szStr1: charset, szStr2: "HANGUL" )) return "CP949" ; |
| 880 | if (SEqualNoCase(szStr1: charset, szStr2: "JOHAB" )) return "CP1361" ; |
| 881 | if (SEqualNoCase(szStr1: charset, szStr2: "CHINESEBIG5" )) return "CP950" ; |
| 882 | if (SEqualNoCase(szStr1: charset, szStr2: "GREEK" )) return "CP1253" ; |
| 883 | if (SEqualNoCase(szStr1: charset, szStr2: "TURKISH" )) return "CP1254" ; |
| 884 | if (SEqualNoCase(szStr1: charset, szStr2: "VIETNAMESE" )) return "CP1258" ; |
| 885 | if (SEqualNoCase(szStr1: charset, szStr2: "HEBREW" )) return "CP1255" ; |
| 886 | if (SEqualNoCase(szStr1: charset, szStr2: "ARABIC" )) return "CP1256" ; |
| 887 | if (SEqualNoCase(szStr1: charset, szStr2: "BALTIC" )) return "CP1257" ; |
| 888 | if (SEqualNoCase(szStr1: charset, szStr2: "RUSSIAN" )) return "CP1251" ; |
| 889 | if (SEqualNoCase(szStr1: charset, szStr2: "THAI" )) return "CP874" ; |
| 890 | if (SEqualNoCase(szStr1: charset, szStr2: "EASTEUROPE" )) return "CP1250" ; |
| 891 | if (SEqualNoCase(szStr1: charset, szStr2: "UTF-8" )) return "UTF-8" ; |
| 892 | // Default |
| 893 | return "CP1252" ; |
| 894 | } |
| 895 | |
| 896 | std::uint8_t C4Config::GetCharsetCode(const char *const charset) noexcept |
| 897 | { |
| 898 | // Match charset name to WinGDI codes |
| 899 | if (SEqualNoCase(szStr1: charset, szStr2: "SHIFTJIS" )) return 128; // SHIFTJIS_CHARSET |
| 900 | if (SEqualNoCase(szStr1: charset, szStr2: "HANGUL" )) return 129; // HANGUL_CHARSET |
| 901 | if (SEqualNoCase(szStr1: charset, szStr2: "JOHAB" )) return 130; // JOHAB_CHARSET |
| 902 | if (SEqualNoCase(szStr1: charset, szStr2: "CHINESEBIG5" )) return 136; // CHINESEBIG5_CHARSET |
| 903 | if (SEqualNoCase(szStr1: charset, szStr2: "GREEK" )) return 161; // GREEK_CHARSET |
| 904 | if (SEqualNoCase(szStr1: charset, szStr2: "TURKISH" )) return 162; // TURKISH_CHARSET |
| 905 | if (SEqualNoCase(szStr1: charset, szStr2: "VIETNAMESE" )) return 163; // VIETNAMESE_CHARSET |
| 906 | if (SEqualNoCase(szStr1: charset, szStr2: "HEBREW" )) return 177; // HEBREW_CHARSET |
| 907 | if (SEqualNoCase(szStr1: charset, szStr2: "ARABIC" )) return 178; // ARABIC_CHARSET |
| 908 | if (SEqualNoCase(szStr1: charset, szStr2: "BALTIC" )) return 186; // BALTIC_CHARSET |
| 909 | if (SEqualNoCase(szStr1: charset, szStr2: "RUSSIAN" )) return 204; // RUSSIAN_CHARSET |
| 910 | if (SEqualNoCase(szStr1: charset, szStr2: "THAI" )) return 222; // THAI_CHARSET |
| 911 | if (SEqualNoCase(szStr1: charset, szStr2: "EASTEUROPE" )) return 238; // EASTEUROPE_CHARSET |
| 912 | if (SEqualNoCase(szStr1: charset, szStr2: "UTF-8" )) return 0; // ANSI_CHARSET - UTF8 needs special handling |
| 913 | // Default |
| 914 | return 0; // ANSI_CHARSET |
| 915 | } |
| 916 | |
| 917 | |
| 918 | std::int32_t C4Config::GetCharsetCodePage(const char *const charset) noexcept |
| 919 | { |
| 920 | // Match charset name to WinGDI codes |
| 921 | if (SEqualNoCase(szStr1: charset, szStr2: "SHIFTJIS" )) return 932; |
| 922 | if (SEqualNoCase(szStr1: charset, szStr2: "HANGUL" )) return 949; |
| 923 | if (SEqualNoCase(szStr1: charset, szStr2: "JOHAB" )) return 1361; |
| 924 | if (SEqualNoCase(szStr1: charset, szStr2: "CHINESEBIG5" )) return 950; |
| 925 | if (SEqualNoCase(szStr1: charset, szStr2: "GREEK" )) return 1253; |
| 926 | if (SEqualNoCase(szStr1: charset, szStr2: "TURKISH" )) return 1254; |
| 927 | if (SEqualNoCase(szStr1: charset, szStr2: "VIETNAMESE" )) return 1258; |
| 928 | if (SEqualNoCase(szStr1: charset, szStr2: "HEBREW" )) return 1255; |
| 929 | if (SEqualNoCase(szStr1: charset, szStr2: "ARABIC" )) return 1256; |
| 930 | if (SEqualNoCase(szStr1: charset, szStr2: "BALTIC" )) return 1257; |
| 931 | if (SEqualNoCase(szStr1: charset, szStr2: "RUSSIAN" )) return 1251; |
| 932 | if (SEqualNoCase(szStr1: charset, szStr2: "THAI" )) return 874; |
| 933 | if (SEqualNoCase(szStr1: charset, szStr2: "EASTEUROPE" )) return 1250; |
| 934 | if (SEqualNoCase(szStr1: charset, szStr2: "UTF-8" )) return -1; // shouldn't be called |
| 935 | // Default |
| 936 | return 1252; |
| 937 | } |
| 938 | |
| 939 | void C4Config::ExpandEnvironmentVariables(char *strPath, int iMaxLen) |
| 940 | { |
| 941 | #ifdef _WIN32 |
| 942 | char buf[_MAX_PATH + 1]; |
| 943 | ExpandEnvironmentStringsA(strPath, buf, _MAX_PATH); |
| 944 | SCopy(buf, strPath, iMaxLen); |
| 945 | #else // __linux__ or __APPLE___ |
| 946 | StdStrBuf home(getenv(name: "HOME" ), false); |
| 947 | char *rest; |
| 948 | if (home && (rest = const_cast<char *>(SSearch(szString: strPath, szIndex: "$HOME" ))) && (SLen(sptr: strPath) - 5 + home.getLength() <= iMaxLen)) |
| 949 | { |
| 950 | // String replace... there might be a more elegant way to do this. |
| 951 | memmove(dest: rest + home.getLength() - SLen(sptr: "$HOME" ), src: rest, n: SLen(sptr: rest) + 1); |
| 952 | strncpy(dest: rest - SLen(sptr: "$HOME" ), src: home.getData(), n: home.getLength()); |
| 953 | } |
| 954 | #endif |
| 955 | } |
| 956 | |
| 957 | #ifdef C4ENGINE |
| 958 | void C4Config::AdaptToCurrentVersion() |
| 959 | { |
| 960 | switch (General.Version) |
| 961 | { |
| 962 | #ifdef __APPLE__ |
| 963 | case 349: |
| 964 | // Mac: set Preloading to false due to it being crash-prone |
| 965 | General.Preloading = false; |
| 966 | break; |
| 967 | #endif |
| 968 | |
| 969 | case 347: |
| 970 | // reset max channels |
| 971 | Sound.MaxChannels = C4AudioSystem::MaxChannels; |
| 972 | [[fallthrough]]; |
| 973 | |
| 974 | case 346: |
| 975 | // reenable ingame music |
| 976 | Sound.RXMusic = true; |
| 977 | break; |
| 978 | |
| 979 | default: |
| 980 | break; |
| 981 | } |
| 982 | |
| 983 | if (General.Version <= 359) |
| 984 | { |
| 985 | constexpr auto migrate = [](char *const field, const char *const oldAddress, const char *const newAddress) |
| 986 | { |
| 987 | if (SEqual(szStr1: field, szStr2: oldAddress)) |
| 988 | { |
| 989 | std::strncpy(dest: field, src: newAddress, n: CFG_MaxString); |
| 990 | } |
| 991 | }; |
| 992 | |
| 993 | migrate(Network.ServerAddress, "league.clonkspot.org:80" , C4CFG_LeagueServer); |
| 994 | migrate(Network.AlternateServerAddress, "league.clonkspot.org:80" , C4CFG_FallbackServer); |
| 995 | migrate(Network.UpdateServerAddress, "update.clonkspot.org/lc/update" , C4CFG_UpdateServer); |
| 996 | migrate(Network.PuncherAddress, "clonk.de:11115" , C4ConfigNetwork::DefaultPuncherServer); |
| 997 | |
| 998 | // enable shaders |
| 999 | Graphics.Shader = true; |
| 1000 | // reenable gamma |
| 1001 | Graphics.DisableGamma = false; |
| 1002 | } |
| 1003 | |
| 1004 | General.Version = C4XVERBUILD; |
| 1005 | } |
| 1006 | #endif |
| 1007 | |