From 52d008d1cd85e5d133afafb040a863098c2fdb68 Mon Sep 17 00:00:00 2001 From: Daniel Walder Date: Mon, 30 Dec 2024 15:08:48 +1000 Subject: [PATCH] Mostly complete support of Worldcraft/VHE3 configuration files --- .../RegistryUtil.cs | 3 +- .../Resources/Worldcraft/GameCfg-wc20.wc | Bin 0 -> 1584 bytes .../Resources/Worldcraft/GameCfg-wc33.wc | Bin 0 -> 2096 bytes .../Sledge.Formats.Configuration.Tests.csproj | 10 + .../TestWorldcraftConfiguration.cs | 220 ++++++++++++++++-- .../Sledge.Formats.Configuration.csproj | 4 + .../Worldcraft/MapType.cs | 8 +- .../Worldcraft/TextureFormat.cs | 6 +- .../Worldcraft2DViewsConfiguration.cs | 2 +- .../Worldcraft3DViewsConfiguration.cs | 4 +- .../Worldcraft/WorldcraftConfiguration.cs | 80 ++++++- .../WorldcraftConfigurationLoadSettings.cs | 12 +- .../Worldcraft/WorldcraftGameConfiguration.cs | 24 +- ...orldcraftGameConfigurationBuildPrograms.cs | 12 +- .../WorldcraftGameConfigurationFile.cs | 141 +++++++++++ .../Worldcraft/WorldcraftRegistryInfo.cs | 36 +++ 16 files changed, 509 insertions(+), 53 deletions(-) create mode 100644 Sledge.Formats.Configuration.Tests/Resources/Worldcraft/GameCfg-wc20.wc create mode 100644 Sledge.Formats.Configuration.Tests/Resources/Worldcraft/GameCfg-wc33.wc create mode 100644 Sledge.Formats.Configuration/Worldcraft/WorldcraftGameConfigurationFile.cs diff --git a/Sledge.Formats.Configuration.Tests/RegistryUtil.cs b/Sledge.Formats.Configuration.Tests/RegistryUtil.cs index fda99e3..6667679 100644 --- a/Sledge.Formats.Configuration.Tests/RegistryUtil.cs +++ b/Sledge.Formats.Configuration.Tests/RegistryUtil.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using Microsoft.Win32; +using Microsoft.Win32; using Sledge.Formats.Configuration.Registry; namespace Sledge.Formats.Configuration.Tests; diff --git a/Sledge.Formats.Configuration.Tests/Resources/Worldcraft/GameCfg-wc20.wc b/Sledge.Formats.Configuration.Tests/Resources/Worldcraft/GameCfg-wc20.wc new file mode 100644 index 0000000000000000000000000000000000000000..2b15ee38c2a305ebf372b9cc45c251ab6cfb265f GIT binary patch literal 1584 zcmZ=y%uQ8r&d*EBOfM};EXmBzD^_sJ%t__tl43|pTV~J5z`#(FT3iC81`L4Qzy!q1 zK%6{4t_X8xWcUnpKng>Jg+Yahg+f|>eu+X(esXqd5t5Vq^7C|)ic%A^6O(dMbrdpl zbMwm}%)I=fk__GA{L+#Ph5Vw-)VvaKXd~$*idmjtl#^0ikXW8alya&oN(A~S-q_Tf z>IM^OU@DR7Xr(L-hryoikn{mg9OR^r)WqTv-Ev?~!=F0RaoA5U;Y4~_gu|XB9OCp6 z&LGnw8*fPZFf*yJGy@hP3b~2N8JT&hMU@K4iRtiEO|blMwu(thPl-v?1Ck7U3=9Q} z+#!V{&|P2TJQ)-|NHRz-@L)L7Doc@xF(+4L0W1#-0uh+r0LHu8qm{UgIatG#Bp#5p-DKUw9K$5}G z9a1p>CG)#I7&g3m2m=U%%!mNWPZ5I9a_k-q3P5o`CRb4X1F}D9fbGu!R>?WQ8X4>ip!4bGe*l$U BueksK literal 0 HcmV?d00001 diff --git a/Sledge.Formats.Configuration.Tests/Sledge.Formats.Configuration.Tests.csproj b/Sledge.Formats.Configuration.Tests/Sledge.Formats.Configuration.Tests.csproj index 378f019..beda79a 100644 --- a/Sledge.Formats.Configuration.Tests/Sledge.Formats.Configuration.Tests.csproj +++ b/Sledge.Formats.Configuration.Tests/Sledge.Formats.Configuration.Tests.csproj @@ -8,6 +8,16 @@ true + + + + + + + + + + diff --git a/Sledge.Formats.Configuration.Tests/TestWorldcraftConfiguration.cs b/Sledge.Formats.Configuration.Tests/TestWorldcraftConfiguration.cs index 2a49489..575aa71 100644 --- a/Sledge.Formats.Configuration.Tests/TestWorldcraftConfiguration.cs +++ b/Sledge.Formats.Configuration.Tests/TestWorldcraftConfiguration.cs @@ -1,8 +1,10 @@ -using Microsoft.Win32; +using System.Drawing; +using Microsoft.Win32; using Sledge.Formats.Configuration.Worldcraft; namespace Sledge.Formats.Configuration.Tests; +#pragma warning disable CS0612 // Type or member is obsolete #pragma warning disable CA1416 [TestClass] @@ -26,10 +28,15 @@ public void TestLoadSettings() var config = WorldcraftConfiguration.LoadFromRegistry(new WorldcraftConfigurationLoadSettings { LoadGameConfigurations = false, - AutodetectRegistryLocation = false, RegistryLocation = reg.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default).OpenSubKey(@"Software\Valve\Worldcraft") }); + Assert.IsNotNull(config.General); + Assert.IsNotNull(config.Views2D); + Assert.IsNotNull(config.Views3D); + Assert.IsNotNull(config.TextureDirectories); + Assert.IsNull(config.GameConfigurations); + Assert.AreEqual(@"C:\Users\WDAGUtilityAccount\Desktop\Worldcraft 3.3", config.General.InstallDirectory, StringComparer.InvariantCultureIgnoreCase); Assert.AreEqual(true, config.General.UseIndependentWindowConfigurations); Assert.AreEqual(false, config.General.LoadDefaultWindowPositionsWithMaps); @@ -37,16 +44,203 @@ public void TestLoadSettings() Assert.AreEqual(true, config.General.AllowGroupingWhileIgnoreGroupsChecked); Assert.AreEqual(false, config.General.StretchArchesToFitOriginalBoundingRectangle); + Assert.AreEqual(true, config.Views2D.CrosshairCursor); + Assert.AreEqual(true, config.Views2D.DefaultTo15DegreeRotations); + Assert.AreEqual(false, config.Views2D.DisplayScrollbars); + Assert.AreEqual(false, config.Views2D.DrawVertices); + Assert.AreEqual(false, config.Views2D.WhiteOnBlackColorScheme); + Assert.AreEqual(false, config.Views2D.KeepGroupWhenCloneDragging); + Assert.AreEqual(true, config.Views2D.CenterOnCameraAfterMovement); + Assert.AreEqual(false, config.Views2D.UseVisgroupColorsForObjectLines); + Assert.AreEqual(true, config.Views2D.ArrowKeysNudgeSelectedObject); + Assert.AreEqual(true, config.Views2D.ReorientPrimitivesOnCreation); + Assert.AreEqual(true, config.Views2D.AutomaticInfiniteSelection); + Assert.AreEqual(true, config.Views2D.SelectionBoxSelectsByCenterHandlesOnly); + Assert.AreEqual(0x20, config.Views2D.Grid.Size); + Assert.AreEqual(0x42, config.Views2D.Grid.Intensity); + Assert.AreEqual(true, config.Views2D.Grid.HighlightEvery64Units); + Assert.AreEqual(0x00, config.Views2D.Grid.HighlightEveryNGridLines); + Assert.AreEqual(false, config.Views2D.Grid.HideGridSmallerThan4Pixels); + Assert.AreEqual(false, config.Views2D.Grid.HighlightEvery1024Units); + Assert.AreEqual(false, config.Views2D.Grid.DottedGrid); + + Assert.AreEqual(0x1ef9, config.Views3D.BackClippingPlane); + Assert.AreEqual(false, config.Views3D.FilterTextures); + Assert.AreEqual(true, config.Views3D.AnimateModels); + Assert.AreEqual(0x190, config.Views3D.ModelRenderDistance); + Assert.AreEqual(false, config.Views3D.UseMouselookNavigation); + Assert.AreEqual(true, config.Views3D.ReverseMouseYAxis); + Assert.AreEqual(0x0b0d, config.Views3D.ForwardSpeed); + Assert.AreEqual(0x0783 / 1000m, config.Views3D.TimeToTopSpeed); + Assert.AreEqual(true, config.Views3D.ReverseSelectionOrder); + Assert.AreEqual(Color.Black.ToArgb(), config.Views3D.BackgroundColor.ToArgb()); + CollectionAssert.AreEqual(new[] { @"c:\users\wdagutilityaccount\desktop\zhlt.wad" }, config.TextureDirectories, StringComparer.InvariantCultureIgnoreCase); } + [TestMethod] + public void TestReadGameConfigFile20() + { + using var file = typeof(TestWorldcraftConfiguration).Assembly.GetManifestResourceStream("Sledge.Formats.Configuration.Tests.Resources.Worldcraft.GameCfg-wc20.wc"); + var configs = new WorldcraftGameConfigurationFile(file).Configurations; + + Assert.AreEqual(configs.Count, 1); + var config = configs[0]; + + Assert.AreEqual("test", config.Name); + CollectionAssert.AreEqual(new[] { @"C:\fgd\a.fgd" }, config.GameDataFiles); + Assert.AreEqual(TextureFormat.Wad3, config.TextureFormat); + Assert.AreEqual(MapType.HalfLife, config.MapType); + Assert.AreEqual("ammo_357", config.DefaultPointEntityClass); + Assert.AreEqual("worldspawn", config.DefaultSolidEntityClass); + Assert.AreEqual("a", config.GameExecutableDirectory); + Assert.AreEqual("", config.ModDirectory); + Assert.AreEqual("", config.GameDirectory); + Assert.AreEqual("b", config.RmfDirectory); + Assert.AreEqual("c", config.PaletteFile); + Assert.AreEqual("d", config.BuildPrograms.GameExecutable); + Assert.AreEqual("", config.BuildPrograms.CsgExecutable); + Assert.AreEqual("e", config.BuildPrograms.BspExecutable); + Assert.AreEqual("g", config.BuildPrograms.VisExecutable); + Assert.AreEqual("f", config.BuildPrograms.RadExecutable); + Assert.AreEqual("h", config.BuildPrograms.BspDirectory); + } + + [TestMethod] + public void TestReadGameConfigFile33() + { + using var file = typeof(TestWorldcraftConfiguration).Assembly.GetManifestResourceStream("Sledge.Formats.Configuration.Tests.Resources.Worldcraft.GameCfg-wc33.wc"); + var configs = new WorldcraftGameConfigurationFile(file).Configurations; + + Assert.AreEqual(configs.Count, 1); + var config = configs[0]; + + Assert.AreEqual("Half-Life", config.Name); + CollectionAssert.AreEqual(new[] { @"C:\fgd\a.fgd", @"C:\fgd\b.fgd", @"C:\fgd\halflife.fgd", }, config.GameDataFiles); + Assert.AreEqual(TextureFormat.Wad3, config.TextureFormat); + Assert.AreEqual(MapType.HalfLife, config.MapType); + Assert.AreEqual("light", config.DefaultPointEntityClass); + Assert.AreEqual("func_door", config.DefaultSolidEntityClass); + Assert.AreEqual(@"C:\hl", config.GameExecutableDirectory); + Assert.AreEqual(@"C:\hl\cstrike", config.ModDirectory); + Assert.AreEqual(@"C:\hl\valve", config.GameDirectory); + Assert.AreEqual(@"C:\rmf", config.RmfDirectory); + Assert.AreEqual("", config.PaletteFile); + Assert.AreEqual(@"C:\hl\hl.exe", config.BuildPrograms.GameExecutable); + Assert.AreEqual(@"C:\hl\build\csg.exe", config.BuildPrograms.CsgExecutable); + Assert.AreEqual(@"C:\hl\build\bsp.exe", config.BuildPrograms.BspExecutable); + Assert.AreEqual(@"C:\hl\build\vis.exe", config.BuildPrograms.VisExecutable); + Assert.AreEqual(@"C:\hl\build\rad.exe", config.BuildPrograms.RadExecutable); + Assert.AreEqual(@"C:\hl\cstrike\maps", config.BuildPrograms.BspDirectory); + } + + [TestMethod] + public void TestWriteGameConfigFile20() + { + var file = new WorldcraftGameConfigurationFile(); + file.Configurations.Add(new WorldcraftGameConfiguration + { + Name = "Half-Life", + GameDataFiles = [@"C:\fgd\a.fgd", @"C:\fgd\b.fgd", @"C:\fgd\halflife.fgd"], + TextureFormat = TextureFormat.Wad3, + MapType = MapType.HalfLife, + DefaultPointEntityClass = "light", + DefaultSolidEntityClass = "func_door", + GameExecutableDirectory = @"C:\hl", + PaletteFile = "palette.pal", + RmfDirectory = @"C:\rmf", + BuildPrograms = new WorldcraftGameConfigurationBuildPrograms + { + GameExecutable = @"C:\hl\hl.exe", + BspExecutable = @"C:\hl\build\bsp.exe", + VisExecutable = @"C:\hl\build\vis.exe", + RadExecutable = @"C:\hl\build\rad.exe", + BspDirectory = @"C:\hl\cstrike\maps", + } + }); + + var ms = new MemoryStream(); + file.Write(ms, WorldcraftGameConfigurationFile.MinVersion); + ms.Position = 0; + + var file2 = new WorldcraftGameConfigurationFile(ms); + Assert.AreEqual(file.Configurations[0].Name, file2.Configurations[0].Name); + CollectionAssert.AreEqual(file.Configurations[0].GameDataFiles, file2.Configurations[0].GameDataFiles); + Assert.AreEqual(file.Configurations[0].TextureFormat, file2.Configurations[0].TextureFormat); + Assert.AreEqual(file.Configurations[0].MapType, file2.Configurations[0].MapType); + Assert.AreEqual(file.Configurations[0].DefaultPointEntityClass, file2.Configurations[0].DefaultPointEntityClass); + Assert.AreEqual(file.Configurations[0].DefaultSolidEntityClass, file2.Configurations[0].DefaultSolidEntityClass); + Assert.AreEqual(file.Configurations[0].GameExecutableDirectory, file2.Configurations[0].GameExecutableDirectory); + Assert.AreEqual(file.Configurations[0].ModDirectory, file2.Configurations[0].ModDirectory); + Assert.AreEqual(file.Configurations[0].GameDirectory, file2.Configurations[0].GameDirectory); + Assert.AreEqual(file.Configurations[0].RmfDirectory, file2.Configurations[0].RmfDirectory); + Assert.AreEqual(file.Configurations[0].PaletteFile, file2.Configurations[0].PaletteFile); + Assert.AreEqual(file.Configurations[0].BuildPrograms.GameExecutable, file2.Configurations[0].BuildPrograms.GameExecutable); + Assert.AreEqual(file.Configurations[0].BuildPrograms.CsgExecutable, file2.Configurations[0].BuildPrograms.CsgExecutable); + Assert.AreEqual(file.Configurations[0].BuildPrograms.BspExecutable, file2.Configurations[0].BuildPrograms.BspExecutable); + Assert.AreEqual(file.Configurations[0].BuildPrograms.VisExecutable, file2.Configurations[0].BuildPrograms.VisExecutable); + Assert.AreEqual(file.Configurations[0].BuildPrograms.RadExecutable, file2.Configurations[0].BuildPrograms.RadExecutable); + Assert.AreEqual(file.Configurations[0].BuildPrograms.BspDirectory, file2.Configurations[0].BuildPrograms.BspDirectory); + } + + [TestMethod] + public void TestWriteGameConfigFile33() + { + var file = new WorldcraftGameConfigurationFile(); + file.Configurations.Add(new WorldcraftGameConfiguration + { + Name = "Half-Life", + GameDataFiles = [@"C:\fgd\a.fgd", @"C:\fgd\b.fgd", @"C:\fgd\halflife.fgd"], + TextureFormat = TextureFormat.Wad3, + MapType = MapType.HalfLife, + DefaultPointEntityClass = "light", + DefaultSolidEntityClass = "func_door", + GameExecutableDirectory = @"C:\hl", + ModDirectory = @"C:\hl\cstrike", + GameDirectory = @"C:\hl\valve", + RmfDirectory = @"C:\rmf", + BuildPrograms = new WorldcraftGameConfigurationBuildPrograms + { + GameExecutable = @"C:\hl\hl.exe", + CsgExecutable = @"C:\hl\build\csg.exe", + BspExecutable = @"C:\hl\build\bsp.exe", + VisExecutable = @"C:\hl\build\vis.exe", + RadExecutable = @"C:\hl\build\rad.exe", + BspDirectory = @"C:\hl\cstrike\maps", + } + }); + + var ms = new MemoryStream(); + file.Write(ms, WorldcraftGameConfigurationFile.MaxVersion); + ms.Position = 0; + + var file2 = new WorldcraftGameConfigurationFile(ms); + Assert.AreEqual(file.Configurations[0].Name, file2.Configurations[0].Name); + CollectionAssert.AreEqual(file.Configurations[0].GameDataFiles, file2.Configurations[0].GameDataFiles); + Assert.AreEqual(file.Configurations[0].TextureFormat, file2.Configurations[0].TextureFormat); + Assert.AreEqual(file.Configurations[0].MapType, file2.Configurations[0].MapType); + Assert.AreEqual(file.Configurations[0].DefaultPointEntityClass, file2.Configurations[0].DefaultPointEntityClass); + Assert.AreEqual(file.Configurations[0].DefaultSolidEntityClass, file2.Configurations[0].DefaultSolidEntityClass); + Assert.AreEqual(file.Configurations[0].GameExecutableDirectory, file2.Configurations[0].GameExecutableDirectory); + Assert.AreEqual(file.Configurations[0].ModDirectory, file2.Configurations[0].ModDirectory); + Assert.AreEqual(file.Configurations[0].GameDirectory, file2.Configurations[0].GameDirectory); + Assert.AreEqual(file.Configurations[0].RmfDirectory, file2.Configurations[0].RmfDirectory); + Assert.AreEqual(file.Configurations[0].PaletteFile, file2.Configurations[0].PaletteFile); + Assert.AreEqual(file.Configurations[0].BuildPrograms.GameExecutable, file2.Configurations[0].BuildPrograms.GameExecutable); + Assert.AreEqual(file.Configurations[0].BuildPrograms.CsgExecutable, file2.Configurations[0].BuildPrograms.CsgExecutable); + Assert.AreEqual(file.Configurations[0].BuildPrograms.BspExecutable, file2.Configurations[0].BuildPrograms.BspExecutable); + Assert.AreEqual(file.Configurations[0].BuildPrograms.VisExecutable, file2.Configurations[0].BuildPrograms.VisExecutable); + Assert.AreEqual(file.Configurations[0].BuildPrograms.RadExecutable, file2.Configurations[0].BuildPrograms.RadExecutable); + Assert.AreEqual(file.Configurations[0].BuildPrograms.BspDirectory, file2.Configurations[0].BuildPrograms.BspDirectory); + } + private const string Worldcraft33RegString = """ Windows Registry Editor Version 5.00 - + [HKEY_CURRENT_USER\SOFTWARE\Valve] - + [HKEY_CURRENT_USER\SOFTWARE\Valve\Worldcraft] - + [HKEY_CURRENT_USER\SOFTWARE\Valve\Worldcraft\2D Views] "Crosshairs"=dword:00000001 "GroupCarve"=dword:00000001 @@ -68,7 +262,7 @@ Windows Registry Editor Version 5.00 "GridDots"=dword:00000000 "Centeroncamera"=dword:00000001 "Usegroupcolors"=dword:00000000 - + [HKEY_CURRENT_USER\SOFTWARE\Valve\Worldcraft\3D Views] "Hardware"=dword:00000000 "Reverse Y"=dword:00000001 @@ -80,13 +274,13 @@ Windows Registry Editor Version 5.00 "TimeToMaxSpeed"=dword:00000783 "FilterTextures"=dword:00000000 "ReverseSelection"=dword:00000001 - + [HKEY_CURRENT_USER\SOFTWARE\Valve\Worldcraft\Configured] "Installed"=dword:6767bfdf "Configured"=dword:00000002 - + [HKEY_CURRENT_USER\SOFTWARE\Valve\Worldcraft\Custom2DColors] - + [HKEY_CURRENT_USER\SOFTWARE\Valve\Worldcraft\General] "Directory"="C:\\Users\\WDAGUtilityAccount\\Desktop\\Worldcraft 3.3" "TextureFileCount"=dword:00000001 @@ -100,12 +294,12 @@ Windows Registry Editor Version 5.00 "GroupWhileIgnore"=dword:00000001 "StretchArches"=dword:00000000 "NewBars"=dword:00000001 - + [HKEY_CURRENT_USER\SOFTWARE\Valve\Worldcraft\Recent File List] "File1"="C:\\Users\\WDAGUtilityAccount\\Desktop\\Worldcraft 3.3\\123" - + [HKEY_CURRENT_USER\SOFTWARE\Valve\Worldcraft\Settings] - - + + """; } \ No newline at end of file diff --git a/Sledge.Formats.Configuration/Sledge.Formats.Configuration.csproj b/Sledge.Formats.Configuration/Sledge.Formats.Configuration.csproj index 9ab8a1e..2db6880 100644 --- a/Sledge.Formats.Configuration/Sledge.Formats.Configuration.csproj +++ b/Sledge.Formats.Configuration/Sledge.Formats.Configuration.csproj @@ -8,4 +8,8 @@ + + + + diff --git a/Sledge.Formats.Configuration/Worldcraft/MapType.cs b/Sledge.Formats.Configuration/Worldcraft/MapType.cs index 45d9b78..b22d676 100644 --- a/Sledge.Formats.Configuration/Worldcraft/MapType.cs +++ b/Sledge.Formats.Configuration/Worldcraft/MapType.cs @@ -4,9 +4,9 @@ namespace Sledge.Formats.Configuration.Worldcraft { public enum MapType { - HalfLife, - [Obsolete] Quake, - [Obsolete] Quake2, - [Obsolete] Hexen2, + HalfLife = 3, + [Obsolete] Quake = 0, + [Obsolete] Quake2 = 2, + [Obsolete] Hexen2 = 1, } } \ No newline at end of file diff --git a/Sledge.Formats.Configuration/Worldcraft/TextureFormat.cs b/Sledge.Formats.Configuration/Worldcraft/TextureFormat.cs index 42da68b..011f104 100644 --- a/Sledge.Formats.Configuration/Worldcraft/TextureFormat.cs +++ b/Sledge.Formats.Configuration/Worldcraft/TextureFormat.cs @@ -4,8 +4,8 @@ namespace Sledge.Formats.Configuration.Worldcraft { public enum TextureFormat { - Wad3, - [Obsolete] Wad2, - [Obsolete] Wal, + Wad3 = 2, + [Obsolete] Wad2 = 0, + [Obsolete] Wal = 1, } } \ No newline at end of file diff --git a/Sledge.Formats.Configuration/Worldcraft/Worldcraft2DViewsConfiguration.cs b/Sledge.Formats.Configuration/Worldcraft/Worldcraft2DViewsConfiguration.cs index 2b69496..f4d663f 100644 --- a/Sledge.Formats.Configuration/Worldcraft/Worldcraft2DViewsConfiguration.cs +++ b/Sledge.Formats.Configuration/Worldcraft/Worldcraft2DViewsConfiguration.cs @@ -65,6 +65,6 @@ public class Worldcraft2DViewsConfiguration /// /// Grid configuration /// - public WorldcraftGridOptions Grid { get; set; } + public WorldcraftGridOptions Grid { get; } = new WorldcraftGridOptions(); } } \ No newline at end of file diff --git a/Sledge.Formats.Configuration/Worldcraft/Worldcraft3DViewsConfiguration.cs b/Sledge.Formats.Configuration/Worldcraft/Worldcraft3DViewsConfiguration.cs index dbddc59..5618b6b 100644 --- a/Sledge.Formats.Configuration/Worldcraft/Worldcraft3DViewsConfiguration.cs +++ b/Sledge.Formats.Configuration/Worldcraft/Worldcraft3DViewsConfiguration.cs @@ -42,7 +42,7 @@ public class Worldcraft3DViewsConfiguration /// /// Time to top speed (0-10 seconds) /// - public float TimeToTopSpeed { get; set; } + public decimal TimeToTopSpeed { get; set; } /// /// Reverse selection order @@ -52,6 +52,6 @@ public class Worldcraft3DViewsConfiguration /// /// Background color /// - public Color BackgroundColor { get; set; } + public Color BackgroundColor { get; set; } = Color.Black; } } \ No newline at end of file diff --git a/Sledge.Formats.Configuration/Worldcraft/WorldcraftConfiguration.cs b/Sledge.Formats.Configuration/Worldcraft/WorldcraftConfiguration.cs index b5cf2fd..e070b5a 100644 --- a/Sledge.Formats.Configuration/Worldcraft/WorldcraftConfiguration.cs +++ b/Sledge.Formats.Configuration/Worldcraft/WorldcraftConfiguration.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Drawing; using System.IO; using System.Linq; using Sledge.Formats.Configuration.Registry; @@ -17,7 +18,7 @@ public static WorldcraftConfiguration LoadFromRegistry(WorldcraftConfigurationLo { settings = settings ?? WorldcraftConfigurationLoadSettings.Default; var key = settings.RegistryLocation; - if (settings.AutodetectRegistryLocation) + if (settings.AutodetectRegistryLocation && key == null) { var reg = new WindowsRegistry(); var baseKey = reg.OpenBaseKey(settings.RegistryHive, settings.RegistryView); @@ -27,9 +28,28 @@ public static WorldcraftConfiguration LoadFromRegistry(WorldcraftConfigurationLo var config = new WorldcraftConfiguration(); (config.General, config.TextureDirectories) = LoadGeneralRegistry(key.OpenSubKey(WorldcraftRegistryInfo.KeyGeneral)); - config.Views2D = null; - config.Views3D = null; - config.GameConfigurations = null; + config.Views2D = LoadViews2DRegistry(key.OpenSubKey(WorldcraftRegistryInfo.Key2DViews)); + config.Views3D = LoadViews3DRegistry(key.OpenSubKey(WorldcraftRegistryInfo.Key3DViews)); + + var installDir = settings.InstallDirectory; + if (settings.AutodetectInstallDirectory && installDir == null) + { + installDir = config.General.InstallDirectory; + } + + if (settings.LoadGameConfigurations && Directory.Exists(installDir)) + { + var configFile = Path.Combine(installDir, "GameCfg.wc"); + if (File.Exists(configFile)) + { + using (var fs = File.OpenRead(configFile)) + { + var cfg = new WorldcraftGameConfigurationFile(fs); + config.GameConfigurations = cfg.Configurations; + } + } + } + return config; } @@ -59,6 +79,58 @@ private static (WorldcraftGeneralConfiguration, List) LoadGeneralRegistr return (config, textures); } + private static Worldcraft2DViewsConfiguration LoadViews2DRegistry(IRegistryKey key) + { + var config = new Worldcraft2DViewsConfiguration(); + if (key != null) + { + config.CrosshairCursor = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsCrosshairs); + config.DefaultTo15DegreeRotations = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsRotateConstrain); + config.DisplayScrollbars = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsScrollbars); + config.DrawVertices = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsDrawVertices); + config.WhiteOnBlackColorScheme = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsWhiteOnBlack); + config.KeepGroupWhenCloneDragging = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsKeepCloneGroup); + config.CenterOnCameraAfterMovement = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsCenterOnCamera); + config.UseVisgroupColorsForObjectLines = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsUseGroupColors); + config.ArrowKeysNudgeSelectedObject = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsNudge); + config.ReorientPrimitivesOnCreation = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsOrientPrimitives); + config.AutomaticInfiniteSelection = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsAutoSelect); + config.SelectionBoxSelectsByCenterHandlesOnly = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsSelectByHandles); + + config.Grid.Size = key.GetIntValue(WorldcraftRegistryInfo.Key2DViewsDefaultGrid); + config.Grid.Intensity = key.GetIntValue(WorldcraftRegistryInfo.Key2DViewsGridIntensity); + config.Grid.HighlightEvery64Units = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsGridHigh64); + var nLines = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsGridHigh10); + config.Grid.HighlightEveryNGridLines = nLines ? key.GetIntValue(WorldcraftRegistryInfo.Key2DViewsGridHighSpec) : 0; + config.Grid.HideGridSmallerThan4Pixels = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsHideSmallGrid); + config.Grid.HighlightEvery1024Units = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsGridHigh1024); + config.Grid.DottedGrid = key.GetBoolValue(WorldcraftRegistryInfo.Key2DViewsGridDots); + + } + return config; + } + + private static Worldcraft3DViewsConfiguration LoadViews3DRegistry(IRegistryKey key) + { + var config = new Worldcraft3DViewsConfiguration(); + if (key != null) + { + //load 3d view settings into config of type Worldcraft3DViewsConfiguration: + config.BackClippingPlane = key.GetIntValue(WorldcraftRegistryInfo.Key3DViewsBackPlane); + config.FilterTextures = key.GetBoolValue(WorldcraftRegistryInfo.Key3DViewsFilterTextures); + config.AnimateModels = key.GetBoolValue(WorldcraftRegistryInfo.Key3DViewsAnimateModels); + config.ModelRenderDistance = key.GetIntValue(WorldcraftRegistryInfo.Key3DViewsModelDistance); + config.UseMouselookNavigation = key.GetBoolValue(WorldcraftRegistryInfo.Key3DViewsUseMouseLook); + config.ReverseMouseYAxis = key.GetBoolValue(WorldcraftRegistryInfo.Key3DViewsReverseY); + config.ForwardSpeed = key.GetIntValue(WorldcraftRegistryInfo.Key3DViewsForwardSpeedMax); + config.TimeToTopSpeed = key.GetIntValue(WorldcraftRegistryInfo.Key3DViewsTimeToMaxSpeed) / 1000m; + config.ReverseSelectionOrder = key.GetBoolValue(WorldcraftRegistryInfo.Key3DViewsReverseSelection); + config.BackgroundColor = Color.FromArgb(255, Color.FromArgb(key.GetIntValue(WorldcraftRegistryInfo.Key3DViewsClearColor))); + } + return config; + } + + private static IRegistryKey FindDefaultRegistryKey(IRegistryKey baseKey) { foreach (var path in WorldcraftRegistryInfo.DefaultRegistryPaths) diff --git a/Sledge.Formats.Configuration/Worldcraft/WorldcraftConfigurationLoadSettings.cs b/Sledge.Formats.Configuration/Worldcraft/WorldcraftConfigurationLoadSettings.cs index 757acb0..e87c5c5 100644 --- a/Sledge.Formats.Configuration/Worldcraft/WorldcraftConfigurationLoadSettings.cs +++ b/Sledge.Formats.Configuration/Worldcraft/WorldcraftConfigurationLoadSettings.cs @@ -1,6 +1,4 @@ -using System.IO; -using System.Linq; -using Microsoft.Win32; +using Microsoft.Win32; using Sledge.Formats.Configuration.Registry; namespace Sledge.Formats.Configuration.Worldcraft @@ -8,7 +6,8 @@ namespace Sledge.Formats.Configuration.Worldcraft public class WorldcraftConfigurationLoadSettings { /// - /// True to attempt to autodetect the registry location from the known default registry locations + /// True to attempt to autodetect the registry location from the known default registry locations. + /// Ignored if RegistryLocation is not null. /// public bool AutodetectRegistryLocation { get; set; } = true; @@ -23,7 +22,7 @@ public class WorldcraftConfigurationLoadSettings public RegistryView RegistryView { get; set; } = RegistryView.Default; /// - /// Set to a non-null value and set AutodetectRegistryLocation to false to specify the registry location. + /// Set to a non-null value to specify the registry location. /// The registry location will usually be called "Worldcraft" or "Valve Hammer Editor" and contain subkeys called "General", "2D Views", "3D Views", etc. /// public IRegistryKey RegistryLocation { get; set; } @@ -36,11 +35,12 @@ public class WorldcraftConfigurationLoadSettings /// /// True to attempt to autodetect the install directory from the registry ([Worldcraft/General/Directory] registry key). /// All worldcraft versions store the install directory in the registry except for version 1.0. + /// Ignored if InstallDirectory is not null. /// public bool AutodetectInstallDirectory { get; set; } = true; /// - /// Set to a non-null value and set AutodetectInstallDirectory to false to specify the install directory + /// Set to a non-null value to specify the install directory /// public string InstallDirectory { get; set; } diff --git a/Sledge.Formats.Configuration/Worldcraft/WorldcraftGameConfiguration.cs b/Sledge.Formats.Configuration/Worldcraft/WorldcraftGameConfiguration.cs index 1e58a34..10ac6aa 100644 --- a/Sledge.Formats.Configuration/Worldcraft/WorldcraftGameConfiguration.cs +++ b/Sledge.Formats.Configuration/Worldcraft/WorldcraftGameConfiguration.cs @@ -8,61 +8,61 @@ public class WorldcraftGameConfiguration /// /// Configuration name /// - public string Name { get; set; } + public string Name { get; set; } = ""; /// /// List of game data files (.fgd) /// - public List GameDataFiles { get; set; } + public List GameDataFiles { get; set; } = new List(); /// /// Texture Format /// - public TextureFormat TextureFormat { get; set; } + public TextureFormat TextureFormat { get; set; } = TextureFormat.Wad3; /// /// Map Type /// - public MapType MapType { get; set; } + public MapType MapType { get; set; } = MapType.HalfLife; /// /// Default PointEntity class /// - public string DefaultPointEntityClass { get; set; } + public string DefaultPointEntityClass { get; set; } = ""; /// /// Default SolidEntity class /// - public string DefaultSolidEntityClass { get; set; } + public string DefaultSolidEntityClass { get; set; } = ""; /// /// Game executable directory (ex: C:\HalfLife) /// - public string GameExecutableDirectory { get; set; } + public string GameExecutableDirectory { get; set; } = ""; /// /// Mod directory (ex: C:\HalfLife\tfc) /// - public string ModDirectory { get; set; } + public string ModDirectory { get; set; } = ""; /// /// Game directory (ex: C:\HalfLife\valve) /// - public string GameDirectory { get; set; } + public string GameDirectory { get; set; } = ""; /// /// RMF directory /// - public string RmfDirectory { get; set; } + public string RmfDirectory { get; set; } = ""; /// /// Palette file /// - [Obsolete] public string PaletteFile { get; set; } + [Obsolete] public string PaletteFile { get; set; } = ""; /// /// Build programs for this configuration /// - public WorldcraftGameConfigurationBuildPrograms BuildPrograms { get; set; } + public WorldcraftGameConfigurationBuildPrograms BuildPrograms { get; set; } = new WorldcraftGameConfigurationBuildPrograms(); } } \ No newline at end of file diff --git a/Sledge.Formats.Configuration/Worldcraft/WorldcraftGameConfigurationBuildPrograms.cs b/Sledge.Formats.Configuration/Worldcraft/WorldcraftGameConfigurationBuildPrograms.cs index d6127fd..9712b6f 100644 --- a/Sledge.Formats.Configuration/Worldcraft/WorldcraftGameConfigurationBuildPrograms.cs +++ b/Sledge.Formats.Configuration/Worldcraft/WorldcraftGameConfigurationBuildPrograms.cs @@ -5,31 +5,31 @@ public class WorldcraftGameConfigurationBuildPrograms /// /// Game executable /// - public string GameExecutable { get; set; } + public string GameExecutable { get; set; } = ""; /// /// CSG executable /// - public string CsgExecutable { get; set; } + public string CsgExecutable { get; set; } = ""; /// /// BSP executable /// - public string BspExecutable { get; set; } + public string BspExecutable { get; set; } = ""; /// /// VIS executable /// - public string VisExecutable { get; set; } + public string VisExecutable { get; set; } = ""; /// /// RAD executable /// - public string RadExecutable { get; set; } + public string RadExecutable { get; set; } = ""; /// /// Place compiled maps in this directory before running the game /// - public string BspDirectory { get; set; } + public string BspDirectory { get; set; } = ""; } } \ No newline at end of file diff --git a/Sledge.Formats.Configuration/Worldcraft/WorldcraftGameConfigurationFile.cs b/Sledge.Formats.Configuration/Worldcraft/WorldcraftGameConfigurationFile.cs new file mode 100644 index 0000000..152c9ce --- /dev/null +++ b/Sledge.Formats.Configuration/Worldcraft/WorldcraftGameConfigurationFile.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Sledge.Formats.Configuration.Worldcraft +{ + public class WorldcraftGameConfigurationFile + { + private const int StringLength = 128; + private static readonly string ConfigFileHeader = "Game Configurations File\r\n" + (char)0x1A + '\0'; + + /// + /// Version 1.3 used for Worldcraft 1.6a-2.1 + /// + public const float MinVersion = 1.3f; + + /// + /// Version 1.4 used for Worldcraft 3.3 and VHE 3.5 + /// + public const float MaxVersion = 1.4f; + + public List Configurations { get; set; } + + public WorldcraftGameConfigurationFile() + { + Configurations = new List(); + } + + public WorldcraftGameConfigurationFile(Stream stream) + { + Configurations = new List(); + ReadFromStream(stream); + } + + public static WorldcraftGameConfigurationFile FromFile(string file) + { + using (var stream = File.OpenRead(file)) + { + return new WorldcraftGameConfigurationFile(stream); + } + } + + private void ReadFromStream(Stream stream) + { + using (var br = new BinaryReader(stream, Encoding.ASCII, true)) + { + var header = br.ReadFixedLengthString(Encoding.ASCII, ConfigFileHeader.Length); + if (header != ConfigFileHeader.TrimEnd('\0')) throw new NotSupportedException($"Incorrect configuration file header. Expected '{ConfigFileHeader}', got '{header}'."); + + var version = br.ReadSingle(); + if (version < MinVersion || version > MaxVersion) throw new NotSupportedException($"Unsupported configuration file version. Expected {MinVersion} or {MaxVersion}, got {version}."); + + var numGames = br.ReadInt32(); + for (var i = 0; i < numGames; i++) + { + var config = new WorldcraftGameConfiguration + { + Name = br.ReadFixedLengthString(Encoding.ASCII, StringLength) + }; + + var numFgds = br.ReadInt32(); + config.TextureFormat = (TextureFormat) br.ReadInt32(); + config.MapType = (MapType) br.ReadInt32(); + + if (Math.Abs(version - 1.3f) < float.Epsilon) + { +#pragma warning disable CS0612 // Type or member is obsolete + config.PaletteFile = br.ReadFixedLengthString(Encoding.ASCII, StringLength); +#pragma warning restore CS0612 + } + + config.BuildPrograms.GameExecutable = br.ReadFixedLengthString(Encoding.ASCII, StringLength); + config.DefaultSolidEntityClass = br.ReadFixedLengthString(Encoding.ASCII, StringLength); + config.DefaultPointEntityClass = br.ReadFixedLengthString(Encoding.ASCII, StringLength); + config.BuildPrograms.BspExecutable = br.ReadFixedLengthString(Encoding.ASCII, StringLength); + config.BuildPrograms.RadExecutable = br.ReadFixedLengthString(Encoding.ASCII, StringLength); + config.BuildPrograms.VisExecutable = br.ReadFixedLengthString(Encoding.ASCII, StringLength); + config.GameExecutableDirectory = br.ReadFixedLengthString(Encoding.ASCII, StringLength); + config.RmfDirectory = br.ReadFixedLengthString(Encoding.ASCII, StringLength); + config.BuildPrograms.BspDirectory = br.ReadFixedLengthString(Encoding.ASCII, StringLength); + if (Math.Abs(version - 1.4f) < float.Epsilon) + { + config.BuildPrograms.CsgExecutable = br.ReadFixedLengthString(Encoding.ASCII, StringLength); + config.ModDirectory = br.ReadFixedLengthString(Encoding.ASCII, StringLength); + config.GameDirectory = br.ReadFixedLengthString(Encoding.ASCII, StringLength); + } + for (var j = 0; j < numFgds; j++) + { + config.GameDataFiles.Add(br.ReadFixedLengthString(Encoding.ASCII, StringLength)); + } + Configurations.Add(config); + } + } + } + + public void Write(Stream stream, float version = MaxVersion) + { + if (version < MinVersion || version > MaxVersion) throw new NotSupportedException($"Unsupported configuration file version. Expected {MinVersion} or {MaxVersion}, got {version}."); + + using (var bw = new BinaryWriter(stream, Encoding.ASCII, true)) + { + bw.WriteFixedLengthString(Encoding.ASCII, ConfigFileHeader.Length, ConfigFileHeader); + bw.Write(version); + bw.Write(Configurations.Count); + foreach (var config in Configurations) + { + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.Name); + bw.Write(config.GameDataFiles.Count); + bw.Write((int)config.TextureFormat); + bw.Write((int)config.MapType); + if (Math.Abs(version - 1.3f) < float.Epsilon) + { +#pragma warning disable CS0612 // Type or member is obsolete + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.PaletteFile); +#pragma warning restore CS0612 + } + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.BuildPrograms.GameExecutable); + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.DefaultSolidEntityClass); + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.DefaultPointEntityClass); + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.BuildPrograms.BspExecutable); + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.BuildPrograms.RadExecutable); + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.BuildPrograms.VisExecutable); + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.GameExecutableDirectory); + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.RmfDirectory); + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.BuildPrograms.BspDirectory); + if (Math.Abs(version - 1.4f) < float.Epsilon) + { + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.BuildPrograms.CsgExecutable); + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.ModDirectory); + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, config.GameDirectory); + } + foreach (var fgd in config.GameDataFiles) + { + bw.WriteFixedLengthString(Encoding.ASCII, StringLength, fgd); + } + } + } + } + } +} diff --git a/Sledge.Formats.Configuration/Worldcraft/WorldcraftRegistryInfo.cs b/Sledge.Formats.Configuration/Worldcraft/WorldcraftRegistryInfo.cs index ea2cd4e..dee91f3 100644 --- a/Sledge.Formats.Configuration/Worldcraft/WorldcraftRegistryInfo.cs +++ b/Sledge.Formats.Configuration/Worldcraft/WorldcraftRegistryInfo.cs @@ -29,5 +29,41 @@ public static class WorldcraftRegistryInfo public const string KeyGeneralTextureFilePrefix = "TextureFile"; public const string KeyGeneralTextureFileCount = "TextureFileCount"; public const string KeyGeneralUndoLevels = "Undo Levels"; + + public const string Key2DViewsCrosshairs = "Crosshairs"; + public const string Key2DViewsGroupCarve = "GroupCarve"; + public const string Key2DViewsScrollbars = "Scrollbars"; + public const string Key2DViewsRotateConstrain = "RotateConstrain"; + public const string Key2DViewsDrawVertices = "Draw Vertices"; + public const string Key2DViewsDefaultGrid = "Default Grid"; + public const string Key2DViewsWhiteOnBlack = "WhiteOnBlack"; + public const string Key2DViewsGridHigh10 = "GridHigh10"; + public const string Key2DViewsGridHigh1024 = "GridHigh1024"; + public const string Key2DViewsGridIntensity = "GridIntensity"; + public const string Key2DViewsHideSmallGrid = "HideSmallGrid"; + public const string Key2DViewsNudge = "Nudge"; + public const string Key2DViewsOrientPrimitives = "OrientPrimitives"; + public const string Key2DViewsAutoSelect = "AutoSelect"; + public const string Key2DViewsSelectByHandles = "SelectByHandles"; + public const string Key2DViewsGridHighSpec = "GridHighSpec"; + public const string Key2DViewsKeepCloneGroup = "KeepCloneGroup"; + public const string Key2DViewsGridHigh64 = "Gridhigh64"; + public const string Key2DViewsGridDots = "GridDots"; + public const string Key2DViewsCenterOnCamera = "Centeroncamera"; + public const string Key2DViewsUseGroupColors = "Usegroupcolors"; + + public const string Key3DViewsHardware = "Hardware"; + public const string Key3DViewsReverseY = "Reverse Y"; + public const string Key3DViewsBackPlane = "BackPlane"; + public const string Key3DViewsUseMouseLook = "UseMouseLook"; + public const string Key3DViewsModelDistance = "ModelDistance"; + public const string Key3DViewsAnimateModels = "AnimateModels"; + public const string Key3DViewsForwardSpeedMax = "ForwardSpeedMax"; + public const string Key3DViewsTimeToMaxSpeed = "TimeToMaxSpeed"; + public const string Key3DViewsFilterTextures = "FilterTextures"; + public const string Key3DViewsReverseSelection = "ReverseSelection"; + public const string Key3DViewsClearColor = "ClearColor"; + + public const string KeyRecentFileListPrefix = "File"; } } \ No newline at end of file