├── Assets
├── about.png
├── ivlogo.ico
├── ivlogo.png
└── ivlogo256.png
├── global.json
├── GTAIVSetupUtility
├── AssemblyInfo.cs
├── MainWindow.xaml
└── MainWindow.xaml.cs
├── .github
└── workflows
│ ├── github-releases-to-discord.yml
│ └── build.yml
├── LICENSE
├── Common
├── AppVersionGrabber.cs
├── DisplayInfoGrabber.cs
└── IniEditor.cs
├── GTAIVSetupUtilityWPF.sln
├── GTAIVSetupUtilityWPF.csproj
├── .gitattributes
├── README.md
├── .gitignore
└── Functions
└── VulkanChecker.cs
/Assets/about.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillian-guide/GTAIVSetupUtilityWPF/HEAD/Assets/about.png
--------------------------------------------------------------------------------
/Assets/ivlogo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillian-guide/GTAIVSetupUtilityWPF/HEAD/Assets/ivlogo.ico
--------------------------------------------------------------------------------
/Assets/ivlogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillian-guide/GTAIVSetupUtilityWPF/HEAD/Assets/ivlogo.png
--------------------------------------------------------------------------------
/Assets/ivlogo256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillian-guide/GTAIVSetupUtilityWPF/HEAD/Assets/ivlogo256.png
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "8.0.0",
4 | "rollForward": "latestMajor",
5 | "allowPrerelease": true
6 | }
7 | }
--------------------------------------------------------------------------------
/GTAIVSetupUtility/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | [assembly: ThemeInfo(
4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5 | //(used if a resource is not found in the page,
6 | // or application resource dictionaries)
7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8 | //(used if a resource is not found in the page,
9 | // app, or any theme specific resource dictionaries)
10 | )]
11 |
--------------------------------------------------------------------------------
/.github/workflows/github-releases-to-discord.yml:
--------------------------------------------------------------------------------
1 | on:
2 | release:
3 | types: [published]
4 |
5 | jobs:
6 | github-releases-to-discord:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout
10 | uses: actions/checkout@v3
11 | - name: Github Releases To Discord
12 | uses: SethCohen/github-releases-to-discord@v1.13.0
13 | with:
14 | webhook_url: ${{ secrets.WEBHOOK_URL }}
15 | color: "2105893"
16 | username: "Setup Utility Updates"
17 | avatar_url: "https://github.com/gillian-guide/gillian-guide.github.io/blob/main/docs/assets/iv-logo-favicon.png?raw=true"
18 | reduce_headings: true
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 GillianMC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Common/AppVersionGrabber.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using NLog;
5 |
6 | namespace GTAIVSetupUtilityWPF.Common
7 | {
8 | public static class AppVersionGrabber
9 | {
10 | private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();
11 |
12 | public static string GetFileVersion(string filePath)
13 | {
14 | const string defaultVersion = "0.0.0.0";
15 |
16 | return (File.Exists(filePath), GetVersionInfo(filePath)) switch
17 | {
18 | (false, _) => defaultVersion,
19 | (true, { } version) when !string.IsNullOrEmpty(version) => version,
20 | _ => defaultVersion
21 | };
22 | }
23 |
24 | private static string? GetVersionInfo(string filePath)
25 | {
26 | try
27 | {
28 | var versionInfo = FileVersionInfo.GetVersionInfo(filePath);
29 | return versionInfo.FileVersion;
30 | }
31 | catch (Exception ex)
32 | {
33 | Logger.Error(ex, "Error retrieving file version for {FilePath}", filePath);
34 | return null;
35 | }
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/GTAIVSetupUtilityWPF.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.5.33516.290
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GTAIVSetupUtilityWPF", "GTAIVSetupUtilityWPF.csproj", "{77D32054-F33D-4DA5-A751-47EBEAC1E868}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{122FC168-A36E-496C-8ABE-864E5E7375D1}"
9 | ProjectSection(SolutionItems) = preProject
10 | .editorconfig = .editorconfig
11 | EndProjectSection
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|Any CPU = Debug|Any CPU
16 | Release|Any CPU = Release|Any CPU
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {77D32054-F33D-4DA5-A751-47EBEAC1E868}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {77D32054-F33D-4DA5-A751-47EBEAC1E868}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {77D32054-F33D-4DA5-A751-47EBEAC1E868}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {77D32054-F33D-4DA5-A751-47EBEAC1E868}.Release|Any CPU.Build.0 = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(SolutionProperties) = preSolution
25 | HideSolutionNode = FALSE
26 | EndGlobalSection
27 | GlobalSection(ExtensibilityGlobals) = postSolution
28 | SolutionGuid = {DD1C2BD9-97AA-4FF7-B716-D1719F7103CE}
29 | EndGlobalSection
30 | EndGlobal
31 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches: [ main, master ]
6 | pull_request:
7 | branches: [ main, master ]
8 | workflow_dispatch:
9 |
10 | jobs:
11 | build:
12 | runs-on: windows-latest
13 |
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v4
17 |
18 | - name: Setup .NET
19 | uses: actions/setup-dotnet@v4
20 | with:
21 | dotnet-version: '8.0.x'
22 |
23 | - name: Restore dependencies
24 | run: dotnet restore GTAIVSetupUtilityWPF.csproj -r win-x64
25 |
26 | - name: Publish Framework-Dependent
27 | run: dotnet publish GTAIVSetupUtilityWPF.csproj --configuration Release --output ./publish/FrameworkDependent --self-contained false -p:PublishSingleFile=true -p:PublishReadyToRun=false -p:DebugType=None -p:DebugSymbols=false
28 |
29 | - name: Publish Self-Contained
30 | run: dotnet publish GTAIVSetupUtilityWPF.csproj --configuration Release --output ./publish/SelfContained --self-contained true -p:PublishSingleFile=true -p:EnableCompressionInSingleFile=true -p:PublishReadyToRun=false -p:InvariantGlobalization=true -p:DebugType=None -p:DebugSymbols=false -r win-x64
31 |
32 | - name: Upload Framework-Dependent Artifact
33 | uses: actions/upload-artifact@v4
34 | with:
35 | name: GTAIVSetupUtilityWPF-FrameworkDependent-${{ github.sha }}
36 | path: ./publish/FrameworkDependent
37 | retention-days: 90
38 |
39 | - name: Upload Self-Contained Artifact
40 | uses: actions/upload-artifact@v4
41 | with:
42 | name: GTAIVSetupUtilityWPF-SelfContained-${{ github.sha }}
43 | path: ./publish/SelfContained
44 | retention-days: 90
45 |
--------------------------------------------------------------------------------
/GTAIVSetupUtilityWPF.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | true
6 | net8.0-windows
7 | enable
8 | true
9 | embedded
10 | latest
11 | true
12 | 1.9.1
13 | Assets\ivlogo.ico
14 | $(MSBuildProjectName)
15 | Gillian's GTA IV Setup Utility
16 | $(Authors)
17 | Gillian
18 | Semi-automatic setup utility for GTA IV
19 | ivlogo256.png
20 | https://gillian-guide.github.io/
21 | README.md
22 | https://github.com/SandeMC/GTAIVSetupUtilityWPF
23 | LICENSE
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | True
53 | \
54 |
55 |
56 | True
57 | \
58 |
59 |
60 | True
61 | \
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Gillian's GTA IV Setup Utility
2 | Semi-automatically installs DXVK and launch options for your GTA IV installation (+extra). It automatically checks your hardware and what options should be available (aswell as setting defaults).
3 |
4 | 
5 |
6 | This version is a re-write of the now-deprecated [Python version](https://github.com/SandeMC/GTAIVSetupUtility).
7 |
8 | ## Usage
9 | - Launch the tool.
10 | - Select your game folder, the one that includes `GTAIV.exe`.
11 | - Press `Install DXVK`, after which, press `Setup launch options`.
12 | - If experienced, play around with the toggles. Defaults should be fine, however, as they're automatically tailored to your hardware.
13 | - Done!
14 |
15 | ## Features
16 | - Automatically installing the best version of DXVK supported by your hardware by checking it's Vulkan capabilities.
17 | - Automatically sets up your launch options, including monitor details and VRAM (VRAM not available for versions older than 1.0.8.0; have to paste options manually on 1.2)
18 | - Accounting for multi-GPU setups, both during DXVK setup and setting up the launch options.
19 | - Detects whether some features are unsupported by your hardware or installed mods.
20 | - Detects if ZolikaPatch is outdated (by checking if the latest option is missing from the ini) and prompts the user to update it.
21 | - Detects if ZolikaPatch and/or FusionFix are installed and prompts to edit their configuration files to be compatible with eachother.
22 | - Properly enables/disables Borderless Fullscreen if using ZolikaPatch or FusionFix
23 | - Warns the user if they have IVSDK .NET and DXVK installed at the same time as RTSS is enabled.
24 | - Prompts the user to fix GFWL achievements if using GFWL (or revert fixing them; this will trigger if using XLivelessNess at the moment).
25 | - Removing `GFWLDLCs.asi` and enabling `LoadDLCs` if up-to-date ZolikaPatch is present.
26 | - Detect obsolete `commandline.txt` options and remove them accordingly.
27 | - Warn the user if they're using `commandline.txt` on Steam version.
28 | - Warn the user if they're using the outdated `dsound.dll` and prompt them to downoad the latest Ultimate ASI Loader.
29 | - Providing tips for what the launch options actually do. And *not* providing useless options.
30 |
31 | ## Contribution
32 | Contribution is highly welcome. I'm poorly experienced with C#, but this rewrite was needed for many reasons. And so, the current code is extremely clunky and works out of prayers.
33 |
34 | ## Attribution
35 | Following NuGet packages were used to create this app:
36 |
37 | - [ByteSize](https://www.nuget.org/packages/ByteSize) by Omar Khudeira - used to calculate and convert the VRAM correctly.
38 | - [Microsoft-WindowsAPICodePack-Shell](https://www.nuget.org/packages/WindowsAPICodePack-Shell) by rpastric, contre, dahall - allows to create a Choose File dialogue box.
39 | - [NLog](https://www.nuget.org/packages/NLog) by Jarek Kowalski, Kim Chriestensen, Julian Verdurmen - used for logging.
40 | - [SharpZipLib](https://www.nuget.org/packages/SharpZipLib) by ICSharpCode - used for extracting a .tar.gz archive provided by DXVK.
41 | - [PromptDialog](https://www.nuget.org/packages/PromptDialog/) by pythonizo - used to prompt the VRAM as a failsafe, as I was too lazy to create my own WPF window for that.
42 | - And Microsoft's official packages such as [System.Management](https://www.nuget.org/packages/System.Management/) for convenience and functional code.
43 |
44 | And these were used during development:
45 |
46 | - [ini-parser](https://github.com/rickyah/ini-parser) by Ricardo Amores Harnandes - was used to edit ZolikaPatch and FusionFix ini files, replaced later due to issues with it.
47 |
--------------------------------------------------------------------------------
/Common/DisplayInfoGrabber.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace GTAIVSetupUtilityWPF.Common
4 | {
5 |
6 | // this code has been brought to you by chatgpt:tm: because i have NO FUCKING IDEA WHAT ANY OF THIS MEANS
7 | // if it breaks i'm switching my gods
8 | public static class DisplayInfo
9 | {
10 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
11 | private struct DISPLAY_DEVICE
12 | {
13 | public int cb;
14 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
15 | public string DeviceName;
16 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
17 | public string DeviceString;
18 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
19 | public string DeviceID;
20 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
21 | public string DeviceKey;
22 | }
23 |
24 | [DllImport("user32.dll", CharSet = CharSet.Unicode)]
25 | private static extern bool EnumDisplayDevices(string? lpDevice, int iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, int dwFlags);
26 |
27 | public static void GetPrimaryDisplayInfo(out int width, out int height, out int refreshRate)
28 | {
29 | DISPLAY_DEVICE primaryDevice = new DISPLAY_DEVICE();
30 | primaryDevice.cb = Marshal.SizeOf(primaryDevice);
31 |
32 | // default values if this doesn't work
33 | width = 1920;
34 | height = 1080;
35 | refreshRate = 60;
36 |
37 | if (!EnumDisplayDevices(null, 0, ref primaryDevice, 0)) return;
38 | DEVMODE devMode = new DEVMODE();
39 | devMode.dmSize = (short)Marshal.SizeOf(devMode);
40 |
41 | if (EnumDisplaySettings(primaryDevice.DeviceName, -1, ref devMode))
42 | {
43 | width = devMode.dmPelsWidth;
44 | height = devMode.dmPelsHeight;
45 | refreshRate = devMode.dmDisplayFrequency;
46 | }
47 | }
48 |
49 | [StructLayout(LayoutKind.Sequential)]
50 | private struct DEVMODE
51 | {
52 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
53 | public string dmDeviceName;
54 | public short dmSpecVersion;
55 | public short dmDriverVersion;
56 | public short dmSize;
57 | public short dmDriverExtra;
58 | public int dmFields;
59 | public int dmPositionX;
60 | public int dmPositionY;
61 | public int dmDisplayOrientation;
62 | public int dmDisplayFixedOutput;
63 | public short dmColor;
64 | public short dmDuplex;
65 | public short dmYResolution;
66 | public short dmTTOption;
67 | public short dmCollate;
68 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
69 | public string dmFormName;
70 | public short dmLogPixels;
71 | public int dmBitsPerPel;
72 | public int dmPelsWidth;
73 | public int dmPelsHeight;
74 | public int dmDisplayFlags;
75 | public int dmDisplayFrequency;
76 | public int dmICMMethod;
77 | public int dmICMIntent;
78 | public int dmMediaType;
79 | public int dmDitherType;
80 | public int dmReserved1;
81 | public int dmReserved2;
82 | public int dmPanningWidth;
83 | public int dmPanningHeight;
84 | }
85 |
86 | [DllImport("user32.dll")]
87 | private static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);
88 | }
89 | }
--------------------------------------------------------------------------------
/Common/IniEditor.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 |
4 | // hi here, i'm an awful coder, so please clean up for me if it really bothers you
5 | // i couldn't find a good ini parser for my needs so i just begged chatgpt for one tbh, idk if it works for anything else but i'd rather not qusetion it
6 |
7 | namespace GTAIVSetupUtilityWPF.Common;
8 |
9 | public sealed class IniEditor
10 | {
11 | private const string DefaultValue = "N/A";
12 |
13 | private readonly Dictionary> _iniData = [];
14 | private readonly string? _filePath;
15 |
16 | public IniEditor(string? filePath)
17 | {
18 | _filePath = filePath;
19 | LoadFile();
20 | }
21 |
22 | private void LoadFile()
23 | {
24 | if (!File.Exists(_filePath))
25 | return;
26 |
27 | string? currentSection = null;
28 |
29 | foreach (var line in File.ReadLines(_filePath))
30 | {
31 | var trimmedLine = line.Trim();
32 |
33 | if (IsSection(trimmedLine))
34 | {
35 | currentSection = ExtractSectionName(trimmedLine);
36 | EnsureSectionExists(currentSection);
37 | }
38 | else if (currentSection is not null)
39 | {
40 | ParseLineIntoSection(trimmedLine, currentSection);
41 | }
42 | }
43 | }
44 |
45 | private static bool IsSection(string line)
46 | => line.StartsWith('[') && line.EndsWith(']');
47 |
48 | private static string ExtractSectionName(string line)
49 | => line[1..^1];
50 |
51 | private void EnsureSectionExists(string section)
52 | {
53 | if (!_iniData.ContainsKey(section))
54 | {
55 | _iniData[section] = [];
56 | }
57 | }
58 |
59 | private void ParseLineIntoSection(string line, string section)
60 | {
61 | int separatorIndex = line.IndexOf('=');
62 |
63 | if (separatorIndex >= 0)
64 | {
65 | string key = line[..separatorIndex].Trim();
66 | string value = line[(separatorIndex + 1)..].Trim();
67 | _iniData[section][key] = value;
68 | }
69 | else
70 | {
71 | // Preserve non-key-value lines (comments, empty lines)
72 | if (!_iniData[section].ContainsKey(line))
73 | {
74 | _iniData[section][line] = null!;
75 | }
76 | }
77 | }
78 |
79 | ///
80 | /// Reads a value from the INI file.
81 | ///
82 | /// The section name (e.g., "MAIN")
83 | /// The key name
84 | /// The value if found, otherwise "N/A"
85 | public string ReadValue(string section, string key)
86 | {
87 | if (_iniData.TryGetValue(section, out var sectionData) &&
88 | sectionData.TryGetValue(key, out var value))
89 | {
90 | return value;
91 | }
92 |
93 | return DefaultValue;
94 | }
95 |
96 | ///
97 | /// Edits or creates a key-value pair in the specified section.
98 | ///
99 | /// The section name
100 | /// The key name
101 | /// The new value to set
102 | public void EditValue(string section, string key, string newValue)
103 | {
104 | EnsureSectionExists(section);
105 | _iniData[section][key] = newValue;
106 | }
107 |
108 | ///
109 | /// Saves the INI data back to the file.
110 | ///
111 | public void SaveFile()
112 | {
113 | var lines = new List();
114 |
115 | foreach (var (section, entries) in _iniData)
116 | {
117 | lines.Add($"[{section}]");
118 |
119 | foreach (var (key, value) in entries)
120 | {
121 | // Preserve non-key-value lines
122 | lines.Add($"{key}={value}");
123 | }
124 | }
125 |
126 | File.WriteAllLines(_filePath!, lines);
127 | }
128 | }
--------------------------------------------------------------------------------
/GTAIVSetupUtility/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 | Welcome to Gillian's GTA IV Setup Utility!
16 |
17 |
18 |
19 |
20 |
21 |
22 | Keep the options at default values unless you know what you're doing.
23 | Tips
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | locked to:
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | Visit my Guide for more info and optimal graphics settings
55 | This tool is open source
56 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
--------------------------------------------------------------------------------
/Functions/VulkanChecker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Management;
7 | using System.Text.Json;
8 | using System.Windows;
9 |
10 | // hi here, i'm an awful coder, so please clean up for me if it really bothers you (and like, this code is *really* stupid, sorry)
11 | // this code accounts for ALL gpu's in the system and tries to work out the best conditions for installing DXVK
12 | // so please don't strip the functionality
13 | namespace GTAIVSetupUtilityWPF.Functions
14 | {
15 | public static class VulkanChecker
16 | {
17 |
18 | private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
19 |
20 | private static (int, int) ConvertApiVersion(uint apiVersion)
21 | {
22 | var major = apiVersion >> 22;
23 | var minor = apiVersion >> 12 & 0x3ff;
24 | return (Convert.ToInt32(major), Convert.ToInt32(minor));
25 | }
26 | public static (int, int, int, bool, bool, bool, bool) VulkanCheck()
27 | {
28 | int gpuCount;
29 | int gplSupport = 0;
30 | int vkDgpuDxvkSupport = 0;
31 | int vkIgpuDxvkSupport = 0;
32 | bool igpuOnly = true;
33 | bool dgpuOnly = true;
34 | bool intelIgpu = false;
35 | bool enableAsync = false;
36 | bool atLeastOneGpuSucceededVulkanInfo = false;
37 | bool atLeastOneGpuSucceededJson = false;
38 | bool atLeastOneGpuFailed = false;
39 | bool atLeastOneGpuFailedGpl = false;
40 | bool atLeastOneGpuFailedFl = false;
41 | bool nvidia50Series = false;
42 | var listOfFailedGpus = new List();
43 | try
44 | {
45 | var query = new ObjectQuery("SELECT * FROM Win32_VideoController");
46 | var searcher = new ManagementObjectSearcher(query);
47 | var videoControllers = searcher.Get();
48 | gpuCount = videoControllers.Count;
49 | }
50 | catch (Exception)
51 | {
52 | Logger.Error($" Ran into error ");
53 | throw;
54 | }
55 | for (int i = 0; i < gpuCount; i++)
56 | {
57 | try
58 | {
59 | Logger.Debug($" Running vulkaninfo on GPU{i}... If this infinitely loops, your GPU is weird!");
60 | using var process = new Process();
61 | process.StartInfo.FileName = "vulkaninfo";
62 | process.StartInfo.Arguments = $"--json={i} --output data{i}.json";
63 | process.StartInfo.RedirectStandardOutput = true;
64 | process.StartInfo.UseShellExecute = false;
65 | process.StartInfo.CreateNoWindow = true;
66 |
67 | process.Start();
68 |
69 | if (!process.WaitForExit(TimeSpan.FromSeconds(10)))
70 | {
71 | atLeastOneGpuFailed = true;
72 | listOfFailedGpus.Add(i);
73 | }
74 | else if (!File.Exists($"data{i}.json"))
75 | {
76 | Logger.Debug($" Failed to run vulkaninfo via the first method, trying again...");
77 | process.StartInfo.Arguments = $"--json={i}";
78 | process.Start();
79 | var output = process.StandardOutput.ReadToEnd();
80 | if (!process.WaitForExit(10) || string.IsNullOrEmpty(output))
81 | {
82 | atLeastOneGpuFailed = true;
83 | listOfFailedGpus.Add(i);
84 | }
85 | else
86 | {
87 | File.WriteAllText($"data{i}.json", output);
88 | }
89 | }
90 | }
91 | catch (Exception ex)
92 | {
93 | Logger.Error(" Ran into error: {Argument1}", ex);
94 | atLeastOneGpuFailed = true;
95 | listOfFailedGpus.Add(i);
96 | }
97 |
98 | if (!listOfFailedGpus.Contains(i))
99 | {
100 | atLeastOneGpuSucceededVulkanInfo = true;
101 | }
102 | else
103 | {
104 | Logger.Error($" Running vulkaninfo on GPU{i} failed! User likely has outdated drivers or an extremely old GPU.");
105 | }
106 | }
107 | if (!atLeastOneGpuSucceededVulkanInfo)
108 | {
109 | MessageBox.Show("The vulkaninfo check failed entirely. This usually means none of your GPU's support Vulkan. Make sure your drivers are up-to-date - don't rely on Windows Update drivers, either.\n\nDXVK is not available.");
110 | Logger.Error(" Running vulkaninfo failed entirely! User likely has outdated drivers or an extremely old GPU.");
111 | return (0, 0, 0, false, false, false, false);
112 | }
113 |
114 | Logger.Debug(" Analyzing the vulkaninfo for every .json generated...");
115 | for (int x = 0; x < gpuCount; x++)
116 | {
117 | if (listOfFailedGpus.Contains(x))
118 | {
119 | Logger.Debug($" GPU{x} is in the failed list, skipping this iteration of the loop...");
120 | continue;
121 | }
122 |
123 | Logger.Debug($" Checking data{x}.json...");
124 | if (File.Exists($"data{x}.json"))
125 | {
126 | using (var file = File.OpenText($"data{x}.json"))
127 | {
128 | int dxvkSupport = 0;
129 | JsonDocument doc;
130 | try
131 | {
132 | doc = JsonDocument.Parse(file.ReadToEnd());
133 | }
134 | catch (JsonException)
135 | {
136 | Logger.Error($" Failed to read data{x}.json.");
137 | atLeastOneGpuFailed = true;
138 | listOfFailedGpus.Add(x);
139 | Logger.Debug($" Removing data{x}.json...");
140 | file.Close();
141 | File.Delete($"data{x}.json");
142 | continue;
143 | }
144 |
145 | var root = doc.RootElement;
146 | JsonElement properties;
147 | JsonElement physicalDeviceProperties;
148 | JsonElement extensions;
149 | JsonElement features;
150 |
151 | if (root.TryGetProperty("capabilities", out var capabilities))
152 | {
153 | var deviceCapabilities = capabilities.GetProperty("device");
154 | properties = deviceCapabilities.GetProperty("properties");
155 | physicalDeviceProperties = properties.GetProperty("VkPhysicalDeviceProperties");
156 |
157 | extensions = deviceCapabilities.GetProperty("extensions");
158 | features = deviceCapabilities.GetProperty("features");
159 | }
160 | else if (root.TryGetProperty("VkPhysicalDeviceProperties", out physicalDeviceProperties)
161 | && root.TryGetProperty("ArrayOfVkExtensionProperties", out extensions))
162 | {
163 | properties = root;
164 | features = root;
165 | }
166 | else
167 | {
168 | Logger.Error($" Failed to read data{x}.json.");
169 | atLeastOneGpuFailed = true;
170 | Logger.Debug($" Removing data{x}.json...");
171 | File.Delete($"data{x}.json");
172 | continue;
173 | }
174 |
175 | var deviceName = physicalDeviceProperties.GetProperty("deviceName").GetString();
176 | var apiVersion = physicalDeviceProperties.GetProperty("apiVersion").GetUInt32();
177 | var (vulkanVerMajor, vulkanVerMinor) = ConvertApiVersion(apiVersion);
178 |
179 | Logger.Info($" {deviceName}'s supported Vulkan version is: {vulkanVerMajor}.{vulkanVerMinor}");
180 | Logger.Debug($" Checking if GPU{x} supports DXVK 2.x...");
181 | if (CheckIfExtensionExists(extensions, "VK_EXT_robustness2")
182 | && CheckIfExtensionExists(extensions,"VK_EXT_transform_feedback")
183 | && features.TryGetProperty("VkPhysicalDeviceRobustness2FeaturesEXT", out var robustnessFeatures)
184 | && robustnessFeatures.TryGetProperty("robustBufferAccess2", out var robustBufferAccess)
185 | && robustBufferAccess.GetBoolean()
186 | && robustnessFeatures.TryGetProperty("nullDescriptor", out var nullDescriptor)
187 | && nullDescriptor.GetBoolean())
188 | {
189 | atLeastOneGpuSucceededJson = true;
190 | Logger.Info($" GPU{x} supports DXVK 2.x, yay!");
191 | dxvkSupport = 3;
192 | }
193 | else
194 | {
195 | Logger.Debug($" GPU{x} doesn't support DXVK 2.x, checking other versions...");
196 | switch (vulkanVerMajor)
197 | {
198 | case 1 when vulkanVerMinor <= 1:
199 | atLeastOneGpuSucceededJson = true;
200 | Logger.Info($" GPU{x} doesn't support DXVK or has outdated drivers.");
201 | break;
202 | case 1 when vulkanVerMinor < 3:
203 | atLeastOneGpuSucceededJson = true;
204 | Logger.Info($" GPU{x} supports Legacy DXVK 1.x.");
205 | dxvkSupport = 1;
206 | break;
207 | }
208 | }
209 |
210 | bool maintenance4;
211 | bool maintenance5;
212 | try
213 | {
214 | if (features.TryGetProperty("VkPhysicalDeviceVulkan13Features", out var vk13Features) &&
215 | vk13Features.TryGetProperty("maintenance4", out var maintenance4Prop))
216 | {
217 | maintenance4 = maintenance4Prop.GetBoolean();
218 | }
219 | else
220 | {
221 | maintenance4 = false;
222 | }
223 |
224 | if (extensions.TryGetProperty("VK_KHR_maintenance5", out var maintenance5Property))
225 | {
226 | maintenance5 = maintenance5Property.GetInt16() == 1;
227 | }
228 | else if (features.TryGetProperty("VkPhysicalDeviceMaintenance5FeaturesKHR", out var maintenance5Property2) &&
229 | maintenance5Property2.TryGetProperty("maintenance5", out var maintenance5Prop))
230 | {
231 | maintenance5 = maintenance5Prop.GetBoolean();
232 | }
233 | else
234 | {
235 | maintenance5 = false;
236 | }
237 | }
238 | catch (Exception ex)
239 | {
240 | Logger.Debug($"Caught an exception while checking maintenance features for GPU{x}: {ex.Message}");
241 | maintenance4 = false;
242 | maintenance5 = false;
243 | }
244 |
245 | if (dxvkSupport == 3 && (!maintenance4 || !maintenance5))
246 | {
247 | Logger.Info($"GPU{x} highest supported DXVK version is DXVK 2.6.2. Versions 2.7 onwards require maintenance4 and maintenance5 extensions.");
248 | dxvkSupport = 2;
249 | }
250 |
251 |
252 | var deviceType = physicalDeviceProperties.GetProperty("deviceType");
253 | var deviceIsDiscreteGpu = deviceType.ValueKind switch
254 | {
255 | JsonValueKind.String => deviceType.GetString() == "VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU",
256 | JsonValueKind.Number => deviceType.GetByte() == 2,
257 | _ => throw new InvalidOperationException($"Unsupported value type {deviceType.ValueKind}"),
258 | };
259 |
260 | if (deviceIsDiscreteGpu && dxvkSupport > vkDgpuDxvkSupport)
261 | {
262 | Logger.Info($" GPU{x} is a discrete GPU.");
263 | vkDgpuDxvkSupport = dxvkSupport;
264 | igpuOnly = false;
265 | if (deviceName!.Contains("RTX 50"))
266 | {
267 | Logger.Info($" GPU{x} is a 50-series NVIDIA GPU.");
268 | nvidia50Series = true;
269 | }
270 | }
271 | else if (dxvkSupport > vkIgpuDxvkSupport)
272 | {
273 | Logger.Info($" GPU{x} is an integrated GPU.");
274 | vkIgpuDxvkSupport = dxvkSupport;
275 | dgpuOnly = false;
276 | if (deviceName!.Contains("Intel"))
277 | {
278 | Logger.Info($" GPU{x} is an integrated Intel iGPU.");
279 | intelIgpu = true;
280 | }
281 | }
282 |
283 | try
284 | {
285 | if (properties.TryGetProperty("VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT", out var pipelinePropsExt))
286 | {
287 | if (pipelinePropsExt.TryGetProperty("graphicsPipelineLibraryIndependentInterpolationDecoration", out var gplVar) && gplVar.GetBoolean())
288 | {
289 | Logger.Info($" GPU{x} supports GPL.");
290 |
291 | if (pipelinePropsExt.TryGetProperty("graphicsPipelineLibraryFastLinking", out var flVar) && flVar.GetBoolean())
292 | {
293 | Logger.Info($" GPU{x} supports Fast Linking.");
294 | if (gplSupport < 2)
295 | gplSupport = 2;
296 | }
297 | else
298 | {
299 | Logger.Debug($" GPU{x} doesn't support Fast Linking.");
300 | atLeastOneGpuFailedFl = true;
301 | if (gplSupport < 1)
302 | gplSupport = 1;
303 | }
304 | }
305 | else
306 | {
307 | Logger.Info($" GPU{x} doesn't support GPL.");
308 | atLeastOneGpuFailedGpl = true;
309 | }
310 | }
311 | else
312 | {
313 | Logger.Info($" GPU{x} doesn't support GPL.");
314 | atLeastOneGpuFailedGpl = true;
315 | }
316 | }
317 | catch
318 | {
319 | Logger.Debug($" Caught an exception, this likely means GPU{x} doesn't support GPL.");
320 | atLeastOneGpuFailedGpl = true;
321 | }
322 | }
323 | Logger.Debug($" Removing data{x}.json...");
324 | File.Delete($"data{x}.json");
325 | }
326 | else { break; }
327 | }
328 |
329 | var messagetext = "";
330 | if (!atLeastOneGpuSucceededJson)
331 | {
332 | messagetext = messagetext + "The vulkaninfo check failed partially. This usually means one of your GPU's may support Vulkan but have outdated drivers - the tool will proceed assuming so, but installing DXVK is not recommended.";
333 | Logger.Error($" Running vulkaninfo failed partially. User likely has outdated drivers or an old GPU.");
334 | igpuOnly = true;
335 | dgpuOnly = false;
336 | intelIgpu = true;
337 | enableAsync = true;
338 | vkIgpuDxvkSupport = 1;
339 | }
340 | else
341 | {
342 | if (atLeastOneGpuFailed && igpuOnly)
343 | {
344 | if (messagetext != "") { messagetext = messagetext + "\n\n"; }
345 | messagetext = messagetext + "The vulkaninfo check failed for discrete GPU but succeeded for the integrated GPU. This usually means your discrete GPU does not support Vulkan.\n\nDXVK is available, but with the assumption that you're going to be playing off the integrated GPU, not the dedicated one.";
346 | }
347 | else if (atLeastOneGpuFailed && !igpuOnly)
348 | {
349 | if (messagetext != "") { messagetext = messagetext + "\n\n"; }
350 | messagetext = messagetext + "The vulkaninfo check failed for one of the GPUs but succeeded for the rest. This usually means one of your discrete GPUs does not support Vulkan.\n\nDXVK is available, but with the assumption that you're going to be playing off the supported GPU.";
351 | }
352 | if ((atLeastOneGpuFailedGpl || atLeastOneGpuFailedFl) && gplSupport == 2)
353 | {
354 | if (messagetext != "") { messagetext = messagetext + "\n\n"; }
355 | messagetext = messagetext + "The GPL check failed for one of the GPUs but Fast Linking is supported by at least one of them. This usually means one of your discrete GPUs or the iGPU does not support DXVK in full.\n\nThe tool will proceed with the assumption that you're going to be playing off the GPU that didn't fail the GPL check (usually your main GPU), but provide options for async just incase.";
356 | enableAsync = true;
357 | }
358 | if (nvidia50Series)
359 | {
360 | if (messagetext != "") { messagetext = messagetext + "\n\n"; }
361 | messagetext = messagetext + "Due to your (likely main) discrete GPU being a 50-series NVIDIA GPU, make sure your drivers are up-to-date, as DXVK may not work on outdated drivers.";
362 | }
363 | if (messagetext != "") { MessageBox.Show(messagetext + "\n\nMake sure your drivers are up-to-date - don't rely on Windows Update drivers, either."); }
364 | }
365 | return (vkDgpuDxvkSupport, vkIgpuDxvkSupport, gplSupport, igpuOnly, dgpuOnly, intelIgpu, enableAsync);
366 | }
367 | private static bool CheckIfExtensionExists(JsonElement extensionElement, string extensionName)
368 | {
369 | switch (extensionElement.ValueKind)
370 | {
371 | case JsonValueKind.Object:
372 | return extensionElement.TryGetProperty(extensionName, out _);
373 | case JsonValueKind.Array:
374 | {
375 | return extensionElement.EnumerateArray().Any(extension => extension.GetProperty("extensionName").GetString() == extensionName);
376 | }
377 | case JsonValueKind.Undefined:
378 | case JsonValueKind.String:
379 | case JsonValueKind.Number:
380 | case JsonValueKind.True:
381 | case JsonValueKind.False:
382 | case JsonValueKind.Null:
383 | default:
384 | throw new ArgumentOutOfRangeException(nameof(extensionElement), $"Unknown extension element kind {extensionElement.ValueKind}");
385 | }
386 | }
387 | }
388 | }
389 |
--------------------------------------------------------------------------------
/GTAIVSetupUtility/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Diagnostics;
5 | using System.Formats.Tar;
6 | using System.IO;
7 | using System.IO.Compression;
8 | using System.Linq;
9 | using System.Management;
10 | using System.Net.Http;
11 | using System.Text.Json;
12 | using System.Threading.Tasks;
13 | using System.Runtime.InteropServices;
14 | using System.Windows;
15 | using ByteSizeLib;
16 | using GTAIVSetupUtilityWPF.Common;
17 | using GTAIVSetupUtilityWPF.Functions;
18 | using Microsoft.WindowsAPICodePack.Dialogs;
19 | using NLog;
20 |
21 | // hi here, i'm an awful coder, so please clean up for me if it really bothers you
22 |
23 | namespace GTAIVSetupUtilityWPF.GTAIVSetupUtility
24 | {
25 | public partial class MainWindow
26 | {
27 | private readonly (int, int, int, bool, bool, bool, bool) _resultVk;
28 | private readonly int _installDxvk;
29 | private int _vram1;
30 | private int _vram2;
31 | private bool _ffix;
32 | private bool _ffixLatest;
33 | private bool _isRetail;
34 | private bool _isIvsdkInstalled;
35 | private bool _dxvkOnIgpu;
36 | private bool _firstGpu = true;
37 | private string _rtssConfig = File.Exists(@"C:\Program Files (x86)\RivaTuner Statistics Server\Profiles\GTAIV.exe.cfg") ? @"C:\Program Files (x86)\RivaTuner Statistics Server\Profiles\GTAIV.exe.cfg" : File.Exists(@"C:\Program Files (x86)\RivaTuner Statistics Server\Profiles\Global") ? @"C:\Program Files (x86)\RivaTuner Statistics Server\Profiles\Global" : string.Empty;
38 | private string? _iniPath;
39 | private string? _iniPathZp;
40 |
41 | private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
42 |
43 | static bool IsLinux()
44 | {
45 | // Direct Linux detection
46 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
47 | return true;
48 |
49 | // Wine detection
50 | if (Environment.GetEnvironmentVariable("WINE_PREFIX") != null ||
51 | Environment.GetEnvironmentVariable("WINEPREFIX") != null)
52 | return true;
53 |
54 | // Check for Linux-specific filesystem structures
55 | if (Directory.Exists("/proc") || Directory.Exists("/sys"))
56 | return true;
57 |
58 | // Check PATH separator (Linux uses ':' while Windows uses ';')
59 | var path = Environment.GetEnvironmentVariable("PATH");
60 | if (path != null && path.Contains(':') && !path.Contains(';'))
61 | return true;
62 |
63 | return false;
64 | }
65 |
66 | [STAThread]
67 | public static void Main()
68 | {
69 | Application app = new Application();
70 | MainWindow mainWindow = new MainWindow();
71 | app.Run(mainWindow);
72 | }
73 | public MainWindow()
74 | {
75 | if (File.Exists("GTAIVSetupUtilityLog.txt")) { File.Delete("GTAIVSetupUtilityLog.txt"); }
76 | LogManager.Setup().LoadConfiguration(builder =>
77 | {
78 | builder.ForLogger().FilterMinLevel(LogLevel.Debug).WriteToFile(fileName: "GTAIVSetupUtilityLog.txt");
79 | });
80 | Logger.Info(" Initializing the main window...");
81 | InitializeComponent();
82 | Logger.Info(" Main window initialized!");
83 |
84 | if (IsLinux())
85 | {
86 | Logger.Info(" Detected Linux - DXVK functionality will be disabled.");
87 | _installDxvk = 0;
88 |
89 | DxvkPanel.IsEnabled = false;
90 |
91 | MessageBox.Show(
92 | "DXVK installation and Vulkan checking is only available on Windows.\n\n" +
93 | "These features have been disabled. Launch options configuration remains available.",
94 | "Linux Detected - DXVK Unavailable",
95 | MessageBoxButton.OK,
96 | MessageBoxImage.Information);
97 |
98 | Logger.Info(" DXVK functionality disabled for Linux.");
99 | return;
100 | }
101 |
102 | Logger.Info(" Initializing the vulkan check...");
103 | _resultVk = VulkanChecker.VulkanCheck();
104 | Logger.Info(" Vulkan check finished!");
105 |
106 | var vkDgpuDxvkSupport = _resultVk.Item1;
107 | var vkIgpuDxvkSupport = _resultVk.Item2;
108 | var gplSupport = _resultVk.Item3;
109 | var igpuOnly = _resultVk.Item4;
110 | var dgpuOnly = _resultVk.Item5;
111 | var intelIgpu = _resultVk.Item6;
112 | var allowAsync = _resultVk.Item7;
113 |
114 | switch (igpuOnly)
115 | {
116 | case true when !dgpuOnly:
117 | Logger.Debug($" User's PC only has an iGPU. Setting Install DXVK to {vkIgpuDxvkSupport}.");
118 | _installDxvk = vkIgpuDxvkSupport;
119 | break;
120 | case false when dgpuOnly:
121 | Logger.Debug($" User's PC only has a dGPU. Setting Install DXVK to {vkDgpuDxvkSupport}.");
122 | _installDxvk = vkDgpuDxvkSupport;
123 | break;
124 | case false when !dgpuOnly:
125 | Logger.Debug(" User's PC has both an iGPU and a dGPU. Doing further checks...");
126 | switch ((vkDgpuDxvkSupport, vkIgpuDxvkSupport))
127 | {
128 | case (0, 1):
129 | case (0, 2):
130 | case (0, 3):
131 | Logger.Debug(" User PC's iGPU supports DXVK, but their dGPU does not - asking them what to do...");
132 | var result = MessageBox.Show("Your iGPU supports DXVK but your GPU doesn't - do you still wish to install?", "Install DXVK?", MessageBoxButton.YesNo, MessageBoxImage.Question);
133 | if (result == MessageBoxResult.Yes)
134 | {
135 | Logger.Debug(" User chose to install DXVK for the iGPU");
136 | _dxvkOnIgpu = true;
137 | Logger.Debug($" Setting Install DXVK to {vkIgpuDxvkSupport}");
138 | _installDxvk = vkIgpuDxvkSupport;
139 | }
140 | else
141 | {
142 | Logger.Debug(" User chose not to install DXVK.");
143 | }
144 | break;
145 | case (1, 2):
146 | case (1, 3):
147 | case (2, 3):
148 | Logger.Debug(" User PC's iGPU supports DXVK, but their dGPU supports an inferior version - asking them what to do...");
149 | var resultVer = MessageBox.Show("Your iGPU supports a greater version of DXVK than your GPU - which version do you wish to install?\n\nPress 'Yes' to install the version matching your GPU.\n\nPress 'No' to install the version matching your iGPU instead.", "Which DXVK version to install?", MessageBoxButton.YesNo, MessageBoxImage.Question);
150 | if (resultVer == MessageBoxResult.Yes)
151 | {
152 | Logger.Debug($" User chose to install DXVK for the dGPU. Setting Install DXVK to {vkDgpuDxvkSupport}.");
153 | _installDxvk = vkDgpuDxvkSupport;
154 | }
155 | else
156 | {
157 | Logger.Debug($" User chose to install DXVK for the iGPU. Setting Install DXVK to {vkIgpuDxvkSupport}.");
158 | _dxvkOnIgpu = true;
159 | _installDxvk = vkIgpuDxvkSupport;
160 | }
161 | break;
162 | case (3, 3):
163 | case (2, 2):
164 | case (1, 1):
165 | case (2, 1):
166 | case (3, 1):
167 | case (3, 2):
168 | case (3, 0):
169 | case (2, 0):
170 | case (1, 0):
171 | Logger.Debug($" User's GPU supports the same or a better version of DXVK as the iGPU. Setting Install DXVK to {vkDgpuDxvkSupport}");
172 | _installDxvk = vkDgpuDxvkSupport;
173 | break;
174 | }
175 |
176 | break;
177 | }
178 |
179 | if (intelIgpu && igpuOnly)
180 | {
181 | Logger.Debug(" User's PC only has an Intel iGPU. Prompting them to install DXVK 1.10.1.");
182 | var result = MessageBox.Show("Your PC only has an Intel iGPU on it. While it does support more modern versions on paper, it's reported that DXVK 1.10.1 might be your only supported version. Do you wish to install it?\n\nIf 'No' is selected, DXVK will be installed following the normal procedure.", "Message", MessageBoxButton.YesNo);
183 |
184 | if (result == MessageBoxResult.Yes)
185 | {
186 | Logger.Debug(" Setting Install DXVK to -1 - a special case to install 1.10.1 for Intel iGPU's.");
187 | _installDxvk = -1;
188 | }
189 | }
190 | if (gplSupport != 2 || _installDxvk < 2) { AsyncCheckbox.Visibility = Visibility.Visible; AsyncCheckbox.IsEnabled = true; AsyncCheckbox.IsChecked = true; Logger.Debug(" User's GPU doesn't support GPL in full, enable async toggle."); }
191 | else if (allowAsync) { AsyncCheckbox.Visibility = Visibility.Visible; AsyncCheckbox.IsEnabled = true; Logger.Debug(" One of user's GPU doesn't support GPL in full, allow enabling async for an edge case scenario."); }
192 | }
193 |
194 |
195 | private static string GetAssemblyVersion()
196 | {
197 | return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version?.ToString()
198 | ?? String.Empty;
199 | }
200 |
201 | private void ResetVars()
202 | {
203 | InstallDxvkBtn.IsDefault = true;
204 | LaunchOptionsBtn.IsDefault = false;
205 | LaunchOptionsBtn.FontWeight = FontWeights.Normal;
206 | InstallDxvkBtn.Width = 160;
207 | InstallDxvkBtn.FontWeight = FontWeights.SemiBold;
208 | InstallDxvkBtn.FontSize = 12;
209 | InstallDxvkBtn.Content = "Install DXVK";
210 | UninstallDxvkBtn.Visibility = Visibility.Collapsed;
211 | VidMemCheckbox.IsEnabled = true;
212 | VidMemCheckbox.IsChecked = true;
213 | Gb3Checkbox.IsEnabled = true;
214 | Gb4Checkbox.IsEnabled = true;
215 | }
216 |
217 | private void DeleteFiles(string directory, List filename)
218 | {
219 | foreach(var file in filename)
220 | {
221 | if (File.Exists($"{directory}/{file}"))
222 | {
223 | File.Delete($"{directory}/{file}");
224 | }
225 | }
226 | }
227 |
228 | private void client_DownloadFileCompleted(object? sender, AsyncCompletedEventArgs e)
229 | {
230 | Dispatcher.BeginInvoke((Action)delegate
231 | {
232 | Logger.Debug(" Successfully downloaded.");
233 | _downloadFinished = true;
234 | InstallDxvkBtn.Content = $"Installing...";
235 | });
236 | }
237 | private void async_Click(object sender, RoutedEventArgs e)
238 | {
239 | Logger.Debug(" User toggled async.");
240 | if (TipsCheckbox.IsChecked != true) return;
241 | Logger.Debug(" Displaying a tip...");
242 | MessageBox.Show("DXVK-async is the next best alternative to stutter-less experience - since your GPU doesn't support GPL with Fast Linking, you can reduce stutter with async shader building, which will be a little faster than traditional shader building.");
243 | }
244 | private void vsync_Click(object sender, RoutedEventArgs e)
245 | {
246 | Logger.Debug(" User toggled VSync.");
247 | if (TipsCheckbox.IsChecked != true) return;
248 | Logger.Debug(" Displaying a tip...");
249 | MessageBox.Show("The in-game VSync implementation produces framepacing issues. DXVK's VSync implementation should be preferred.\n\nIt's recommended to keep this on and in-game's implementation off.");
250 | }
251 | private void Latency_Click(object sender, RoutedEventArgs e)
252 | {
253 | Logger.Debug(" User toggled Max Frame Latency.");
254 | if (TipsCheckbox.IsChecked != true) return;
255 | Logger.Debug(" Displaying a tip...");
256 | MessageBox.Show("This option may help avoiding further framepacing issues. It's recommended to keep this on.");
257 | }
258 | private void NoRestrictions_Click(object sender, RoutedEventArgs e)
259 | {
260 | Logger.Debug(" User toggled -norestrictions.");
261 | if (TipsCheckbox.IsChecked != true) return;
262 | Logger.Debug(" Displaying a tip...");
263 | MessageBox.Show("This option allows you to set any in-game settings independently of what the game restricts you to. It's recommended to keep this on. ");
264 | }
265 |
266 | private void NoMemRestrict_Click(object sender, RoutedEventArgs e)
267 | {
268 | Logger.Debug(" User toggled -nomemrestrict.");
269 | if (TipsCheckbox.IsChecked != true) return;
270 | Logger.Debug(" Displaying a tip...");
271 | MessageBox.Show("-nomemrestrict allows the game to use all the dedicated memory resources up to the limits. It's recommended to keep this on.");
272 | }
273 | private void Windowed_Click(object sender, RoutedEventArgs e)
274 | {
275 | if (TipsCheckbox.IsChecked != true) return;
276 | Logger.Debug(" Displaying a tip...");
277 | MessageBox.Show("This option allows to use Borderless Fullscreen instead of Exclusive Fullscreen. Provides better experience and sometimes better performance. It's recommended to keep this on.");
278 | }
279 | private void VidMem_Click(object sender, RoutedEventArgs e)
280 | {
281 | Logger.Debug(" User toggled -availablevidmem.");
282 | if (VidMemCheckbox.IsChecked == false)
283 | {
284 | Gb3Checkbox.IsEnabled = false;
285 | Gb4Checkbox.IsEnabled = false;
286 | }
287 | else
288 | {
289 | Gb3Checkbox.IsEnabled = true;
290 | Gb4Checkbox.IsEnabled = true;
291 | }
292 |
293 | if (TipsCheckbox.IsChecked != true) return;
294 | Logger.Debug(" Displaying a tip...");
295 | MessageBox.Show("This option forces a specific value of video memory due to the game not being able to do so automatically sometimes. It's recommended to keep this at default.");
296 | }
297 |
298 | private void VidMemLock_Click(object sender, RoutedEventArgs e)
299 | {
300 | Logger.Debug(" User changed the vidmem lock");
301 | if (TipsCheckbox.IsChecked != true) return;
302 | Logger.Debug(" Displaying a tip...");
303 | MessageBox.Show("This option allows to change between a 3GB and a 4GB VRAM lock when setting -availablevidmem up.\n\nThe game is 32-bit, so going beyond 4GB is entirely pointless. However, some people reported less issues with it being locked to 3GB instead.\n\nIt's recommended to keep this at default. Option is disabled if latest FusionFix is detected.");
304 | }
305 | private void MonitorDetail_Click(object sender, RoutedEventArgs e)
306 | {
307 | Logger.Debug(" User toggled Monitor Details.");
308 | if (TipsCheckbox.IsChecked != true) return;
309 | Logger.Debug(" Displaying a tip...");
310 | MessageBox.Show("This option forces a specific resolution and refresh rate due to the game not being able to do so automatically sometimes. It's recommended to keep this at default.");
311 | }
312 |
313 | private void AboutButton_Click(object sender, RoutedEventArgs e)
314 | {
315 | Logger.Debug(" User opened the About window.");
316 | MessageBox.Show(
317 | "This software is made by Gillian for the Modding Guide. Below is debug text, you don't need it normally.\n\n" +
318 | $"DXVK to install: {_installDxvk}\n" +
319 | $"dGPU DXVK Support: {_resultVk.Item1}\n" +
320 | $"iGPU DXVK Support: {_resultVk.Item2}\n" +
321 | $"GPL support state: {_resultVk.Item3}\n" +
322 | $"iGPU Only: {_resultVk.Item4}\n" +
323 | $"dGPU Only: {_resultVk.Item5}\n" +
324 | $"Intel iGPU: {_resultVk.Item6}\n\n" +
325 | $"Version: {GetAssemblyVersion()}",
326 | "Information");
327 | }
328 | private void Button_Click(object sender, RoutedEventArgs e)
329 | {
330 | Logger.Debug(" User is selecting the game folder...");
331 | while (true)
332 | {
333 | var dialog = new CommonOpenFileDialog();
334 | dialog.InitialDirectory = @"C:\Program Files (x86)\Steam\steamapps\Grand Theft Auto IV\GTAIV";
335 | dialog.IsFolderPicker = true;
336 | if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
337 | {
338 | Logger.Debug(" User selected a folder, proceeding...");
339 | var gameVersion = AppVersionGrabber.GetFileVersion($"{dialog.FileName}\\GTAIV.exe");
340 | var isDxvkInstalled = false;
341 | var fusionFixIniPath = Directory.GetFiles(dialog.FileName, "GTAIV.EFLC.FusionFix.ini", SearchOption.AllDirectories).FirstOrDefault();
342 | var fusionFixCfgPath = Directory.GetFiles(dialog.FileName, "GTAIV.EFLC.FusionFix.cfg", SearchOption.AllDirectories).FirstOrDefault();
343 | var zolikaPatchIniPath = Directory.GetFiles(dialog.FileName, "ZolikaPatch.ini", SearchOption.AllDirectories).FirstOrDefault();
344 | if (gameVersion.StartsWith("1, 0") || (gameVersion.StartsWith("1.2")))
345 | {
346 | if (File.Exists($@"{dialog.FileName}\commandline.txt")) {
347 | Logger.Debug(" Filtering commandline.txt from garbage because I know *somebody* will have a garbage commandline.");
348 | var lines = File.ReadAllLines($@"{dialog.FileName}\commandline.txt");
349 | var filteredLines = lines.Where(line =>
350 | !line.Contains("-no_3GB") &&
351 | !line.Contains("-noprecache") &&
352 | !line.Contains("-notimefix") &&
353 | !line.Contains("-novblank") &&
354 | !line.Contains("-percentvidmem") &&
355 | !line.Contains("-memrestrict") &&
356 | !line.Contains("-reserve") &&
357 | !line.Contains("-reservedApp") &&
358 | !line.Contains("-disableimposters") &&
359 | !line.Contains("-force2vb") &&
360 | !line.Contains("-minspecaudio"));
361 | File.WriteAllLines($@"{dialog.FileName}\commandline.txt", filteredLines);
362 | }
363 | if (gameVersion.StartsWith("1, 0")) { _isRetail = true; Logger.Debug(" Folder contains a retail exe."); }
364 | else {
365 | Logger.Debug(" Folder contains an exe of Steam Version.");
366 | _isRetail = false;
367 | if (File.Exists($@"{dialog.FileName}\commandline.txt")) {
368 | Logger.Info("commandline.txt detected on Steam Version...");
369 | MessageBox.Show("You appear to have a commandline.txt, however your are using the Steam version which doesn't use that file. Consider moving these options to Steam Launch Options or launch arguments.");
370 | }
371 | }
372 | if (_isRetail && !gameVersion.StartsWith("1, 0, 8"))
373 | { VidMemCheckbox.IsEnabled = false; Gb3Checkbox.IsEnabled = false; Gb4Checkbox.IsEnabled = false; Logger.Debug(" Folder contains an exe of some pre-1.0.8.0 version. Disabling the -availablevidmem toggle."); }
374 | if (File.Exists($@"{dialog.FileName}\dsound.dll"))
375 | {
376 | Logger.Info("dsound.dll detected, warning the user...");
377 | var result = MessageBox.Show("You appear to have an outdated ASI Loader (dsound.dll). Consider removing it.\n\nPress 'Yes' to get redirected to download to the latest version - download the non-x64 one, rename dinput8.dll to xlive.dll if you don't plan to play GFWL and using non-Steam version.", "Outdated ASI loader.", MessageBoxButton.YesNo, MessageBoxImage.Question);
378 | if (result == MessageBoxResult.Yes)
379 | {
380 | var psi = new ProcessStartInfo
381 | {
382 | FileName = "cmd",
383 | Arguments = "/c start https://github.com/ThirteenAG/Ultimate-ASI-Loader/releases/latest",
384 | CreateNoWindow = true,
385 | UseShellExecute = false,
386 | };
387 | Process.Start(psi);
388 | }
389 | }
390 | DirectoryTxt.Text = "Game Directory:";
391 | DirectoryTxt.FontWeight = FontWeights.Normal;
392 | DirectoryTxt.TextDecorations = null;
393 | TipsNote.TextDecorations = TextDecorations.Underline;
394 | GameDirectory.Text = dialog.FileName;
395 | LaunchOptionsPanel.IsEnabled = true;
396 | DirectoryBtn.IsDefault = false;
397 | ResetVars();
398 |
399 | _isIvsdkInstalled = Directory.GetFiles(dialog.FileName, "IVSDKDotNet.asi", SearchOption.AllDirectories).FirstOrDefault() != null;
400 |
401 | if (File.Exists($@"{dialog.FileName}\vulkan.dll") ||
402 | File.Exists($@"{dialog.FileName}\d3d9.dll"))
403 | {
404 | isDxvkInstalled = true;
405 | VidMemCheckbox.IsEnabled = false;
406 | VidMemCheckbox.IsChecked = false;
407 | Gb3Checkbox.IsEnabled = false;
408 | Gb4Checkbox.IsEnabled = false;
409 | UninstallDxvkBtn.Visibility = Visibility.Collapsed;
410 | }
411 |
412 | if (File.Exists($@"{dialog.FileName}\vulkan.dll"))
413 | {
414 | _ffix = true;
415 | _ffixLatest = true;
416 | }
417 | else
418 | {
419 | _ffixLatest = false;
420 | MessageBox.Show("If you have FusionFix installed, please note that is outdated. It is highly recommended to install the latest version.");
421 | }
422 |
423 | if (_resultVk is { Item1: 0, Item2: 0 })
424 | { DxvkPanel.IsEnabled = false; Logger.Debug(" DXVK is not supported - disabling the DXVK panel."); }
425 | else
426 | {
427 | if (isDxvkInstalled)
428 | {
429 | Logger.Debug(" Detected that DXVK is likely already installed.");
430 | if (_ffixLatest)
431 | {
432 | InstallDxvkBtn.Content = "Update DXVK";
433 | }
434 | else
435 | {
436 | InstallDxvkBtn.Content = "Reinstall DXVK";
437 | InstallDxvkBtn.IsDefault = false;
438 | LaunchOptionsBtn.IsDefault = true;
439 | LaunchOptionsBtn.FontWeight = FontWeights.SemiBold;
440 | InstallDxvkBtn.FontWeight = FontWeights.Normal;
441 | InstallDxvkBtn.Width = 78;
442 | InstallDxvkBtn.FontSize = 11;
443 | UninstallDxvkBtn.Visibility = Visibility.Visible;
444 | }
445 | MonitorDetailsCheckbox.IsChecked = true;
446 | if (File.Exists($@"{dialog.FileName}\commandline.txt"))
447 | {
448 | Logger.Debug(" Filtering commandline.txt from -managed incase present.");
449 | var lines = File.ReadAllLines($@"{dialog.FileName}\commandline.txt");
450 | var filteredLines = lines.Where(line =>
451 | !line.Contains("-managed"));
452 | File.WriteAllLines($@"{dialog.FileName}\commandline.txt", filteredLines);
453 | }
454 |
455 | var rtssGtaivConfig = new IniEditor(_rtssConfig);
456 | if (_isIvsdkInstalled && !string.IsNullOrEmpty(_rtssConfig))
457 | {
458 | if (rtssGtaivConfig.ReadValue("Hooking", "EnableHooking") == "1")
459 | {
460 | Logger.Info(" User has DXVK and IVSDK .NET and RTSS enabled at the same time. Showing the warning prompt.");
461 | MessageBox.Show($"You currently have RivaTuner Statistics Server enabled (it might be a part of MSI Afterburner).\n\nTo avoid issues with the game launching when DXVK and IVSDK .NET are both installed, go into your tray icons, press on the little monitor icon with the number 60, press 'Add' in bottom left, select GTA IV's executable and set Application detection level to 'None'.\n\nIf you want the statistics, set it to Low and restart the tool, with the game running.");
462 | }
463 | }
464 | }
465 | else
466 | {
467 | Logger.Debug(" DXVK is not installed, updating the button name incase the user changed directories before.");
468 | InstallDxvkBtn.Content = "Install DXVK";
469 | }
470 | Logger.Debug(" Enabled the DXVK panel.");
471 | DxvkPanel.IsEnabled = true;
472 | }
473 |
474 | var optionsChanged = false;
475 | var optToChangeOptions = false;
476 | var iniParserZp = new IniEditor(_iniPathZp);
477 | var incompatibleOptions = new List
478 | {
479 | "BenchmarkFix",
480 | "BikePhoneAnimsFix",
481 | "BorderlessWindowed",
482 | "BuildingAlphaFix",
483 | "BuildingDynamicShadows",
484 | "CarDynamicShadowFix",
485 | "CarPartsShadowFix",
486 | "CutsceneFixes",
487 | "DoNotPauseOnMinimize",
488 | "DualVehicleHeadlights",
489 | "EmissiveLerpFix",
490 | "EpisodicVehicleSupport",
491 | "EpisodicWeaponSupport",
492 | "ForceCarHeadlightShadows",
493 | "ForceDynamicShadowsEverywhere",
494 | "ForceShadowsOnObjects",
495 | "HighFPSBikePhysicsFix",
496 | "HighFPSSpeedupFix",
497 | "HighQualityReflections",
498 | "ImprovedShaderStreaming",
499 | "MouseFix",
500 | "NewMemorySystem",
501 | "NoLiveryLimit",
502 | "NoLODLightHeightCutoff",
503 | "OutOfCommissionFix",
504 | "PoliceEpisodicWeaponSupport",
505 | "RemoveUselessChecks",
506 | "RemoveBoundingBoxCulling",
507 | "ReversingLightFix",
508 | "SkipIntro",
509 | "SkipMenu"
510 | };
511 | switch (!string.IsNullOrEmpty(fusionFixIniPath), !string.IsNullOrEmpty(zolikaPatchIniPath))
512 | {
513 | case (false, false):
514 | Logger.Debug(" User doesn't have neither ZolikaPatch or FusionFix. Disabling the Borderless Windowed toggle.");
515 | WindowedCheckbox.IsChecked = false;
516 | WindowedCheckbox.IsEnabled = false;
517 | break;
518 | case (true, false):
519 | _iniPath = !string.IsNullOrEmpty(fusionFixCfgPath) ? fusionFixCfgPath : fusionFixIniPath;
520 | _ffix = true;
521 | Logger.Debug(" User has FusionFix.");
522 | if (File.Exists($@"{dialog.FileName}\commandline.txt"))
523 | {
524 | Logger.Debug(" Filtering commandline.txt from -windowed and -noBlockOnLostFocus incase present.");
525 | var lines = File.ReadAllLines($@"{dialog.FileName}\commandline.txt");
526 | var filteredLines = lines.Where(line =>
527 | !line.Contains("-windowed") &&
528 | !line.Contains("-noBlockOnLostFocus"));
529 | File.WriteAllLines($@"{dialog.FileName}\commandline.txt", filteredLines);
530 | }
531 | break;
532 | case (false, true):
533 | _iniPathZp = zolikaPatchIniPath;
534 | _ffix = false;
535 | Logger.Debug(" User has ZolikaPatch.");
536 | var iniParser = new IniEditor(_iniPathZp);
537 | if (iniParser.ReadValue("Options", "RestoreDeathMusic") == "N/A")
538 | {
539 | var result = MessageBox.Show("Your ZolikaPatch is outdated.\n\nDo you wish to download the latest version? (this will redirect to Zolika1351's website for manual download)", "ZolikaPatch is outdated", MessageBoxButton.YesNo, MessageBoxImage.Question);
540 | if (result == MessageBoxResult.Yes)
541 | {
542 | var psi = new ProcessStartInfo
543 | {
544 | FileName = "cmd",
545 | Arguments = "/c start https://zolika1351.pages.dev/mods/ivpatch",
546 | CreateNoWindow = true,
547 | UseShellExecute = false,
548 | };
549 | Process.Start(psi);
550 | MessageBox.Show("Press OK to restart the app after updating ZolikaPatch. Do not unpack 'PlayGTAIV.exe'.", "ZolikaPatch is outdated");
551 | System.Windows.Forms.Application.Restart();
552 | Environment.Exit(0);
553 | }
554 | }
555 | if (File.Exists($@"{dialog.FileName}\GFWLDLC.asi"))
556 | {
557 | File.Delete($@"{dialog.FileName}\GFWLDLC.asi");
558 | }
559 | if (iniParser.ReadValue("Options", "LoadDLCs") == "0")
560 | {
561 | iniParser.EditValue("Options", "LoadDLCs", "1");
562 | }
563 | if (File.Exists($@"{dialog.FileName}\dinput8.dll") && !File.Exists($@"{dialog.FileName}\xlive.dll"))
564 | {
565 | var result = MessageBox.Show("You appear to be using GFWL. Do you wish to remove Steam Achievements (if exists) and fix ZolikaPatch options to receive GFWL achievements?\n\nPressing 'No' can revert this if you agreed to this earlier.", "GFWL Achievements", MessageBoxButton.YesNo, MessageBoxImage.Question);
566 | if (result == MessageBoxResult.Yes)
567 | {
568 | if (File.Exists($@"{dialog.FileName}\SteamAchievements.asi"))
569 | {
570 | if (!Directory.Exists($@"{dialog.FileName}\backup"))
571 | {
572 | Directory.CreateDirectory($@"{dialog.FileName}\backup");
573 | }
574 | File.Move($@"{dialog.FileName}\SteamAchievements.asi", $@"{dialog.FileName}\backup\SteamAchievements.asi");
575 | }
576 | if (iniParser.ReadValue("Options", "TryToSkipAllErrors") == "1")
577 | {
578 | iniParser.EditValue("Options", "TryToSkipAllErrors", "0");
579 | }
580 | if (iniParser.ReadValue("Options", "VSyncFix") == "1")
581 | {
582 | iniParser.EditValue("Options", "VSyncFix", "0");
583 | }
584 | }
585 | else
586 | {
587 | if (File.Exists($@"{dialog.FileName}\backup\SteamAchievements.asi")) { File.Move($@"{dialog.FileName}\backup\SteamAchievements.asi", $"{dialog.FileName}\\SteamAchievements.asi"); }
588 | if (iniParser.ReadValue("Options", "TryToSkipAllErrors") == "0")
589 | {
590 | iniParser.EditValue("Options", "TryToSkipAllErrors", "1");
591 | }
592 | if (iniParser.ReadValue("Options", "VSyncFix") == "0")
593 | {
594 | iniParser.EditValue("Options", "VSyncFix", "1");
595 | }
596 | }
597 | }
598 |
599 | break;
600 | case (true, true):
601 | Logger.Debug(" User has FusionFix and ZolikaPatch. Asking the user if they want to change incompatible ZolikaPatch options...");
602 | _iniPath = fusionFixCfgPath;
603 | _iniPathZp = zolikaPatchIniPath;
604 | _ffix = true;
605 |
606 | if (File.Exists($"{dialog.FileName}\\commandline.txt"))
607 | {
608 | Logger.Debug(" Filtering commandline.txt from -windowed and -noBlockOnLostFocus incase present.");
609 | var lines = File.ReadAllLines($@"{dialog.FileName}\commandline.txt");
610 | var filteredLines = lines.Where(line =>
611 | !line.Contains("-windowed") &&
612 | !line.Contains("-noBlockOnLostFocus"));
613 | File.WriteAllLines($@"{dialog.FileName}\commandline.txt", filteredLines);
614 |
615 | }
616 | if (iniParserZp.ReadValue("Options", "RestoreDeathMusic") == "N/A")
617 | {
618 | var result = MessageBox.Show("Your ZolikaPatch is outdated.\n\nDo you wish to download the latest version? (this will redirect to Zolika1351's website for manual download)", "ZolikaPatch is outdated", MessageBoxButton.YesNo, MessageBoxImage.Question);
619 | if (result == MessageBoxResult.Yes)
620 | {
621 | var psi = new ProcessStartInfo
622 | {
623 | FileName = "cmd",
624 | Arguments = "/c start https://zolika1351.pages.dev/mods/ivpatch",
625 | CreateNoWindow = true,
626 | UseShellExecute = false,
627 | };
628 | Process.Start(psi);
629 | MessageBox.Show("Press OK to restart the app after updating ZolikaPatch. Do not unpack 'PlayGTAIV.exe'.", "ZolikaPatch is outdated");
630 | System.Windows.Forms.Application.Restart();
631 | Environment.Exit(0);
632 | }
633 | }
634 | if (File.Exists($@"{dialog.FileName}\GFWLDLC.asi"))
635 | {
636 | if (iniParserZp.ReadValue("Options", "LoadDLCs") == "0" && iniParserZp.ReadValue("Options", "LoadDLCs") != "N/A")
637 | {
638 | iniParserZp.EditValue("Options", "LoadDLCs", "1");
639 | File.Delete($@"{dialog.FileName}\GFWLDLC.asi");
640 | }
641 | }
642 |
643 | if (File.Exists($@"{dialog.FileName}\dinput8.dll") && !File.Exists($@"{dialog.FileName}\xlive.dll"))
644 | {
645 | var result = MessageBox.Show("You appear to be using GFWL. Do you wish to remove Steam Achievements (if exists) and fix ZolikaPatch options to receive GFWL achievements?\n\nPressing 'No' can revert this if you agreed to this earlier.", "GFWL Achievements", MessageBoxButton.YesNo, MessageBoxImage.Question);
646 | if (result == MessageBoxResult.Yes)
647 | {
648 | if (File.Exists($@"{dialog.FileName}\SteamAchievements.asi"))
649 | {
650 | if (!Directory.Exists($@"{dialog.FileName}\backup"))
651 | {
652 | Directory.CreateDirectory($@"{dialog.FileName}\backup");
653 | }
654 | File.Move($@"{dialog.FileName}\SteamAchievements.asi", $@"{dialog.FileName}\backup\SteamAchievements.asi");
655 | }
656 | if (iniParserZp.ReadValue("Options", "TryToSkipAllErrors") == "1")
657 | {
658 | iniParserZp.EditValue("Options", "TryToSkipAllErrors", "0");
659 | }
660 | if (iniParserZp.ReadValue("Options", "VSyncFix") == "1")
661 | {
662 | iniParserZp.EditValue("Options", "VSyncFix", "0");
663 | }
664 | }
665 | else
666 | {
667 | if (File.Exists($@"{dialog.FileName}\backup\SteamAchievements.asi")) { File.Move($@"{dialog.FileName}\backup\SteamAchievements.asi", $@"{dialog.FileName}\SteamAchievements.asi"); }
668 | if (iniParserZp.ReadValue("Options", "TryToSkipAllErrors") == "0")
669 | {
670 | iniParserZp.EditValue("Options", "TryToSkipAllErrors", "1");
671 | }
672 | if (iniParserZp.ReadValue("Options", "VSyncFix") == "0")
673 | {
674 | iniParserZp.EditValue("Options", "VSyncFix", "1");
675 | }
676 | }
677 | }
678 |
679 | foreach (var option in incompatibleOptions.Where(option => iniParserZp.ReadValue("Options", option) == "1"))
680 | {
681 | if (!optToChangeOptions)
682 | {
683 | var result = MessageBox.Show("Your ZolikaPatch options are incompatible with FusionFix. This may lead to crashes, inconsistencies, visual issues etc.\n\nDo you wish to fix the options?", "Fix ZolikaPatch - FusionFix compatibility?", MessageBoxButton.YesNo, MessageBoxImage.Question);
684 | if (result == MessageBoxResult.Yes)
685 | {
686 | optToChangeOptions = true;
687 | iniParserZp.EditValue("Options", option, "0");
688 | optionsChanged = true;
689 | }
690 | else
691 | {
692 | break;
693 | }
694 | }
695 | else
696 | {
697 | iniParserZp.EditValue("Options", option, "0");
698 | optionsChanged = true;
699 | }
700 | }
701 | if (optionsChanged) { iniParserZp.SaveFile(); }
702 | break;
703 |
704 | }
705 |
706 | break;
707 | }
708 | Logger.Debug(" User selected the wrong folder. Displaying a MessageBox.");
709 | MessageBox.Show("The selected folder does not contain a supported version of GTA IV.");
710 | }
711 | else
712 | {
713 | break;
714 | }
715 |
716 | }
717 | }
718 |
719 | private bool _downloadFinished;
720 | private bool _extractFinished;
721 |
722 | private async Task InstallDxvk(string downloadUrl)
723 | {
724 | try
725 | {
726 | Logger.Debug(" Downloading the .tar.gz...");
727 | Logger.Debug(" Downloading the selected release...");
728 |
729 | using (var client = new HttpClient())
730 | {
731 | using (var response = await client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead))
732 | {
733 | response.EnsureSuccessStatusCode();
734 |
735 | var totalBytes = response.Content.Headers.ContentLength ?? -1L;
736 | await using (var contentStream = await response.Content.ReadAsStreamAsync())
737 | await using (var fileStream = new FileStream("./dxvk.tar.gz", FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
738 | {
739 | var buffer = new byte[8192];
740 | long totalBytesRead = 0L;
741 | int bytesRead;
742 |
743 | while ((bytesRead = await contentStream.ReadAsync(buffer)) != 0)
744 | {
745 | await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead));
746 | totalBytesRead += bytesRead;
747 |
748 | if (totalBytes <= 0) continue;
749 | var percentage = (double)totalBytesRead / totalBytes * 100;
750 | int percentageInt = Convert.ToInt16(percentage);
751 |
752 | await Dispatcher.InvokeAsync(() =>
753 | {
754 | InstallDxvkBtn.Content = $"Downloading... ({percentageInt}%)";
755 | });
756 | }
757 | }
758 | }
759 | }
760 |
761 | _downloadFinished = true;
762 | }
763 | catch (Exception ex)
764 | {
765 | Logger.Debug(ex, "Error downloading DXVK");
766 | throw;
767 | }
768 | }
769 | private async Task ExtractDxvk(string installationDir, List dxvkConf)
770 | {
771 | Logger.Debug(" Extracting the d3d9.dll from the archive...");
772 | await using (var fsIn = new FileStream("./dxvk.tar.gz", FileMode.Open))
773 | await using (var gzipStream = new GZipStream(fsIn, CompressionMode.Decompress))
774 | {
775 | var tarReader = new TarReader(gzipStream);
776 | while (await tarReader.GetNextEntryAsync() is { } entry)
777 | {
778 | Logger.Debug(entry.Name);
779 | if (!entry.Name.EndsWith("x32/d3d9.dll")) continue;
780 | await using var fsOut = File.Create(Path.Combine(installationDir, _ffixLatest ? "vulkan.dll" : "d3d9.dll"));
781 | await entry.DataStream!.CopyToAsync(fsOut);
782 | Logger.Debug(" d3d9.dll extracted into the game folder.");
783 | break;
784 | }
785 | }
786 |
787 | Logger.Debug(" Deleting the .tar.gz...");
788 | File.Delete("dxvk.tar.gz");
789 |
790 | Logger.Debug(" Writing the dxvk.conf...");
791 | await using (var confWriter = File.CreateText(Path.Combine(installationDir, "dxvk.conf")))
792 | {
793 | foreach (var option in dxvkConf)
794 | {
795 | await confWriter.WriteLineAsync(option);
796 | }
797 | }
798 | Logger.Debug(" dxvk.conf successfully written to game folder.");
799 | _extractFinished = true;
800 | }
801 |
802 | private async Task DownloadDxvk(string link, List dxvkConf, bool gitlab, bool alt, int release = 0)
803 | {
804 | var httpClient = new HttpClient();
805 | httpClient.DefaultRequestHeaders.Add("User-Agent", "Other");
806 | var firstResponse = await httpClient.GetAsync(link);
807 | firstResponse.EnsureSuccessStatusCode();
808 | var firstResponseBody = await firstResponse.Content.ReadAsStringAsync();
809 | var parsed = JsonDocument.Parse(firstResponseBody).RootElement;
810 | var downloadUrl = (gitlab, alt) switch
811 | {
812 | (false, false) => parsed.GetProperty("assets")[release].GetProperty("browser_download_url").GetString(),
813 | (true, false) => parsed[release]
814 | .GetProperty("assets")
815 | .GetProperty("links")
816 | .EnumerateArray()
817 | .First(jsonElement => jsonElement.GetProperty("name").GetString()!.Contains("tar.gz"))
818 | .GetProperty("url")
819 | .GetString(),
820 | (false, true) => parsed.GetProperty("browser_download_url").GetString(),
821 | (true, true) => parsed.GetProperty("assets")
822 | .GetProperty("links")
823 | .EnumerateArray()
824 | .First(jsonElement => jsonElement.GetProperty("name").GetString()!.Contains("tar.gz"))
825 | .GetProperty("url")
826 | .GetString()
827 | };
828 | await InstallDxvk(downloadUrl!);
829 | while (!_downloadFinished)
830 | {
831 | await Task.Delay(500);
832 | }
833 | _downloadFinished = false;
834 | await ExtractDxvk(GameDirectory.Text, dxvkConf);
835 | }
836 | private async void InstallDxvkBtn_Click(object sender, RoutedEventArgs e)
837 | {
838 | Logger.Debug(" User clicked on the Install DXVK button.");
839 | DxvkPanel.IsEnabled = false;
840 |
841 | Logger.Info(" Removing old files if present.");
842 | List tobedeleted = ["d3d10.dll", "d3d10_1.dll", "d3d10core.dll", "d3d11.dll", "dxgi.dll", "dxvk.conf", "GTAIV.dxvk-cache", "PlayGTAIV.dxvk-cache", "LaunchGTAIV.dxvk-cache", "GTAIV_d3d9.log", "PlayGTAIV_d3d9.log", "LaunchGTAIV_d3d9.log"];
843 | if (!_ffixLatest) tobedeleted.Add("d3d9.dll");
844 | DeleteFiles(GameDirectory.Text, tobedeleted);
845 |
846 | InstallDxvkBtn.Content = "Installing...";
847 | var rtssGtaivConfig = new IniEditor(_rtssConfig);
848 | if (_isIvsdkInstalled && !string.IsNullOrEmpty(_rtssConfig))
849 | {
850 | if (rtssGtaivConfig.ReadValue("Hooking", "EnableHooking") == "1")
851 | {
852 | Logger.Info(" User has IVSDK .NET installed and RTSS enabled at the same time and wants to install DXVK. Showing the warning prompt.");
853 | MessageBox.Show($"You currently have RivaTuner Statistics Server enabled (it might be a part of MSI Afterburner).\n\nTo avoid issues with the game launching when DXVK and IVSDK .NET are both installed, go into your tray icons, press on the little monitor icon with the number 60, press 'Add' in bottom left, select GTA IV's executable and set Application detection level to 'None'.\n\nIf you want the statistics, set it to Low and restart the tool, with the game running.");
854 | }
855 | }
856 |
857 | List dxvkConfig = [];
858 |
859 | Logger.Debug(" Setting up dxvk.conf in accordance with user's choices.");
860 |
861 | if (VSyncCheckbox.IsChecked == true)
862 | {
863 | Logger.Debug(" Adding d3d9.presentInterval = 1 and d3d9.numBackBuffers = 3");
864 | dxvkConfig.Add("d3d9.presentInterval = 1");
865 | dxvkConfig.Add("d3d9.numBackBuffers = 3");
866 | }
867 | if (FrameLatencyCheckBox.IsChecked == true)
868 | {
869 | Logger.Debug(" Adding d3d9.maxFrameLatency = 1");
870 | dxvkConfig.Add("d3d9.maxFrameLatency = 1");
871 | }
872 |
873 | Logger.Debug(" Quering links to install DXVK...");
874 | switch (_installDxvk)
875 | {
876 | case 1:
877 | // we're using the "if" in each case because of the async checkbox
878 | if (AsyncCheckbox.IsChecked == true)
879 | {
880 | Logger.Info(" Installing Latest DXVK-Sarek-async...");
881 | dxvkConfig.Add("dxvk.enableAsync = true");
882 | await DownloadDxvk("https://api.github.com/repos/pythonlover02/dxvk-Sarek/releases/latest", dxvkConfig, false, false);
883 | while (!_extractFinished)
884 | {
885 | await Task.Delay(500);
886 | }
887 | _extractFinished = false;
888 | MessageBox.Show($"Latest DXVK-Sarek-async has been installed!\n\nConsider going to Steam - Settings - Downloads and disable `Enable Shader Pre-caching` - this may improve your performance.");
889 | Logger.Info(" Latest DXVK-Sarek-async has been installed!");
890 | }
891 | else
892 | {
893 | Logger.Info(" Installing Latest DXVK-Sarek...");
894 | await DownloadDxvk("https://api.github.com/repos/pythonlover02/dxvk-Sarek/releases/latest", dxvkConfig, false, false, 1);
895 | while (!_extractFinished)
896 | {
897 | await Task.Delay(500);
898 | }
899 | _extractFinished = false;
900 | MessageBox.Show($"Latest DXVK-Sarek has been installed!\n\nConsider going to Steam - Settings - Downloads and disable `Enable Shader Pre-caching` - this may improve your performance.");
901 | Logger.Info(" Latest DXVK-Sarek has been installed!");
902 | }
903 | break;
904 | case 2:
905 | if (AsyncCheckbox.IsChecked == true)
906 | {
907 | Logger.Info(" Installing DXVK-gplasync 2.6.2...");
908 | dxvkConfig.Add("dxvk.enableAsync = true");
909 | await DownloadDxvk("https://gitlab.com/api/v4/projects/43488626/releases/v2.6.2-1", dxvkConfig, true, true);
910 | while (!_extractFinished)
911 | {
912 | await Task.Delay(500);
913 | }
914 | _extractFinished = false;
915 | MessageBox.Show($"DXVK-gplasync 2.6.2 has been installed!\n\nConsider going to Steam - Settings - Downloads and disable `Enable Shader Pre-caching` - this may improve your performance.");
916 | Logger.Info(" DXVK-async 2.6.2 has been installed!");
917 | }
918 | else
919 | {
920 | Logger.Info(" Installing DXVK 2.6.2...");
921 | await DownloadDxvk("https://api.github.com/repos/doitsujin/dxvk/releases/assets/222230856", dxvkConfig, false, true);
922 | while (!_extractFinished)
923 | {
924 | await Task.Delay(500);
925 | }
926 | _extractFinished = false;
927 | MessageBox.Show($"DXVK 2.6.2 has been installed!\n\nConsider going to Steam - Settings - Downloads and disable `Enable Shader Pre-caching` - this may improve your performance.", "Information");
928 | Logger.Info(" DXVK 2.6.2 has been installed!");
929 | }
930 | break;
931 | case 3:
932 | if (AsyncCheckbox.IsChecked == true)
933 | {
934 | Logger.Info(" Installing Latest DXVK-gplasync...");
935 | dxvkConfig.Add("dxvk.enableAsync = true");
936 | dxvkConfig.Add("dxvk.gplAsyncCache = true");
937 | await DownloadDxvk("https://gitlab.com/api/v4/projects/43488626/releases/", dxvkConfig, true, false);
938 | while (!_extractFinished)
939 | {
940 | await Task.Delay(500);
941 | }
942 | _extractFinished = false;
943 | MessageBox.Show($"Latest DXVK-gplasync has been installed!\n\nConsider going to Steam - Settings - Downloads and disable `Enable Shader Pre-caching` - this may improve your performance.");
944 | Logger.Info(" Latest DXVK-gplasync has been installed!");
945 | }
946 | else
947 | {
948 | Logger.Info(" Installing Latest DXVK...");
949 | await DownloadDxvk("https://api.github.com/repos/doitsujin/dxvk/releases/latest", dxvkConfig, false, false);
950 | while (!_extractFinished)
951 | {
952 | await Task.Delay(500);
953 | }
954 | _extractFinished = false;
955 | MessageBox.Show($"Latest DXVK has been installed!\n\nConsider going to Steam - Settings - Downloads and disable `Enable Shader Pre-caching` - this may improve your performance.");
956 | Logger.Info(" Latest DXVK has been installed!");
957 | }
958 | break;
959 | case -1:
960 | if (AsyncCheckbox.IsChecked == true)
961 | {
962 | Logger.Info(" Installing DXVK-async 1.10.1...");
963 | dxvkConfig.Add("dxvk.enableAsync = true");
964 | await DownloadDxvk("https://api.github.com/repos/Sporif/dxvk-async/releases/assets/60677007", dxvkConfig, false, true);
965 | while (!_extractFinished)
966 | {
967 | await Task.Delay(500);
968 | }
969 | _extractFinished = false;
970 | MessageBox.Show($"DXVK-async 1.10.1 has been installed!\n\nConsider going to Steam - Settings - Downloads and disable `Enable Shader Pre-caching` - this may improve your performance.");
971 | Logger.Info(" DXVK-async 1.10.1 has been installed!");
972 | }
973 | else
974 | {
975 | Logger.Info(" Installing DXVK 1.10.1...");
976 | await DownloadDxvk("https://api.github.com/repos/doitsujin/dxvk/releases/assets/60669426", dxvkConfig, false, true);
977 | while (!_extractFinished)
978 | {
979 | await Task.Delay(500);
980 | }
981 | _extractFinished = false;
982 | MessageBox.Show($"DXVK 1.10.1 has been installed!\n\nConsider going to Steam - Settings - Downloads and disable `Enable Shader Pre-caching` - this may improve your performance.", "Information");
983 | Logger.Info(" DXVK 1.10.1 has been installed!");
984 | }
985 | break;
986 | }
987 | Logger.Debug(" DXVK installed, editing the launch options toggles and enabling the panels back...");
988 | if (_ffixLatest)
989 | {
990 | InstallDxvkBtn.Content = "Update DXVK";
991 | }
992 | else
993 | {
994 | InstallDxvkBtn.Content = "Reinstall DXVK";
995 | InstallDxvkBtn.Width = 78;
996 | InstallDxvkBtn.FontSize = 11;
997 | UninstallDxvkBtn.Visibility = Visibility.Visible;
998 | }
999 | InstallDxvkBtn.IsDefault = false;
1000 | LaunchOptionsBtn.IsDefault = true;
1001 | InstallDxvkBtn.FontWeight = FontWeights.Normal;
1002 | MonitorDetailsCheckbox.IsChecked = true;
1003 | DxvkPanel.IsEnabled = true;
1004 | }
1005 |
1006 | private void UninstallDxvkBtn_Click(object sender, RoutedEventArgs e)
1007 | {
1008 | DxvkPanel.IsEnabled = false;
1009 | Logger.Info(" Removing all DXVK files present.");
1010 | List tobedeleted = ["d3d10.dll", "d3d10_1.dll", "d3d10core.dll", "d3d11.dll", "dxgi.dll", "dxvk.conf", "GTAIV.dxvk-cache", "PlayGTAIV.dxvk-cache", "LaunchGTAIV.dxvk-cache", "GTAIV_d3d9.log", "PlayGTAIV_d3d9.log", "LaunchGTAIV_d3d9.log"];
1011 | if (!_ffixLatest) tobedeleted.Add("d3d9.dll");
1012 | foreach (var element in tobedeleted)
1013 | {
1014 | Console.WriteLine(element);
1015 | }
1016 | DeleteFiles(GameDirectory.Text, tobedeleted);
1017 | UninstallDxvkBtn.Visibility = Visibility.Collapsed;
1018 | InstallDxvkBtn.Content = "Install DXVK";
1019 | InstallDxvkBtn.IsDefault = true;
1020 | InstallDxvkBtn.FontWeight = FontWeights.SemiBold;
1021 | InstallDxvkBtn.Width = 160;
1022 | InstallDxvkBtn.FontSize = 12;
1023 | LaunchOptionsBtn.IsDefault = false;
1024 | MonitorDetailsCheckbox.IsChecked = false;
1025 | MessageBox.Show("DXVK with all it's remains successfully uninstalled.");
1026 | DxvkPanel.IsEnabled = true;
1027 | }
1028 | private void SetupLaunchOptions_Click(object sender, RoutedEventArgs e)
1029 | {
1030 | Logger.Debug(" User clicked on Setup Launch Options, checking the toggles...");
1031 | var launchOptions = new List();
1032 | if (NoRestrictionsCheckbox.IsChecked == true) { launchOptions.Add("-norestrictions"); Logger.Debug(" Added -norestrictions."); }
1033 | if (NoMemRestrictCheckbox.IsChecked == true) { launchOptions.Add("-nomemrestrict"); Logger.Debug(" Added -nomemrestrict."); }
1034 |
1035 | var ffWindowed = true;
1036 | var ffBorderless = true;
1037 | var ffFocusLossless = true;
1038 | if (WindowedCheckbox.IsEnabled)
1039 | {
1040 | var iniParser = new IniEditor(_iniPath);
1041 | bool borderlessWindowedValue;
1042 | if (_ffix)
1043 | {
1044 | ffWindowed = iniParser.ReadValue("MAIN", "Windowed") == "1";
1045 | ffBorderless = iniParser.ReadValue("MAIN", "BorderlessWindowed") == "1";
1046 | ffFocusLossless = iniParser.ReadValue("MAIN", "BlockOnLostFocus") == "0";
1047 | borderlessWindowedValue = ffWindowed && ffBorderless && ffFocusLossless;
1048 | }
1049 | else
1050 | {
1051 | borderlessWindowedValue = iniParser.ReadValue("Options", "BorderlessWindowed") == "1";
1052 | }
1053 | switch (WindowedCheckbox.IsChecked)
1054 | {
1055 | case true:
1056 | {
1057 | Logger.Debug(" User chose to enable Borderless Windowed");
1058 | if (!borderlessWindowedValue)
1059 | {
1060 | Logger.Debug(" Borderless Windowed is disabled in the ini, enabling it back...");
1061 | if (_ffix)
1062 | {
1063 | if (!ffWindowed) { iniParser.EditValue("MAIN", "Windowed", "1"); }
1064 | if (!ffBorderless) { iniParser.EditValue("MAIN", "BorderlessWindowed", "1"); }
1065 | if (!ffFocusLossless) { iniParser.EditValue("MAIN", "BlockOnLostFocus", "0"); }
1066 | Logger.Debug(" Enabled Borderless Windowed and disabled Pause Game on Focus Loss.");
1067 | }
1068 | else
1069 | {
1070 | launchOptions.Add("-windowed");
1071 | launchOptions.Add("-noBlockOnLostFocus");
1072 | iniParser.EditValue("Options", "BorderlessWindowed", "1");
1073 | Logger.Debug(" Added -windowed and -noBlockOnLostFocus.");
1074 | }
1075 | iniParser.SaveFile();
1076 | }
1077 |
1078 | break;
1079 | }
1080 | case false when (borderlessWindowedValue || ffWindowed || ffBorderless || ffFocusLossless):
1081 | {
1082 | Logger.Debug(" User chose to disable Borderless Windowed but it's enabled in the ini, disabling it...");
1083 | if (_ffix)
1084 | {
1085 | iniParser.EditValue("MAIN", "Windowed", "0");
1086 | iniParser.EditValue("MAIN", "BorderlessWindowed", "0");
1087 | iniParser.EditValue("MAIN", "BlockOnLostFocus", "1");
1088 | }
1089 | else
1090 | {
1091 | iniParser.EditValue("Options", "BorderlessWindowed", "0");
1092 | }
1093 | iniParser.SaveFile();
1094 | break;
1095 | }
1096 | }
1097 | }
1098 | if (!_ffixLatest)
1099 | {
1100 | if (VidMemCheckbox.IsChecked == true)
1101 | {
1102 | Logger.Debug(" -availablevidmem checked, quering user's VRAM...");
1103 | try
1104 | {
1105 | if (IsLinux()) throw new Exception();
1106 | var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_VideoController");
1107 | var videoControllers = searcher.Get();
1108 |
1109 | foreach (var o in videoControllers)
1110 | {
1111 | var obj = (ManagementObject)o;
1112 | var adapterRam = obj["AdapterRAM"] != null ? obj["AdapterRAM"].ToString() : "N/A";
1113 | if (adapterRam == "N/A") continue;
1114 | var tempVram = Convert.ToInt16(ByteSize.FromBytes(Convert.ToDouble(adapterRam)).MebiBytes + 1);
1115 | if (_firstGpu)
1116 | {
1117 | Logger.Debug($"GPU0 has {tempVram}MB of VRAM");
1118 | _vram1 = tempVram;
1119 | _firstGpu = false;
1120 | }
1121 | else if (tempVram > _vram1 || tempVram > _vram2)
1122 | {
1123 | Logger.Debug($"Next GPU has {tempVram}MB of VRAM");
1124 | _vram2 = tempVram;
1125 | }
1126 | }
1127 |
1128 | if (_resultVk.Item4 || _resultVk.Item5)
1129 | {
1130 | if (Gb3Checkbox.IsChecked == true)
1131 | {
1132 | if (_vram1 > 3072) _vram1 = 3072;
1133 | }
1134 | else
1135 | {
1136 | if (_vram1 > 4096) _vram1 = 4096;
1137 | }
1138 | launchOptions.Add($"-availablevidmem {_vram1}");
1139 | Logger.Debug($" Added -availablevidmem {_vram1}.");
1140 | }
1141 | else
1142 | {
1143 | var vram = !_dxvkOnIgpu ? Math.Max(_vram1, _vram2) : Math.Min(_vram1, _vram2);
1144 | if (Gb3Checkbox.IsChecked == true)
1145 | {
1146 | if (vram > 3072) vram = 3072;
1147 | }
1148 | else
1149 | {
1150 | if (vram > 4096) vram = 4096;
1151 | }
1152 | launchOptions.Add($"-availablevidmem {vram}");
1153 | Logger.Debug($" Added -availablevidmem {vram}.");
1154 | }
1155 | }
1156 | catch (Exception ex)
1157 | {
1158 | // i know this is an awful and unoptimized and full of bad practices implementation, plz forgib
1159 | Logger.Error(ex, "Had some weird error during quering vram; asking the user for manual input");
1160 | var noerror = false;
1161 | var vram = 0;
1162 | while (!noerror)
1163 | {
1164 | try
1165 | {
1166 | vram = Convert.ToInt16(PromptDialog.Dialog.Prompt("VRAM could not be queried automatically.\n\nInput your VRAM value (in MB):", "Failsafe VRAM input"));
1167 | noerror = true;
1168 | }
1169 | catch
1170 | {
1171 | Logger.Error("Didn't receive a number, requesting again");
1172 | MessageBox.Show("Not a number, try again...");
1173 | }
1174 |
1175 | }
1176 | if (Gb3Checkbox.IsChecked == true)
1177 | {
1178 | if (vram > 3072) vram = 3072;
1179 | }
1180 | else
1181 | {
1182 | if (vram > 4096) vram = 4096;
1183 | }
1184 | launchOptions.Add($"-availablevidmem {vram}");
1185 | Logger.Debug($" Added -availablevidmem {vram}.");
1186 | }
1187 | }
1188 | }
1189 | if (MonitorDetailsCheckbox.IsChecked == true)
1190 | {
1191 | Logger.Debug(" Monitor Details checked, quering user's monitor details...");
1192 | DisplayInfo.GetPrimaryDisplayInfo(out var width, out var height, out var refreshRate);
1193 | launchOptions.Add($"-width {width}");
1194 | launchOptions.Add($"-height {height}");
1195 | launchOptions.Add($"-refreshrate {refreshRate}");
1196 | Logger.Debug($" Added -width {width}, -height {height}, -refreshrate {refreshRate}.");
1197 | }
1198 | if (!File.Exists($"{GameDirectory.Text}\\d3d9.dll") && !_ffixLatest)
1199 | {
1200 | launchOptions.Add("-managed");
1201 | }
1202 | if (_isRetail)
1203 | {
1204 | Logger.Debug(" Game .exe is retail - inputting values via commandline.txt...");
1205 | if (File.Exists($"{GameDirectory.Text}\\commandline.txt"))
1206 | {
1207 | Logger.Debug(" Old commandline.txt detected, removing...");
1208 | File.Delete($"{GameDirectory.Text}\\commandline.txt");
1209 | }
1210 | Logger.Debug(" Writing new commandline.txt...");
1211 | using (var writer = new StreamWriter($@"{GameDirectory.Text}\commandline.txt"))
1212 | {
1213 | foreach (var line in launchOptions)
1214 | {
1215 | writer.WriteLine(line);
1216 | }
1217 | }
1218 | Logger.Info($" Following launch options have been set to commandline.txt: {string.Join(" ", launchOptions)}");
1219 | MessageBox.Show($"Following launch options have been set up automatically for you: \n\n{string.Join(" ", launchOptions)}\n\nDo not worry that VRAM value isn't your full value - that is intentional and you can change that if you need to.");
1220 | }
1221 | else
1222 | {
1223 | Logger.Info($" Game .exe is 1.2 or later - asked user to input the values on their own and copied them to clipboard: {string.Join(" ", launchOptions)}");
1224 | MessageBox.Show($" The app can't set the launch options automatically, paste them in Steam's Launch Options manually (will be copied to clipboard after you press Ok):\n\n{string.Join(" ", launchOptions)}\n\nDo not worry that VRAM value isn't your full value - that is intentional and you can change that if you need to.");
1225 | try { Clipboard.SetText(string.Join(" ", launchOptions)); } catch (Exception ex) { MessageBox.Show($" The app couldn't copy the options to clipboard - input them manually:\n\n{string.Join(" ", launchOptions)}"); Logger.Debug(ex, " Weird issues with clipboard access."); }
1226 | }
1227 |
1228 | }
1229 | private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
1230 | {
1231 | Logger.Debug(" User clicked on a hyperlink from the main window.");
1232 | var psi = new ProcessStartInfo
1233 | {
1234 | FileName = "cmd",
1235 | Arguments = $"/c start {e.Uri.AbsoluteUri}",
1236 | CreateNoWindow = true,
1237 | UseShellExecute = false,
1238 | };
1239 | Process.Start(psi);
1240 | }
1241 | }
1242 | }
1243 |
--------------------------------------------------------------------------------