From feec973e10d565b5dd994c3c55e2d5351f807dc8 Mon Sep 17 00:00:00 2001
From: captainurist <73941350+captainurist@users.noreply.github.com>
Date: Sat, 16 Nov 2024 22:07:55 +0000
Subject: [PATCH 1/2] Write out game config nicely

---
 src/Application/GameConfig.cpp          |   5 +-
 src/Application/GameConfig.h            | 448 +++++++++++++-----------
 src/Application/Startup/GameStarter.cpp |   2 +-
 src/Library/Config/Config.cpp           |  28 +-
 src/Utility/CMakeLists.txt              |   9 +-
 src/Utility/String/Ascii.h              |   4 +
 src/Utility/String/Tests/Wrap_ut.cpp    | 132 +++++++
 src/Utility/String/Wrap.cpp             |  46 +++
 src/Utility/String/Wrap.h               |   6 +
 9 files changed, 458 insertions(+), 222 deletions(-)
 create mode 100644 src/Utility/String/Tests/Wrap_ut.cpp
 create mode 100644 src/Utility/String/Wrap.cpp
 create mode 100644 src/Utility/String/Wrap.h

diff --git a/src/Application/GameConfig.cpp b/src/Application/GameConfig.cpp
index b9401204cda3..54e6fd81fd3e 100644
--- a/src/Application/GameConfig.cpp
+++ b/src/Application/GameConfig.cpp
@@ -72,7 +72,6 @@ GameConfig::CheatCommands::CheatCommands(GameConfig *config) : ConfigSection(con
 
 void GameConfig::CheatCommands::_addCommand(int commandIndex, const std::string& defaultValue) {
     std::string name = fmt::format("command{:02}", commandIndex + 1);
-    auto item = std::make_unique<String>(this, name, defaultValue,
-        "Cheat Command. Example: 'xp add 1000|Give 1000 xp to current Character'");
-    CommandList.push_back(std::move(item));
+    auto item = std::make_unique<String>(this, name, defaultValue, fmt::format("Cheat command #{}.", commandIndex + 1));
+    _commandList.push_back(std::move(item));
 }
diff --git a/src/Application/GameConfig.h b/src/Application/GameConfig.h
index cfde91635bf1..9c1d25cad2e5 100644
--- a/src/Application/GameConfig.h
+++ b/src/Application/GameConfig.h
@@ -44,72 +44,91 @@ class GameConfig : public Config {
         Bool DisableHRTF = {this, "disable_hrtf", true, "Disable HRTF for headphones."};
     };
 
-    Audio audio{ this };
+    Audio audio{this};
 
     class Debug : public ConfigSection {
      public:
         explicit Debug(GameConfig *config) : ConfigSection(config, "debug") {}
 
         Bool AllMagic = {this, "all_magic", false,
-                         "Enable all available spells for each character in spellbook bypassing all class restrictions. "
-                         "Currently also all skills will behave like they are on GM level."};
+            "Enable all available spells for each character in spellbook bypassing all class restrictions. "
+            "Currently also all skills will behave like they are on GM level."};
 
-        Bool InfiniteFood = {this, "infinite_food", false, "Enable unlimited food, using food won't spend it."};
-        Bool InfiniteGold = {this, "infinite_gold", false, "Enable unlimited gold, paying in shops won't spend it."};
+        Bool InfiniteFood = {this, "infinite_food", false,
+            "Enable unlimited food, using food won't spend it."};
 
-        Bool LightmapDecals = {this, "lightmap_decals", false, "Draw lightmap and decals outlines."};
+        Bool InfiniteGold = {this, "infinite_gold", false,
+            "Enable unlimited gold, paying in shops won't spend it."};
 
-        Bool PortalOutlines = {this, "portal_outlines", false, "Draw BLV portal outlines."};
-        Bool Terrain = {this, "terrain", false, "Draw terrain as wireframe."};
+        Bool LightmapDecals = {this, "lightmap_decals", false,
+            "Draw lightmap and decals outlines."};
+
+        Bool PortalOutlines = {this, "portal_outlines", false,
+            "Draw BLV portal outlines."};
+
+        Bool Terrain = {this, "terrain", false,
+            "Draw terrain as wireframe."};
 
         Bool TownPortal = {this, "town_portal", false,
-                               "Make all game locations reachable via town portal spell without requiring to visit them first."};
+            "Make all game locations reachable via town portal spell without requiring to visit them first."};
 
         Bool TurboSpeed = {this, "turbo_speed", false,
-                           "Increase party movement speed by 12x. Most likely you want to use that option with "
-                           "no_damage option enabled as collision physics often will shoot you in the air."};
+            "Increase party movement speed by 12x. Most likely you want to use that option with "
+            "no_damage option enabled as collision physics often will shoot you in the air."};
 
-        Bool WizardEye = {this, "wizard_eye", false, "Activate wizard eye spell that never expires."};
+        Bool WizardEye = {this, "wizard_eye", false,
+            "Activate wizard eye spell that never expires."};
 
-        Bool ShowFPS = {this, "show_fps", false, "Show debug HUD with FPS and other debug information."};
+        Bool ShowFPS = {this, "show_fps", false,
+            "Show debug HUD with FPS and other debug information."};
 
         Bool ShowPickedFace = {this, "show_picked_face", false,
-                               "Face pointed with mouse will flash with red for buildings or green for dungeons."};
+            "Face pointed with mouse will flash with red for buildings or green for dungeons."};
 
-        Bool NoIntro = {this, "no_intro", false, "Skip intro movie on startup."};
+        Bool NoIntro = {this, "no_intro", false,
+            "Skip intro movie on startup."};
 
-        Bool NoLogo = {this, "no_logo", false, "Skip 3do logo on startup."};
+        Bool NoLogo = {this, "no_logo", false,
+            "Skip 3do logo on startup."};
 
         // TODO(captainurist): Move to [audio]?
-        Bool NoSound = {this, "no_sound", false, "Don't play any sounds. Currently in-house movies are not affected."};
+        Bool NoSound = {this, "no_sound", false,
+            "Don't play any sounds. Currently in-house movies are not affected."};
 
-        Bool NoVideo = {this, "no_video", false, "Don't play any movies."};
+        Bool NoVideo = {this, "no_video", false,
+            "Don't play any movies."};
 
-        Bool NoActors = {this, "no_actors", false, "Disable all actors."};
+        Bool NoActors = {this, "no_actors", false,
+            "Disable all actors."};
 
-        Bool NoDamage = {this, "no_damage", false, "Disable all incoming damage to party."};
+        Bool NoDamage = {this, "no_damage", false,
+            "Disable all incoming damage to party."};
 
-        Bool NoDecorations = {this, "no_decorations", false, "Disable all decorations."};
+        Bool NoDecorations = {this, "no_decorations", false,
+            "Disable all decorations."};
 
-        Bool NoMargaret = {this, "no_margareth", false, "Disable Margaret's tour messages on Emerald Island."};
+        Bool NoMargaret = {this, "no_margareth", false,
+            "Disable Margaret's tour messages on Emerald Island."};
 
         ConfigEntry<::LogLevel> LogLevel = {this, "log_level", LOG_INFO,
-                                            "Default log level. One of 'none', 'trace', 'debug', 'info', 'warning', 'error' and 'critical'."};
+            "Default log level. One of 'none', 'trace', 'debug', 'info', 'warning', 'error' and 'critical'."};
 
         // TODO(captainurist): move all Trace* options into a separate section.
 
         Int TraceFrameTimeMs = {this, "trace_frame_time_ms", 100, &ValidateFrameTime,
-                                "Number of milliseconds per frame when recording game traces."};
+            "Number of milliseconds per frame when recording game traces."};
 
         ConfigEntry<RandomEngineType> TraceRandomEngine = {this, "trace_random_engine", RANDOM_ENGINE_MERSENNE_TWISTER,
-                                                           "Random engine to use for trace recording, 'sequential' or 'mersenne_twister'."};
+            "Random engine to use for trace recording, 'sequential' or 'mersenne_twister'."};
 
