From 5ecf5d6d6d9d1e794617f57a7bc3a418823e8e38 Mon Sep 17 00:00:00 2001 From: MathiasL Date: Thu, 27 Jan 2022 16:16:42 +0100 Subject: [PATCH] Add DamageCalculator --- DamageCalculator/.vs/Config Manager/v16/.suo | Bin 0 -> 197632 bytes .../.vs/DamageCalculator/v16/.suo | Bin 0 -> 57344 bytes DamageCalculator/DamageCalculator.sln | 25 + DamageCalculator/DamageCalculator/27.ico | Bin 0 -> 137478 bytes DamageCalculator/DamageCalculator/About.xaml | 22 + .../DamageCalculator/About.xaml.cs | 28 + DamageCalculator/DamageCalculator/App.config | 6 + DamageCalculator/DamageCalculator/App.xaml | 13 + DamageCalculator/DamageCalculator/App.xaml.cs | 17 + .../DamageCalculator/CsgoHelper.cs | 342 + .../DamageCalculator/DDSImageParser.cs | 2015 +++ .../DamageCalculator/DamageCalculator.csproj | 159 + DamageCalculator/DamageCalculator/Globals.cs | 28 + .../DamageCalculator/MainWindow.xaml | 99 + .../DamageCalculator/MainWindow.xaml.cs | 569 + .../Models/CsgoMapOverview.cs | 41 + .../DamageCalculator/Models/CsgoWeapon.cs | 49 + .../DamageCalculator/Models/MapPoint.cs | 17 + .../DamageCalculator/Models/Settings.cs | 15 + .../DamageCalculator/Models/SteamGame.cs | 62 + .../DamageCalculator/Models/SteamLibrary.cs | 32 + .../Properties/AssemblyInfo.cs | 55 + .../Properties/Resources.Designer.cs | 63 + .../Properties/Resources.resx | 117 + .../Properties/Settings.Designer.cs | 26 + .../Properties/Settings.settings | 7 + .../DamageCalculator/Serializer.cs | 31 + .../DamageCalculator/SteamHelper.cs | 267 + .../Themes/ColourfulDarkTheme.xaml | 4496 ++++++ .../Themes/ColourfulDarkTheme.xaml.cs | 33 + .../Themes/ColourfulLightTheme.xaml | 4546 +++++++ .../Themes/ColourfulLightTheme.xaml.cs | 33 + .../DamageCalculator/Themes/DarkTheme.xaml | 4467 ++++++ .../DamageCalculator/Themes/DarkTheme.xaml.cs | 33 + .../DamageCalculator/Themes/LightTheme.xaml | 4452 ++++++ .../Themes/LightTheme.xaml.cs | 33 + .../Themes/ThemesController.cs | 46 + .../ZatVdfParser/BackupVdfReader.cs | 144 + .../DamageCalculator/ZatVdfParser/Element.cs | 95 + .../DamageCalculator/ZatVdfParser/VdfFile.cs | 111 + .../bin/Debug/CSGO Damage Calculator.exe | Bin 0 -> 986112 bytes .../Debug/CSGO Damage Calculator.exe.config | 6 + .../bin/Debug/CSGO Damage Calculator.pdb | Bin 0 -> 259584 bytes .../bin/Release/CSGO Damage Calculator.exe | Bin 0 -> 768000 bytes .../Release/CSGO Damage Calculator.exe.config | 6 + .../bin/Release/CSGO Damage Calculator.pdb | Bin 0 -> 241152 bytes .../DamageCalculator/icon_a_site.png | Bin 0 -> 6289 bytes .../DamageCalculator/icon_b_site.png | Bin 0 -> 5888 bytes DamageCalculator/DamageCalculator/icon_ct.png | Bin 0 -> 7178 bytes DamageCalculator/DamageCalculator/icon_t.png | Bin 0 -> 4124 bytes ...amework,Version=v4.8.AssemblyAttributes.cs | 4 + .../DamageCalculator/obj/Debug/About.g.cs | 89 + .../DamageCalculator/obj/Debug/About.g.i.cs | 89 + .../DamageCalculator/obj/Debug/App.g.cs | 83 + .../DamageCalculator/obj/Debug/App.g.i.cs | 83 + .../obj/Debug/CSGO Damage Calculator.exe | Bin 0 -> 986112 bytes .../Debug/CSGO Damage Calculator.g.resources | Bin 0 -> 773689 bytes .../obj/Debug/CSGO Damage Calculator.pdb | Bin 0 -> 259584 bytes ...CSGO Damage Calculator_MarkupCompile.cache | 20 + ...GO Damage Calculator_MarkupCompile.i.cache | 20 + .../CSGO Damage Calculator_MarkupCompile.lref | 5 + ...fig Manager.csproj.AssemblyReference.cache | Bin 0 -> 2002 bytes ...fig Manager.csproj.CoreCompileInputs.cache | 1 + ...Config Manager.csproj.FileListAbsolute.txt | 26 + ...nfig Manager.csproj.GenerateResource.cache | Bin 0 -> 954 bytes .../obj/Debug/Config Manager_Content.g.i.cs | 13 + .../Config Manager_MarkupCompile.i.cache | 20 + ...eCalculator.csproj.AssemblyReference.cache | Bin 0 -> 8294 bytes ...eCalculator.csproj.CoreCompileInputs.cache | 1 + ...mageCalculator.csproj.FileListAbsolute.txt | 25 + ...geCalculator.csproj.GenerateResource.cache | Bin 0 -> 954 bytes ..._Calculator.Properties.Resources.resources | Bin 0 -> 180 bytes .../DesignTimeResolveAssemblyReferences.cache | Bin 0 -> 135 bytes ...gnTimeResolveAssemblyReferencesInput.cache | Bin 0 -> 7645 bytes .../obj/Debug/MainWindow.g.cs | 456 + .../obj/Debug/MainWindow.g.i.cs | 456 + .../Properties.Resources.Designer.cs.dll | Bin 0 -> 3584 bytes .../obj/Debug/Themes/ColourfulDarkTheme.baml | Bin 0 -> 151248 bytes .../obj/Debug/Themes/ColourfulDarkTheme.g.cs | 127 + .../Debug/Themes/ColourfulDarkTheme.g.i.cs | 127 + .../obj/Debug/Themes/ColourfulLightTheme.baml | Bin 0 -> 152895 bytes .../obj/Debug/Themes/ColourfulLightTheme.g.cs | 127 + .../Debug/Themes/ColourfulLightTheme.g.i.cs | 127 + .../obj/Debug/Themes/DarkTheme.baml | Bin 0 -> 149608 bytes .../obj/Debug/Themes/DarkTheme.g.cs | 127 + .../obj/Debug/Themes/DarkTheme.g.i.cs | 127 + .../obj/Debug/Themes/LightTheme.baml | Bin 0 -> 149597 bytes .../obj/Debug/Themes/LightTheme.g.cs | 127 + .../obj/Debug/Themes/LightTheme.g.i.cs | 127 + ...amework,Version=v4.8.AssemblyAttributes.cs | 4 + .../DamageCalculator/obj/Release/About.baml | Bin 0 -> 1575 bytes .../DamageCalculator/obj/Release/About.g.cs | 89 + .../DamageCalculator/obj/Release/About.g.i.cs | 89 + .../DamageCalculator/obj/Release/App.baml | Bin 0 -> 827 bytes .../DamageCalculator/obj/Release/App.g.cs | 83 + .../DamageCalculator/obj/Release/App.g.i.cs | 83 + .../obj/Release/CSGO Damage Calculator.exe | Bin 0 -> 768000 bytes .../CSGO Damage Calculator.g.resources | Bin 0 -> 560940 bytes .../obj/Release/CSGO Damage Calculator.pdb | Bin 0 -> 241152 bytes ...CSGO Damage Calculator_MarkupCompile.cache | 20 + ...GO Damage Calculator_MarkupCompile.i.cache | 20 + ...SGO Damage Calculator_MarkupCompile.i.lref | 5 + .../CSGO Damage Calculator_MarkupCompile.lref | 5 + ...fig Manager.csproj.AssemblyReference.cache | Bin 0 -> 2002 bytes ...fig Manager.csproj.CoreCompileInputs.cache | 1 + ...Config Manager.csproj.FileListAbsolute.txt | 26 + ...nfig Manager.csproj.GenerateResource.cache | Bin 0 -> 954 bytes .../obj/Release/Config Manager_Content.g.i.cs | 13 + .../Config Manager_MarkupCompile.i.cache | 20 + .../Config Manager_MarkupCompile.i.lref | 5 + ...fig_Manager.Properties.Resources.resources | Bin 0 -> 180 bytes .../DesignTimeResolveAssemblyReferences.cache | Bin 0 -> 11 bytes ...gnTimeResolveAssemblyReferencesInput.cache | Bin 0 -> 7664 bytes .../obj/Release/MainWindow.baml | Bin 0 -> 3787 bytes .../obj/Release/MainWindow.g.cs | 456 + .../obj/Release/MainWindow.g.i.cs | 456 + .../Properties.Resources.Designer.cs.dll | Bin 0 -> 3584 bytes .../Release/Themes/ColourfulDarkTheme.baml | Bin 0 -> 98614 bytes .../Release/Themes/ColourfulDarkTheme.g.cs | 127 + .../Release/Themes/ColourfulDarkTheme.g.i.cs | 127 + .../Release/Themes/ColourfulLightTheme.baml | Bin 0 -> 99689 bytes .../Release/Themes/ColourfulLightTheme.g.cs | 127 + .../Release/Themes/ColourfulLightTheme.g.i.cs | 127 + .../obj/Release/Themes/DarkTheme.baml | Bin 0 -> 97341 bytes .../obj/Release/Themes/DarkTheme.g.cs | 127 + .../obj/Release/Themes/DarkTheme.g.i.cs | 127 + .../obj/Release/Themes/LightTheme.baml | Bin 0 -> 97366 bytes .../obj/Release/Themes/LightTheme.g.cs | 127 + .../obj/Release/Themes/LightTheme.g.i.cs | 127 + .../DamageCalculator/packages.config | 5 + .../Gameloop.Vdf.0.6.1/.signature.p7s | Bin 0 -> 9465 bytes .../Gameloop.Vdf.0.6.1.nupkg | Bin 0 -> 52636 bytes .../lib/net45/Gameloop.Vdf.dll | Bin 0 -> 41472 bytes .../lib/net45/Gameloop.Vdf.xml | 119 + .../lib/netstandard1.0/Gameloop.Vdf.dll | Bin 0 -> 43008 bytes .../lib/netstandard1.0/Gameloop.Vdf.xml | 119 + .../packages/Gameloop.Vdf.0.6.1/readme.txt | 91 + .../Newtonsoft.Json.12.0.3/.signature.p7s | Bin 0 -> 18492 bytes .../Newtonsoft.Json.12.0.3/LICENSE.md | 20 + .../Newtonsoft.Json.12.0.3.nupkg | Bin 0 -> 2596051 bytes .../lib/net20/Newtonsoft.Json.dll | Bin 0 -> 570792 bytes .../lib/net20/Newtonsoft.Json.xml | 10298 ++++++++++++++ .../lib/net35/Newtonsoft.Json.dll | Bin 0 -> 505776 bytes .../lib/net35/Newtonsoft.Json.xml | 9446 +++++++++++++ .../lib/net40/Newtonsoft.Json.dll | Bin 0 -> 574376 bytes .../lib/net40/Newtonsoft.Json.xml | 9646 +++++++++++++ .../lib/net45/Newtonsoft.Json.dll | Bin 0 -> 700336 bytes .../lib/net45/Newtonsoft.Json.xml | 11262 ++++++++++++++++ .../lib/netstandard1.0/Newtonsoft.Json.dll | Bin 0 -> 669104 bytes .../lib/netstandard1.0/Newtonsoft.Json.xml | 10950 +++++++++++++++ .../lib/netstandard1.3/Newtonsoft.Json.dll | Bin 0 -> 688040 bytes .../lib/netstandard1.3/Newtonsoft.Json.xml | 11072 +++++++++++++++ .../lib/netstandard2.0/Newtonsoft.Json.dll | Bin 0 -> 693680 bytes .../lib/netstandard2.0/Newtonsoft.Json.xml | 11237 +++++++++++++++ .../Newtonsoft.Json.dll | Bin 0 -> 468912 bytes .../Newtonsoft.Json.xml | 9010 +++++++++++++ .../Newtonsoft.Json.dll | Bin 0 -> 668584 bytes .../Newtonsoft.Json.xml | 10950 +++++++++++++++ .../Newtonsoft.Json.12.0.3/packageIcon.png | Bin 0 -> 8956 bytes 159 files changed, 121629 insertions(+) create mode 100644 DamageCalculator/.vs/Config Manager/v16/.suo create mode 100644 DamageCalculator/.vs/DamageCalculator/v16/.suo create mode 100644 DamageCalculator/DamageCalculator.sln create mode 100644 DamageCalculator/DamageCalculator/27.ico create mode 100644 DamageCalculator/DamageCalculator/About.xaml create mode 100644 DamageCalculator/DamageCalculator/About.xaml.cs create mode 100644 DamageCalculator/DamageCalculator/App.config create mode 100644 DamageCalculator/DamageCalculator/App.xaml create mode 100644 DamageCalculator/DamageCalculator/App.xaml.cs create mode 100644 DamageCalculator/DamageCalculator/CsgoHelper.cs create mode 100644 DamageCalculator/DamageCalculator/DDSImageParser.cs create mode 100644 DamageCalculator/DamageCalculator/DamageCalculator.csproj create mode 100644 DamageCalculator/DamageCalculator/Globals.cs create mode 100644 DamageCalculator/DamageCalculator/MainWindow.xaml create mode 100644 DamageCalculator/DamageCalculator/MainWindow.xaml.cs create mode 100644 DamageCalculator/DamageCalculator/Models/CsgoMapOverview.cs create mode 100644 DamageCalculator/DamageCalculator/Models/CsgoWeapon.cs create mode 100644 DamageCalculator/DamageCalculator/Models/MapPoint.cs create mode 100644 DamageCalculator/DamageCalculator/Models/Settings.cs create mode 100644 DamageCalculator/DamageCalculator/Models/SteamGame.cs create mode 100644 DamageCalculator/DamageCalculator/Models/SteamLibrary.cs create mode 100644 DamageCalculator/DamageCalculator/Properties/AssemblyInfo.cs create mode 100644 DamageCalculator/DamageCalculator/Properties/Resources.Designer.cs create mode 100644 DamageCalculator/DamageCalculator/Properties/Resources.resx create mode 100644 DamageCalculator/DamageCalculator/Properties/Settings.Designer.cs create mode 100644 DamageCalculator/DamageCalculator/Properties/Settings.settings create mode 100644 DamageCalculator/DamageCalculator/Serializer.cs create mode 100644 DamageCalculator/DamageCalculator/SteamHelper.cs create mode 100644 DamageCalculator/DamageCalculator/Themes/ColourfulDarkTheme.xaml create mode 100644 DamageCalculator/DamageCalculator/Themes/ColourfulDarkTheme.xaml.cs create mode 100644 DamageCalculator/DamageCalculator/Themes/ColourfulLightTheme.xaml create mode 100644 DamageCalculator/DamageCalculator/Themes/ColourfulLightTheme.xaml.cs create mode 100644 DamageCalculator/DamageCalculator/Themes/DarkTheme.xaml create mode 100644 DamageCalculator/DamageCalculator/Themes/DarkTheme.xaml.cs create mode 100644 DamageCalculator/DamageCalculator/Themes/LightTheme.xaml create mode 100644 DamageCalculator/DamageCalculator/Themes/LightTheme.xaml.cs create mode 100644 DamageCalculator/DamageCalculator/Themes/ThemesController.cs create mode 100644 DamageCalculator/DamageCalculator/ZatVdfParser/BackupVdfReader.cs create mode 100644 DamageCalculator/DamageCalculator/ZatVdfParser/Element.cs create mode 100644 DamageCalculator/DamageCalculator/ZatVdfParser/VdfFile.cs create mode 100644 DamageCalculator/DamageCalculator/bin/Debug/CSGO Damage Calculator.exe create mode 100644 DamageCalculator/DamageCalculator/bin/Debug/CSGO Damage Calculator.exe.config create mode 100644 DamageCalculator/DamageCalculator/bin/Debug/CSGO Damage Calculator.pdb create mode 100644 DamageCalculator/DamageCalculator/bin/Release/CSGO Damage Calculator.exe create mode 100644 DamageCalculator/DamageCalculator/bin/Release/CSGO Damage Calculator.exe.config create mode 100644 DamageCalculator/DamageCalculator/bin/Release/CSGO Damage Calculator.pdb create mode 100644 DamageCalculator/DamageCalculator/icon_a_site.png create mode 100644 DamageCalculator/DamageCalculator/icon_b_site.png create mode 100644 DamageCalculator/DamageCalculator/icon_ct.png create mode 100644 DamageCalculator/DamageCalculator/icon_t.png create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/.NETFramework,Version=v4.8.AssemblyAttributes.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/About.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/About.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/App.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/App.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/CSGO Damage Calculator.exe create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/CSGO Damage Calculator.g.resources create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/CSGO Damage Calculator.pdb create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/CSGO Damage Calculator_MarkupCompile.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/CSGO Damage Calculator_MarkupCompile.i.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/CSGO Damage Calculator_MarkupCompile.lref create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Config Manager.csproj.AssemblyReference.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Config Manager.csproj.CoreCompileInputs.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Config Manager.csproj.FileListAbsolute.txt create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Config Manager.csproj.GenerateResource.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Config Manager_Content.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Config Manager_MarkupCompile.i.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/DamageCalculator.csproj.AssemblyReference.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/DamageCalculator.csproj.CoreCompileInputs.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/DamageCalculator.csproj.FileListAbsolute.txt create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/DamageCalculator.csproj.GenerateResource.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Damage_Calculator.Properties.Resources.resources create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/DesignTimeResolveAssemblyReferences.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/MainWindow.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/MainWindow.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/TempPE/Properties.Resources.Designer.cs.dll create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Themes/ColourfulDarkTheme.baml create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Themes/ColourfulDarkTheme.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Themes/ColourfulDarkTheme.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Themes/ColourfulLightTheme.baml create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Themes/ColourfulLightTheme.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Themes/ColourfulLightTheme.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Themes/DarkTheme.baml create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Themes/DarkTheme.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Themes/DarkTheme.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Themes/LightTheme.baml create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Themes/LightTheme.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Debug/Themes/LightTheme.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/.NETFramework,Version=v4.8.AssemblyAttributes.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/About.baml create mode 100644 DamageCalculator/DamageCalculator/obj/Release/About.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/About.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/App.baml create mode 100644 DamageCalculator/DamageCalculator/obj/Release/App.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/App.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/CSGO Damage Calculator.exe create mode 100644 DamageCalculator/DamageCalculator/obj/Release/CSGO Damage Calculator.g.resources create mode 100644 DamageCalculator/DamageCalculator/obj/Release/CSGO Damage Calculator.pdb create mode 100644 DamageCalculator/DamageCalculator/obj/Release/CSGO Damage Calculator_MarkupCompile.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Release/CSGO Damage Calculator_MarkupCompile.i.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Release/CSGO Damage Calculator_MarkupCompile.i.lref create mode 100644 DamageCalculator/DamageCalculator/obj/Release/CSGO Damage Calculator_MarkupCompile.lref create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Config Manager.csproj.AssemblyReference.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Config Manager.csproj.CoreCompileInputs.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Config Manager.csproj.FileListAbsolute.txt create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Config Manager.csproj.GenerateResource.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Config Manager_Content.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Config Manager_MarkupCompile.i.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Config Manager_MarkupCompile.i.lref create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Config_Manager.Properties.Resources.resources create mode 100644 DamageCalculator/DamageCalculator/obj/Release/DesignTimeResolveAssemblyReferences.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache create mode 100644 DamageCalculator/DamageCalculator/obj/Release/MainWindow.baml create mode 100644 DamageCalculator/DamageCalculator/obj/Release/MainWindow.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/MainWindow.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/TempPE/Properties.Resources.Designer.cs.dll create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Themes/ColourfulDarkTheme.baml create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Themes/ColourfulDarkTheme.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Themes/ColourfulDarkTheme.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Themes/ColourfulLightTheme.baml create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Themes/ColourfulLightTheme.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Themes/ColourfulLightTheme.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Themes/DarkTheme.baml create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Themes/DarkTheme.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Themes/DarkTheme.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Themes/LightTheme.baml create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Themes/LightTheme.g.cs create mode 100644 DamageCalculator/DamageCalculator/obj/Release/Themes/LightTheme.g.i.cs create mode 100644 DamageCalculator/DamageCalculator/packages.config create mode 100644 DamageCalculator/packages/Gameloop.Vdf.0.6.1/.signature.p7s create mode 100644 DamageCalculator/packages/Gameloop.Vdf.0.6.1/Gameloop.Vdf.0.6.1.nupkg create mode 100644 DamageCalculator/packages/Gameloop.Vdf.0.6.1/lib/net45/Gameloop.Vdf.dll create mode 100644 DamageCalculator/packages/Gameloop.Vdf.0.6.1/lib/net45/Gameloop.Vdf.xml create mode 100644 DamageCalculator/packages/Gameloop.Vdf.0.6.1/lib/netstandard1.0/Gameloop.Vdf.dll create mode 100644 DamageCalculator/packages/Gameloop.Vdf.0.6.1/lib/netstandard1.0/Gameloop.Vdf.xml create mode 100644 DamageCalculator/packages/Gameloop.Vdf.0.6.1/readme.txt create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/.signature.p7s create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/LICENSE.md create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/Newtonsoft.Json.12.0.3.nupkg create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/net20/Newtonsoft.Json.dll create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/net20/Newtonsoft.Json.xml create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/net35/Newtonsoft.Json.dll create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/net35/Newtonsoft.Json.xml create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/net40/Newtonsoft.Json.dll create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/net40/Newtonsoft.Json.xml create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/net45/Newtonsoft.Json.dll create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/net45/Newtonsoft.Json.xml create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/netstandard1.0/Newtonsoft.Json.dll create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/netstandard1.0/Newtonsoft.Json.xml create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/netstandard1.3/Newtonsoft.Json.dll create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/netstandard1.3/Newtonsoft.Json.xml create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/netstandard2.0/Newtonsoft.Json.dll create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/netstandard2.0/Newtonsoft.Json.xml create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.dll create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.xml create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.dll create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.xml create mode 100644 DamageCalculator/packages/Newtonsoft.Json.12.0.3/packageIcon.png diff --git a/DamageCalculator/.vs/Config Manager/v16/.suo b/DamageCalculator/.vs/Config Manager/v16/.suo new file mode 100644 index 0000000000000000000000000000000000000000..4953adef12c79144cdeaa8e6db864689e4429b28 GIT binary patch literal 197632 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 Q9Wb!nRrQK;N#*bVKM^1zTL1t6 literal 0 HcmV?d00001 diff --git a/DamageCalculator/.vs/DamageCalculator/v16/.suo b/DamageCalculator/.vs/DamageCalculator/v16/.suo new file mode 100644 index 0000000000000000000000000000000000000000..e638e135eb6045ca7ac1ade803b1853a8943e47a GIT binary patch literal 57344 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 F{6F6(r|JLz literal 0 HcmV?d00001 diff --git a/DamageCalculator/DamageCalculator.sln b/DamageCalculator/DamageCalculator.sln new file mode 100644 index 0000000..fcfbaee --- /dev/null +++ b/DamageCalculator/DamageCalculator.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31515.178 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Damage Calculator", "DamageCalculator\DamageCalculator.csproj", "{7A7AE40F-8677-44E3-873A-4AB7C9FCFEB4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7A7AE40F-8677-44E3-873A-4AB7C9FCFEB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A7AE40F-8677-44E3-873A-4AB7C9FCFEB4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A7AE40F-8677-44E3-873A-4AB7C9FCFEB4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A7AE40F-8677-44E3-873A-4AB7C9FCFEB4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DB4B39C3-0168-457A-BB47-A934F038897A} + EndGlobalSection +EndGlobal diff --git a/DamageCalculator/DamageCalculator/27.ico b/DamageCalculator/DamageCalculator/27.ico new file mode 100644 index 0000000000000000000000000000000000000000..9279709a43ee6628cc4a3fe224ecda969141e5f4 GIT binary patch literal 137478 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
  • ;rz5596laefHmC1K1f1 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 bLkX6bunN5hVMYfaNMxWvsWX}CIQ#zryyA8t literal 0 HcmV?d00001 diff --git a/DamageCalculator/DamageCalculator/About.xaml b/DamageCalculator/DamageCalculator/About.xaml new file mode 100644 index 0000000..7e48e49 --- /dev/null +++ b/DamageCalculator/DamageCalculator/About.xaml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + diff --git a/DamageCalculator/DamageCalculator/About.xaml.cs b/DamageCalculator/DamageCalculator/About.xaml.cs new file mode 100644 index 0000000..77e35b5 --- /dev/null +++ b/DamageCalculator/DamageCalculator/About.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace Damage_Calculator +{ + /// + /// Interaction logic for About.xaml + /// + public partial class About : Window + { + public About() + { + InitializeComponent(); + txtVersion.Text = "Version " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); + } + } +} diff --git a/DamageCalculator/DamageCalculator/App.config b/DamageCalculator/DamageCalculator/App.config new file mode 100644 index 0000000..193aecc --- /dev/null +++ b/DamageCalculator/DamageCalculator/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DamageCalculator/DamageCalculator/App.xaml b/DamageCalculator/DamageCalculator/App.xaml new file mode 100644 index 0000000..812bf13 --- /dev/null +++ b/DamageCalculator/DamageCalculator/App.xaml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/DamageCalculator/DamageCalculator/App.xaml.cs b/DamageCalculator/DamageCalculator/App.xaml.cs new file mode 100644 index 0000000..4c26115 --- /dev/null +++ b/DamageCalculator/DamageCalculator/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace Damage_Calculator +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/DamageCalculator/DamageCalculator/CsgoHelper.cs b/DamageCalculator/DamageCalculator/CsgoHelper.cs new file mode 100644 index 0000000..a3ce3a3 --- /dev/null +++ b/DamageCalculator/DamageCalculator/CsgoHelper.cs @@ -0,0 +1,342 @@ +using Damage_Calculator.Models; +using Damage_Calculator.ZatVdfParser; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Damage_Calculator +{ + public class CsgoHelper + { + public string CsgoPath { get; set; } + + /// + /// Gets the prefixes allowed for maps when using . + /// + private readonly string[] validMapPrefixes = new[] + { + "de", + "cs", + "dz", + "ar" + }; + + /// + /// Gets the files relative to the CS:GO install path that are checked when validating. + /// + private readonly string[] filesToValidate = new[] + { + "csgo\\scripts\\items\\items_game.txt" // Item info (weapon stats etc.) + }; + + /// + /// Gets the directories relative to the CS:GO install path that are checked when validating. + /// + private readonly string[] directoriesToValidate = new[] + { + "csgo\\resource\\overviews" // Map overviews + }; + + public CsgoHelper() + { + // Nothing to do + } + + public CsgoHelper(string csgoPath) + { + this.CsgoPath = csgoPath; + } + + /// + /// Validates files and directories for CS:GO installed in the path. + /// + /// whether the files and directories exist. + public bool Validate() + { + return this.Validate(this.CsgoPath); + } + + + /// + /// Validates files and directories for CS:GO installed in the given path. + /// + /// The path to the CS:GO install directory, in which the executable resides. + /// whether the files and directories exist. + public bool Validate(string csgoPath) + { + foreach (string file in this.filesToValidate) + { + if (!File.Exists(Path.Combine(csgoPath, file))) + return false; + } + + foreach (string dir in this.directoriesToValidate) + { + if (!Directory.Exists(Path.Combine(csgoPath, dir))) + return false; + } + + return true; + } + + public List GetMaps() + { + List mapTextFiles = Directory.GetFiles(System.IO.Path.Combine(this.CsgoPath, "csgo\\resource\\overviews")).ToList().Where(f => f.ToLower().EndsWith(".txt")).Where(f => + this.mapFileNameValid(f)).ToList(); + + List maps = new List(); + + foreach (string file in mapTextFiles) + { + string potentialRadarFile = System.IO.Path.Combine(this.CsgoPath, "csgo\\resource\\overviews", System.IO.Path.GetFileNameWithoutExtension(file) + "_radar.dds"); + var map = new CsgoMapOverview(); + if (File.Exists(potentialRadarFile)) + { + map.MapImagePath = potentialRadarFile; + } + + var vdf = new VDFFile(file); + if (vdf.RootElements.Count > 0) + { + var rootNode = vdf.RootElements.First(); + if (float.TryParse(rootNode["scale"]?.Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float scale)) + { + map.MapSizeMultiplier = scale; + } + if (float.TryParse(rootNode["pos_x"]?.Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float posX)) + { + map.UpperLeftWorldXCoordinate = posX; + } + if (float.TryParse(rootNode["pos_y"]?.Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float posY)) + { + map.UpperLeftWorldYCoordinate = posY; + } + if (float.TryParse(rootNode["CTSpawn_x"]?.Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float ctX)) + { + map.CTSpawnMultiplierX = ctX; + } + if (float.TryParse(rootNode["CTSpawn_y"]?.Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float ctY)) + { + map.CTSpawnMultiplierY = ctY; + } + if (float.TryParse(rootNode["TSpawn_x"]?.Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float tX)) + { + map.TSpawnMultiplierX = tX; + } + if (float.TryParse(rootNode["TSpawn_y"]?.Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float tY)) + { + map.TSpawnMultiplierY = tY; + } + if (float.TryParse(rootNode["bombA_x"]?.Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float bombAX)) + { + map.BombAX = bombAX; + } + if (float.TryParse(rootNode["bombA_y"]?.Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float bombAY)) + { + map.BombAY = bombAY; + } + if (float.TryParse(rootNode["bombB_x"]?.Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float bombBX)) + { + map.BombBX = bombBX; + } + if (float.TryParse(rootNode["bombB_y"]?.Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float bombBY)) + { + map.BombBY = bombBY; + } + } + + map.MapFileName = System.IO.Path.GetFileNameWithoutExtension(file).Split('_').Last(); + + DDSImage image; + try + { + image = new DDSImage(System.IO.File.ReadAllBytes(map.MapImagePath)); + } + catch + { + continue; + } + + if (image.BitmapImage.Width != image.BitmapImage.Height) + continue; + + System.Windows.Application.Current.Dispatcher.Invoke((Action)delegate + { + map.MapImage = Globals.BitmapToImageSource(image.BitmapImage); + }); + + maps.Add(map); + } + + return maps; + } + + public List GetWeapons() + { + string filePath = Path.Combine(this.CsgoPath, "csgo\\scripts\\items\\items_game.txt"); + if (!File.Exists(filePath)) + return null; + + var vdfItems = new VDFFile(filePath); + Element prefabs = vdfItems["items_game"]?["prefabs"]; + Element items = vdfItems["items_game"]?["items"]; + + if (prefabs == null || items == null) + return null; + + var weapons = new List(); + + foreach(var item in items.Children) + { + string itemPrefab = item["prefab"]?.Value; + string itemName = item["name"].Value; + + if (itemPrefab == null || !itemName.StartsWith("weapon_")) + continue; + + var weapon = new CsgoWeapon(); + weapon.ClassName = itemName; + + if(this.tryPopulateWeapon(weapon, prefabs, itemPrefab)) + { + weapons.Add(weapon); + } + } + + return weapons; + } + + private bool tryPopulateWeapon(CsgoWeapon weapon, Element prefabs, string prefabName, List prefabTrace = null) + { + Element prefab = prefabs[prefabName]; + + if (prefab == null) + // Prefab not existent (example was prefab named "valve csgo_tool") + return false; + + string nextPrefab = prefab["prefab"]?.Value; + + if (prefab == null || (nextPrefab == null && prefabTrace?.FirstOrDefault(pr => pr == "primary" || pr == "secondary") == null)) + // We've reached the end of abstraction but it wasn't found to be primary nor secondary + return false; + + bool gatheredAllInfo = true; + + Element attributes = prefab["attributes"]; + + if (attributes == null) + return false; + + // =========================== ATTRIBUTES =========================== // + + // Base damage + if (weapon.BaseDamage == -1) + { + string damage = attributes["damage"]?.Value; + if (damage != null) + { + // damage field exists + if (int.TryParse(damage, out int dmg)) + { + weapon.BaseDamage = dmg; + } + } + else + gatheredAllInfo = false; + } + + // Armor penetration + if (weapon.ArmorPenetration == -1) + { + string penetration = attributes["armor ratio"]?.Value; + if (penetration != null) + { + // Armor penetration field exists + if (float.TryParse(penetration, NumberStyles.Any, CultureInfo.InvariantCulture, out float pen)) + { + weapon.ArmorPenetration = pen * 100f / 2f; + } + } + else + gatheredAllInfo = false; + } + + // Damage dropoff + if (weapon.DamageDropoff == -1) + { + string dropoff = attributes["range modifier"]?.Value; + if (dropoff != null) + { + // Damage dropoff field exists + if (double.TryParse(dropoff, NumberStyles.Any, CultureInfo.InvariantCulture, out double drop)) + { + weapon.DamageDropoff = drop; + } + } + else + gatheredAllInfo = false; + } + + // Max range + if (weapon.MaxBulletRange == -1) + { + string maxrange = attributes["range"]?.Value; + if (maxrange != null) + { + // Max range field exists + if (int.TryParse(maxrange, out int range)) + { + weapon.MaxBulletRange = range; + } + } + else + gatheredAllInfo = false; + } + + // Headshot modifier + if (weapon.HeadshotModifier == -1) + { + string headshotModifier = attributes["headshot multiplier"]?.Value; + if (headshotModifier != null) + { + // Headshot modifier field exists + if (float.TryParse(headshotModifier, NumberStyles.Any, CultureInfo.InvariantCulture, out float hs)) + { + weapon.HeadshotModifier = hs; + } + } + else + gatheredAllInfo = false; + } + + // ================================================================== // + + if (gatheredAllInfo || nextPrefab == null) + return true; // ? + + if (prefabTrace == null) + prefabTrace = new List(); + + prefabTrace.Add(prefab.Name); + + return this.tryPopulateWeapon(weapon, prefabs, nextPrefab, prefabTrace); + } + + private bool mapFileNameValid(string mapPath) + { + string fileName = Path.GetFileName(mapPath.ToLower()); + + foreach(string prefix in this.validMapPrefixes) + { + if (fileName.StartsWith(prefix.ToLower())) + return true; + } + + return false; + } + } +} diff --git a/DamageCalculator/DamageCalculator/DDSImageParser.cs b/DamageCalculator/DamageCalculator/DDSImageParser.cs new file mode 100644 index 0000000..cc172f2 --- /dev/null +++ b/DamageCalculator/DamageCalculator/DDSImageParser.cs @@ -0,0 +1,2015 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using System.IO; + +namespace Damage_Calculator +{ + #region DDSImage Class + public class DDSImage : IDisposable + { + #region Variables + private bool m_isValid = false; + private System.Drawing.Bitmap m_bitmap = null; + #endregion + + #region Constructor/Destructor + public DDSImage(byte[] ddsImage) + { + if (ddsImage == null) return; + if (ddsImage.Length == 0) return; + + using (MemoryStream stream = new MemoryStream(ddsImage.Length)) + { + stream.Write(ddsImage, 0, ddsImage.Length); + stream.Seek(0, SeekOrigin.Begin); + + using (BinaryReader reader = new BinaryReader(stream)) + { + this.Parse(reader); + } + } + } + + public DDSImage(Stream ddsImage) + { + if (ddsImage == null) return; + if (!ddsImage.CanRead) return; + + using (BinaryReader reader = new BinaryReader(ddsImage)) + { + this.Parse(reader); + } + } + + private DDSImage(System.Drawing.Bitmap bitmap) + { + this.m_bitmap = bitmap; + } + #endregion + + #region Override Methods + #endregion + + #region Private Methods + private void Parse(BinaryReader reader) + { + DDSStruct header = new DDSStruct(); + PixelFormat pixelFormat = PixelFormat.UNKNOWN; + byte[] data = null; + + if (this.ReadHeader(reader, ref header)) + { + this.m_isValid = true; + // patches for stuff + if (header.depth == 0) header.depth = 1; + + uint blocksize = 0; + pixelFormat = this.GetFormat(header, ref blocksize); + if (pixelFormat == PixelFormat.UNKNOWN) + { + throw new InvalidFileHeaderException(); + } + + data = this.ReadData(reader, header); + if (data != null) + { + byte[] rawData = this.DecompressData(header, data, pixelFormat); + this.m_bitmap = this.CreateBitmap((int)header.width, (int)header.height, rawData); + } + } + } + + private byte[] ReadData(BinaryReader reader, DDSStruct header) + { + byte[] compdata = null; + uint compsize = 0; + + if ((header.flags & DDSD_LINEARSIZE) > 1) + { + compdata = reader.ReadBytes((int)header.sizeorpitch); + compsize = (uint)compdata.Length; + } + else + { + uint bps = header.width * header.pixelformat.rgbbitcount / 8; + compsize = bps * header.height * header.depth; + compdata = new byte[compsize]; + + MemoryStream mem = new MemoryStream((int)compsize); + + byte[] temp; + for (int z = 0; z < header.depth; z++) + { + for (int y = 0; y < header.height; y++) + { + temp = reader.ReadBytes((int)bps); + mem.Write(temp, 0, temp.Length); + } + } + mem.Seek(0, SeekOrigin.Begin); + + mem.Read(compdata, 0, compdata.Length); + mem.Close(); + } + + return compdata; + } + + private System.Drawing.Bitmap CreateBitmap(int width, int height, byte[] rawData) + { + System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + + BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height) + , ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + IntPtr scan = data.Scan0; + int size = bitmap.Width * bitmap.Height * 4; + + unsafe + { + byte* p = (byte*)scan; + for (int i = 0; i < size; i += 4) + { + // iterate through bytes. + // Bitmap stores it's data in RGBA order. + // DDS stores it's data in BGRA order. + p[i] = rawData[i + 2]; // blue + p[i + 1] = rawData[i + 1]; // green + p[i + 2] = rawData[i]; // red + p[i + 3] = rawData[i + 3]; // alpha + } + } + + bitmap.UnlockBits(data); + return bitmap; + } + + private bool ReadHeader(BinaryReader reader, ref DDSStruct header) + { + byte[] signature = reader.ReadBytes(4); + if (!(signature[0] == 'D' && signature[1] == 'D' && signature[2] == 'S' && signature[3] == ' ')) + return false; + + header.size = reader.ReadUInt32(); + if (header.size != 124) + return false; + + //convert the data + header.flags = reader.ReadUInt32(); + header.height = reader.ReadUInt32(); + header.width = reader.ReadUInt32(); + header.sizeorpitch = reader.ReadUInt32(); + header.depth = reader.ReadUInt32(); + header.mipmapcount = reader.ReadUInt32(); + header.alphabitdepth = reader.ReadUInt32(); + + header.reserved = new uint[10]; + for (int i = 0; i < 10; i++) + { + header.reserved[i] = reader.ReadUInt32(); + } + + //pixelfromat + header.pixelformat.size = reader.ReadUInt32(); + header.pixelformat.flags = reader.ReadUInt32(); + header.pixelformat.fourcc = reader.ReadUInt32(); + header.pixelformat.rgbbitcount = reader.ReadUInt32(); + header.pixelformat.rbitmask = reader.ReadUInt32(); + header.pixelformat.gbitmask = reader.ReadUInt32(); + header.pixelformat.bbitmask = reader.ReadUInt32(); + header.pixelformat.alphabitmask = reader.ReadUInt32(); + + //caps + header.ddscaps.caps1 = reader.ReadUInt32(); + header.ddscaps.caps2 = reader.ReadUInt32(); + header.ddscaps.caps3 = reader.ReadUInt32(); + header.ddscaps.caps4 = reader.ReadUInt32(); + header.texturestage = reader.ReadUInt32(); + + return true; + } + + private PixelFormat GetFormat(DDSStruct header, ref uint blocksize) + { + PixelFormat format = PixelFormat.UNKNOWN; + if ((header.pixelformat.flags & DDPF_FOURCC) == DDPF_FOURCC) + { + blocksize = ((header.width + 3) / 4) * ((header.height + 3) / 4) * header.depth; + + switch (header.pixelformat.fourcc) + { + case FOURCC_DXT1: + format = PixelFormat.DXT1; + blocksize *= 8; + break; + + case FOURCC_DXT2: + format = PixelFormat.DXT2; + blocksize *= 16; + break; + + case FOURCC_DXT3: + format = PixelFormat.DXT3; + blocksize *= 16; + break; + + case FOURCC_DXT4: + format = PixelFormat.DXT4; + blocksize *= 16; + break; + + case FOURCC_DXT5: + format = PixelFormat.DXT5; + blocksize *= 16; + break; + + case FOURCC_ATI1: + format = PixelFormat.ATI1N; + blocksize *= 8; + break; + + case FOURCC_ATI2: + format = PixelFormat.THREEDC; + blocksize *= 16; + break; + + case FOURCC_RXGB: + format = PixelFormat.RXGB; + blocksize *= 16; + break; + + case FOURCC_DOLLARNULL: + format = PixelFormat.A16B16G16R16; + blocksize = header.width * header.height * header.depth * 8; + break; + + case FOURCC_oNULL: + format = PixelFormat.R16F; + blocksize = header.width * header.height * header.depth * 2; + break; + + case FOURCC_pNULL: + format = PixelFormat.G16R16F; + blocksize = header.width * header.height * header.depth * 4; + break; + + case FOURCC_qNULL: + format = PixelFormat.A16B16G16R16F; + blocksize = header.width * header.height * header.depth * 8; + break; + + case FOURCC_rNULL: + format = PixelFormat.R32F; + blocksize = header.width * header.height * header.depth * 4; + break; + + case FOURCC_sNULL: + format = PixelFormat.G32R32F; + blocksize = header.width * header.height * header.depth * 8; + break; + + case FOURCC_tNULL: + format = PixelFormat.A32B32G32R32F; + blocksize = header.width * header.height * header.depth * 16; + break; + + default: + format = PixelFormat.UNKNOWN; + blocksize *= 16; + break; + } // switch + } + else + { + // uncompressed image + if ((header.pixelformat.flags & DDPF_LUMINANCE) == DDPF_LUMINANCE) + { + if ((header.pixelformat.flags & DDPF_ALPHAPIXELS) == DDPF_ALPHAPIXELS) + { + format = PixelFormat.LUMINANCE_ALPHA; + } + else + { + format = PixelFormat.LUMINANCE; + } + } + else + { + if ((header.pixelformat.flags & DDPF_ALPHAPIXELS) == DDPF_ALPHAPIXELS) + { + format = PixelFormat.RGBA; + } + else + { + format = PixelFormat.RGB; + } + } + + blocksize = (header.width * header.height * header.depth * (header.pixelformat.rgbbitcount >> 3)); + } + + return format; + } + + #region Helper Methods + // iCompFormatToBpp + private uint PixelFormatToBpp(PixelFormat pf, uint rgbbitcount) + { + switch (pf) + { + case PixelFormat.LUMINANCE: + case PixelFormat.LUMINANCE_ALPHA: + case PixelFormat.RGBA: + case PixelFormat.RGB: + return rgbbitcount / 8; + + case PixelFormat.THREEDC: + case PixelFormat.RXGB: + return 3; + + case PixelFormat.ATI1N: + return 1; + + case PixelFormat.R16F: + return 2; + + case PixelFormat.A16B16G16R16: + case PixelFormat.A16B16G16R16F: + case PixelFormat.G32R32F: + return 8; + + case PixelFormat.A32B32G32R32F: + return 16; + + default: + return 4; + } + } + + // iCompFormatToBpc + private uint PixelFormatToBpc(PixelFormat pf) + { + switch (pf) + { + case PixelFormat.R16F: + case PixelFormat.G16R16F: + case PixelFormat.A16B16G16R16F: + return 4; + + case PixelFormat.R32F: + case PixelFormat.G32R32F: + case PixelFormat.A32B32G32R32F: + return 4; + + case PixelFormat.A16B16G16R16: + return 2; + + default: + return 1; + } + } + + private bool Check16BitComponents(DDSStruct header) + { + if (header.pixelformat.rgbbitcount != 32) + return false; + // a2b10g10r10 format + if (header.pixelformat.rbitmask == 0x3FF00000 && header.pixelformat.gbitmask == 0x000FFC00 && header.pixelformat.bbitmask == 0x000003FF + && header.pixelformat.alphabitmask == 0xC0000000) + return true; + // a2r10g10b10 format + else if (header.pixelformat.rbitmask == 0x000003FF && header.pixelformat.gbitmask == 0x000FFC00 && header.pixelformat.bbitmask == 0x3FF00000 + && header.pixelformat.alphabitmask == 0xC0000000) + return true; + + return false; + } + + private void CorrectPremult(uint pixnum, ref byte[] buffer) + { + for (uint i = 0; i < pixnum; i++) + { + byte alpha = buffer[i + 3]; + if (alpha == 0) continue; + int red = (buffer[i] << 8) / alpha; + int green = (buffer[i + 1] << 8) / alpha; + int blue = (buffer[i + 2] << 8) / alpha; + + buffer[i] = (byte)red; + buffer[i + 1] = (byte)green; + buffer[i + 2] = (byte)blue; + } + } + + private void ComputeMaskParams(uint mask, ref int shift1, ref int mul, ref int shift2) + { + shift1 = 0; mul = 1; shift2 = 0; + while ((mask & 1) == 0) + { + mask >>= 1; + shift1++; + } + uint bc = 0; + while ((mask & (1 << (int)bc)) != 0) bc++; + while ((mask * mul) < 255) + mul = (mul << (int)bc) + 1; + mask *= (uint)mul; + + while ((mask & ~0xff) != 0) + { + mask >>= 1; + shift2++; + } + } + + private unsafe void DxtcReadColors(byte* data, ref Colour8888[] op) + { + byte r0, g0, b0, r1, g1, b1; + + b0 = (byte)(data[0] & 0x1F); + g0 = (byte)(((data[0] & 0xE0) >> 5) | ((data[1] & 0x7) << 3)); + r0 = (byte)((data[1] & 0xF8) >> 3); + + b1 = (byte)(data[2] & 0x1F); + g1 = (byte)(((data[2] & 0xE0) >> 5) | ((data[3] & 0x7) << 3)); + r1 = (byte)((data[3] & 0xF8) >> 3); + + op[0].red = (byte)(r0 << 3 | r0 >> 2); + op[0].green = (byte)(g0 << 2 | g0 >> 3); + op[0].blue = (byte)(b0 << 3 | b0 >> 2); + + op[1].red = (byte)(r1 << 3 | r1 >> 2); + op[1].green = (byte)(g1 << 2 | g1 >> 3); + op[1].blue = (byte)(b1 << 3 | b1 >> 2); + } + + private void DxtcReadColor(ushort data, ref Colour8888 op) + { + byte r, g, b; + + b = (byte)(data & 0x1f); + g = (byte)((data & 0x7E0) >> 5); + r = (byte)((data & 0xF800) >> 11); + + op.red = (byte)(r << 3 | r >> 2); + op.green = (byte)(g << 2 | g >> 3); + op.blue = (byte)(b << 3 | r >> 2); + } + + private unsafe void DxtcReadColors(byte* data, ref Colour565 color_0, ref Colour565 color_1) + { + color_0.blue = (byte)(data[0] & 0x1F); + color_0.green = (byte)(((data[0] & 0xE0) >> 5) | ((data[1] & 0x7) << 3)); + color_0.red = (byte)((data[1] & 0xF8) >> 3); + + color_0.blue = (byte)(data[2] & 0x1F); + color_0.green = (byte)(((data[2] & 0xE0) >> 5) | ((data[3] & 0x7) << 3)); + color_0.red = (byte)((data[3] & 0xF8) >> 3); + } + + private void GetBitsFromMask(uint mask, ref uint shiftLeft, ref uint shiftRight) + { + uint temp, i; + + if (mask == 0) + { + shiftLeft = shiftRight = 0; + return; + } + + temp = mask; + for (i = 0; i < 32; i++, temp >>= 1) + { + if ((temp & 1) != 0) + break; + } + shiftRight = i; + + // Temp is preserved, so use it again: + for (i = 0; i < 8; i++, temp >>= 1) + { + if ((temp & 1) == 0) + break; + } + shiftLeft = 8 - i; + } + + // This function simply counts how many contiguous bits are in the mask. + private uint CountBitsFromMask(uint mask) + { + uint i, testBit = 0x01, count = 0; + bool foundBit = false; + + for (i = 0; i < 32; i++, testBit <<= 1) + { + if ((mask & testBit) != 0) + { + if (!foundBit) + foundBit = true; + count++; + } + else if (foundBit) + return count; + } + + return count; + } + + private uint HalfToFloat(ushort y) + { + int s = (y >> 15) & 0x00000001; + int e = (y >> 10) & 0x0000001f; + int m = y & 0x000003ff; + + if (e == 0) + { + if (m == 0) + { + // + // Plus or minus zero + // + return (uint)(s << 31); + } + else + { + // + // Denormalized number -- renormalize it + // + while ((m & 0x00000400) == 0) + { + m <<= 1; + e -= 1; + } + + e += 1; + m &= ~0x00000400; + } + } + else if (e == 31) + { + if (m == 0) + { + // + // Positive or negative infinity + // + return (uint)((s << 31) | 0x7f800000); + } + else + { + // + // Nan -- preserve sign and significand bits + // + return (uint)((s << 31) | 0x7f800000 | (m << 13)); + } + } + + // + // Normalized number + // + e = e + (127 - 15); + m = m << 13; + + // + // Assemble s, e and m. + // + return (uint)((s << 31) | (e << 23) | m); + } + + private unsafe void ConvFloat16ToFloat32(uint* dest, ushort* src, uint size) + { + uint i; + for (i = 0; i < size; ++i, ++dest, ++src) + { + //float: 1 sign bit, 8 exponent bits, 23 mantissa bits + //half: 1 sign bit, 5 exponent bits, 10 mantissa bits + *dest = HalfToFloat(*src); + } + } + + private unsafe void ConvG16R16ToFloat32(uint* dest, ushort* src, uint size) + { + uint i; + for (i = 0; i < size; i += 3) + { + //float: 1 sign bit, 8 exponent bits, 23 mantissa bits + //half: 1 sign bit, 5 exponent bits, 10 mantissa bits + *dest++ = HalfToFloat(*src++); + *dest++ = HalfToFloat(*src++); + *((float*)dest++) = 1.0f; + } + } + + private unsafe void ConvR16ToFloat32(uint* dest, ushort* src, uint size) + { + uint i; + for (i = 0; i < size; i += 3) + { + //float: 1 sign bit, 8 exponent bits, 23 mantissa bits + //half: 1 sign bit, 5 exponent bits, 10 mantissa bits + *dest++ = HalfToFloat(*src++); + *((float*)dest++) = 1.0f; + *((float*)dest++) = 1.0f; + } + } + #endregion + + #region Decompress Methods + private byte[] DecompressData(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + System.Diagnostics.Debug.WriteLine(pixelFormat); + // allocate bitmap + byte[] rawData = null; + + switch (pixelFormat) + { + case PixelFormat.RGBA: + rawData = this.DecompressRGBA(header, data, pixelFormat); + break; + + case PixelFormat.RGB: + rawData = this.DecompressRGB(header, data, pixelFormat); + break; + + case PixelFormat.LUMINANCE: + case PixelFormat.LUMINANCE_ALPHA: + rawData = this.DecompressLum(header, data, pixelFormat); + break; + + case PixelFormat.DXT1: + rawData = this.DecompressDXT1(header, data, pixelFormat); + break; + + case PixelFormat.DXT2: + rawData = this.DecompressDXT2(header, data, pixelFormat); + break; + + case PixelFormat.DXT3: + rawData = this.DecompressDXT3(header, data, pixelFormat); + break; + + case PixelFormat.DXT4: + rawData = this.DecompressDXT4(header, data, pixelFormat); + break; + + case PixelFormat.DXT5: + rawData = this.DecompressDXT5(header, data, pixelFormat); + break; + + case PixelFormat.THREEDC: + rawData = this.Decompress3Dc(header, data, pixelFormat); + break; + + case PixelFormat.ATI1N: + rawData = this.DecompressAti1n(header, data, pixelFormat); + break; + + case PixelFormat.RXGB: + rawData = this.DecompressRXGB(header, data, pixelFormat); + break; + + case PixelFormat.R16F: + case PixelFormat.G16R16F: + case PixelFormat.A16B16G16R16F: + case PixelFormat.R32F: + case PixelFormat.G32R32F: + case PixelFormat.A32B32G32R32F: + rawData = this.DecompressFloat(header, data, pixelFormat); + break; + + default: + throw new UnknownFileFormatException(); + } + + return rawData; + } + + private unsafe byte[] DecompressDXT1(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int bpp = (int)(PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); + int bps = (int)(header.width * bpp * PixelFormatToBpc(pixelFormat)); + int sizeofplane = (int)(bps * header.height); + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + // DXT1 decompressor + byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; + + Colour8888[] colours = new Colour8888[4]; + colours[0].alpha = 0xFF; + colours[1].alpha = 0xFF; + colours[2].alpha = 0xFF; + + fixed (byte* bytePtr = data) + { + byte* temp = bytePtr; + for (int z = 0; z < depth; z++) + { + for (int y = 0; y < height; y += 4) + { + for (int x = 0; x < width; x += 4) + { + ushort colour0 = *((ushort*)temp); + ushort colour1 = *((ushort*)(temp + 2)); + DxtcReadColor(colour0, ref colours[0]); + DxtcReadColor(colour1, ref colours[1]); + + uint bitmask = ((uint*)temp)[1]; + temp += 8; + + if (colour0 > colour1) + { + // Four-color block: derive the other two colors. + // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3 + // These 2-bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3); + colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3); + colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3); + //colours[2].alpha = 0xFF; + + colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3); + colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3); + colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3); + colours[3].alpha = 0xFF; + } + else + { + // Three-color block: derive the other color. + // 00 = color_0, 01 = color_1, 10 = color_2, + // 11 = transparent. + // These 2-bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + colours[2].blue = (byte)((colours[0].blue + colours[1].blue) / 2); + colours[2].green = (byte)((colours[0].green + colours[1].green) / 2); + colours[2].red = (byte)((colours[0].red + colours[1].red) / 2); + //colours[2].alpha = 0xFF; + + colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3); + colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3); + colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3); + colours[3].alpha = 0x00; + } + + for (int j = 0, k = 0; j < 4; j++) + { + for (int i = 0; i < 4; i++, k++) + { + int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2); + Colour8888 col = colours[select]; + if (((x + i) < width) && ((y + j) < height)) + { + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp); + rawData[offset + 0] = (byte)col.red; + rawData[offset + 1] = (byte)col.green; + rawData[offset + 2] = (byte)col.blue; + rawData[offset + 3] = (byte)col.alpha; + } + } + } + } + } + } + } + + return rawData; + } + + private byte[] DecompressDXT2(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + // Can do color & alpha same as dxt3, but color is pre-multiplied + // so the result will be wrong unless corrected. + byte[] rawData = DecompressDXT3(header, data, pixelFormat); + CorrectPremult((uint)(width * height * depth), ref rawData); + + return rawData; + } + + private unsafe byte[] DecompressDXT3(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int bpp = (int)(PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); + int bps = (int)(header.width * bpp * PixelFormatToBpc(pixelFormat)); + int sizeofplane = (int)(bps * header.height); + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + // DXT3 decompressor + byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; + Colour8888[] colours = new Colour8888[4]; + + fixed (byte* bytePtr = data) + { + byte* temp = bytePtr; + for (int z = 0; z < depth; z++) + { + for (int y = 0; y < height; y += 4) + { + for (int x = 0; x < width; x += 4) + { + byte* alpha = temp; + temp += 8; + + DxtcReadColors(temp, ref colours); + temp += 4; + + uint bitmask = ((uint*)temp)[1]; + temp += 4; + + // Four-color block: derive the other two colors. + // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3 + // These 2-bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3); + colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3); + colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3); + //colours[2].alpha = 0xFF; + + colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3); + colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3); + colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3); + //colours[3].alpha = 0xFF; + + for (int j = 0, k = 0; j < 4; j++) + { + for (int i = 0; i < 4; k++, i++) + { + int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2); + + if (((x + i) < width) && ((y + j) < height)) + { + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp); + rawData[offset + 0] = (byte)colours[select].red; + rawData[offset + 1] = (byte)colours[select].green; + rawData[offset + 2] = (byte)colours[select].blue; + } + } + } + + for (int j = 0; j < 4; j++) + { + //ushort word = (ushort)(alpha[2 * j] + 256 * alpha[2 * j + 1]); + ushort word = (ushort)(alpha[2 * j] | (alpha[2 * j + 1] << 8)); + for (int i = 0; i < 4; i++) + { + if (((x + i) < width) && ((y + j) < height)) + { + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); + rawData[offset] = (byte)(word & 0x0F); + rawData[offset] = (byte)(rawData[offset] | (rawData[offset] << 4)); + } + word >>= 4; + } + } + } + } + } + } + return rawData; + } + + private byte[] DecompressDXT4(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + // Can do color & alpha same as dxt5, but color is pre-multiplied + // so the result will be wrong unless corrected. + byte[] rawData = DecompressDXT5(header, data, pixelFormat); + CorrectPremult((uint)(width * height * depth), ref rawData); + + return rawData; + } + + private unsafe byte[] DecompressDXT5(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int bpp = (int)(PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); + int bps = (int)(header.width * bpp * PixelFormatToBpc(pixelFormat)); + int sizeofplane = (int)(bps * header.height); + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; + Colour8888[] colours = new Colour8888[4]; + ushort[] alphas = new ushort[8]; + + fixed (byte* bytePtr = data) + { + byte* temp = bytePtr; + for (int z = 0; z < depth; z++) + { + for (int y = 0; y < height; y += 4) + { + for (int x = 0; x < width; x += 4) + { + if (y >= height || x >= width) + break; + + alphas[0] = temp[0]; + alphas[1] = temp[1]; + byte* alphamask = (temp + 2); + temp += 8; + + DxtcReadColors(temp, ref colours); + uint bitmask = ((uint*)temp)[1]; + temp += 8; + + // Four-color block: derive the other two colors. + // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3 + // These 2-bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3); + colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3); + colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3); + //colours[2].alpha = 0xFF; + + colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3); + colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3); + colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3); + //colours[3].alpha = 0xFF; + + int k = 0; + for (int j = 0; j < 4; j++) + { + for (int i = 0; i < 4; k++, i++) + { + int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2); + Colour8888 col = colours[select]; + // only put pixels out < width or height + if (((x + i) < width) && ((y + j) < height)) + { + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp); + rawData[offset] = (byte)col.red; + rawData[offset + 1] = (byte)col.green; + rawData[offset + 2] = (byte)col.blue; + } + } + } + + // 8-alpha or 6-alpha block? + if (alphas[0] > alphas[1]) + { + // 8-alpha block: derive the other six alphas. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alphas[2] = (ushort)((6 * alphas[0] + 1 * alphas[1] + 3) / 7); // bit code 010 + alphas[3] = (ushort)((5 * alphas[0] + 2 * alphas[1] + 3) / 7); // bit code 011 + alphas[4] = (ushort)((4 * alphas[0] + 3 * alphas[1] + 3) / 7); // bit code 100 + alphas[5] = (ushort)((3 * alphas[0] + 4 * alphas[1] + 3) / 7); // bit code 101 + alphas[6] = (ushort)((2 * alphas[0] + 5 * alphas[1] + 3) / 7); // bit code 110 + alphas[7] = (ushort)((1 * alphas[0] + 6 * alphas[1] + 3) / 7); // bit code 111 + } + else + { + // 6-alpha block. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alphas[2] = (ushort)((4 * alphas[0] + 1 * alphas[1] + 2) / 5); // Bit code 010 + alphas[3] = (ushort)((3 * alphas[0] + 2 * alphas[1] + 2) / 5); // Bit code 011 + alphas[4] = (ushort)((2 * alphas[0] + 3 * alphas[1] + 2) / 5); // Bit code 100 + alphas[5] = (ushort)((1 * alphas[0] + 4 * alphas[1] + 2) / 5); // Bit code 101 + alphas[6] = 0x00; // Bit code 110 + alphas[7] = 0xFF; // Bit code 111 + } + + // Note: Have to separate the next two loops, + // it operates on a 6-byte system. + + // First three bytes + //uint bits = (uint)(alphamask[0]); + uint bits = (uint)((alphamask[0]) | (alphamask[1] << 8) | (alphamask[2] << 16)); + for (int j = 0; j < 2; j++) + { + for (int i = 0; i < 4; i++) + { + // only put pixels out < width or height + if (((x + i) < width) && ((y + j) < height)) + { + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); + rawData[offset] = (byte)alphas[bits & 0x07]; + } + bits >>= 3; + } + } + + // Last three bytes + //bits = (uint)(alphamask[3]); + bits = (uint)((alphamask[3]) | (alphamask[4] << 8) | (alphamask[5] << 16)); + for (int j = 2; j < 4; j++) + { + for (int i = 0; i < 4; i++) + { + // only put pixels out < width or height + if (((x + i) < width) && ((y + j) < height)) + { + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); + rawData[offset] = (byte)alphas[bits & 0x07]; + } + bits >>= 3; + } + } + } + } + } + } + + return rawData; + } + + private unsafe byte[] DecompressRGB(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); + int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); + int sizeofplane = (int)(bps * header.height); + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; + + uint valMask = (uint)((header.pixelformat.rgbbitcount == 32) ? ~0 : (1 << (int)header.pixelformat.rgbbitcount) - 1); + uint pixSize = (uint)(((int)header.pixelformat.rgbbitcount + 7) / 8); + int rShift1 = 0; int rMul = 0; int rShift2 = 0; + ComputeMaskParams(header.pixelformat.rbitmask, ref rShift1, ref rMul, ref rShift2); + int gShift1 = 0; int gMul = 0; int gShift2 = 0; + ComputeMaskParams(header.pixelformat.gbitmask, ref gShift1, ref gMul, ref gShift2); + int bShift1 = 0; int bMul = 0; int bShift2 = 0; + ComputeMaskParams(header.pixelformat.bbitmask, ref bShift1, ref bMul, ref bShift2); + + int offset = 0; + int pixnum = width * height * depth; + fixed (byte* bytePtr = data) + { + byte* temp = bytePtr; + while (pixnum-- > 0) + { + uint px = *((uint*)temp) & valMask; + temp += pixSize; + uint pxc = px & header.pixelformat.rbitmask; + rawData[offset + 0] = (byte)(((pxc >> rShift1) * rMul) >> rShift2); + pxc = px & header.pixelformat.gbitmask; + rawData[offset + 1] = (byte)(((pxc >> gShift1) * gMul) >> gShift2); + pxc = px & header.pixelformat.bbitmask; + rawData[offset + 2] = (byte)(((pxc >> bShift1) * bMul) >> bShift2); + rawData[offset + 3] = 0xff; + offset += 4; + } + } + return rawData; + } + + private unsafe byte[] DecompressRGBA(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); + int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); + int sizeofplane = (int)(bps * header.height); + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; + + uint valMask = (uint)((header.pixelformat.rgbbitcount == 32) ? ~0 : (1 << (int)header.pixelformat.rgbbitcount) - 1); + // Funny x86s, make 1 << 32 == 1 + uint pixSize = (header.pixelformat.rgbbitcount + 7) / 8; + int rShift1 = 0; int rMul = 0; int rShift2 = 0; + ComputeMaskParams(header.pixelformat.rbitmask, ref rShift1, ref rMul, ref rShift2); + int gShift1 = 0; int gMul = 0; int gShift2 = 0; + ComputeMaskParams(header.pixelformat.gbitmask, ref gShift1, ref gMul, ref gShift2); + int bShift1 = 0; int bMul = 0; int bShift2 = 0; + ComputeMaskParams(header.pixelformat.bbitmask, ref bShift1, ref bMul, ref bShift2); + int aShift1 = 0; int aMul = 0; int aShift2 = 0; + ComputeMaskParams(header.pixelformat.alphabitmask, ref aShift1, ref aMul, ref aShift2); + + int offset = 0; + int pixnum = width * height * depth; + fixed (byte* bytePtr = data) + { + byte* temp = bytePtr; + + while (pixnum-- > 0) + { + uint px = *((uint*)temp) & valMask; + temp += pixSize; + uint pxc = px & header.pixelformat.rbitmask; + rawData[offset + 0] = (byte)(((pxc >> rShift1) * rMul) >> rShift2); + pxc = px & header.pixelformat.gbitmask; + rawData[offset + 1] = (byte)(((pxc >> gShift1) * gMul) >> gShift2); + pxc = px & header.pixelformat.bbitmask; + rawData[offset + 2] = (byte)(((pxc >> bShift1) * bMul) >> bShift2); + pxc = px & header.pixelformat.alphabitmask; + rawData[offset + 3] = (byte)(((pxc >> aShift1) * aMul) >> aShift2); + offset += 4; + } + } + return rawData; + } + + private unsafe byte[] Decompress3Dc(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); + int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); + int sizeofplane = (int)(bps * header.height); + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; + byte[] yColours = new byte[8]; + byte[] xColours = new byte[8]; + + int offset = 0; + fixed (byte* bytePtr = data) + { + byte* temp = bytePtr; + for (int z = 0; z < depth; z++) + { + for (int y = 0; y < height; y += 4) + { + for (int x = 0; x < width; x += 4) + { + byte* temp2 = temp + 8; + + //Read Y palette + int t1 = yColours[0] = temp[0]; + int t2 = yColours[1] = temp[1]; + temp += 2; + if (t1 > t2) + for (int i = 2; i < 8; ++i) + yColours[i] = (byte)(t1 + ((t2 - t1) * (i - 1)) / 7); + else + { + for (int i = 2; i < 6; ++i) + yColours[i] = (byte)(t1 + ((t2 - t1) * (i - 1)) / 5); + yColours[6] = 0; + yColours[7] = 255; + } + + // Read X palette + t1 = xColours[0] = temp2[0]; + t2 = xColours[1] = temp2[1]; + temp2 += 2; + if (t1 > t2) + for (int i = 2; i < 8; ++i) + xColours[i] = (byte)(t1 + ((t2 - t1) * (i - 1)) / 7); + else + { + for (int i = 2; i < 6; ++i) + xColours[i] = (byte)(t1 + ((t2 - t1) * (i - 1)) / 5); + xColours[6] = 0; + xColours[7] = 255; + } + + //decompress pixel data + int currentOffset = offset; + for (int k = 0; k < 4; k += 2) + { + // First three bytes + uint bitmask = ((uint)(temp[0]) << 0) | ((uint)(temp[1]) << 8) | ((uint)(temp[2]) << 16); + uint bitmask2 = ((uint)(temp2[0]) << 0) | ((uint)(temp2[1]) << 8) | ((uint)(temp2[2]) << 16); + for (int j = 0; j < 2; j++) + { + // only put pixels out < height + if ((y + k + j) < height) + { + for (int i = 0; i < 4; i++) + { + // only put pixels out < width + if (((x + i) < width)) + { + int t; + byte tx, ty; + + t1 = currentOffset + (x + i) * 3; + rawData[t1 + 1] = ty = yColours[bitmask & 0x07]; + rawData[t1 + 0] = tx = xColours[bitmask2 & 0x07]; + + //calculate b (z) component ((r/255)^2 + (g/255)^2 + (b/255)^2 = 1 + t = 127 * 128 - (tx - 127) * (tx - 128) - (ty - 127) * (ty - 128); + if (t > 0) + rawData[t1 + 2] = (byte)(Math.Sqrt(t) + 128); + else + rawData[t1 + 2] = 0x7F; + } + bitmask >>= 3; + bitmask2 >>= 3; + } + currentOffset += bps; + } + } + temp += 3; + temp2 += 3; + } + + //skip bytes that were read via Temp2 + temp += 8; + } + offset += bps * 4; + } + } + } + + return rawData; + } + + private unsafe byte[] DecompressAti1n(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); + int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); + int sizeofplane = (int)(bps * header.height); + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; + byte[] colours = new byte[8]; + + uint offset = 0; + fixed (byte* bytePtr = data) + { + byte* temp = bytePtr; + for (int z = 0; z < depth; z++) + { + for (int y = 0; y < height; y += 4) + { + for (int x = 0; x < width; x += 4) + { + //Read palette + int t1 = colours[0] = temp[0]; + int t2 = colours[1] = temp[1]; + temp += 2; + if (t1 > t2) + for (int i = 2; i < 8; ++i) + colours[i] = (byte)(t1 + ((t2 - t1) * (i - 1)) / 7); + else + { + for (int i = 2; i < 6; ++i) + colours[i] = (byte)(t1 + ((t2 - t1) * (i - 1)) / 5); + colours[6] = 0; + colours[7] = 255; + } + + //decompress pixel data + uint currOffset = offset; + for (int k = 0; k < 4; k += 2) + { + // First three bytes + uint bitmask = ((uint)(temp[0]) << 0) | ((uint)(temp[1]) << 8) | ((uint)(temp[2]) << 16); + for (int j = 0; j < 2; j++) + { + // only put pixels out < height + if ((y + k + j) < height) + { + for (int i = 0; i < 4; i++) + { + // only put pixels out < width + if (((x + i) < width)) + { + t1 = (int)(currOffset + (x + i)); + rawData[t1] = colours[bitmask & 0x07]; + } + bitmask >>= 3; + } + currOffset += (uint)bps; + } + } + temp += 3; + } + } + offset += (uint)(bps * 4); + } + } + } + return rawData; + } + + private unsafe byte[] DecompressLum(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); + int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); + int sizeofplane = (int)(bps * header.height); + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; + + int lShift1 = 0; int lMul = 0; int lShift2 = 0; + ComputeMaskParams(header.pixelformat.rbitmask, ref lShift1, ref lMul, ref lShift2); + + int offset = 0; + int pixnum = width * height * depth; + fixed (byte* bytePtr = data) + { + byte* temp = bytePtr; + while (pixnum-- > 0) + { + byte px = *(temp++); + rawData[offset + 0] = (byte)(((px >> lShift1) * lMul) >> lShift2); + rawData[offset + 1] = (byte)(((px >> lShift1) * lMul) >> lShift2); + rawData[offset + 2] = (byte)(((px >> lShift1) * lMul) >> lShift2); + rawData[offset + 3] = (byte)(((px >> lShift1) * lMul) >> lShift2); + offset += 4; + } + } + return rawData; + } + + private unsafe byte[] DecompressRXGB(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); + int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); + int sizeofplane = (int)(bps * header.height); + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; + + Colour565 color_0 = new Colour565(); + Colour565 color_1 = new Colour565(); + Colour8888[] colours = new Colour8888[4]; + byte[] alphas = new byte[8]; + + fixed (byte* bytePtr = data) + { + byte* temp = bytePtr; + for (int z = 0; z < depth; z++) + { + for (int y = 0; y < height; y += 4) + { + for (int x = 0; x < width; x += 4) + { + if (y >= height || x >= width) + break; + alphas[0] = temp[0]; + alphas[1] = temp[1]; + byte* alphamask = temp + 2; + temp += 8; + + DxtcReadColors(temp, ref color_0, ref color_1); + temp += 4; + + uint bitmask = ((uint*)temp)[1]; + temp += 4; + + colours[0].red = (byte)(color_0.red << 3); + colours[0].green = (byte)(color_0.green << 2); + colours[0].blue = (byte)(color_0.blue << 3); + colours[0].alpha = 0xFF; + + colours[1].red = (byte)(color_1.red << 3); + colours[1].green = (byte)(color_1.green << 2); + colours[1].blue = (byte)(color_1.blue << 3); + colours[1].alpha = 0xFF; + + // Four-color block: derive the other two colors. + // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3 + // These 2-bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3); + colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3); + colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3); + colours[2].alpha = 0xFF; + + colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3); + colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3); + colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3); + colours[3].alpha = 0xFF; + + int k = 0; + for (int j = 0; j < 4; j++) + { + for (int i = 0; i < 4; i++, k++) + { + int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2); + Colour8888 col = colours[select]; + + // only put pixels out < width or height + if (((x + i) < width) && ((y + j) < height)) + { + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp); + rawData[offset + 0] = col.red; + rawData[offset + 1] = col.green; + rawData[offset + 2] = col.blue; + } + } + } + + // 8-alpha or 6-alpha block? + if (alphas[0] > alphas[1]) + { + // 8-alpha block: derive the other six alphas. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alphas[2] = (byte)((6 * alphas[0] + 1 * alphas[1] + 3) / 7); // bit code 010 + alphas[3] = (byte)((5 * alphas[0] + 2 * alphas[1] + 3) / 7); // bit code 011 + alphas[4] = (byte)((4 * alphas[0] + 3 * alphas[1] + 3) / 7); // bit code 100 + alphas[5] = (byte)((3 * alphas[0] + 4 * alphas[1] + 3) / 7); // bit code 101 + alphas[6] = (byte)((2 * alphas[0] + 5 * alphas[1] + 3) / 7); // bit code 110 + alphas[7] = (byte)((1 * alphas[0] + 6 * alphas[1] + 3) / 7); // bit code 111 + } + else + { + // 6-alpha block. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alphas[2] = (byte)((4 * alphas[0] + 1 * alphas[1] + 2) / 5); // Bit code 010 + alphas[3] = (byte)((3 * alphas[0] + 2 * alphas[1] + 2) / 5); // Bit code 011 + alphas[4] = (byte)((2 * alphas[0] + 3 * alphas[1] + 2) / 5); // Bit code 100 + alphas[5] = (byte)((1 * alphas[0] + 4 * alphas[1] + 2) / 5); // Bit code 101 + alphas[6] = 0x00; // Bit code 110 + alphas[7] = 0xFF; // Bit code 111 + } + + // Note: Have to separate the next two loops, + // it operates on a 6-byte system. + // First three bytes + uint bits = *((uint*)alphamask); + for (int j = 0; j < 2; j++) + { + for (int i = 0; i < 4; i++) + { + // only put pixels out < width or height + if (((x + i) < width) && ((y + j) < height)) + { + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); + rawData[offset] = alphas[bits & 0x07]; + } + bits >>= 3; + } + } + + // Last three bytes + bits = *((uint*)&alphamask[3]); + for (int j = 2; j < 4; j++) + { + for (int i = 0; i < 4; i++) + { + // only put pixels out < width or height + if (((x + i) < width) && ((y + j) < height)) + { + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); + rawData[offset] = alphas[bits & 0x07]; + } + bits >>= 3; + } + } + } + } + } + } + return rawData; + } + + private unsafe byte[] DecompressFloat(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); + int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); + int sizeofplane = (int)(bps * header.height); + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; + int size = 0; + fixed (byte* bytePtr = data) + { + byte* temp = bytePtr; + fixed (byte* destPtr = rawData) + { + byte* destData = destPtr; + switch (pixelFormat) + { + case PixelFormat.R32F: // Red float, green = blue = max + size = width * height * depth * 3; + for (int i = 0, j = 0; i < size; i += 3, j++) + { + ((float*)destData)[i] = ((float*)temp)[j]; + ((float*)destData)[i + 1] = 1.0f; + ((float*)destData)[i + 2] = 1.0f; + } + break; + + case PixelFormat.A32B32G32R32F: // Direct copy of float RGBA data + Array.Copy(data, rawData, data.Length); + break; + + case PixelFormat.G32R32F: // Red float, green float, blue = max + size = width * height * depth * 3; + for (int i = 0, j = 0; i < size; i += 3, j += 2) + { + ((float*)destData)[i] = ((float*)temp)[j]; + ((float*)destData)[i + 1] = ((float*)temp)[j + 1]; + ((float*)destData)[i + 2] = 1.0f; + } + break; + + case PixelFormat.R16F: // Red float, green = blue = max + size = width * height * depth * bpp; + ConvR16ToFloat32((uint*)destData, (ushort*)temp, (uint)size); + break; + + case PixelFormat.A16B16G16R16F: // Just convert from half to float. + size = width * height * depth * bpp; + ConvFloat16ToFloat32((uint*)destData, (ushort*)temp, (uint)size); + break; + + case PixelFormat.G16R16F: // Convert from half to float, set blue = max. + size = width * height * depth * bpp; + ConvG16R16ToFloat32((uint*)destData, (ushort*)temp, (uint)size); + break; + + default: + break; + } + } + } + + return rawData; + } + + #region UNUSED + private unsafe byte[] DecompressARGB(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int bpp = (int)(PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); + int bps = (int)(header.width * bpp * PixelFormatToBpc(pixelFormat)); + int sizeofplane = (int)(bps * header.height); + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + if (Check16BitComponents(header)) + return DecompressARGB16(header, data, pixelFormat); + + int sizeOfData = (int)((header.width * header.pixelformat.rgbbitcount / 8) * header.height * header.depth); + byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; + + if ((pixelFormat == PixelFormat.LUMINANCE) && (header.pixelformat.rgbbitcount == 16) && (header.pixelformat.rbitmask == 0xFFFF)) + { + Array.Copy(data, rawData, data.Length); + return rawData; + } + + uint readI = 0, tempBpp; + uint redL = 0, redR = 0; + uint greenL = 0, greenR = 0; + uint blueL = 0, blueR = 0; + uint alphaL = 0, alphaR = 0; + + GetBitsFromMask(header.pixelformat.rbitmask, ref redL, ref redR); + GetBitsFromMask(header.pixelformat.gbitmask, ref greenL, ref greenR); + GetBitsFromMask(header.pixelformat.bbitmask, ref blueL, ref blueR); + GetBitsFromMask(header.pixelformat.alphabitmask, ref alphaL, ref alphaR); + tempBpp = header.pixelformat.rgbbitcount / 8; + + fixed (byte* bytePtr = data) + { + byte* temp = bytePtr; + for (int i = 0; i < sizeOfData; i += bpp) + { + //@TODO: This is SLOOOW... + //but the old version crashed in release build under + //winxp (and xp is right to stop this code - I always + //wondered that it worked the old way at all) + if (sizeOfData - i < 4) + { + //less than 4 byte to write? + if (tempBpp == 3) + { + //this branch is extra-SLOOOW + readI = (uint)(*temp | ((*(temp + 1)) << 8) | ((*(temp + 2)) << 16)); + } + else if (tempBpp == 1) + readI = *((byte*)temp); + else if (tempBpp == 2) + readI = (uint)(temp[0] | (temp[1] << 8)); + } + else + readI = (uint)(temp[0] | (temp[1] << 8) | (temp[2] << 16) | (temp[3] << 24)); + temp += tempBpp; + + rawData[i] = (byte)((((int)readI & (int)header.pixelformat.rbitmask) >> (int)redR) << (int)redL); + + if (bpp >= 3) + { + rawData[i + 1] = (byte)((((int)readI & (int)header.pixelformat.gbitmask) >> (int)greenR) << (int)greenL); + rawData[i + 2] = (byte)((((int)readI & header.pixelformat.bbitmask) >> (int)blueR) << (int)blueL); + + if (bpp == 4) + { + rawData[i + 3] = (byte)((((int)readI & (int)header.pixelformat.alphabitmask) >> (int)alphaR) << (int)alphaL); + if (alphaL >= 7) + { + rawData[i + 3] = (byte)(rawData[i + 3] != 0 ? 0xFF : 0x00); + } + else if (alphaL >= 4) + { + rawData[i + 3] = (byte)(rawData[i + 3] | (rawData[i + 3] >> 4)); + } + } + } + else if (bpp == 2) + { + rawData[i + 1] = (byte)((((int)readI & (int)header.pixelformat.alphabitmask) >> (int)alphaR) << (int)alphaL); + if (alphaL >= 7) + { + rawData[i + 1] = (byte)(rawData[i + 1] != 0 ? 0xFF : 0x00); + } + else if (alphaL >= 4) + { + rawData[i + 1] = (byte)(rawData[i + 1] | (rawData[i + 3] >> 4)); + } + } + } + } + return rawData; + } + + private unsafe byte[] DecompressARGB16(DDSStruct header, byte[] data, PixelFormat pixelFormat) + { + // allocate bitmap + int bpp = (int)(PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); + int bps = (int)(header.width * bpp * PixelFormatToBpc(pixelFormat)); + int sizeofplane = (int)(bps * header.height); + int width = (int)header.width; + int height = (int)header.height; + int depth = (int)header.depth; + + int sizeOfData = (int)((header.width * header.pixelformat.rgbbitcount / 8) * header.height * header.depth); + byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; + + uint readI = 0, tempBpp = 0; + uint redL = 0, redR = 0; + uint greenL = 0, greenR = 0; + uint blueL = 0, blueR = 0; + uint alphaL = 0, alphaR = 0; + uint redPad = 0, greenPad = 0, bluePad = 0, alphaPad = 0; + + GetBitsFromMask(header.pixelformat.rbitmask, ref redL, ref redR); + GetBitsFromMask(header.pixelformat.gbitmask, ref greenL, ref greenR); + GetBitsFromMask(header.pixelformat.bbitmask, ref blueL, ref blueR); + GetBitsFromMask(header.pixelformat.alphabitmask, ref alphaL, ref alphaR); + redPad = 16 - CountBitsFromMask(header.pixelformat.rbitmask); + greenPad = 16 - CountBitsFromMask(header.pixelformat.gbitmask); + bluePad = 16 - CountBitsFromMask(header.pixelformat.bbitmask); + alphaPad = 16 - CountBitsFromMask(header.pixelformat.alphabitmask); + + redL = redL + redPad; + greenL = greenL + greenPad; + blueL = blueL + bluePad; + alphaL = alphaL + alphaPad; + + tempBpp = header.pixelformat.rgbbitcount / 8; + fixed (byte* bytePtr = data) + { + byte* temp = bytePtr; + fixed (byte* destPtr = rawData) + { + byte* destData = destPtr; + for (int i = 0; i < sizeOfData / 2; i += bpp) + { + //@TODO: This is SLOOOW... + //but the old version crashed in release build under + //winxp (and xp is right to stop this code - I always + //wondered that it worked the old way at all) + if (sizeOfData - i < 4) + { + //less than 4 byte to write? + if (tempBpp == 3) + { + //this branch is extra-SLOOOW + readI = (uint)(*temp | ((*(temp + 1)) << 8) | ((*(temp + 2)) << 16)); + } + else if (tempBpp == 1) + readI = *((byte*)temp); + else if (tempBpp == 2) + readI = (uint)(temp[0] | (temp[1] << 8)); + } + else + readI = (uint)(temp[0] | (temp[1] << 8) | (temp[2] << 16) | (temp[3] << 24)); + temp += tempBpp; + + ((ushort*)destData)[i + 2] = (ushort)((((int)readI & (int)header.pixelformat.rbitmask) >> (int)redR) << (int)redL); + + if (bpp >= 3) + { + ((ushort*)destData)[i + 1] = (ushort)((((int)readI & (int)header.pixelformat.gbitmask) >> (int)greenR) << (int)greenL); + ((ushort*)destData)[i] = (ushort)((((int)readI & (int)header.pixelformat.bbitmask) >> (int)blueR) << (int)blueL); + + if (bpp == 4) + { + ((ushort*)destData)[i + 3] = (ushort)((((int)readI & (int)header.pixelformat.alphabitmask) >> (int)alphaR) << (int)alphaL); + if (alphaL >= 7) + { + ((ushort*)destData)[i + 3] = (ushort)(((ushort*)destData)[i + 3] != 0 ? 0xFF : 0x00); + } + else if (alphaL >= 4) + { + ((ushort*)destData)[i + 3] = (ushort)(((ushort*)destData)[i + 3] | (((ushort*)destData)[i + 3] >> 4)); + } + } + } + else if (bpp == 2) + { + ((ushort*)destData)[i + 1] = (ushort)((((int)readI & (int)header.pixelformat.alphabitmask) >> (int)alphaR) << (int)alphaL); + if (alphaL >= 7) + { + ((ushort*)destData)[i + 1] = (ushort)(((ushort*)destData)[i + 1] != 0 ? 0xFF : 0x00); + } + else if (alphaL >= 4) + { + ((ushort*)destData)[i + 1] = (ushort)(((ushort*)destData)[i + 1] | (rawData[i + 3] >> 4)); + } + } + } + } + } + return rawData; + } + #endregion + + #endregion + + #endregion + + #region Public Methods + public void Dispose() + { + if (this.m_bitmap != null) + { + this.m_bitmap.Dispose(); + this.m_bitmap = null; + } + } + #endregion + + #region Properties + /// + /// Returns a System.Imaging.Bitmap containing the DDS image. + /// + public System.Drawing.Bitmap BitmapImage + { + get { return this.m_bitmap; } + } + + /// + /// Returns the DDS image is valid format. + /// + public bool IsValid + { + get { return this.m_isValid; } + } + #endregion + + #region Operators + public static implicit operator DDSImage(System.Drawing.Bitmap value) + { + return new DDSImage(value); + } + + public static explicit operator System.Drawing.Bitmap(DDSImage value) + { + return value.BitmapImage; + } + #endregion + + #region Nested Types + + #region Colour8888 + [StructLayout(LayoutKind.Sequential)] + private struct Colour8888 + { + public byte red; + public byte green; + public byte blue; + public byte alpha; + } + #endregion + + #region Colour565 + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct Colour565 + { + public ushort blue; //: 5; + public ushort green; //: 6; + public ushort red; //: 5; + } + #endregion + + #region DDSStruct + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct DDSStruct + { + public uint size; // equals size of struct (which is part of the data file!) + public uint flags; + public uint height; + public uint width; + public uint sizeorpitch; + public uint depth; + public uint mipmapcount; + public uint alphabitdepth; + //[MarshalAs(UnmanagedType.U4, SizeConst = 11)] + public uint[] reserved;//[11]; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct pixelformatstruct + { + public uint size; // equals size of struct (which is part of the data file!) + public uint flags; + public uint fourcc; + public uint rgbbitcount; + public uint rbitmask; + public uint gbitmask; + public uint bbitmask; + public uint alphabitmask; + } + public pixelformatstruct pixelformat; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct ddscapsstruct + { + public uint caps1; + public uint caps2; + public uint caps3; + public uint caps4; + } + public ddscapsstruct ddscaps; + public uint texturestage; + + //#ifndef __i386__ + //void to_little_endian() + //{ + // size_t size = sizeof(DDSStruct); + // assert(size % 4 == 0); + // size /= 4; + // for (size_t i=0; i + /// Various pixel formats/compressors used by the DDS image. + /// + private enum PixelFormat + { + /// + /// 32-bit image, with 8-bit red, green, blue and alpha. + /// + RGBA, + /// + /// 24-bit image with 8-bit red, green, blue. + /// + RGB, + /// + /// 16-bit DXT-1 compression, 1-bit alpha. + /// + DXT1, + /// + /// DXT-2 Compression + /// + DXT2, + /// + /// DXT-3 Compression + /// + DXT3, + /// + /// DXT-4 Compression + /// + DXT4, + /// + /// DXT-5 Compression + /// + DXT5, + /// + /// DX10 Compression + /// + DX10, + /// + /// 3DC Compression + /// + THREEDC, + /// + /// ATI1n Compression + /// + ATI1N, + LUMINANCE, + LUMINANCE_ALPHA, + RXGB, + A16B16G16R16, + R16F, + G16R16F, + A16B16G16R16F, + R32F, + G32R32F, + A32B32G32R32F, + /// + /// Unknown pixel format. + /// + UNKNOWN + } + #endregion + + #endregion + } + #endregion + + #region Exceptions Class + /// + /// Thrown when an invalid file header has been encountered. + /// + public class InvalidFileHeaderException : Exception + { + } + + /// + /// Thrown when the data does not contain a DDS image. + /// + public class NotADDSImageException : Exception + { + + } + + /// + /// Thrown when there is an unknown compressor used in the DDS file. + /// + public class UnknownFileFormatException : Exception + { + } + #endregion +} \ No newline at end of file diff --git a/DamageCalculator/DamageCalculator/DamageCalculator.csproj b/DamageCalculator/DamageCalculator/DamageCalculator.csproj new file mode 100644 index 0000000..c9d72b2 --- /dev/null +++ b/DamageCalculator/DamageCalculator/DamageCalculator.csproj @@ -0,0 +1,159 @@ + + + + + Debug + AnyCPU + {7A7AE40F-8677-44E3-873A-4AB7C9FCFEB4} + WinExe + Damage_Calculator + CSGO Damage Calculator + v4.8 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + 27.ico + + + + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + About.xaml + + + + + + + + + + + + + + + + + + + + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DamageCalculator/DamageCalculator/Globals.cs b/DamageCalculator/DamageCalculator/Globals.cs new file mode 100644 index 0000000..7a0b488 --- /dev/null +++ b/DamageCalculator/DamageCalculator/Globals.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media.Imaging; + +namespace Damage_Calculator +{ + static class Globals + { + public static Models.Settings Settings = new Models.Settings(); + + public static BitmapImage BitmapToImageSource(Bitmap src) + { + MemoryStream ms = new MemoryStream(); + ((System.Drawing.Bitmap)src).Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); + BitmapImage image = new BitmapImage(); + image.BeginInit(); + ms.Seek(0, SeekOrigin.Begin); + image.StreamSource = ms; + image.EndInit(); + return image; + } + } +} diff --git a/DamageCalculator/DamageCalculator/MainWindow.xaml b/DamageCalculator/DamageCalculator/MainWindow.xaml new file mode 100644 index 0000000..7d88da3 --- /dev/null +++ b/DamageCalculator/DamageCalculator/MainWindow.xaml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DamageCalculator/DamageCalculator/MainWindow.xaml.cs b/DamageCalculator/DamageCalculator/MainWindow.xaml.cs new file mode 100644 index 0000000..8fd773b --- /dev/null +++ b/DamageCalculator/DamageCalculator/MainWindow.xaml.cs @@ -0,0 +1,569 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Damage_Calculator.Models; +using Damage_Calculator.ZatVdfParser; +using System.Xml.Serialization; + +namespace Damage_Calculator +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + /// + /// Gets or sets the point that will be there when left-clicking a map. + /// + private MapPoint leftPoint = new MapPoint(); + private Color leftPointColour = Color.FromArgb(140, 255, 0, 0); + + /// + /// Gets or sets the point that will be there when right-clicking a map. + /// + private MapPoint rightPoint = new MapPoint(); + private Color rightPointColour = Color.FromArgb(140, 0, 255, 0); + + private Line connectingLine = new Line(); + + private Image CTSpawnIcon; + private Image TSpawnIcon; + private Image ASiteIcon; + private Image BSiteIcon; + + private double unitsDistance = -1; + + /// + /// Gets or sets the currently loaded map. + /// + private CsgoMapOverview loadedMap; + private CsgoWeapon selectedWeapon; + + private BackgroundWorker bgWorker = new BackgroundWorker(); + + private bool lineDrawn = false; + + public MainWindow() + { + InitializeComponent(); + + Globals.Settings.CsgoHelper.CsgoPath = Globals.Settings.SteamHelper.GetGamePathFromExactName("Counter-Strike: Global Offensive"); + if (Globals.Settings.CsgoHelper.CsgoPath == null) + { + MessageBox.Show("Make sure you have installed CS:GO and Steam correctly.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + this.Close(); + } + + bgWorker.DoWork += BgWorker_DoWork; + bgWorker.RunWorkerCompleted += BgWorker_RunWorkerCompleted; + bgWorker.ProgressChanged += BgWorker_ProgressChanged; + bgWorker.WorkerReportsProgress = true; + + this.gridLoading.Visibility = Visibility.Visible; + bgWorker.RunWorkerAsync(); + } + + #region background worker + private void BgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == 0) + { + // Add maps + var maps = new List(); + + foreach (var map in e.UserState as List) + { + var item = new ComboBoxItem(); + + item.Tag = map; + item.Content = map.MapFileName; + + maps.Add(item); + } + + this.comboBoxMaps.ItemsSource = maps.OrderBy(m => m.Content); + if (maps.Count > 0) + this.comboBoxMaps.SelectedIndex = 0; + } + else if(e.ProgressPercentage == 1) + { + // Add weapons + var weaponItems = new List(); + + foreach (var wpn in e.UserState as List) + { + var item = new ComboBoxItem(); + + item.Tag = wpn; + item.Content = wpn.ClassName.Substring(wpn.ClassName.IndexOf('_') + 1); + + weaponItems.Add(item); + } + + comboWeapons.ItemsSource = weaponItems.OrderBy(w => w.Content); + if (weaponItems.Count > 0) + this.comboWeapons.SelectedIndex = 0; + } + } + + private static string calculateMD5(string filename) + { + using (var md5 = System.Security.Cryptography.MD5.Create()) + { + using (var stream = File.OpenRead(filename)) + { + var hash = md5.ComputeHash(stream); + return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } + } + } + + private void BgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + this.gridLoading.Visibility = Visibility.Collapsed; + } + + private void BgWorker_DoWork(object sender, DoWorkEventArgs e) + { + var maps = Globals.Settings.CsgoHelper.GetMaps(); + bgWorker.ReportProgress(0, maps); + var serializer = new XmlSerializer(typeof(List)); + + List weapons; + + string itemsFile = System.IO.Path.Combine(Globals.Settings.CsgoHelper.CsgoPath, "csgo\\scripts\\items\\items_game.txt"); + string saveFileDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "CSGO Damage Calculator"); + string currentHash = calculateMD5(itemsFile); + + if (Directory.Exists(saveFileDir)) + { + string[] files = Directory.GetFiles(saveFileDir); + if (files.Length == 1) + { + // Compare hashes + string oldHash = System.IO.Path.GetFileName(files[0]); + + if(currentHash == oldHash) + { + weapons = (List)serializer.Deserialize(new FileStream(System.IO.Path.Combine(saveFileDir, currentHash), FileMode.Open)); + bgWorker.ReportProgress(1, weapons); + return; + } + else + { + foreach (string file in files) + { + File.Delete(file); + } + } + } + else + { + foreach(string file in files) + { + File.Delete(file); + } + } + } + else + { + Directory.CreateDirectory(saveFileDir); + } + + weapons = Globals.Settings.CsgoHelper.GetWeapons(); + serializer.Serialize(new FileStream(System.IO.Path.Combine(saveFileDir, currentHash), FileMode.Create), weapons); + bgWorker.ReportProgress(1, weapons); + } + #endregion + + private void resetCanvas() + { + this.pointsCanvas.Children.Clear(); + this.leftPoint = null; + this.rightPoint = null; + this.connectingLine = null; + this.unitsDistance = -1; + this.textDistanceMetres.Text = "0"; + this.textDistanceUnits.Text = "0"; + this.txtResult.Text = "0"; + this.txtResultArmor.Text = "0"; + } + + private void loadMap(CsgoMapOverview map) + { + mapImage.Source = map.MapImage; + } + + private Ellipse getPointEllipse(Color strokeColour) + { + Ellipse circle = new Ellipse(); + circle.Fill = null; + circle.Width = circle.Height = 14; + circle.Stroke = new SolidColorBrush(strokeColour); + circle.StrokeThickness = 2; + circle.IsHitTestVisible = false; + + return circle; + } + + private void updateCirclePositions() + { + if (this.connectingLine == null) + this.connectingLine = new Line(); + + if (leftPoint?.Circle != null) + { + Canvas.SetLeft(leftPoint.Circle, (leftPoint.PercentageX * pointsCanvas.ActualWidth / 100f) - (leftPoint.Circle.Width / 2)); + Canvas.SetTop(leftPoint.Circle, (leftPoint.PercentageY * pointsCanvas.ActualHeight / 100f) - (leftPoint.Circle.Height / 2)); + } + + if (rightPoint?.Circle != null) + { + Canvas.SetLeft(rightPoint.Circle, (rightPoint.PercentageX * pointsCanvas.ActualWidth / 100f) - (rightPoint.Circle.Width / 2)); + Canvas.SetTop(rightPoint.Circle, (rightPoint.PercentageY * pointsCanvas.ActualHeight / 100f) - (rightPoint.Circle.Height / 2)); + } + + if(leftPoint?.Circle != null && rightPoint?.Circle != null) + { + this.connectingLine.X1 = Canvas.GetLeft(leftPoint.Circle) + (leftPoint.Circle.Width / 2); + this.connectingLine.Y1 = Canvas.GetTop(leftPoint.Circle) + (leftPoint.Circle.Height / 2); + this.connectingLine.X2 = Canvas.GetLeft(rightPoint.Circle) + (rightPoint.Circle.Width / 2); + this.connectingLine.Y2 = Canvas.GetTop(rightPoint.Circle) + (rightPoint.Circle.Height / 2); + this.connectingLine.Fill = null; + this.connectingLine.Stroke = new SolidColorBrush(Color.FromArgb(140, 255, 255, 255)); + this.connectingLine.StrokeThickness = 2; + this.connectingLine.IsHitTestVisible = false; + + int indexLine = pointsCanvas.Children.IndexOf(this.connectingLine); + if (indexLine < 0) + { + pointsCanvas.Children.Add(this.connectingLine); + this.lineDrawn = true; + } + + this.unitsDistance = this.calculateDotDistanceInUnits(); + this.textDistanceUnits.Text = Math.Round(this.unitsDistance, 2).ToString(); + this.textDistanceMetres.Text = Math.Round(this.unitsDistance / 39.37, 2).ToString(); + this.settings_Updated(null, null); + } + else + { + this.lineDrawn = false; + } + + if(this.loadedMap != null && this.loadedMap.CTSpawnMultiplierX != -1 && this.loadedMap.CTSpawnMultiplierY != -1) + { + // CT Icon + if (this.CTSpawnIcon == null) + { + this.CTSpawnIcon = new Image(); + this.CTSpawnIcon.Source = new BitmapImage(new Uri("icon_ct.png", UriKind.RelativeOrAbsolute)); + this.CTSpawnIcon.Width = 25; + this.CTSpawnIcon.Height = 25; + this.CTSpawnIcon.Opacity = 0.6; + this.CTSpawnIcon.IsHitTestVisible = false; + } + + if(pointsCanvas.Children.IndexOf(CTSpawnIcon) == -1) + pointsCanvas.Children.Add(CTSpawnIcon); + + + Canvas.SetLeft(CTSpawnIcon, this.loadedMap.CTSpawnMultiplierX * this.mapImage.ActualWidth - (CTSpawnIcon.ActualWidth / 2)); + Canvas.SetTop(CTSpawnIcon, this.loadedMap.CTSpawnMultiplierY * this.mapImage.ActualWidth - (CTSpawnIcon.ActualHeight / 2)); + + // T Icon + if (this.TSpawnIcon == null) + { + this.TSpawnIcon = new Image(); + this.TSpawnIcon.Source = new BitmapImage(new Uri("icon_t.png", UriKind.RelativeOrAbsolute)); + this.TSpawnIcon.Width = 25; + this.TSpawnIcon.Height = 25; + this.TSpawnIcon.Opacity = 0.6; + this.TSpawnIcon.IsHitTestVisible = false; + } + + if (pointsCanvas.Children.IndexOf(TSpawnIcon) == -1) + pointsCanvas.Children.Add(TSpawnIcon); + + Canvas.SetLeft(TSpawnIcon, this.loadedMap.TSpawnMultiplierX * this.mapImage.ActualWidth - (TSpawnIcon.ActualWidth / 2)); + Canvas.SetTop(TSpawnIcon, this.loadedMap.TSpawnMultiplierY * this.mapImage.ActualWidth - (TSpawnIcon.ActualHeight / 2)); + + // Bomb A Icon + if (this.ASiteIcon == null) + { + this.ASiteIcon = new Image(); + this.ASiteIcon.Source = new BitmapImage(new Uri("icon_a_site.png", UriKind.RelativeOrAbsolute)); + this.ASiteIcon.Width = 25; + this.ASiteIcon.Height = 25; + this.ASiteIcon.Opacity = 0.6; + this.ASiteIcon.IsHitTestVisible = false; + } + + if (pointsCanvas.Children.IndexOf(ASiteIcon) == -1) + pointsCanvas.Children.Add(ASiteIcon); + + Canvas.SetLeft(ASiteIcon, this.loadedMap.BombAX * this.mapImage.ActualWidth - (ASiteIcon.ActualWidth / 2)); + Canvas.SetTop(ASiteIcon, this.loadedMap.BombAY * this.mapImage.ActualWidth - (ASiteIcon.ActualHeight / 2)); + + // Bomb B Icon + if (this.BSiteIcon == null) + { + this.BSiteIcon = new Image(); + this.BSiteIcon.Source = new BitmapImage(new Uri("icon_b_site.png", UriKind.RelativeOrAbsolute)); + this.BSiteIcon.Width = 25; + this.BSiteIcon.Height = 25; + this.BSiteIcon.Opacity = 0.6; + this.BSiteIcon.IsHitTestVisible = false; + } + + if (pointsCanvas.Children.IndexOf(BSiteIcon) == -1) + pointsCanvas.Children.Add(BSiteIcon); + + Canvas.SetLeft(BSiteIcon, this.loadedMap.BombBX * this.mapImage.ActualWidth - (BSiteIcon.ActualWidth / 2)); + Canvas.SetTop(BSiteIcon, this.loadedMap.BombBY * this.mapImage.ActualWidth - (BSiteIcon.ActualHeight / 2)); + } + } + + private double calculateDotDistanceInUnits() + { + Ellipse circleLeft = pointsCanvas.Children[pointsCanvas.Children.IndexOf(leftPoint.Circle)] as Ellipse; + double leftX = Canvas.GetLeft(circleLeft); + double leftY = Canvas.GetTop(circleLeft); + + Ellipse circleRight = pointsCanvas.Children[pointsCanvas.Children.IndexOf(rightPoint.Circle)] as Ellipse; + double rightX = Canvas.GetLeft(circleRight); + double rightY = Canvas.GetTop(circleRight); + + // Distance in shown pixels + double diffPixels = Math.Sqrt(Math.Pow(Math.Abs(leftX - rightX), 2) + Math.Pow(Math.Abs(leftY - rightY), 2)); + + // Percentage on shown pixels + double diffPerc = diffPixels * 100f / this.mapImage.ActualWidth; + + // Distance on original pixel scales + double diffPixelsOriginal = diffPerc * (this.mapImage.Source as BitmapSource).PixelWidth / 100f; + + // times scale multiplier + double unitsDifference = diffPixelsOriginal * this.loadedMap.MapSizeMultiplier; + + return unitsDifference; + } + + #region events + private void mapImage_LayoutUpdated(object sender, EventArgs e) + { + this.updateCirclePositions(); + } + + private void mapImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) + { + if (this.leftPoint == null) + leftPoint = new MapPoint(); + + Point mousePos = Mouse.GetPosition(pointsCanvas); + pointsCanvas.Children.Remove(leftPoint.Circle); + + var circle = this.getPointEllipse(this.leftPointColour); + + pointsCanvas.Children.Add(circle); + + leftPoint.PercentageX = mousePos.X * 100f / pointsCanvas.ActualWidth; + leftPoint.PercentageY = mousePos.Y * 100f / pointsCanvas.ActualHeight; + + leftPoint.Circle = circle; + + this.updateCirclePositions(); + } + + private void mapImage_MouseRightButtonUp(object sender, MouseButtonEventArgs e) + { + if (this.rightPoint == null) + this.rightPoint = new MapPoint(); + + Point mousePos = Mouse.GetPosition(pointsCanvas); + pointsCanvas.Children.Remove(rightPoint.Circle); + + var circle = this.getPointEllipse(this.rightPointColour); + + pointsCanvas.Children.Add(circle); + + rightPoint.PercentageX = mousePos.X * 100f / pointsCanvas.ActualWidth; + rightPoint.PercentageY = mousePos.Y * 100f / pointsCanvas.ActualHeight; + + rightPoint.Circle = circle; + + this.updateCirclePositions(); + } + + private void changeTheme_Click(object sender, RoutedEventArgs e) + { + switch (int.Parse(((MenuItem)sender).Uid)) + { + case 0: REghZyFramework.Themes.ThemesController.SetTheme(REghZyFramework.Themes.ThemesController.ThemeTypes.Dark); + rectTop.Fill = rectSide.Fill = new SolidColorBrush(Colors.White); + txtEasterEggMetres.Text = "Metres:"; + break; + case 1: REghZyFramework.Themes.ThemesController.SetTheme(REghZyFramework.Themes.ThemesController.ThemeTypes.Light); + rectTop.Fill = rectSide.Fill = new SolidColorBrush(Colors.Black); + txtEasterEggMetres.Text = "Meters:"; + break; + } + e.Handled = true; + } + + private void comboBoxMaps_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + var map = ((sender as ComboBox).SelectedItem as ComboBoxItem)?.Tag as CsgoMapOverview; + + if (map != null) + this.loadMap(map); + + this.resetCanvas(); + this.loadedMap = map; + } + + private void settings_Updated(object sender, EventArgs e) + { + if (this.selectedWeapon == null || !this.lineDrawn) + { + if(txtResult != null && txtResultArmor != null) + txtResult.Text = txtResultArmor.Text = "0"; + + return; + } + + double damage = this.selectedWeapon.BaseDamage; + double absorbedDamageByArmor = 0; + bool wasArmorHit = false; + + if(this.unitsDistance > this.selectedWeapon.MaxBulletRange) + { + damage = 0; + txtResult.Text = txtResultArmor.Text = damage.ToString(); + return; + } + + // Range + damage *= Math.Pow(this.selectedWeapon.DamageDropoff, double.Parse((this.unitsDistance / 500f).ToString())); + + // Multipliers and armor penetration + if (radioHead.IsChecked == true) + { + // Headshot + damage *= this.selectedWeapon.HeadshotModifier; + + if (chkHelmet.IsChecked == true) + { + // Has helmet + double previousDamage = damage; + damage *= this.selectedWeapon.ArmorPenetration / 100f; + absorbedDamageByArmor = previousDamage - (int)damage; + wasArmorHit = true; + } + } + else if(radioChestArms.IsChecked == true) + { + // Chest or arms + if (chkKevlar.IsChecked == true) + { + // Has kevlar + double previousDamage = damage; + damage *= this.selectedWeapon.ArmorPenetration / 100f; + absorbedDamageByArmor = previousDamage - (int)damage; + wasArmorHit = true; + } + } + else if(radioStomach.IsChecked == true) + { + // Stomach + damage *= 1.25f; + + if (chkKevlar.IsChecked == true) + { + // Has kevlar + double previousDamage = damage; + damage *= this.selectedWeapon.ArmorPenetration / 100f; + absorbedDamageByArmor = previousDamage - (int)damage; + wasArmorHit = true; + } + } + else + { + // Legs + damage *= 0.75f; + } + + txtResult.Text = ((int)damage).ToString(); + + txtResultArmor.Text = (wasArmorHit ? (int)(absorbedDamageByArmor / 2) : 0).ToString(); + + // TODO: HP and armor and HP and armor left after shot + } + + private void comboWeapons_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + var weapon = ((sender as ComboBox).SelectedItem as ComboBoxItem)?.Tag as CsgoWeapon; + + this.selectedWeapon = weapon; + settings_Updated(null, null); + } + + private void mnuHelp_Click(object sender, RoutedEventArgs e) + { + About about = new About(); + about.Owner = this; + about.ShowDialog(); + } + + private void Window_MouseMove(object sender, MouseEventArgs e) + { + if (this.mapImage.Source == null) + return; + + var position = Mouse.GetPosition(mapImage); + if (position.X >= 0 && position.Y >= 0 && position.X <= mapImage.ActualWidth && position.Y <= mapImage.ActualHeight) + { + // Percentage on shown pixels + double diffPercX = position.X * 100f / this.mapImage.ActualWidth; + // Distance on original pixel scales + double diffPixelsOriginalX = diffPercX * (this.mapImage.Source as BitmapSource).PixelWidth / 100f; + // times scale multiplier + double unitsDifferenceX = diffPixelsOriginalX * this.loadedMap.MapSizeMultiplier; + txtCursorX.Text = Math.Round(this.loadedMap.UpperLeftWorldXCoordinate + unitsDifferenceX, 2).ToString(System.Globalization.CultureInfo.InvariantCulture); + + // Percentage on shown pixels + double diffPercY = position.Y * 100f / this.mapImage.ActualWidth; + // Distance on original pixel scales + double diffPixelsOriginalY = diffPercY * (this.mapImage.Source as BitmapSource).PixelWidth / 100f; + // times scale multiplier + double unitsDifferenceY = diffPixelsOriginalY * this.loadedMap.MapSizeMultiplier; + txtCursorY.Text = Math.Round(this.loadedMap.UpperLeftWorldYCoordinate - unitsDifferenceY, 2).ToString(System.Globalization.CultureInfo.InvariantCulture); + } + else + { + txtCursorX.Text = txtCursorY.Text = "0"; + } + } + private void Window_KeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.C && Keyboard.Modifiers == ModifierKeys.Control) + { + Clipboard.SetText(txtCursorX.Text + " " + txtCursorY.Text); + } + } + #endregion + } +} diff --git a/DamageCalculator/DamageCalculator/Models/CsgoMapOverview.cs b/DamageCalculator/DamageCalculator/Models/CsgoMapOverview.cs new file mode 100644 index 0000000..e223fa7 --- /dev/null +++ b/DamageCalculator/DamageCalculator/Models/CsgoMapOverview.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media.Imaging; + +namespace Damage_Calculator.Models +{ + public class CsgoMapOverview + { + public BitmapSource MapImage { get; set; } + + public string MapImagePath { get; set; } + + public string MapFileName { get; set; } + + public float MapSizeMultiplier { get; set; } + + public float UpperLeftWorldXCoordinate { get; set; } = -1; + + public float UpperLeftWorldYCoordinate { get; set; } = -1; + + public float CTSpawnMultiplierX { get; set; } = -1; + + public float CTSpawnMultiplierY { get; set; } = -1; + + public float TSpawnMultiplierX { get; set; } = -1; + + public float TSpawnMultiplierY { get; set; } = -1; + + public float BombAX { get; set; } = -1; + + public float BombAY { get; set; } = -1; + + public float BombBX { get; set; } = -1; + + public float BombBY { get; set; } = -1; + } +} diff --git a/DamageCalculator/DamageCalculator/Models/CsgoWeapon.cs b/DamageCalculator/DamageCalculator/Models/CsgoWeapon.cs new file mode 100644 index 0000000..51df07b --- /dev/null +++ b/DamageCalculator/DamageCalculator/Models/CsgoWeapon.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media.Imaging; + +namespace Damage_Calculator.Models +{ + public class CsgoWeapon + { + /// + /// Gets or sets the class name of this weapon. + /// e.g. AK47 would be "weapon_ak47" + /// + public string ClassName { get; set; } + + /// + /// Gets or sets the base damage that the weapon deals, in health points. + /// e.g. AK47 would have 36 base damage + /// + public int BaseDamage { get; set; } = -1; + + /// + /// Gets or sets the armor penetration of this weapon as a percentage. + /// In the files this is saved as a float from 0 (0% penetration) to 2 (100% penetration). + /// + public float ArmorPenetration { get; set; } = -1; + + /// + /// Gets or sets the damage dropoff that this weapon has at 500 units distance. It's saved as a "RangeModifier" stat, + /// where the modifier is a float value between 0 and 1 that contains the factor of the damage left. + /// A value of 0.75 would mean, at 500 units the weapon loses 25% of its damage. + /// The damage left can be calculated by: DAMAGE * RangeModifier^(DISTANCE / 500) + /// + public double DamageDropoff { get; set; } = -1; + + /// + /// Gets or sets the maximum range a bullet of this weapon can travel in units. After this distance the "bullet" will just disappear. + /// + public int MaxBulletRange { get; set; } = -1; + + /// + /// Gets or sets the multiplier of headshots by this weapon. At the point of writing this is "4" for all weapons except two. + /// + public float HeadshotModifier { get; set; } = -1; + } +} diff --git a/DamageCalculator/DamageCalculator/Models/MapPoint.cs b/DamageCalculator/DamageCalculator/Models/MapPoint.cs new file mode 100644 index 0000000..3616dd1 --- /dev/null +++ b/DamageCalculator/DamageCalculator/Models/MapPoint.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Damage_Calculator.Models +{ + class MapPoint + { + public System.Windows.Shapes.Ellipse Circle { get; set; } + + public double PercentageX { get; set; } + + public double PercentageY { get; set; } + } +} diff --git a/DamageCalculator/DamageCalculator/Models/Settings.cs b/DamageCalculator/DamageCalculator/Models/Settings.cs new file mode 100644 index 0000000..bf4f887 --- /dev/null +++ b/DamageCalculator/DamageCalculator/Models/Settings.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Damage_Calculator.Models +{ + public class Settings + { + public SteamHelper SteamHelper = new SteamHelper(); + + public CsgoHelper CsgoHelper = new CsgoHelper(); + } +} diff --git a/DamageCalculator/DamageCalculator/Models/SteamGame.cs b/DamageCalculator/DamageCalculator/Models/SteamGame.cs new file mode 100644 index 0000000..e22c640 --- /dev/null +++ b/DamageCalculator/DamageCalculator/Models/SteamGame.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Damage_Calculator.Models +{ + public class SteamGame + { + public string Name { get; set; } + + public string InstallFolderName { get; set; } + + public int AppId { get; set; } + + public int GameState { get; set; } + + public DateTime LastUpdated { get; set; } + + public long LastOwnerSteam64Id { get; set; } + + public long BytesToDownload { get; set; } + + public long BytesDownloaded { get; set; } + + public long BytesToStage { get; set; } + + public long BytesStaged { get; set; } + + public bool KeepAutomaticallyUpdated { get; set; } + + public bool AllowOtherUpdatesWhileRunning { get; set; } + } + + [Flags] + enum GameState + { + StateInvalid = 0, + StateUninstalled = 1, + StateUpdateRequired = 2, + StateFullyInstalled = 4, + StateEncrypted = 8, + StateLocked = 16, + StateFilesMissing = 32, + StateAppRunning = 64, + StateFilesCorrupt = 128, + StateUpdateRunning = 256, + StateUpdatePaused = 512, + StateUpdateStarted = 1024, + StateUninstalling = 2048, + StateBackupRunning = 4096, + StateReconfiguring = 65536, + StateValidating = 131072, + StateAddingFiles = 262144, + StatePreallocating = 524288, + StateDownloading = 1048576, + StateStaging = 2097152, + StateCommitting = 4194304, + StateUpdateStopping = 8388608 + } +} diff --git a/DamageCalculator/DamageCalculator/Models/SteamLibrary.cs b/DamageCalculator/DamageCalculator/Models/SteamLibrary.cs new file mode 100644 index 0000000..91d8733 --- /dev/null +++ b/DamageCalculator/DamageCalculator/Models/SteamLibrary.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Damage_Calculator.Models +{ + public class SteamLibrary + { + public SteamLibrary() + { + // Nothing to do + } + + public SteamLibrary(string path) + { + this.Path = path; + this.DoesExist = System.IO.Directory.Exists(path); + } + + public SteamLibrary(string path, bool doesExist) + { + this.Path = path; + this.DoesExist = doesExist; + } + + public string Path { get; set; } + + public bool DoesExist { get; set; } + } +} diff --git a/DamageCalculator/DamageCalculator/Properties/AssemblyInfo.cs b/DamageCalculator/DamageCalculator/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e70fd94 --- /dev/null +++ b/DamageCalculator/DamageCalculator/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CSGO Damage Calculator")] +[assembly: AssemblyDescription("CS:GO Damage Calculator")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Matty_L")] +[assembly: AssemblyProduct("CSGO Damage Calculator")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DamageCalculator/DamageCalculator/Properties/Resources.Designer.cs b/DamageCalculator/DamageCalculator/Properties/Resources.Designer.cs new file mode 100644 index 0000000..10d224b --- /dev/null +++ b/DamageCalculator/DamageCalculator/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Damage_Calculator.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Damage_Calculator.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/DamageCalculator/DamageCalculator/Properties/Resources.resx b/DamageCalculator/DamageCalculator/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/DamageCalculator/DamageCalculator/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/DamageCalculator/DamageCalculator/Properties/Settings.Designer.cs b/DamageCalculator/DamageCalculator/Properties/Settings.Designer.cs new file mode 100644 index 0000000..94e3df9 --- /dev/null +++ b/DamageCalculator/DamageCalculator/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Damage_Calculator.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/DamageCalculator/DamageCalculator/Properties/Settings.settings b/DamageCalculator/DamageCalculator/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/DamageCalculator/DamageCalculator/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/DamageCalculator/DamageCalculator/Serializer.cs b/DamageCalculator/DamageCalculator/Serializer.cs new file mode 100644 index 0000000..3c4eb40 --- /dev/null +++ b/DamageCalculator/DamageCalculator/Serializer.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Serialization; + +namespace Damage_Calculator +{ + static class Serializer + { + public static void ToXml(object obj, string path) + { + var serializer = new XmlSerializer(obj.GetType()); + using(var writer = XmlWriter.Create(path)) + { + serializer.Serialize(writer, obj); + } + } + + public static object FromXml(Type type, string pathToXml) + { + var serializer = new XmlSerializer(type); + using (var reader = XmlReader.Create(pathToXml)) + { + return serializer.Deserialize(reader); + } + } + } +} diff --git a/DamageCalculator/DamageCalculator/SteamHelper.cs b/DamageCalculator/DamageCalculator/SteamHelper.cs new file mode 100644 index 0000000..f1937e9 --- /dev/null +++ b/DamageCalculator/DamageCalculator/SteamHelper.cs @@ -0,0 +1,267 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Damage_Calculator.ZatVdfParser; +using Microsoft.Win32; +using Damage_Calculator.Models; + +namespace Damage_Calculator +{ + public class SteamHelper + { + private string steamPath; + private List steamLibraries; + + /// + /// Gets the absolute path to the Steam install directory. + /// If it can't be fetched (i.e. Steam is not installed) null is returned. + /// + public string SteamPath + { + get + { + if(this.steamPath == null) + { + this.UpdateSteamPath(); + } + return this.steamPath; + } + } + + /// + /// Gets a list of all Steam libraries, and whether they're existent or not. + /// If it can't be fetched (i.e. Steam is not installed) null is returned. + /// + public List SteamLibraries + { + get + { + if (this.steamLibraries == null) + { + this.UpdateSteamLibraries(); + } + return this.steamLibraries; + } + } + + /// + /// Forcefully tries to update the property with the current Steam path, even if it should be already set. + /// + public void UpdateSteamPath() + { + this.steamPath = this.GetSteamPath(); + } + + /// + /// Gets the path to the Steam install directory. (For external use is preferred.) + /// + /// The absolute path to the Steam install directory, or null if it can't be fetched. + public string GetSteamPath() + { + var steamKey = Registry.CurrentUser.OpenSubKey("software\\valve\\steam"); + + if (steamKey == null) + return null; + + var steamPath = steamKey.GetValue("SteamPath"); + + if (steamPath == null) + return null; + + return steamPath.ToString(); + } + + /// + /// Forcefully tries to update the property with the current Steam path, even if it should be already set. + /// + public void UpdateSteamLibraries() + { + this.steamLibraries = this.GetSteamLibraries(); + } + + /// + /// Fetches a list of Steam libraries, which are deposited in the Steam config, as well as whether the libraries exist on the drive. + /// + /// A list of all deposited Steam libraries, and if they exist. + public List GetSteamLibraries() + { + if (this.steamPath == null) + this.steamPath = this.GetSteamPath(); + + if (this.steamPath == null) + return null; + + string configFilePath = Path.Combine(this.steamPath, "config\\config.vdf"); + if (!File.Exists(configFilePath)) + return null; + + // Fetch additional libraries + var configFile = new VDFFile(configFilePath); + IEnumerable additionalSteamLibraries = configFile["InstallConfigStore"]?["Software"]?["valve"]?["Steam"].Children.Where(c => c.Name.StartsWith("BaseInstallFolder_")).Select(c => c.Value); + + // List libraries plus default Steam directory, because that's the default library + var allLibraries = new List { new SteamLibrary(this.steamPath) }; + foreach(string addLib in additionalSteamLibraries) + { + allLibraries.Add(new SteamLibrary(addLib.Replace("\\\\", "\\"))); + } + + return allLibraries; + } + + public List GetInstalledGames() + { + var steamLibraries = this.GetSteamLibraries(); + + if (steamLibraries == null) + return null; + + var allGames = new List(); + + foreach(var library in steamLibraries) + { + if (!library.DoesExist) + continue; + + List manifestFiles = Directory.GetFiles(Path.Combine(library.Path, "steamapps")).ToList().Where(f => this.isAppManifestFile(f)).ToList(); + + foreach (string manifestFile in manifestFiles) + { + var manifestVDF = new VDFFile(manifestFile); + + if (manifestVDF.RootElements.Count < 1) + // App manifest might be still existent but the game might not be installed (happened during testing) + continue; + + var root = manifestVDF["AppState"]; + + var currGame = new SteamGame(); + + this.populateGameInfo(currGame, root); + + if((currGame.GameState & (int)GameState.StateFullyInstalled) != 0) + { + // Game was fully installed according to steam + allGames.Add(currGame); + } + } + } + + return allGames; + } + + public string GetGamePathFromExactName(string gameName) + { + var steamLibraries = this.GetSteamLibraries(); + + if (steamLibraries == null) + return null; + + var allGames = new List(); + + foreach (var library in steamLibraries) + { + if (!library.DoesExist) + continue; + + List manifestFiles = Directory.GetFiles(Path.Combine(library.Path, "steamapps")).ToList().Where(f => this.isAppManifestFile(f)).ToList(); + + foreach (string manifestFile in manifestFiles) + { + var manifestVDF = new VDFFile(manifestFile); + + if (manifestVDF.RootElements.Count < 1) + // App manifest might be still existent but the game might not be installed (happened during testing) + continue; + + var root = manifestVDF["AppState"]; + + if(root["name"].Value.Trim().ToLower() != gameName.Trim().ToLower()) + { + // Not our wanted game, skip + continue; + } + + var currGame = new SteamGame(); + + this.populateGameInfo(currGame, root); + + if ((currGame.GameState & (int)GameState.StateFullyInstalled) != 0) + { + // Game was fully installed according to steam + return Path.Combine(library.Path, "steamapps", "common", currGame.InstallFolderName); + } + } + } + + return null; + } + + #region Private Methods + private bool isAppManifestFile(string filePath) + { + return System.Text.RegularExpressions.Regex.IsMatch(filePath.Split(new[] { '\\', '/' }).Last(), "appmanifest_\\d+.acf"); ; + } + + private DateTime fromUnixFormat(long unixFormat) + { + DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Local); + return dateTime.AddSeconds(unixFormat); + } + + private void populateGameInfo(SteamGame game, Element appStateVdf) + { + game.Name = appStateVdf["name"].Value; + + game.InstallFolderName = appStateVdf["installdir"].Value; + + if (int.TryParse(appStateVdf["appid"].Value, out int appId)) + { + game.AppId = appId; + } + + if (int.TryParse(appStateVdf["StateFlags"].Value, out int stateFlags)) + { + game.GameState = stateFlags; + } + + if (long.TryParse(appStateVdf["LastUpdated"].Value, out long lastUpdated)) + { + game.LastUpdated = fromUnixFormat(lastUpdated); + } + + if (long.TryParse(appStateVdf["LastOwner"].Value, out long lastOwner)) + { + game.LastOwnerSteam64Id = lastOwner; + } + + if (long.TryParse(appStateVdf["BytesToDownload"].Value, out long bytesToDownload)) + { + game.BytesToDownload = bytesToDownload; + } + + if (long.TryParse(appStateVdf["BytesDownloaded"].Value, out long bytesDownloaded)) + { + game.BytesDownloaded = bytesDownloaded; + } + + if (long.TryParse(appStateVdf["BytesToStage"].Value, out long bytesToStage)) + { + game.BytesToStage = bytesToStage; + } + + if (long.TryParse(appStateVdf["BytesStaged"].Value, out long bytesStaged)) + { + game.BytesStaged = bytesStaged; + } + + game.KeepAutomaticallyUpdated = appStateVdf["AutoUpdateBehavior"].Value != "0"; + + game.AllowOtherUpdatesWhileRunning = appStateVdf["AllowOtherDownloadsWhileRunning"].Value != "0"; + } + #endregion + } +} diff --git a/DamageCalculator/DamageCalculator/Themes/ColourfulDarkTheme.xaml b/DamageCalculator/DamageCalculator/Themes/ColourfulDarkTheme.xaml new file mode 100644 index 0000000..7f2ecfd --- /dev/null +++ b/DamageCalculator/DamageCalculator/Themes/ColourfulDarkTheme.xaml @@ -0,0 +1,4496 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +