Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/mh3g-save-converter-windows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,15 @@ after that test, the disposable synthetic `write -> rollback` smoke is skipped
rather than touching it. Do not use `-SkipTests` or `-SkipTransactionSmoke` for
a normal distribution build.

If the UI reports `Unable to prepare patched user save`,
`tools\mh3g-save-convert.exe` came from the temporary v0.0.3 compatibility
wrapper rather than the native Rust CLI used by 0.0.4 and newer. Do not copy
`tools\compatibility-wrapper\dist\mh3g-save-convert.exe` into a WinUI package.
Delete the old `artifacts\mh3g-save-convert-windows-x64` directory and ZIP,
then rerun the exact `package-mh3g-save-converter-windows.ps1 -Bootstrap`
command above. Current packaging and the WinUI runtime both reject that legacy
wrapper before it can surface a misleading JSON error.

If it fails, send the **first failed command block** from
`artifacts\mh3g-save-convert-windows-build-transcript.txt`: begin at its `>>`
line and include the failure lines below it. Do not switch to a different manual
Expand Down
9 changes: 9 additions & 0 deletions apps/mh3g-save-converter-windows/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ Cemu_release、Nemessix 和 Azahar,发现仍在运行就列出进程名并提
这些进程。若测试之后模拟器才启动,则只跳过临时目录中的合成 `write -> rollback` smoke,
不会触碰真实数据。常规发包不要使用 `-SkipTests` 或 `-SkipTransactionSmoke`。

如果界面报告 `Unable to prepare patched user save`,当前包中的
`tools\mh3g-save-convert.exe` 是仅供 v0.0.3 临时更新使用的旧兼容包装器,不是
0.0.4 之后的原生 Rust CLI。不要把
`tools\compatibility-wrapper\dist\mh3g-save-convert.exe` 复制进 WinUI 包;
删除旧的 `artifacts\mh3g-save-convert-windows-x64` 和 ZIP 后,使用上面的
`package-mh3g-save-converter-windows.ps1 -Bootstrap` 原命令重新打包。新版
打包脚本和 WinUI 运行时都会识别并拒绝旧包装器,不再把它的 stderr 误报成 JSON
解析故障。

若仍失败,请提供
`artifacts\mh3g-save-convert-windows-build-transcript.txt` 中**首个失败命令的完整输出块**:
从对应的 `>>` 行开始,连同其下方的失败信息一起发回;不要改用另一组手工构建命令。
Expand Down
37 changes: 37 additions & 0 deletions apps/mh3g-save-converter-windows/Services/ConverterCliClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ namespace MHToolkit.MH3GSaveConverter.Windows.Services;
/// </summary>
public sealed class ConverterCliClient
{
private static readonly byte[] LegacyWrapperMarker =
"mh3g-save-convert-core.exe"u8.ToArray();

public async Task<CliExecutionResult> ExecuteAsync(
string executable,
IEnumerable<string> arguments,
Expand All @@ -37,6 +40,22 @@ public async Task<CliExecutionResult> ExecuteAsync(
};
}

try
{
var executableBytes = await File.ReadAllBytesAsync(executable, cancellationToken);
if (ContainsLegacyWrapperMarker(executableBytes))
{
return LaunchFailure(
executable,
argumentList,
"Legacy compatibility wrapper detected. Rebuild the Windows package from 0.0.4 or newer so tools\\mh3g-save-convert.exe is the native Rust CLI. / 检测到旧版兼容包装器,请使用 0.0.4 或更高版本源码重新打包,确保 tools\\mh3g-save-convert.exe 为原生 Rust CLI。");
}
}
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException)
{
return LaunchFailure(executable, argumentList, $"The converter CLI could not be inspected: {exception.Message}");
}

var startInfo = new ProcessStartInfo
{
FileName = executable,
Expand Down Expand Up @@ -97,6 +116,24 @@ private static CliExecutionResult LaunchFailure(string executable, IReadOnlyList
};
}

private static bool ContainsLegacyWrapperMarker(byte[] bytes)
{
if (bytes.Length < LegacyWrapperMarker.Length)
{
return false;
}

for (var offset = 0; offset <= bytes.Length - LegacyWrapperMarker.Length; offset++)
{
if (bytes.AsSpan(offset, LegacyWrapperMarker.Length).SequenceEqual(LegacyWrapperMarker))
{
return true;
}
}

return false;
}

private static JsonElement? TryParseLastJsonLine(string stdout, out string? parseError)
{
parseError = null;
Expand Down
14 changes: 14 additions & 0 deletions scripts/package-mh3g-save-converter-windows.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,18 @@ function Write-Sha256File {
return $hash
}

function Assert-NativeConverterSidecar {
param([Parameter(Mandatory = $true)][string]$FilePath)

$legacyMarker = "mh3g-save-convert-core.exe"
$binaryText = [System.Text.Encoding]::ASCII.GetString(
[System.IO.File]::ReadAllBytes($FilePath)
)
if ($binaryText.Contains($legacyMarker)) {
throw "Refusing the legacy MH3G compatibility wrapper: $FilePath. Build and package the native Rust converter from 0.0.4 or newer."
}
}

function Get-RunningSupportedEmulators {
# The Rust integration suite intentionally exercises guarded writes against
# synthetic files. Its process probe must see no real emulator, just as a
Expand Down Expand Up @@ -748,6 +760,7 @@ function Test-PackagedLayout {
if ($actualSidecarHash -ne $expectedSidecarHash) {
throw "Package self-check found a sidecar checksum mismatch."
}
Assert-NativeConverterSidecar -FilePath $sidecar

$powershell = Get-ExternalCommand "powershell.exe"
if ($null -eq $powershell) {
Expand Down Expand Up @@ -906,6 +919,7 @@ try {
if (-not (Test-Path -LiteralPath $sidecar -PathType Leaf)) {
throw "Rust x64 sidecar is missing after cargo build: $sidecar"
}
Assert-NativeConverterSidecar -FilePath $sidecar
Invoke-External -FilePath $sidecar -Arguments @("--help") -Description "release sidecar smoke"

# Recheck directly before recursive deletion in case an existing stage
Expand Down
13 changes: 12 additions & 1 deletion scripts/verify-mh3g-save-converter-windows-source.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ def verify_local_packaging_script() -> None:
"reparse point" in script.lower(),
"Windows package output cleanup must reject junctions and symlinks",
)
require(
"Assert-NativeConverterSidecar" in script
and "mh3g-save-convert-core.exe" in script,
"Windows packaging must reject the legacy compatibility wrapper before publishing",
)
require(
'$versionLine[0] -match \'^rustc\\s+(?<version>\\d+\\.\\d+\\.\\d+)\'' in script
and '$versionText = $Matches["version"]' in script
Expand Down Expand Up @@ -270,7 +275,13 @@ def main() -> int:
require((APP / "assets" / "Artwork" / artwork).is_file(), f"missing packaged artwork {artwork}")

bridge = read("Services/ConverterCliClient.cs")
for expected in ("UseShellExecute = false", "startInfo.ArgumentList.Add(argument)", "JsonDocument.Parse(candidate)"):
for expected in (
"UseShellExecute = false",
"startInfo.ArgumentList.Add(argument)",
"JsonDocument.Parse(candidate)",
"mh3g-save-convert-core.exe",
"Legacy compatibility wrapper",
):
require(expected in bridge, f"argv/JSON bridge is missing {expected}")
require("startInfo.Arguments" not in bridge, "CLI bridge must not build a command-string argument list")
require("cmd.exe" not in bridge and "powershell" not in bridge.lower(), "CLI bridge must not invoke a shell")
Expand Down
Loading