-        Bool TraceNoVideo = {this, "trace_no_video", true, "Don't play movies when recording traces."};
+        Bool TraceNoVideo = {this, "trace_no_video", true,
+            "Don't play movies when recording traces."};
 
         Bool TraceNoPartyActorCollisions = {this, "trace_no_party_actor_collisions", false,
-                                            "Disable collisions between the party and monsters on the map when recording traces."};
+            "Disable collisions between the party and monsters on the map when recording traces."};
 
-        Bool FullMonsterID = { this, "full_monster_id", false, "Full monster info on popup." };
+        Bool FullMonsterID = {this, "full_monster_id", false,
+            "Full monster info on popup."};
 
      private:
         static int ValidateFrameTime(int frameTime) {
@@ -117,118 +136,130 @@ class GameConfig : public Config {
         }
     };
 
-    Debug debug{ this };
+    Debug debug{this};
 
     class Gameplay : public ConfigSection {
      public:
         explicit Gameplay(GameConfig *config): ConfigSection(config, "gameplay") {}
 
         Bool AlternativeConditionPriorities = {this, "alternative_condition_priorities", true,
-                                               "Use condition priorities from Grayface patches (e.g. Zombie has the lowest priority)."};
+            "Use condition priorities from Grayface patches (e.g. Zombie has the lowest priority)."};
 
         Int ArtifactLimit = {this, "artifact_limit", 13, &ValidateArtifactLimit,
-                             "Max number of artifacts that the game can generate as loot in any one playthrough. Use 0 for unlimited."};
+            "Max number of artifacts that the game can generate as loot in any one playthrough. Use 0 for unlimited."};
 
         Int ChestTryPlaceItems = {this, "chest_try_place_items", 2,
-                                  "Fix problems with item loss in high-level chests. "
-                                  "Use 0 for vanilla behaviour, items that don't fit will be lost. "
-                                  "Use 1 to try to place items that didn't fit every time the chest is opened again. "
-                                  "Use 2 to try to place items that didn't fit every time an item is picked up from the chest."};
+            "Fix problems with item loss in high-level chests. "
+            "Use 0 for vanilla behaviour, items that don't fit will be lost. "
+            "Use 1 to try to place items that didn't fit every time the chest is opened again. "
+            "Use 2 to try to place items that didn't fit every time an item is picked up from the chest."};
 
         Int FloorChecksEps = {this, "floor_checks_eps", 3, &ValidateFloorChecksEps, // TODO(pskelton): Move to debug
-                              "Maximum allowed slack for point-inside-a-polygon checks when calculating floor z level. "
-                              "This is needed because there are actual holes in level geometry sometimes, up to several units wide."};
+            "Maximum allowed slack for point-inside-a-polygon checks when calculating floor z level. "
+            "This is needed because there are actual holes in level geometry sometimes, up to several units wide."};
 
-        Int Gravity = {this, "gravity", 5, "Gravity strength, the higher the more gravity, 0 disables gravity completely."};
+        Int Gravity = {this, "gravity", 5,
+            "Gravity strength, the higher the more gravity, 0 disables gravity completely."};
 
         Int KeyboardInteractionDepth = {this, "keyboard_interaction_depth", 512, &ValidateInteractionDepth,
-                                        "Maximum range for item pickup / opening chests / activating levers / etc "
-                                        "with a keyboard (by pressing the interaction key, see keybindings.event_trigger)."};
+            "Maximum range for item pickup / opening chests / activating levers / etc "
+            "with a keyboard (by pressing the interaction key, see keybindings.event_trigger)."};
 
         Int MouseInteractionDepth = {this, "mouse_interaction_depth", 512, &ValidateInteractionDepth,
-                                     "Maximum range for item pickup / opening chests / activating levers / etc with a mouse."};
+            "Maximum range for item pickup / opening chests / activating levers / etc with a mouse."};
 
         Int MinRecoveryMelee = {this, "minimum_recovery_melee", 30, &ValidateRecovery,
-                                "Minimum recovery time for melee weapons. Was 30 in vanilla."};
+            "Minimum recovery time for melee weapons. Was 30 in vanilla."};
 
         Int MinRecoveryRanged = {this, "minimum_recovery_ranged", 5, &ValidateRecovery,
-                                 "Minimum recovery time for ranged weapons. Was 0 in vanilla, 5 in GrayFace patches."};
+            "Minimum recovery time for ranged weapons. Was 0 in vanilla, 5 in GrayFace patches."};
 
         Int MinRecoveryBlasters = {this, "minimum_recovery_blasters", 5, &ValidateRecovery,
-                                   "Minimum recovery time for blasters. Was 0 in vanilla, 5 in Grayface patches"};
+            "Minimum recovery time for blasters. Was 0 in vanilla, 5 in Grayface patches."};
 
         Int MaxFlightHeight = {this, "max_flight_height", 4000, &ValidateMaxFlightHeight,
-                               "Maximum height for the fly spell."};
+            "Maximum height for the fly spell."};
 
         Int MouseInfoDepthIndoor = {this, "mouse_info_depth_indoor", 16192, &ValidateInteractionDepth,
-                                    "Maximum range at which right clicking on a monster produces a popup indoors. "
-                                    "Also this is the max range for the souldrinker spell indoors."};
+            "Maximum range at which right clicking on a monster produces a popup indoors. "
+            "Also this is the max range for the souldrinker spell indoors."};
+
         Int MouseInfoDepthOutdoor = {this, "mouse_info_depth_outdoor", 12800, &ValidateInteractionDepth,
-                                     "Maximum range at which right clicking on a monster produces a popup outdoors. "
-                                     "Default value is 12800 = 25 * 512, 25 map cells. "
-                                     "Also this is the max range for the souldrinker spell outdoors."};
+            "Maximum range at which right clicking on a monster produces a popup outdoors. "
+            "Default value is 12800 = 25 * 512, 25 map cells. "
+            "Also this is the max range for the souldrinker spell outdoors."};
+
+        Int NewGameFood = {this, "new_game_food", 7,
+            "Starting food."};
+
+        Int NewGameGold = {this, "new_game_gold", 200,
+            "Starting gold."};
 
-        Int NewGameFood = {this, "new_game_food", 7, "Starting food."};
-        Int NewGameGold = {this, "new_game_gold", 200, "Starting gold."};
-        String StartingMap = String(this, "starting_map", "out01.odm", "New Game starting map.");
+        String StartingMap = String(this, "starting_map", "out01.odm",
+            "New Game starting map.");
 
