using System; using System.IO; using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Collections.Generic; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; using System.Diagnostics; using System.Management; class Program { static string gameDir = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..")); static string updateDir = AppContext.BaseDirectory; static string streamingAssetsDir => Path.Combine(gameDir, "Sinmai_Data", "StreamingAssets"); static string configPath => Path.Combine(updateDir, "config.yml"); static string serverJsonUrl = ""; static async Task Main() { try { Console.ForegroundColor = ConsoleColor.White; Console.Clear(); Console.Title = "Sinmai 更新工具"; Config cfg = LoadOrCreateConfig(configPath); if (!await VerifyAPIAsync(cfg.mc)) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("机器码验证失败了呢zako~"); Console.ResetColor(); return; } Console.WriteLine("检查更新中..."); var updates = await CheckUpdates(); if (updates.Count == 0) { Console.WriteLine("当前版本已是最新版本"); } else { foreach (var file in updates) { await DownloadFile(file.Name, file.Url); } } StartGame(cfg); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("程序运行出现异常了呢zako:"); Console.WriteLine(ex.ToString()); Console.ResetColor(); Console.WriteLine("按任意键退出..."); Console.ReadKey(); } } static Config LoadOrCreateConfig(string path) { var deserializer = new DeserializerBuilder() .WithNamingConvention(UnderscoredNamingConvention.Instance) .Build(); var serializer = new SerializerBuilder() .WithNamingConvention(UnderscoredNamingConvention.Instance) .Build(); Config cfg; if (File.Exists(path)) { string yaml = File.ReadAllText(path); cfg = deserializer.Deserialize(yaml); string newMC = GenerateMachineCode(); if (cfg.mc != newMC) { cfg.mc = newMC; string newYaml = serializer.Serialize(cfg); File.WriteAllText(path, newYaml); } } else { cfg = new Config { sinmaiargs = "-screen-fullscreen 1 -screen-width 1080 -screen-height 1920 -silent-crashes", mc = GenerateMachineCode() }; string yaml = serializer.Serialize(cfg); File.WriteAllText(path, yaml); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("首次运行已生成 config.yml"); Console.WriteLine("请复制以下机器码保存:"); Console.ResetColor(); Console.WriteLine($"机器码:{cfg.mc}"); Environment.Exit(0); } return cfg; } public class Config { public string sinmaiargs { get; set; } = ""; public string mc { get; set; } = ""; } static string GenerateMachineCode() { try { string boardSerial = ""; string cpuId = ""; } catch { return Guid.NewGuid().ToString("N"); } } static async Task VerifyAPIAsync(string mc) { try { using var client = new HttpClient(); client.DefaultRequestHeaders.UserAgent.ParseAdd(""); string url = $""; string response = await client.GetStringAsync(url); var jsonDoc = System.Text.Json.JsonDocument.Parse(response); if (jsonDoc.RootElement.TryGetProperty("success", out var successProp)) { return successProp.GetBoolean(); } return false; } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("API 验证异常:"); Console.WriteLine(ex.ToString()); Console.ResetColor(); return false; } } public class UpdateFile { public string Name { get; set; } = ""; public string Url { get; set; } = ""; } static async Task> CheckUpdates() { List updates = new(); try { using var client = new HttpClient(); client.DefaultRequestHeaders.UserAgent.ParseAdd("Maimai/xin"); string json = await client.GetStringAsync(serverJsonUrl); var serverFiles = System.Text.Json.JsonSerializer.Deserialize>(json, new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); foreach (var file in serverFiles ?? new List()) { string localPath = Path.Combine(streamingAssetsDir, file.Name.Replace('/', Path.DirectorySeparatorChar)); if (!File.Exists(localPath)) { updates.Add(file); } } } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("检查更新异常:"); Console.WriteLine(ex.ToString()); Console.ResetColor(); } return updates; } static async Task DownloadFile(string relativePath, string url) { string localPath = Path.Combine(streamingAssetsDir, relativePath.Replace('/', Path.DirectorySeparatorChar)); Directory.CreateDirectory(Path.GetDirectoryName(localPath)!); try { using var client = new HttpClient(); using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); response.EnsureSuccessStatusCode(); using var stream = await response.Content.ReadAsStreamAsync(); using var fileStream = File.Create(localPath); byte[] buffer = new byte[8192]; long totalRead = 0; long total = response.Content.Headers.ContentLength ?? -1; int read; while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0) { await fileStream.WriteAsync(buffer, 0, read); totalRead += read; if (total > 0) { Console.Write($"\r下载 {relativePath} {totalRead * 100 / total}%"); Console.Out.Flush(); } } Console.WriteLine($"\r下载 {relativePath} 完成"); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"下载 {relativePath} 出错:"); Console.WriteLine(ex.ToString()); Console.ResetColor(); } } static void StartGame(Config cfg) { try { string gameRoot = gameDir; string batPath = Path.Combine(gameRoot, ""); string batContent = $@" "; File.WriteAllText(batPath, batContent, Encoding.Default); var psi = new ProcessStartInfo { FileName = batPath, WorkingDirectory = gameRoot, UseShellExecute = true }; Process.Start(psi); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("启动游戏出错:"); Console.WriteLine(ex.ToString()); Console.ResetColor(); } } #endregion }