mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-10-22 06:07:43 -04:00
NTSC-U and NTSC-J support (#326)
* add ntsc-u and ntsc-j support Co-authored-by: DarthMDev <mgracer48@yahoo.com> Co-authored-by: rooklz <rookieb@muzo-works.com> * hash inc files and require project for shards * port remaining stale rr us hooks * drop map report generation * drop one-off region porters --------- Co-authored-by: DarthMDev <mgracer48@yahoo.com> Co-authored-by: rooklz <rookieb@muzo-works.com>
This commit is contained in:
@@ -11,29 +11,30 @@ namespace WiiCompiled.Setup.Linux;
|
||||
/// </summary>
|
||||
internal static class DiscTool
|
||||
{
|
||||
public static async Task ValidateAndExtractAsync(
|
||||
string isoPath, ProjectManifest manifest, string assetsDirectory, string workspace,
|
||||
/// <summary>Validates and extracts the disc, returning the supported release it is.</summary>
|
||||
public static async Task<ProjectManifest> ValidateAndExtractAsync(
|
||||
string isoPath, IReadOnlyList<ProjectManifest> releases, string assetsDirectory, string workspace,
|
||||
string? nodToolBin, IInstallReporter reporter, CancellationToken cancellationToken)
|
||||
{
|
||||
if (IsAlreadyExtracted(manifest, assetsDirectory))
|
||||
{
|
||||
reporter.Progress(InstallStages.ExtractDisc, "Reusing the verified extracted disc", 6);
|
||||
return;
|
||||
}
|
||||
|
||||
var nodTool = nodToolBin ?? await NodToolProvider.ResolveAsync(workspace, cancellationToken);
|
||||
|
||||
// `nodtool info` only decodes the disc/partition headers (milliseconds); `nodtool extract`
|
||||
// copies the whole data partition to disk (tens of seconds for a custom-track-heavy MKWii
|
||||
// ISO). Checking the game ID first, before extracting, means a wrong disc fails fast -
|
||||
// matching the original dolphin-tool `header` step this replaces.
|
||||
// ISO). The game ID picks the release, so a wrong disc fails fast and an existing
|
||||
// extraction is only reused for the same release.
|
||||
reporter.Progress(InstallStages.ExtractDisc, "Reading the disc header", 2);
|
||||
var info = NodToolInfoParser.Parse(await RunInfoAsync(nodTool, isoPath, cancellationToken));
|
||||
if (!string.Equals(info.GameId, manifest.GameId, StringComparison.Ordinal))
|
||||
var manifest = releases.FirstOrDefault(release =>
|
||||
string.Equals(info.GameId, release.GameId, StringComparison.Ordinal))
|
||||
?? throw new InvalidOperationException(
|
||||
$"This disc is '{info.GameId}', which is not a supported Mario Kart Wii release " +
|
||||
$"({ProjectManifest.Describe(releases)}). " +
|
||||
"Only your own legally-owned copy of a supported game/region can be used.");
|
||||
|
||||
if (IsAlreadyExtracted(manifest, assetsDirectory))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"This disc is '{info.GameId}', not the expected '{manifest.GameId}' (Mario Kart Wii, region {manifest.Region}). " +
|
||||
"Only your own legally-owned copy of that exact game/region can be used.");
|
||||
reporter.Progress(InstallStages.ExtractDisc, "Reusing the verified extracted disc", 6);
|
||||
return manifest;
|
||||
}
|
||||
|
||||
// Extracted straight into Assets/DATA (kept, not a scratch dir) - the runtime reads course/
|
||||
@@ -71,6 +72,22 @@ internal static class DiscTool
|
||||
File.Copy(relPath, Path.Combine(assetsDirectory, "StaticR.rel"), overwrite: true);
|
||||
File.WriteAllText(Path.Combine(assetsDirectory, ExtractionMarker), ExtractionRecord(dolSha, dataDir));
|
||||
reporter.Progress(InstallStages.ExtractDisc, "Disc validated and extracted", 6);
|
||||
return manifest;
|
||||
}
|
||||
|
||||
/// <summary>The release already extracted into Assets, chosen by its main.dol like local-build.sh.</summary>
|
||||
public static ProjectManifest SelectExtracted(IReadOnlyList<ProjectManifest> releases, string assetsDirectory)
|
||||
{
|
||||
var dolSha = Sha256Of(Path.Combine(assetsDirectory, "main.dol"));
|
||||
var manifest = releases.FirstOrDefault(release => string.Equals(dolSha, release.DolSha256, StringComparison.Ordinal))
|
||||
?? throw new InvalidOperationException(
|
||||
$"Assets/main.dol (sha256 {dolSha}) is not a supported clean Mario Kart Wii release " +
|
||||
$"({ProjectManifest.Describe(releases)}).");
|
||||
var relSha = Sha256Of(Path.Combine(assetsDirectory, "StaticR.rel"));
|
||||
if (!string.Equals(relSha, manifest.RelSha256, StringComparison.Ordinal))
|
||||
throw new InvalidOperationException(
|
||||
$"Assets/StaticR.rel does not match the clean {manifest.GameId} release of Assets/main.dol.");
|
||||
return manifest;
|
||||
}
|
||||
|
||||
private static async Task<string> RunInfoAsync(string nodTool, string isoPath, CancellationToken cancellationToken)
|
||||
@@ -97,7 +114,7 @@ internal static class DiscTool
|
||||
|
||||
private const string ExtractionMarker = ".disc-extracted";
|
||||
|
||||
// Only one disc revision is accepted, so a completed, verified extraction is identical to a fresh one.
|
||||
// Each release has one accepted revision, so a completed, verified extraction is identical to a fresh one.
|
||||
private static bool IsAlreadyExtracted(ProjectManifest manifest, string assetsDirectory)
|
||||
{
|
||||
var marker = Path.Combine(assetsDirectory, ExtractionMarker);
|
||||
|
||||
@@ -95,13 +95,14 @@ internal static class Program
|
||||
if (installsRetro) retroDir = RetroRewindSource.ResolveRetroRewind6(retroDir!);
|
||||
|
||||
var workspace = flags.GetValueOrDefault("workspace") ?? WorkspaceLocator.FindFrom(AppContext.BaseDirectory);
|
||||
var manifest = ProjectManifest.Load(Path.Combine(workspace, "projects", "mkwii", "recomp.yml"));
|
||||
var releases = ProjectManifest.LoadAll(workspace);
|
||||
var assetsDir = Path.Combine(workspace, "Assets");
|
||||
|
||||
reporter.Progress(InstallStages.Validate, "Checking prerequisites", 1);
|
||||
ProjectManifest release;
|
||||
if (flags.TryGetValue("game", out var isoPath) && !string.IsNullOrEmpty(isoPath))
|
||||
{
|
||||
await DiscTool.ValidateAndExtractAsync(isoPath, manifest, assetsDir, workspace,
|
||||
release = await DiscTool.ValidateAndExtractAsync(isoPath, releases, assetsDir, workspace,
|
||||
flags.GetValueOrDefault("disc-tool-bin"), reporter, token);
|
||||
}
|
||||
else
|
||||
@@ -114,6 +115,7 @@ internal static class Program
|
||||
"No --game ISO was given and Assets/main.dol + Assets/StaticR.rel are not already present. " +
|
||||
"Either pass --game <path-to-iso>, or extract them yourself first (see translator/README.md).");
|
||||
}
|
||||
release = DiscTool.SelectExtracted(releases, assetsDir);
|
||||
}
|
||||
|
||||
var state = JsonState.TryRead<InstallState>(StatePath) ?? new InstallState { Workspace = workspace };
|
||||
@@ -133,10 +135,10 @@ internal static class Program
|
||||
try
|
||||
{
|
||||
// A valid signature authenticates a payload, but does not prove it is the latest
|
||||
// signed revision. Always ask the fixed endpoint for the current snapshot; the
|
||||
// downloader verifies it before atomically replacing the cache.
|
||||
// signed revision. Always ask the release's fixed endpoint for the current snapshot;
|
||||
// the downloader verifies it before atomically replacing the cache.
|
||||
await RetroWfcPayload.DownloadRetroWfcPayloadAsync(
|
||||
RetroWfcPayload.CurrentRetroWfcPayloadUri, cacheDir, token);
|
||||
RetroWfcPayload.PayloadUri(release.GameId), cacheDir, token);
|
||||
}
|
||||
catch (Exception downloadFailure) when (!token.IsCancellationRequested &&
|
||||
downloadFailure is HttpRequestException or TimeoutException
|
||||
@@ -147,7 +149,7 @@ internal static class Program
|
||||
// that must remain a hard failure instead of hiding possible endpoint tampering.
|
||||
try
|
||||
{
|
||||
RetroWfcPayload.ValidateStagedRetroWfcPayloadDirectory(cacheDir);
|
||||
RetroWfcPayload.ValidateStagedRetroWfcPayloadDirectory(cacheDir, gameId: release.GameId);
|
||||
}
|
||||
catch (Exception cacheFailure) when (cacheFailure is IOException or
|
||||
UnauthorizedAccessException or InvalidDataException)
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Text.RegularExpressions;
|
||||
namespace WiiCompiled.Setup.Linux;
|
||||
|
||||
/// <summary>
|
||||
/// The handful of facts this tool needs out of projects/mkwii/recomp.yml. Parsed literally line by
|
||||
/// The handful of facts this tool needs out of a projects/*/recomp.yml. Parsed literally line by
|
||||
/// line - the same approach Launcher/NativeBuildFlags.ps1's Get-MkwProjectPins and
|
||||
/// Launcher/local-build.sh already use - rather than pulling in a YAML library, since the manifest
|
||||
/// is machine-written with a fixed shape.
|
||||
@@ -67,4 +67,21 @@ internal sealed class ProjectManifest
|
||||
|
||||
return new ProjectManifest { GameId = gameId, Region = region, DolSha256 = dolSha, RelSha256 = relSha };
|
||||
}
|
||||
|
||||
/// <summary>Every supported release: one per projects/*/recomp.yml, as local-build.sh selects them.</summary>
|
||||
public static IReadOnlyList<ProjectManifest> LoadAll(string workspace)
|
||||
{
|
||||
var projects = Directory.EnumerateDirectories(Path.Combine(workspace, "projects"))
|
||||
.Select(directory => Path.Combine(directory, "recomp.yml"))
|
||||
.Where(File.Exists)
|
||||
.Order(StringComparer.Ordinal)
|
||||
.Select(Load)
|
||||
.ToList();
|
||||
if (projects.Count == 0)
|
||||
throw new InvalidOperationException($"No projects/*/recomp.yml was found under {workspace}.");
|
||||
return projects;
|
||||
}
|
||||
|
||||
public static string Describe(IEnumerable<ProjectManifest> projects) =>
|
||||
string.Join(", ", projects.Select(project => $"{project.GameId} (region {project.Region})"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user