-        Int PartyEyeLevel = {this, "party_eye_level", 160, "Party eye level."};
-        Int PartyHeight = {this, "party_height", 192, "Party height."};
-        Int PartyWalkSpeed = {this, "party_walk_speed", 384, "Party walk speed."};
+        Int PartyEyeLevel = {this, "party_eye_level", 160,
+            "Party eye level."};
+
+        Int PartyHeight = {this, "party_height", 192,
+            "Party height."};
+
+        Int PartyWalkSpeed = {this, "party_walk_speed", 384,
+            "Party walk speed."};
 
         Int RangedAttackDepth = {this, "ranged_attack_depth", 5120, &ValidateRangedAttackDepth,
-                                 "Max depth for ranged attacks and ranged spells. "
-                                 "It's impossible to target monsters that are further away than this value. "
-                                 "This is also the depth at which status bar tips are displayed on mouse over."};
+            "Max depth for ranged attacks and ranged spells. "
+            "It's impossible to target monsters that are further away than this value. "
+            "This is also the depth at which status bar tips are displayed on mouse over."};
 
         Int AoeDamageDistance = {this, "aoe_damage_distance", 512, &ValidateAoeDistance,
-                                 "Distance from point of impact of harmful AOE spell. "
-                                 "Characters and monsters will suffer damage if they are close to point of impact by this value."};
+            "Distance from point of impact of harmful AOE spell. "
+            "Characters and monsters will suffer damage if they are close to point of impact by this value."};
 
         Int ShrinkRayAoeDistance = {this, "shrink_ray_aoe_distance", 256, &ValidateAoeDistance,
-                                    "Distance from point of impact of Shrinking Ray cast at GM mastery. "
-                                    "Monsters will be affected by this spell if they are close to point of impact by this value."};
+            "Distance from point of impact of Shrinking Ray cast at GM mastery. "
+            "Monsters will be affected by this spell if they are close to point of impact by this value."};
 
         Bool ShowUndentifiedItem = {this, "show_unidentified_item", true,
-                                    "Show unidentified items with a green tint in inventory. "
-                                    "If not set, vanilla behavior will be used with green tint applied in shops only."};
+            "Show unidentified items with a green tint in inventory. "
+            "If not set, vanilla behavior will be used with green tint applied in shops only."};
 
         Bool TreatClubAsMace = {this, "treat_club_as_mace", false,
-                                "Treat clubs as maces. "
-                                "In vanilla clubs are using a separate hidden skill and can be equipped without learning the mace skill."};
+            "Treat clubs as maces. "
+            "In vanilla clubs are using a separate hidden skill and can be equipped without learning the mace skill."};
 
         Bool FixWaterWalkManaDrain = {this, "fix_water_walk_mana_drain", true,
-                                      "Change water walk mana drain interval to 20 minutes. "
-                                      "Spell description says that mana is drained every 20 minutes, but in vanilla, it was every 5 minutes."};
+            "Change water walk mana drain interval to 20 minutes. "
+            "Spell description says that mana is drained every 20 minutes, but in vanilla, it was every 5 minutes."};
 
         Float SpellFailureRecoveryMod = {this, "spell_failure_recovery_mod", 0.5f, &ValidateSpellFailureRecoveryMod,
-                                         "Recovery time modifier when spell casting ended in failure for the reason where spell cannot be cast at all in current context. "
-                                         "Context include situation where outdoor spell is casted indoor or targeted spell is casted with no characters on screen."};
+            "Recovery time modifier when spell casting ended in failure for the reason where spell cannot be cast at all in current context. "
+            "Context include situation where outdoor spell is casted indoor or targeted spell is casted with no characters on screen."};
 
-        String QuickSaveName = { this, "quick_saves_name", "quicksave",
-                                "What name to use to store the quick saves as." };
+        String QuickSaveName = {this, "quick_saves_name", "quicksave",
+            "What name to use to store the quick saves as."};
 
-        Int QuickSavesCount = { this, "quick_saves_count", 4, &ValidateQuickSaveCount,
-                                "How many quick saves have currently been used."
-                                "This will rotate back to 0 when 5 saves has been reached" };
+        Int QuickSavesCount = {this, "quick_saves_count", 4, &ValidateQuickSaveCount,
+            "How many quick saves have currently been used."
+            "This will rotate back to 0 when 5 saves has been reached."};
 
         Bool NoPartyActorCollisions = {this, "no_party_actor_collisions", false, // TODO(pskelton): Move to debug
-                                       "Disable collisions between the party and monsters on the map. Mainly useful for debugging and tests."};
+            "Disable collisions between the party and monsters on the map. Mainly useful for debugging and tests."};
 
-        Bool NoIndoorFallDamage = { this, "no_indoor_fall_damage", false,
-                                  "Disable fall damage for indoor maps." };
+        Bool NoIndoorFallDamage = {this, "no_indoor_fall_damage", false,
+            "Disable fall damage for indoor maps."};
 
-        Float SpawnCountMultiplier = { this, "spawn_count_multiplier", 1.0f,
-                                    "Multiplication factor for how many enemies are spawned over original." };
+        Float SpawnCountMultiplier = {this, "spawn_count_multiplier", 1.0f,
+            "Multiplication factor for how many enemies are spawned over original."};
 
-        Int MaxActors = { this, "max_actors", 500, &ValidateMaxActors,
-                        "Limit to how many total actors are possible on a map." };
+        Int MaxActors = {this, "max_actors", 500, &ValidateMaxActors,
+            "Limit to how many total actors are possible on a map."};
 
-        Int MaxActiveAIActors = { this, "max_active_ai_actors", 30, &ValidateMaxActiveAIActors,
-                                "Limit to how many actors can be in full AI state at once." };
+        Int MaxActiveAIActors = {this, "max_active_ai_actors", 30, &ValidateMaxActiveAIActors,
+            "Limit to how many actors can be in full AI state at once."};
 
-        Bool RegenStacking = { this, "regen_stacking", true,
-                                "Disable for vanilla like mode where only one item will trigger HP/SP regeneration." };
+        Bool RegenStacking = {this, "regen_stacking", true,
+            "Disable for vanilla like mode where only one item will trigger HP/SP regeneration."};
 
      private:
         static int ValidateMaxFlightHeight(int max_flight_height) {
@@ -275,7 +306,7 @@ class GameConfig : public Config {
         }
     };
 
