Adds Avalonia 11 + CommunityToolkit.Mvvm package references (no UI code yet), a flat-key Loc resource loader with embedded cs.json/en.json, the LauncherSettings JSON store with dev-mode-gated manifest URL override, and an optional IProgress<long> bytesProgress hook on BlobDownloader so the upcoming GUI can drive a progress bar from per-blob byte counts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
94 lines
2.6 KiB
C#
94 lines
2.6 KiB
C#
using System.IO;
|
|
using System.Text.Json;
|
|
using Metin2Launcher.Logging;
|
|
|
|
namespace Metin2Launcher.Config;
|
|
|
|
/// <summary>
|
|
/// User-editable settings persisted to .updates/launcher-settings.json.
|
|
/// Only honored when DevMode == true for the URL override (defense in depth: a
|
|
/// tampered local file alone cannot redirect updates).
|
|
/// </summary>
|
|
public sealed class LauncherSettings
|
|
{
|
|
public string Locale { get; set; } = "cs";
|
|
public bool DevMode { get; set; } = false;
|
|
public string? ManifestUrlOverride { get; set; }
|
|
|
|
private static readonly JsonSerializerOptions _opts = new()
|
|
{
|
|
WriteIndented = true,
|
|
};
|
|
|
|
public static LauncherSettings Load(string path)
|
|
{
|
|
try
|
|
{
|
|
if (!File.Exists(path))
|
|
return new LauncherSettings();
|
|
var json = File.ReadAllText(path);
|
|
return JsonSerializer.Deserialize<LauncherSettings>(json) ?? new LauncherSettings();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Warn("failed to read launcher settings, using defaults: " + ex.Message);
|
|
return new LauncherSettings();
|
|
}
|
|
}
|
|
|
|
public void Save(string path)
|
|
{
|
|
try
|
|
{
|
|
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
|
var json = JsonSerializer.Serialize(this, _opts);
|
|
File.WriteAllText(path, json);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Warn("failed to save launcher settings: " + ex.Message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effective manifest URL respecting dev-mode gating.
|
|
/// </summary>
|
|
public string EffectiveManifestUrl()
|
|
{
|
|
if (DevMode && !string.IsNullOrWhiteSpace(ManifestUrlOverride))
|
|
return ManifestUrlOverride!;
|
|
return LauncherConfig.ManifestUrl;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Derives signature URL from the (possibly overridden) manifest URL.
|
|
/// </summary>
|
|
public string EffectiveSignatureUrl()
|
|
{
|
|
var m = EffectiveManifestUrl();
|
|
if (m == LauncherConfig.ManifestUrl)
|
|
return LauncherConfig.SignatureUrl;
|
|
return m + ".sig";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Derives the blob base URL from the (possibly overridden) manifest URL.
|
|
/// Same host, sibling /files path.
|
|
/// </summary>
|
|
public string EffectiveBlobBaseUrl()
|
|
{
|
|
var m = EffectiveManifestUrl();
|
|
if (m == LauncherConfig.ManifestUrl)
|
|
return LauncherConfig.BlobBaseUrl;
|
|
try
|
|
{
|
|
var u = new Uri(m);
|
|
return $"{u.Scheme}://{u.Authority}/files";
|
|
}
|
|
catch
|
|
{
|
|
return LauncherConfig.BlobBaseUrl;
|
|
}
|
|
}
|
|
}
|