XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

AutoMiningScript

【SpaceEngineer】全自动挖矿脚本

公开
关注 0 Fork 0 Star 0
UTF-8
[CmdletBinding()]
param(
    [ValidateSet('zh-CN', 'en-US')][string]$Language = 'zh-CN',
    [string]$ResourceDirectory = (Join-Path $PSScriptRoot '../Localization'),
    [string]$OutputPath,
    [switch]$ValidateOnly
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest

function Read-Resources([string]$Path) {
    $document = [System.Text.Json.JsonDocument]::Parse([IO.File]::ReadAllText($Path))
    try {
        if ($document.RootElement.ValueKind -ne [System.Text.Json.JsonValueKind]::Object) { throw "Resource root must be an object: $Path" }
        $entries = [Collections.Generic.Dictionary[string,string]]::new([StringComparer]::Ordinal)
        foreach ($property in $document.RootElement.EnumerateObject()) {
            if ($property.Name -notmatch '^[A-Z][A-Za-z0-9]*$' -or $property.Name -in @('Language', 'Brand')) { throw "Invalid/reserved resource key: $($property.Name) in $Path" }
            if ($property.Value.ValueKind -ne [System.Text.Json.JsonValueKind]::String) { throw "Resource must be a string: $($property.Name) in $Path" }
            if ($entries.ContainsKey($property.Name)) { throw "Duplicate resource key: $($property.Name) in $Path" }
            $value = $property.Value.GetString()
            if ([string]::IsNullOrWhiteSpace($value)) { throw "Empty resource: $($property.Name) in $Path" }
            $entries.Add($property.Name, $value)
        }
        return ,$entries
    }
    finally { $document.Dispose() }
}
function Get-Placeholders([string]$Value, [string]$Key) {
    # Validate the same composite-format syntax that the C# runtime will use.
    try { $null = [string]::Format([Globalization.CultureInfo]::InvariantCulture, $Value, [object[]](@(0) * 32)) }
    catch { throw "Invalid format string for $Key : $Value" }
    $unescaped = $Value.Replace('{{', '').Replace('}}', '')
    return (([regex]::Matches($unescaped, '\{([0-9]+)(?:,[^}:]+)?(?::[^}]+)?\}') | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique) -join ',')
}
function CSharp-Literal([string]$Value) {
    $result = $Value.Replace('\', '\\').Replace('"', '\"').Replace("`r", '\r').Replace("`n", '\n').Replace("`t", '\t')
    return '"' + $result + '"'
}

$files = @(Get-ChildItem -LiteralPath $ResourceDirectory -Filter '*.zh-CN.json' | Sort-Object Name)
if ($files.Count -eq 0) { throw "No localization resources found: $ResourceDirectory" }
$all = [Collections.Generic.SortedDictionary[string,string]]::new([StringComparer]::Ordinal)
$domains = [Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal)
foreach ($file in $files) {
    $domain = $file.Name.Substring(0, $file.Name.Length - '.zh-CN.json'.Length)
    $null = $domains.Add($domain)
    $englishPath = Join-Path $ResourceDirectory ($domain + '.en-US.json')
    if (-not (Test-Path -LiteralPath $englishPath)) { throw "Missing English resource file: $englishPath" }
    $chinese = Read-Resources $file.FullName
    $english = Read-Resources $englishPath
    if ($chinese.Count -ne $english.Count) { throw "Resource key count mismatch: $domain" }
    foreach ($key in $chinese.Keys) {
        if (-not $english.ContainsKey($key)) { throw "Missing English resource: $key" }
        if ((Get-Placeholders $chinese[$key] $key) -cne (Get-Placeholders $english[$key] $key)) { throw "Resource placeholder mismatch: $key" }
        if ($all.ContainsKey($key)) { throw "Duplicate resource key across domains: $key" }
        $all.Add($key, $(if ($Language -eq 'zh-CN') { $chinese[$key] } else { $english[$key] }))
    }
}
foreach ($file in Get-ChildItem -LiteralPath $ResourceDirectory -Filter '*.en-US.json') {
    if (-not $domains.Contains($file.Name.Substring(0, $file.Name.Length - '.en-US.json'.Length))) { throw "Missing Chinese resource counterpart: $($file.Name)" }
}
if ($ValidateOnly) { Write-Host "XFE AMS resources: $($all.Count) matching keys validated."; return }
if ([string]::IsNullOrWhiteSpace($OutputPath)) { throw 'OutputPath is required unless ValidateOnly is set.' }
$lines = [Collections.Generic.List[string]]::new()
$lines.Add('// Generated from Localization/*.json. Edit resources, not this file.')
$lines.Add('namespace AutoMiningScript { public partial class Program { public static partial class L {')
$lines.Add('public const string Brand = "XFE AMS";')
$lines.Add('public const string Language = ' + (CSharp-Literal $Language) + ';')
foreach ($entry in $all.GetEnumerator()) { $lines.Add('public const string ' + $entry.Key + ' = ' + (CSharp-Literal $entry.Value) + ';') }
$lines.Add('} } }')
$content = ($lines -join "`n") + "`n"
$fullPath = [IO.Path]::GetFullPath($OutputPath)
$null = [IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($fullPath))
if (-not [IO.File]::Exists($fullPath) -or [IO.File]::ReadAllText($fullPath) -cne $content) { [IO.File]::WriteAllText($fullPath, $content, [Text.UTF8Encoding]::new($false)) }
Write-Host "XFE AMS $Language resources: $($all.Count) strings."