-    Gameplay gameplay{ this };
+    Gameplay gameplay{this };
 
     class Gamepad : public ConfigSection {
      public:
@@ -300,9 +331,9 @@ class GameConfig : public Config {
         Key LookDown = {this, "look_down", PlatformKey::KEY_GAMEPAD_RIGHTSTICK_DOWN, "Look down key."};
         Key LookUp = {this, "look_up", PlatformKey::KEY_GAMEPAD_RIGHTSTICK_UP, "Look up key."};
         Key MapBook = {this, "map_book", PlatformKey::KEY_GAMEPAD_LEFT, "Open map key."};
-        Key Pass = {this, "pass", PlatformKey::KEY_GAMEPAD_GUIDE, "Pass turn key"};
-        Key Quest = {this, "quest", PlatformKey::KEY_GAMEPAD_RIGHT, "Open quest book key"};
-        Key QuickReference = {this, "quick_reference", PlatformKey::KEY_NONE, "Open quick reference menu"};
+        Key Pass = {this, "pass", PlatformKey::KEY_GAMEPAD_GUIDE, "Pass turn key."};
+        Key Quest = {this, "quest", PlatformKey::KEY_GAMEPAD_RIGHT, "Open quest book key."};
+        Key QuickReference = {this, "quick_reference", PlatformKey::KEY_NONE, "Open quick reference menu key."};
         Key Rest = {this, "rest", PlatformKey::KEY_GAMEPAD_BACK, "Rest key."};
         Key Right = {this, "right", PlatformKey::KEY_GAMEPAD_RIGHTSTICK_RIGHT, "Turn right key."};
         Key StepLeft = {this, "step_left", PlatformKey::KEY_GAMEPAD_LEFTSTICK_LEFT, "Strafe left key."};
@@ -311,54 +342,54 @@ class GameConfig : public Config {
         Key Yell = {this, "yell", PlatformKey::KEY_GAMEPAD_X, "Yell key."};
         Key ZoomIn = {this, "zoom_in", PlatformKey::KEY_NONE, "Zoom in automap key."};
         Key ZoomOut = {this, "zoom_out", PlatformKey::KEY_NONE, "Zoom out automap key."};
-        Key QuickSave = {this, "quick_save", PlatformKey::KEY_NONE, "Quick save key"};
-        Key QuickLoad = {this, "quick_load", PlatformKey::KEY_NONE, "Quick load key"};
-        Key History = {this, "history", PlatformKey::KEY_NONE, "History book key"};
-        Key Stats = {this, "stats", PlatformKey::KEY_GAMEPAD_A, "Stats tab key"};
-        Key Skills = {this, "skills", PlatformKey::KEY_GAMEPAD_X, "Skills tab key"};
-        Key Inventory = {this, "inventory", PlatformKey::KEY_GAMEPAD_Y, "Inventory tab key"};
-        Key Awards = {this, "awards", PlatformKey::KEY_GAMEPAD_L1, "Stats tab key"};
-        Key NewGame = {this, "new_game", PlatformKey::KEY_GAMEPAD_A, "New Game menu key"};
-        Key SaveGame = {this, "save_game", PlatformKey::KEY_GAMEPAD_Y, "Save Game menu key"};
-        Key LoadGame = {this, "load_game", PlatformKey::KEY_GAMEPAD_X, "Load Game menu key"};
-        Key ExitGame = {this, "exit_game", PlatformKey::KEY_GAMEPAD_B, "Exit Game key"};
-        Key ReturnToGame = {this, "return_to_game", PlatformKey::KEY_GAMEPAD_B, "Return to Game mode key"};
-        Key Controls = {this, "controls", PlatformKey::KEY_GAMEPAD_L1, "Controls change menu key"};
-        Key Options = {this, "options", PlatformKey::KEY_GAMEPAD_R1, "Options menu key"};
-        Key Credits = {this, "credits", PlatformKey::KEY_GAMEPAD_Y, "Credits menu key"};
-        Key Clear = {this, "clear", PlatformKey::KEY_GAMEPAD_Y, "Clear button in New Party Creation menu"};
-        Key Return = {this, "return", PlatformKey::KEY_GAMEPAD_A, "Ok button in New Party Creation menu"};
-        Key Minus = {this, "minus", PlatformKey::KEY_GAMEPAD_L1, "Minus button in New Party Creation menu"};
-        Key Plus = {this, "plus", PlatformKey::KEY_GAMEPAD_R1, "Plus button in New Party Creation menu"};
-        Key Yes = {this, "yes", PlatformKey::KEY_GAMEPAD_A, "Yes answer key"};
-        Key No = {this, "no", PlatformKey::KEY_GAMEPAD_B, "No answer key"};
-        Key Rest8Hours = {this, "rest_8_hours", PlatformKey::KEY_GAMEPAD_BACK, "Rest for 8 hours key in Rest menu"};
-        Key WaitTillDawn = {this, "wait_till_dawn", PlatformKey::KEY_GAMEPAD_Y, "Wait till dawn key in Rest menu"};
-        Key WaitHour = {this, "wait_hour", PlatformKey::KEY_GAMEPAD_X, "Wait hour in Rest menu"};
-        Key Wait5Minutes = {this, "wait_5_minutes", PlatformKey::KEY_GAMEPAD_A, "Wait 5 minutes in Rest menu"};
-        Key Screenshot = {this, "screenshot", PlatformKey::KEY_NONE, "Make screenshot key"};
-        Key Console = {this, "console", PlatformKey::KEY_NONE, "Show/Hide overlays"};
-        Key ToggleMouseGrab = {this, "toggle_mouse_grab", PlatformKey::KEY_NONE, "Toggle mouse grab key"};
-        Key ToggleBorderless = {this, "toggle_borderless", PlatformKey::KEY_NONE, "Toggle window borderless key"};
-        Key ToggleFullscreen = {this, "toggle_fullscreen", PlatformKey::KEY_NONE, "Toggle window fullscreen key"};
-        Key ToggleResizable = {this, "toggle_resizable", PlatformKey::KEY_NONE, "Toggle window resizable key"};
-        Key CycleFilter = {this, "cycle_filter", PlatformKey::KEY_NONE, "Cycle rescale filter modes key"};
-        Key ReloadShaders = {this, "reload_shaders", PlatformKey::KEY_NONE, "Reload shaders key"};
-        Key SelectChar1 = {this, "select_char_1", PlatformKey::KEY_NONE, "Select 1 character key"};
-        Key SelectChar2 = {this, "select_char_2", PlatformKey::KEY_NONE, "Select 2 character key"};
-        Key SelectChar3 = {this, "select_char_3", PlatformKey::KEY_NONE, "Select 3 character key"};
-        Key SelectChar4 = {this, "select_char_4", PlatformKey::KEY_NONE, "Select 4 character key"};
-        Key SelectNPC1 = {this, "select_npc_1", PlatformKey::KEY_NONE, "Select 1 hireling key"};
-        Key SelectNPC2 = {this, "select_npc_2", PlatformKey::KEY_NONE, "Select 2 hireling key"};
-        Key DialogUp = {this, "dialog_up", PlatformKey::KEY_GAMEPAD_UP, "Dialog up key"};
-        Key DialogDown = {this, "dialog_down", PlatformKey::KEY_GAMEPAD_DOWN, "Dialog down key"};
-        Key DialogLeft = {this, "dialog_left", PlatformKey::KEY_GAMEPAD_LEFT, "Dialog left key"};
-        Key DialogRight = {this, "dialog_right", PlatformKey::KEY_GAMEPAD_RIGHT, "Dialog right key"};
-        Key DialogSelect = {this, "dialog_select", PlatformKey::KEY_GAMEPAD_A, "Dialog select key"};
-        Key Escape = {this, "escape", PlatformKey::KEY_GAMEPAD_B, "Escape key"};
+        Key QuickSave = {this, "quick_save", PlatformKey::KEY_NONE, "Quick save key."};
+        Key QuickLoad = {this, "quick_load", PlatformKey::KEY_NONE, "Quick load key."};
+        Key History = {this, "history", PlatformKey::KEY_NONE, "History book key."};
+        Key Stats = {this, "stats", PlatformKey::KEY_GAMEPAD_A, "Stats tab key."};
+        Key Skills = {this, "skills", PlatformKey::KEY_GAMEPAD_X, "Skills tab key."};
+        Key Inventory = {this, "inventory", PlatformKey::KEY_GAMEPAD_Y, "Inventory tab key."};
+        Key Awards = {this, "awards", PlatformKey::KEY_GAMEPAD_L1, "Stats tab key."};
+        Key NewGame = {this, "new_game", PlatformKey::KEY_GAMEPAD_A, "New Game menu key."};
+        Key SaveGame = {this, "save_game", PlatformKey::KEY_GAMEPAD_Y, "Save Game menu key."};
+        Key LoadGame = {this, "load_game", PlatformKey::KEY_GAMEPAD_X, "Load Game menu key."};
+        Key ExitGame = {this, "exit_game", PlatformKey::KEY_GAMEPAD_B, "Exit Game key."};
+        Key ReturnToGame = {this, "return_to_game", PlatformKey::KEY_GAMEPAD_B, "Return to Game mode key."};
+        Key Controls = {this, "controls", PlatformKey::KEY_GAMEPAD_L1, "Controls change menu key."};
+        Key Options = {this, "options", PlatformKey::KEY_GAMEPAD_R1, "Options menu key."};
+        Key Credits = {this, "credits", PlatformKey::KEY_GAMEPAD_Y, "Credits menu key."};
+        Key Clear = {this, "clear", PlatformKey::KEY_GAMEPAD_Y, "Clear button in New Party Creation menu."};
+        Key Return = {this, "return", PlatformKey::KEY_GAMEPAD_A, "Ok button in New Party Creation menu."};
+        Key Minus = {this, "minus", PlatformKey::KEY_GAMEPAD_L1, "Minus button in New Party Creation menu."};
+        Key Plus = {this, "plus", PlatformKey::KEY_GAMEPAD_R1, "Plus button in New Party Creation menu."};
+        Key Yes = {this, "yes", PlatformKey::KEY_GAMEPAD_A, "Yes answer key."};
+        Key No = {this, "no", PlatformKey::KEY_GAMEPAD_B, "No answer key."};
+        Key Rest8Hours = {this, "rest_8_hours", PlatformKey::KEY_GAMEPAD_BACK, "Rest for 8 hours key in Rest menu."};
+        Key WaitTillDawn = {this, "wait_till_dawn", PlatformKey::KEY_GAMEPAD_Y, "Wait till dawn key in Rest menu."};
+        Key WaitHour = {this, "wait_hour", PlatformKey::KEY_GAMEPAD_X, "Wait one hour key in Rest menu."};
+        Key Wait5Minutes = {this, "wait_5_minutes", PlatformKey::KEY_GAMEPAD_A, "Wait 5 minutes key in Rest menu."};
+        Key Screenshot = {this, "screenshot", PlatformKey::KEY_NONE, "Make screenshot key."};
+        Key Console = {this, "console", PlatformKey::KEY_NONE, "Show/Hide overlays key."};
+        Key ToggleMouseGrab = {this, "toggle_mouse_grab", PlatformKey::KEY_NONE, "Toggle mouse grab key."};
+        Key ToggleBorderless = {this, "toggle_borderless", PlatformKey::KEY_NONE, "Toggle window borderless key."};
+        Key ToggleFullscreen = {this, "toggle_fullscreen", PlatformKey::KEY_NONE, "Toggle window fullscreen key."};
+        Key ToggleResizable = {this, "toggle_resizable", PlatformKey::KEY_NONE, "Toggle window resizable key."};
+        Key CycleFilter = {this, "cycle_filter", PlatformKey::KEY_NONE, "Cycle rescale filter modes key."};
+        Key ReloadShaders = {this, "reload_shaders", PlatformKey::KEY_NONE, "Reload shaders key."};
+        Key SelectChar1 = {this, "select_char_1", PlatformKey::KEY_NONE, "Select 1 character key."};
+        Key SelectChar2 = {this, "select_char_2", PlatformKey::KEY_NONE, "Select 2 character key."};
+        Key SelectChar3 = {this, "select_char_3", PlatformKey::KEY_NONE, "Select 3 character key."};
+        Key SelectChar4 = {this, "select_char_4", PlatformKey::KEY_NONE, "Select 4 character key."};
+        Key SelectNPC1 = {this, "select_npc_1", PlatformKey::KEY_NONE, "Select 1 hireling key."};
+        Key SelectNPC2 = {this, "select_npc_2", PlatformKey::KEY_NONE, "Select 2 hireling key."};
+        Key DialogUp = {this, "dialog_up", PlatformKey::KEY_GAMEPAD_UP, "Dialog up key."};
+        Key DialogDown = {this, "dialog_down", PlatformKey::KEY_GAMEPAD_DOWN, "Dialog down key."};
+        Key DialogLeft = {this, "dialog_left", PlatformKey::KEY_GAMEPAD_LEFT, "Dialog left key."};
+        Key DialogRight = {this, "dialog_right", PlatformKey::KEY_GAMEPAD_RIGHT, "Dialog right key."};
+        Key DialogSelect = {this, "dialog_select", PlatformKey::KEY_GAMEPAD_A, "Dialog select key."};
+        Key Escape = {this, "escape", PlatformKey::KEY_GAMEPAD_B, "Escape key."};
     };
 
-    Gamepad gamepad{ this };
+    Gamepad gamepad{this};
 
     class Graphics : public ConfigSection {
      public:
@@ -427,8 +458,8 @@ class GameConfig : public Config {
                             "Filtering method when scaling rendered framebuffer to window dimensions if they differ."
                             " 0 - disabled (render dimensions will always match window dimensions), 1 - linear filter, 2 - nearest filter"};
 
-        Float Saturation = { this, "saturation", 0.65f, "Colour saturation multiplier for textures and palettes" };
-        Float Lightness = { this, "lightness", 1.1f, "Colour lightness multiplier for textures and palettes" };
+        Float Saturation = {this, "saturation", 0.65f, "Colour saturation multiplier for textures and palettes"};
+        Float Lightness = {this, "lightness", 1.1f, "Colour lightness multiplier for textures and palettes"};
 
      private:
         static int ValidateGamma(int level) {
@@ -460,7 +491,7 @@ class GameConfig : public Config {
         }
     };
 
-    Graphics graphics{ this };
+    Graphics graphics{this};
 
     class Keybindings : public ConfigSection {
      public:
@@ -485,9 +516,9 @@ class GameConfig : public Config {
         Key LookDown = {this, "look_down", PlatformKey::KEY_DELETE, "Look down key."};
         Key LookUp = {this, "look_up", PlatformKey::KEY_PAGEDOWN, "Look up key."};
         Key MapBook = {this, "map_book", PlatformKey::KEY_M, "Open map key."};
-        Key Pass = {this, "pass", PlatformKey::KEY_B, "Pass turn key"};
-        Key Quest = {this, "quest", PlatformKey::KEY_Q, "Open quest book key"};
-        Key QuickReference = {this, "quick_reference", PlatformKey::KEY_Z, "Open quick reference menu"};
+        Key Pass = {this, "pass", PlatformKey::KEY_B, "Pass turn key."};
+        Key Quest = {this, "quest", PlatformKey::KEY_Q, "Open quest book key."};
+        Key QuickReference = {this, "quick_reference", PlatformKey::KEY_Z, "Open quick reference menu key."};
         Key Rest = {this, "rest", PlatformKey::KEY_R, "Rest key."};
         Key Right = {this, "right", PlatformKey::KEY_RIGHT, "Turn right key."};
         Key StepLeft = {this, "step_left", PlatformKey::KEY_LEFTBRACKET, "Strafe left key."};
@@ -496,54 +527,54 @@ class GameConfig : public Config {
         Key Yell = {this, "yell", PlatformKey::KEY_Y, "Yell key."};
         Key ZoomIn = {this, "zoom_in", PlatformKey::KEY_ADD, "Zoom in automap key."};
         Key ZoomOut = {this, "zoom_out", PlatformKey::KEY_SUBTRACT, "Zoom out automap key."};
-        Key QuickSave = {this, "quick_save", PlatformKey::KEY_F5, "Quick save key"};
-        Key QuickLoad = {this, "quick_load", PlatformKey::KEY_F9, "Quick load key"};
-        Key History = {this, "history", PlatformKey::KEY_H, "History book key"};
-        Key Stats = {this, "stats", PlatformKey::KEY_C, "Stats tab key"};
-        Key Skills = {this, "skills", PlatformKey::KEY_S, "Skills tab key"};
-        Key Inventory = {this, "inventory", PlatformKey::KEY_I, "Inventory tab key"};
-        Key Awards = {this, "awards", PlatformKey::KEY_A, "Stats tab key"};
-        Key NewGame = {this, "new_game", PlatformKey::KEY_N, "New Game menu key"};
-        Key SaveGame = {this, "save_game", PlatformKey::KEY_S, "Save Game menu key"};
-        Key LoadGame = {this, "load_game", PlatformKey::KEY_L, "Load Game menu key"};
-        Key ExitGame = {this, "exit_game", PlatformKey::KEY_Q, "Exit Game key"};
-        Key ReturnToGame = {this, "return_to_game", PlatformKey::KEY_R, "Return to Game mode key"};
-        Key Controls = {this, "controls", PlatformKey::KEY_C, "Controls change menu key"};
-        Key Options = {this, "options", PlatformKey::KEY_O, "Options menu key"};
-        Key Credits = {this, "credits", PlatformKey::KEY_C, "Credits menu key"};
-        Key Clear = {this, "clear", PlatformKey::KEY_C, "Clear button in New Party Creation menu"};
-        Key Return = {this, "return", PlatformKey::KEY_RETURN, "Ok button in New Party Creation menu"};
-        Key Minus = {this, "minus", PlatformKey::KEY_SUBTRACT, "Minus button in New Party Creation menu"};
-        Key Plus = {this, "plus", PlatformKey::KEY_ADD, "Plus button in New Party Creation menu"};
-        Key Yes = {this, "yes", PlatformKey::KEY_Y, "Yes answer key"};
-        Key No = {this, "no", PlatformKey::KEY_N, "No answer key"};
-        Key Rest8Hours = {this, "rest_8_hours", PlatformKey::KEY_R, "Rest for 8 hours key in Rest menu"};
-        Key WaitTillDawn = {this, "wait_till_dawn", PlatformKey::KEY_D, "Wait till dawn key in Rest menu"};
-        Key WaitHour = {this, "wait_hour", PlatformKey::KEY_H, "Wait hour in Rest menu"};
-        Key Wait5Minutes = {this, "wait_5_minutes", PlatformKey::KEY_M, "Wait 5 minutes in Rest menu"};
-        Key Screenshot = {this, "screenshot", PlatformKey::KEY_F2, "Make screenshot key"};
-        Key Console = {this, "console", PlatformKey::KEY_TILDE, "Show/Hide overlays"};
-        Key ToggleMouseGrab = {this, "toggle_mouse_grab", PlatformKey::KEY_F1, "Toggle mouse grab key"};
-        Key ToggleBorderless = {this, "toggle_borderless", PlatformKey::KEY_F3, "Toggle window borderless key"};
-        Key ToggleFullscreen = {this, "toggle_fullscreen", PlatformKey::KEY_F4, "Toggle window fullscreen key"};
-        Key ToggleResizable = {this, "toggle_resizable", PlatformKey::KEY_F6, "Toggle window resizable key"};
-        Key CycleFilter = {this, "cycle_filter", PlatformKey::KEY_F7, "Cycle rescale filter modes key"};
-        Key ReloadShaders = {this, "reload_shaders", PlatformKey::KEY_BACKSPACE, "Reload shaders key"};
-        Key SelectChar1 = {this, "select_char_1", PlatformKey::KEY_DIGIT_1, "Select 1 character key"};
-        Key SelectChar2 = {this, "select_char_2", PlatformKey::KEY_DIGIT_2, "Select 2 character key"};
-        Key SelectChar3 = {this, "select_char_3", PlatformKey::KEY_DIGIT_3, "Select 3 character key"};
-        Key SelectChar4 = {this, "select_char_4", PlatformKey::KEY_DIGIT_4, "Select 4 character key"};
-        Key SelectNPC1 = {this, "select_npc_1", PlatformKey::KEY_DIGIT_5, "Select 1 hireling key"};
-        Key SelectNPC2 = {this, "select_npc_2", PlatformKey::KEY_DIGIT_6, "Select 2 hireling key"};
-        Key DialogUp = {this, "dialog_up", PlatformKey::KEY_UP, "Dialog up key"};
-        Key DialogDown = {this, "dialog_down", PlatformKey::KEY_DOWN, "Dialog down key"};
-        Key DialogLeft = {this, "dialog_left", PlatformKey::KEY_LEFT, "Dialog left key"};
-        Key DialogRight = {this, "dialog_right", PlatformKey::KEY_RIGHT, "Dialog right key"};
-        Key DialogSelect = {this, "dialog_select", PlatformKey::KEY_RETURN, "Dialog select key"};
-        Key Escape = {this, "escape", PlatformKey::KEY_ESCAPE, "Escape key"};
+        Key QuickSave = {this, "quick_save", PlatformKey::KEY_F5, "Quick save key."};
+        Key QuickLoad = {this, "quick_load", PlatformKey::KEY_F9, "Quick load key."};
+        Key History = {this, "history", PlatformKey::KEY_H, "History book key."};
+        Key Stats = {this, "stats", PlatformKey::KEY_C, "Stats tab key."};
+        Key Skills = {this, "skills", PlatformKey::KEY_S, "Skills tab key."};
+        Key Inventory = {this, "inventory", PlatformKey::KEY_I, "Inventory tab key."};
+        Key Awards = {this, "awards", PlatformKey::KEY_A, "Stats tab key."};
+        Key NewGame = {this, "new_game", PlatformKey::KEY_N, "New Game menu key."};
+        Key SaveGame = {this, "save_game", PlatformKey::KEY_S, "Save Game menu key."};
+        Key LoadGame = {this, "load_game", PlatformKey::KEY_L, "Load Game menu key."};
+        Key ExitGame = {this, "exit_game", PlatformKey::KEY_Q, "Exit Game key."};
+        Key ReturnToGame = {this, "return_to_game", PlatformKey::KEY_R, "Return to Game mode key."};
+        Key Controls = {this, "controls", PlatformKey::KEY_C, "Controls change menu key."};
+        Key Options = {this, "options", PlatformKey::KEY_O, "Options menu key."};
+        Key Credits = {this, "credits", PlatformKey::KEY_C, "Credits menu key."};
+        Key Clear = {this, "clear", PlatformKey::KEY_C, "Clear button in New Party Creation menu."};
+        Key Return = {this, "return", PlatformKey::KEY_RETURN, "Ok button in New Party Creation menu."};
+        Key Minus = {this, "minus", PlatformKey::KEY_SUBTRACT, "Minus button in New Party Creation menu."};
+        Key Plus = {this, "plus", PlatformKey::KEY_ADD, "Plus button in New Party Creation menu."};
+        Key Yes = {this, "yes", PlatformKey::KEY_Y, "Yes answer key."};
+        Key No = {this, "no", PlatformKey::KEY_N, "No answer key."};
+        Key Rest8Hours = {this, "rest_8_hours", PlatformKey::KEY_R, "Rest for 8 hours key in Rest menu."};
+        Key WaitTillDawn = {this, "wait_till_dawn", PlatformKey::KEY_D, "Wait till dawn key in Rest menu."};
+        Key WaitHour = {this, "wait_hour", PlatformKey::KEY_H, "Wait one hour key in Rest menu."};
+        Key Wait5Minutes = {this, "wait_5_minutes", PlatformKey::KEY_M, "Wait 5 minutes key in Rest menu."};
+        Key Screenshot = {this, "screenshot", PlatformKey::KEY_F2, "Make screenshot key."};
+        Key Console = {this, "console", PlatformKey::KEY_TILDE, "Show/Hide overlays key."};
+        Key ToggleMouseGrab = {this, "toggle_mouse_grab", PlatformKey::KEY_F1, "Toggle mouse grab key."};
+        Key ToggleBorderless = {this, "toggle_borderless", PlatformKey::KEY_F3, "Toggle window borderless key."};
+        Key ToggleFullscreen = {this, "toggle_fullscreen", PlatformKey::KEY_F4, "Toggle window fullscreen key."};
+        Key ToggleResizable = {this, "toggle_resizable", PlatformKey::KEY_F6, "Toggle window resizable key."};
+        Key CycleFilter = {this, "cycle_filter", PlatformKey::KEY_F7, "Cycle rescale filter modes key."};
+        Key ReloadShaders = {this, "reload_shaders", PlatformKey::KEY_BACKSPACE, "Reload shaders key."};
+        Key SelectChar1 = {this, "select_char_1", PlatformKey::KEY_DIGIT_1, "Select 1 character key."};
+        Key SelectChar2 = {this, "select_char_2", PlatformKey::KEY_DIGIT_2, "Select 2 character key."};
+        Key SelectChar3 = {this, "select_char_3", PlatformKey::KEY_DIGIT_3, "Select 3 character key."};
+        Key SelectChar4 = {this, "select_char_4", PlatformKey::KEY_DIGIT_4, "Select 4 character key."};
+        Key SelectNPC1 = {this, "select_npc_1", PlatformKey::KEY_DIGIT_5, "Select 1 hireling key."};
+        Key SelectNPC2 = {this, "select_npc_2", PlatformKey::KEY_DIGIT_6, "Select 2 hireling key."};
+        Key DialogUp = {this, "dialog_up", PlatformKey::KEY_UP, "Dialog up key."};
+        Key DialogDown = {this, "dialog_down", PlatformKey::KEY_DOWN, "Dialog down key."};
+        Key DialogLeft = {this, "dialog_left", PlatformKey::KEY_LEFT, "Dialog left key."};
+        Key DialogRight = {this, "dialog_right", PlatformKey::KEY_RIGHT, "Dialog right key."};
+        Key DialogSelect = {this, "dialog_select", PlatformKey::KEY_RETURN, "Dialog select key."};
+        Key Escape = {this, "escape", PlatformKey::KEY_ESCAPE, "Escape key."};
     };
 
-    Keybindings keybindings{ this };
+    Keybindings keybindings{this};
 
     class Settings : public ConfigSection {
      public:
@@ -585,34 +616,42 @@ class GameConfig : public Config {
         }
     };
 
-    Settings settings{ this };
+    Settings settings{this};
 
     class Window : public ConfigSection {
      public:
         explicit Window(GameConfig *config): ConfigSection(config, "window") {}
 
-        String Title = String(this, "title", "OpenEnroth", &ValidateTitle, "Game window title.");
+        String Title = String(this, "title", "OpenEnroth", &ValidateTitle,
+            "Game window title.");
 
         Int Display = {this, "display", 0,
-                       "Display number as exposed by SDL. "
-                       "Order is platform-specific, e.g. on windows 0 is main display"};
+            "Display number as exposed by SDL. "
+            "Order is platform-specific, e.g. on windows 0 is main display."};
 
         ConfigEntry<PlatformWindowMode> Mode = {this, "mode", ConfigWindowMode,
-                                                "Window mode, one of 'windowed', 'borderless', 'fullscreen' or 'fullscreen_borderless'."};
+            "Window mode, one of 'windowed', 'borderless', 'fullscreen' or 'fullscreen_borderless'."};
 
         Int PositionX = {this, "position_x", -1, &ValidatePosition,
-                         "Game window x position in display coordinates. Use -1 for centered."};
+            "Game window x position in display coordinates. Use -1 for centered."};
+
         Int PositionY = {this, "position_y", -1, &ValidatePosition,
-                         "Game window y position in display coordinates. Use -1 for centered."};
+            "Game window y position in display coordinates. Use -1 for centered."};
 
-        Int Width = {this, "width", 640, &ValidateWidth, "Window width."};
-        Int Height = {this, "height", 480, &ValidateHeight, "Window height."};
+        Int Width = {this, "width", 640, &ValidateWidth,
+            "Window width."};
 
-        Bool MouseGrab = {this, "mouse_grab", false, "Restrict mouse movement to game window when it's in focus."};
+        Int Height = {this, "height", 480, &ValidateHeight,
+            "Window height."};
 
-        Bool Resizable = {this, "resizable", true, "Make window resizable by user or not."};
+        Bool MouseGrab = {this, "mouse_grab", false,
+            "Restrict mouse movement to game window when it's in focus."};
 
-        Bool ReloadTex = {this, "reload_tex", true, "Reload texture assets if window is reinitialised."};
+        Bool Resizable = {this, "resizable", true,
+            "Make window resizable by user or not."};
+
+        Bool ReloadTex = {this, "reload_tex", true,
+            "Reload texture assets if window is reinitialised."};
 
      private:
          static std::string ValidateTitle(std::string title) {
@@ -641,8 +680,7 @@ class GameConfig : public Config {
         }
     };
 
-    Window window{ this };
-
+    Window window{this};
 
     class CheatCommands : public ConfigSection {
      public:
@@ -650,8 +688,8 @@ class GameConfig : public Config {
 
      private:
         void _addCommand(int commandIndex, const std::string& defaultValue);
-        std::vector<std::unique_ptr<String>> CommandList;
+        std::vector<std::unique_ptr<String>> _commandList;
     };
 
-    CheatCommands commands{ this };
+    CheatCommands commands{this};
 };
diff --git a/src/Application/Startup/GameStarter.cpp b/src/Application/Startup/GameStarter.cpp
index 3ae485718b50..65429f1ada34 100644
--- a/src/Application/Startup/GameStarter.cpp
+++ b/src/Application/Startup/GameStarter.cpp
@@ -276,7 +276,7 @@ void GameStarter::run() {
 
         _application->component<GameWindowHandler>()->UpdateConfigFromWindow(_config.get());
         _config->save(ufs->openForWriting(configName).get());
-        logger->info("Configuration file '{}' saved!", configName);
+        logger->info("Configuration file '{}' saved!", ufs->displayPath(configName));
     } catch (const std::exception &e) {
         // Log the exception so that it goes to all registered loggers.
         logger->critical("Terminated with exception: {}", e.what());
diff --git a/src/Library/Config/Config.cpp b/src/Library/Config/Config.cpp
index 5c0d21ff90af..7d1cdfc1be1a 100644
--- a/src/Library/Config/Config.cpp
+++ b/src/Library/Config/Config.cpp
@@ -10,6 +10,8 @@
 #include "Utility/Streams/FileInputStream.h"
 #include "Utility/Streams/FileOutputStream.h"
 #include "Utility/MapAccess.h"
+#include "Utility/String/Format.h"
+#include "Utility/String/Wrap.h"
 
 void Config::load(std::string_view path) {
     FileInputStream stream(path); // Will throw if file doesn't exist.
@@ -26,6 +28,7 @@ void Config::load(InputStream *stream) {
     std::istringstream stdStream(stream->readAll());
 
     ini::IniFile ini;
+    ini.setCommentChar(';'); // Use ini comment char, not '#'.
     ini.decode(stdStream); // This can throw.
 
     for (const auto &[sectionName, iniSection] : ini)
@@ -36,16 +39,21 @@ void Config::load(InputStream *stream) {
 }
 
 void Config::save(OutputStream *stream) const {
-    ini::IniFile ini;
-    for (ConfigSection *section : sections())
-        for (AnyConfigEntry *entry : section->entries())
-            ini[section->name()][entry->name()] = entry->string();
-
-    std::ostringstream stdStream;
-    ini.encode(stdStream); // This can throw.
-
-    // Same here - we'd rather handle FS errors on our side.
-    stream->write(stdStream.str());
+    // ini::IniFile doesn't support comments so we just write things out ourselves.
+    for (ConfigSection *section : sections()) {
+        stream->write(fmt::format("[{}]\n", section->name()));
+
+        for (AnyConfigEntry *entry : section->entries()) {
+            if (!entry->description().empty())
+                for (const std::string &line : wrapText(entry->description(), 78))
+                    stream->write(fmt::format("; {}\n", line));
+            stream->write(fmt::format("; Default is '{}'.\n", entry->defaultString()));
+            stream->write(fmt::format("{} = {}\n", entry->name(), entry->string()));
+            stream->write("\n");
+        }
+
+        stream->write("\n");
+    }
 }
 
 void Config::reset() {
diff --git a/src/Utility/CMakeLists.txt b/src/Utility/CMakeLists.txt
index 212108273416..e2aae1f1d2e3 100644
--- a/src/Utility/CMakeLists.txt
+++ b/src/Utility/CMakeLists.txt
@@ -14,7 +14,8 @@ set(UTILITY_SOURCES
         String/Ascii.cpp
         String/Split.cpp
         String/Transformations.cpp
-        UnicodeCrt.cpp)
+        UnicodeCrt.cpp
+        String/Wrap.cpp)
 
 set(UTILITY_HEADERS
         Embedded.h
@@ -46,7 +47,8 @@ set(UTILITY_HEADERS
         Unaligned.h
         UnicodeCrt.h
         SmallVector.h
-        ScopedRollback.h)
+        ScopedRollback.h
+        String/Wrap.h)
 
 if(OE_BUILD_PLATFORM STREQUAL "windows")
     list(APPEND UTILITY_HEADERS Win/Unicode.h)
@@ -78,7 +80,8 @@ if(OE_BUILD_TESTS)
             String/Tests/TransparentFunctors_ut.cpp
             String/Tests/Ascii_ut.cpp
             String/Tests/Split_ut.cpp
-            String/Tests/Join_ut.cpp)
+            String/Tests/Join_ut.cpp
+            String/Tests/Wrap_ut.cpp)
 
     if(OE_BUILD_PLATFORM STREQUAL "windows")
         list(APPEND TEST_UTILITY_SOURCES Win/Tests/Unicode_ut.cpp)
diff --git a/src/Utility/String/Ascii.h b/src/Utility/String/Ascii.h
index f57fb6a156ca..b769b1b0b894 100644
--- a/src/Utility/String/Ascii.h
+++ b/src/Utility/String/Ascii.h
@@ -21,6 +21,10 @@ inline char toUpper(char c) {
     return isLower(c) ? c - 'a' + 'A' : c;
 }
 
+inline bool isSpace(char c) {
+    return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
+}
+
 std::string toLower(std::string_view text);
 std::string toUpper(std::string_view text);
 
diff --git a/src/Utility/String/Tests/Wrap_ut.cpp b/src/Utility/String/Tests/Wrap_ut.cpp
new file mode 100644
index 000000000000..4b756dce3ff7
--- /dev/null
+++ b/src/Utility/String/Tests/Wrap_ut.cpp
@@ -0,0 +1,132 @@
+#include <vector>
+#include <string>
+
+#include "Testing/Unit/UnitTest.h"
+
+#include "Utility/String/Wrap.h"
+
+UNIT_TEST(StringWrap, BasicSentence) {
+    std::string text = "This is a simple test";
+    size_t width = 10;
+    std::vector<std::string> expected = {"This is a", "simple", "test"};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, EmptyString) {
+    std::string text = "";
+    size_t width = 10;
+    std::vector<std::string> expected = {""};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, SingleShortWord) {
+    std::string text = "Hello";
+    size_t width = 10;
+    std::vector<std::string> expected = {"Hello"};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, SingleLongWord) {
+    std::string text = "Supercalifragilisticexpialidocious";
+    size_t width = 10;
+    std::vector<std::string> expected = {"Supercalif", "ragilistic", "expialidoc", "ious"};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, WordsLongerThanWidth) {
+    std::string text = "Hello Supercalifragilisticexpialidocious World";
+    size_t width = 10;
+    std::vector<std::string> expected = {"Hello", "Supercalif", "ragilistic", "expialidoc", "ious World"};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, ExactFitWidth) {
+    std::string text = "1234567890 12345";
+    size_t width = 10;
+    std::vector<std::string> expected = {"1234567890", "12345"};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, LeadingTrailingSpaces) {
+    std::string text = "   Hello world  ";
+    size_t width = 10;
+    std::vector<std::string> expected = {"   Hello", "world  "};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, WidthGreaterThanTextLength) {
+    std::string text = "Hello world";
+    size_t width = 20;
+    std::vector<std::string> expected = {"Hello world"};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, WidthEqualToTextLength) {
+    std::string text = "Hello world";
+    size_t width = 11;
+    std::vector<std::string> expected = {"Hello world"};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, WidthOne) {
+    std::string text = "AB CD EFGH";
+    size_t width = 1;
+    std::vector<std::string> expected = {"A", "B", "C", "D", "E", "F", "G", "H"};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, AllSpaces) {
+    std::string text = "     ";
+    size_t width = 10;
+    std::vector<std::string> expected = {"     "};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, ForcedLineBreak) {
+    std::string text = "Hello\nWorld";
+    size_t width = 10;
+    std::vector<std::string> expected = {"Hello", "World"};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, ForcedLineBreakWrap) {
+    std::string text = "This is a\ntest with forced\nline breaks.";
+    size_t width = 15;
+    std::vector<std::string> expected = {"This is a", "test with", "forced", "line breaks."};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, ForcedLineBreakRepeated) {
+    std::string text = "Hello\n\nWorld";
+    size_t width = 10;
+    std::vector<std::string> expected = {"Hello", "", "World"};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, NewlineWithLongWordExceedingWidth) {
+    std::string text = "Hello\nSupercalifragilisticexpialidocious\nWorld";
+    size_t width = 10;
+    std::vector<std::string> expected = {"Hello", "Supercalif", "ragilistic", "expialidoc", "ious", "World"};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, NewlineAtEndOfText) {
+    std::string text = "This is a test\n";
+    size_t width = 15;
+    std::vector<std::string> expected = {"This is a test", ""};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, NewlineAtStartOfText) {
+    std::string text = "\nThis is a test";
+    size_t width = 10;
+    std::vector<std::string> expected = {"", "This is a", "test"};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
+
+UNIT_TEST(StringWrap, MultipleNewlinesOnly) {
+    std::string text = "\n\n\n";
+    size_t width = 10;
+    std::vector<std::string> expected = {"", "", "", ""};
+    EXPECT_EQ(wrapText(text, width), expected);
+}
diff --git a/src/Utility/String/Wrap.cpp b/src/Utility/String/Wrap.cpp
new file mode 100644
index 000000000000..49847b63a25e
--- /dev/null
+++ b/src/Utility/String/Wrap.cpp
@@ -0,0 +1,46 @@
+#include "Wrap.h"
+
+#include <string>
+#include <vector>
+
+#include "Ascii.h"
+
+std::vector<std::string> wrapText(std::string_view text, size_t size) {
+    std::vector<std::string> result;
+
+    size_t breakablePos = 0;
+    size_t lineLength = 0;
+    size_t lineStart = 0;
+    for (size_t i = 0; i <= text.size(); ++i) {
+        lineLength++;
+
+        char c = i == text.size() ? '\n' : text[i]; // Simple hack so that we don't have to do weird ifs after the loop.
+        if (ascii::isSpace(c))
+            breakablePos = i;
+
+        size_t breakPos = static_cast<size_t>(-1);
+        size_t startPos = 0;
+        if (lineLength > size && breakablePos != 0) {
+            /* OK to break! */
+            breakPos = breakablePos;
+            startPos = breakablePos + 1;
+        } else if (lineLength > size && breakablePos == 0) {
+            /* Nowhere to break, so break mid-word. */
+            breakPos = i;
+            startPos = i;
+        } else if (c == '\n') {
+            /* Forced break. */
+            breakPos = i;
+            startPos = i + 1;
+        }
+
+        if (breakPos != static_cast<size_t>(-1)) {
+            result.emplace_back(text.substr(lineStart, breakPos - lineStart));
+            breakablePos = 0;
+            lineLength = i - startPos + 1;
+            lineStart = startPos;
+        }
+    }
+
+    return result;
+}
diff --git a/src/Utility/String/Wrap.h b/src/Utility/String/Wrap.h
new file mode 100644
index 000000000000..6ed09990f135
--- /dev/null
+++ b/src/Utility/String/Wrap.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+std::vector<std::string> wrapText(std::string_view text, size_t size);

From 60a0de3583fa9db25c5ed1630a2a3da5d2ef6499 Mon Sep 17 00:00:00 2001
From: captainurist <73941350+captainurist@users.noreply.github.com>
Date: Tue, 19 Nov 2024 10:57:42 +0000
Subject: [PATCH 2/2] Don't migrate config

---
 src/Application/Startup/GameStarter.cpp | 9 ---------
 src/Library/Config/Config.cpp           | 1 -
 2 files changed, 10 deletions(-)

diff --git a/src/Application/Startup/GameStarter.cpp b/src/Application/Startup/GameStarter.cpp
index 65429f1ada34..c0aba5a38a55 100644
--- a/src/Application/Startup/GameStarter.cpp
+++ b/src/Application/Startup/GameStarter.cpp
@@ -259,15 +259,6 @@ void GameStarter::migrateUserData() {
             }
         }
     }
-
-    if (ufs->exists(configName)) {
-        logger->info("    Target config exists, skipping config migration.");
-    } else if (!dfs->exists(configName)) {
-        logger->info("    No config file to migrate.");
-    } else {
-        ufs->write(configName, dfs->read(configName));
-        logger->info("    Copied '{}'.", configName);
-    }
 }
 
 void GameStarter::run() {
diff --git a/src/Library/Config/Config.cpp b/src/Library/Config/Config.cpp
index 7d1cdfc1be1a..29bce0fc810c 100644
--- a/src/Library/Config/Config.cpp
+++ b/src/Library/Config/Config.cpp
@@ -24,7 +24,6 @@ void Config::save(std::string_view path) const {
 }
 
 void Config::load(InputStream *stream) {
-    // We'd rather handle FS errors on our side.
     std::istringstream stdStream(stream->readAll());
 
     ini::IniFile ini;