1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
using System.IO;
using System.IO.Compression;
using System.Net.Http;
namespace AdamsToolkit.Core;
/// <summary>
/// Instala o pack ReShade do WestRP na pasta de plugins do FiveM
/// (FiveM Application Data). Substitui qualquer ReShade existente:
/// os ficheiros antigos saem e este fica no lugar.
/// </summary>
public static class ReshadeInstaller
{
private static readonly HttpClient Http = new() { Timeout = TimeSpan.FromMinutes(30) };
// localização real por PC via FiveMLocator (registry → default), nunca caminho fixo
public static string? FiveMAppData => FiveMLocator.AppDataDir;
public static string? PluginsDir =>
FiveMAppData == null ? null : Path.Combine(FiveMAppData, "plugins");
public static bool FiveMPresent => FiveMAppData != null;
public static bool IsInstalled
{
get
{
var p = PluginsDir;
return p != null &&
File.Exists(Path.Combine(p, "dxgi.dll")) &&
Directory.Exists(Path.Combine(p, "reshade-shaders"));
}
}
public static async Task<(bool ok, string message)> InstallAsync(
IProgress<(double pct, string label)> progress, CancellationToken ct)
{
var pluginsDir = PluginsDir;
if (pluginsDir == null)
return (false, "Pasta do FiveM não encontrada — instala e abre o FiveM primeiro.");
var tmp = Path.Combine(ConfigService.DataDir, "reshade-westrp.zip");
try
{
// download com progresso
using (var res = await Http.GetAsync(ConfigService.Current.ReshadeZipUrl,
HttpCompletionOption.ResponseHeadersRead, ct))
{
res.EnsureSuccessStatusCode();
var total = res.Content.Headers.ContentLength ?? -1L;
await using var src = await res.Content.ReadAsStreamAsync(ct);
await using var dst = File.Create(tmp);
var buffer = new byte[81920];
long read = 0; int n;
while ((n = await src.ReadAsync(buffer, ct)) > 0)
{
await dst.WriteAsync(buffer.AsMemory(0, n), ct);
read += n;
if (total > 0)
progress.Report((read * 90.0 / total,
$"{read / 1048576.0:0.0} / {total / 1048576.0:0.0} MB"));
}
}
progress.Report((92, "A remover ReShade antigo…"));
Directory.CreateDirectory(pluginsDir);
// "a outra sai e esta fica no lugar": limpa qualquer instalação anterior
foreach (var f in new[] { "dxgi.dll", "d3d9.dll", "d3d11.dll", "opengl32.dll",
"ReShade.ini", "ReShadePreset.ini", "dxgi.log", "ReShade.log" })
{
var p = Path.Combine(pluginsDir, f);
if (File.Exists(p)) File.Delete(p);
}
var shaders = Path.Combine(pluginsDir, "reshade-shaders");
if (Directory.Exists(shaders)) Directory.Delete(shaders, true);
progress.Report((95, "A instalar…"));
ZipFile.ExtractToDirectory(tmp, pluginsDir, overwriteFiles: true);
progress.Report((100, "Concluído"));
return (true, "ReShade instalado! Abre o FiveM — tecla Home abre o menu ReShade.");
}
catch (OperationCanceledException)
{
return (false, "Cancelado.");
}
catch (IOException)
{
return (false, "Não foi possível substituir os ficheiros — fecha o FiveM e tenta de novo.");
}
catch (Exception e)
{
return (false, $"Falhou: {e.Message}");
}
finally
{
try { File.Delete(tmp); } catch { }
}
}
}