FFXIVLauncher-Netmaui

Форк
0
203 строки · 6.8 Кб
1
using System;
2
using System.Collections.Generic;
3
using System.IO;
4
using System.Net;
5
using System.Net.Http;
6
using System.Net.Http.Headers;
7

8
using System.Security.Cryptography;
9
using System.Text.Json;
10
using System.Text.Json.Serialization;
11
using System.Threading.Tasks;
12
using XIVLauncher.Common.Util;
13

14
namespace LibDalamud.Common.Dalamud
15
{
16
    public class AssetManager
17
    {
18
        private const string ASSET_STORE_URL = "https://kamori.goats.dev/Dalamud/Asset/Meta";
19

20
        internal class AssetInfo
21
        {
22
            [JsonPropertyName("version")]
23
            public int Version { get; set; }
24

25
            [JsonPropertyName("assets")]
26
            public IReadOnlyList<Asset> Assets { get; set; }
27

28
            public class Asset
29
            {
30
                [JsonPropertyName("url")]
31
                public string Url { get; set; }
32

33
                [JsonPropertyName("fileName")]
34
                public string FileName { get; set; }
35

36
                [JsonPropertyName("hash")]
37
                public string Hash { get; set; }
38
            }
39
        }
40

41
        public static async Task<DirectoryInfo> EnsureAssets(DirectoryInfo baseDir, bool forceProxy)
42
        {
43
            using var client = new HttpClient
44
            {
45
                Timeout = TimeSpan.FromMinutes(4),
46
            };
47

48
            client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue
49
            {
50
                NoCache = true,
51
            };
52

53
            using var sha1 = SHA1.Create();
54

55
            Console.WriteLine("[DASSET] Starting asset download");
56

57
            var (isRefreshNeeded, info) = CheckAssetRefreshNeeded(baseDir);
58

59
            // NOTE(goat): We should use a junction instead of copying assets to a new folder. There is no C# API for junctions in .NET Framework.
60

61
            var assetsDir = new DirectoryInfo(Path.Combine(baseDir.FullName, info.Version.ToString()));
62
            var devDir = new DirectoryInfo(Path.Combine(baseDir.FullName, "dev"));
63

64
            foreach (var entry in info.Assets)
65
            {
66
                var filePath = Path.Combine(assetsDir.FullName, entry.FileName);
67
                var filePathDev = Path.Combine(devDir.FullName, entry.FileName);
68

69
                Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
70

71
                try
72
                {
73
                    Directory.CreateDirectory(Path.GetDirectoryName(filePathDev)!);
74
                }
75
                catch
76
                {
77
                    // ignored
78
                }
79

80
                var refreshFile = false;
81

82
                if (File.Exists(filePath) && !string.IsNullOrEmpty(entry.Hash))
83
                {
84
                    try
85
                    {
86
                        using var file = File.OpenRead(filePath);
87
                        var fileHash = sha1.ComputeHash(file);
88
                        var stringHash = BitConverter.ToString(fileHash).Replace("-", "");
89
                        refreshFile = stringHash != entry.Hash;
90
                        Console.WriteLine("[DASSET] {0} has {1}, remote {2}", entry.FileName, stringHash, entry.Hash);
91
                    }
92
                    catch (Exception ex)
93
                    {
94
                        Console.WriteLine(ex.Message, "[DASSET] Could not read asset");
95
                    }
96
                }
97

98
                if (!File.Exists(filePath) || isRefreshNeeded || refreshFile)
99
                {
100
                    var url = entry.Url;
101

102
                    if (forceProxy && url.Contains("/File/Get/"))
103
                    {
104
                        url = url.Replace("/File/Get/", "/File/GetProxy/");
105
                    }
106

107
                    Console.WriteLine("[DASSET] Downloading {0} to {1}...", url, entry.FileName);
108

109
                    var request = await client.GetAsync(url).ConfigureAwait(true);
110
                    request.EnsureSuccessStatusCode();
111
                    File.WriteAllBytes(filePath, await request.Content.ReadAsByteArrayAsync().ConfigureAwait(true));
112

113
                    try
114
                    {
115
                        File.Copy(filePath, filePathDev, true);
116
                    }
117
                    catch
118
                    {
119
                        // ignored
120
                    }
121
                }
122
            }
123

124
            if (isRefreshNeeded)
125
                SetLocalAssetVer(baseDir, info.Version);
126

127
            Console.WriteLine("[DASSET] Assets OK at {0}", assetsDir.FullName);
128

129
            CleanUpOld(baseDir, info.Version - 1);
130

131
            return assetsDir;
132
        }
133

134
        private static string GetAssetVerPath(DirectoryInfo baseDir)
135
        {
136
            return Path.Combine(baseDir.FullName, "asset.ver");
137
        }
138

139
        /// <summary>
140
        ///     Check if an asset update is needed. When this fails, just return false - the route to github
141
        ///     might be bad, don't wanna just bail out in that case
142
        /// </summary>
143
        /// <param name="baseDir">Base directory for assets</param>
144
        /// <returns>Update state</returns>
145
        private static (bool isRefreshNeeded, AssetInfo info) CheckAssetRefreshNeeded(DirectoryInfo baseDir)
146
        {
147
            using var client = new WebClient();
148

149
            var localVerFile = GetAssetVerPath(baseDir);
150
            var localVer = 0;
151

152
            try
153
            {
154
                if (File.Exists(localVerFile))
155
                    localVer = int.Parse(File.ReadAllText(localVerFile));
156
            }
157
            catch (Exception ex)
158
            {
159
                // This means it'll stay on 0, which will redownload all assets - good by me
160
                Console.WriteLine(ex.Message, "[DASSET] Could not read asset.ver");
161
            }
162

163
            var remoteVer = JsonSerializer.Deserialize<AssetInfo>(client.DownloadString(ASSET_STORE_URL));
164

165
            Console.WriteLine("[DASSET] Ver check - local:{0} remote:{1}", localVer, remoteVer.Version);
166

167
            var needsUpdate = remoteVer.Version > localVer;
168

169
            return (needsUpdate, remoteVer);
170
        }
171

172
        private static void SetLocalAssetVer(DirectoryInfo baseDir, int version)
173
        {
174
            try
175
            {
176
                var localVerFile = GetAssetVerPath(baseDir);
177
                File.WriteAllText(localVerFile, version.ToString());
178
            }
179
            catch (Exception e)
180
            {
181
                Console.WriteLine(e.Message, "[DASSET] Could not write local asset version");
182
            }
183
        }
184

185
        private static void CleanUpOld(DirectoryInfo baseDir, int version)
186
        {
187
            if (GameHelpers.CheckIsGameOpen())
188
                return;
189

190
            var toDelete = Path.Combine(baseDir.FullName, version.ToString());
191

192
            try
193
            {
194
                if (Directory.Exists(toDelete))
195
                    Directory.Delete(toDelete, true);
196
            }
197
            catch (Exception ex)
198
            {
199
                Console.WriteLine(ex.Message, "Could not clean up old assets");
200
            }
201
        }
202
    }
203
}

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.