Compare commits

...

2 Commits

Author SHA1 Message Date
patchzyy 3246732029 Show sha 2026-10-18 11:32:55 +02:00
patchzyy 90589131b5 Fix the date 2026-10-18 00:09:54 +02:00
3 changed files with 66 additions and 12 deletions
@@ -501,17 +501,31 @@ internal sealed class InstallerEngine
var dol = Path.Combine(dataRoot, "sys", "main.dol");
var rel = Path.Combine(dataRoot, "files", "rel", "StaticR.rel");
var fst = Path.Combine(dataRoot, "sys", "fst.bin");
if (!File.Exists(dol) || !File.Exists(rel) || !File.Exists(fst))
throw new InvalidDataException("The disc extraction is missing required Mario Kart Wii files.");
var dolHash = InputValidation.Sha256File(dol);
var relHash = InputValidation.Sha256File(rel);
if (!dolHash.Equals(manifest.ExpectedDolSha256, StringComparison.OrdinalIgnoreCase) ||
!relHash.Equals(manifest.ExpectedRelSha256, StringComparison.OrdinalIgnoreCase))
{
var missing = new[] { dol, rel, fst }.Where(path => !File.Exists(path))
.Select(path => Path.GetRelativePath(dataRoot, path)).ToList();
if (missing.Count > 0)
throw new InvalidDataException(
"The disc is RMCP01 but does not match the supported clean PAL revision. " +
"Patched or otherwise modified game code cannot be installed safely.");
"The disc extraction is missing required Mario Kart Wii files: " +
string.Join(", ", missing) + ".");
var mismatches = new List<string>();
foreach (var (name, path, expected) in new[]
{
("main.dol", dol, manifest.ExpectedDolSha256),
("StaticR.rel", rel, manifest.ExpectedRelSha256)
})
{
var actual = InputValidation.Sha256File(path);
if (!actual.Equals(expected, StringComparison.OrdinalIgnoreCase))
mismatches.Add($"{name} is {new FileInfo(path).Length:N0} bytes with SHA-256 {actual} " +
$"(expected {expected})");
}
if (mismatches.Count > 0)
throw new InvalidDataException(
"The disc is RMCP01 but its game code does not match the supported clean PAL revision: " +
string.Join("; ", mismatches) + ". " +
"Patched or otherwise modified game code cannot be installed safely.");
}
private static void ValidateInstallDirectory(string path) =>
+4 -2
View File
@@ -51,8 +51,10 @@ dol=$(find "$scratch/extracted" -type f -path '*/sys/main.dol' -print -quit)
rel=$(find "$scratch/extracted" -type f -path '*/files/rel/StaticR.rel' -print -quit)
[[ -n "$dol" ]] || fail 'nodtool extraction did not contain sys/main.dol'
[[ -n "$rel" ]] || fail 'nodtool extraction did not contain files/rel/StaticR.rel'
[[ $(sha256 "$dol") == "$EXPECTED_DOL_SHA256" ]] || fail 'disc is not the supported clean PAL RMCP01 Mario Kart Wii image'
[[ $(sha256 "$rel") == "$EXPECTED_REL_SHA256" ]] || fail 'disc has an unexpected StaticR.rel; use a clean PAL RMCP01 image'
dol_sha=$(sha256 "$dol")
rel_sha=$(sha256 "$rel")
[[ $dol_sha == "$EXPECTED_DOL_SHA256" ]] || fail "main.dol does not match the clean PAL RMCP01 image (expected $EXPECTED_DOL_SHA256, got $dol_sha)"
[[ $rel_sha == "$EXPECTED_REL_SHA256" ]] || fail "StaticR.rel does not match the clean PAL RMCP01 image (expected $EXPECTED_REL_SHA256, got $rel_sha)"
data_root=$(dirname "$(dirname "$dol")")
[[ -d "$data_root/files" ]] || fail 'nodtool extraction did not contain the Wii files directory'
+39 -1
View File
@@ -12,6 +12,7 @@
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <ctime>
#include <stdexcept>
#include <limits>
@@ -44,11 +45,48 @@ uint32_t g_reservationAddr = 0;
bool g_hasReservation = false;
const auto g_timeBaseStart = std::chrono::steady_clock::now();
// Days from 1970-01-01 to the given civil date (Howard Hinnant's algorithm).
constexpr int64_t DaysFromCivil(int64_t y, unsigned m, unsigned d)
{
y -= m <= 2;
const int64_t era = (y >= 0 ? y : y - 399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400);
const unsigned doy = (153 * (m > 2 ? m - 3 : m + 9) + 2) / 5 + d - 1;
const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
return era * 146097 + static_cast<int64_t>(doe) - 719468;
}
// The Wii RTC holds local time; the OS counts ticks from 2000-01-01.
uint64_t InitialTimeBase()
{
const std::time_t now = std::time(nullptr);
std::tm local{};
#if defined(_WIN32)
if (localtime_s(&local, &now) != 0) {
return 0;
}
#else
if (localtime_r(&now, &local) == nullptr) {
return 0;
}
#endif
const int64_t days = DaysFromCivil(local.tm_year + 1900, static_cast<unsigned>(local.tm_mon + 1),
static_cast<unsigned>(local.tm_mday)) -
DaysFromCivil(2000, 1, 1);
const int64_t seconds = days * 86400 + local.tm_hour * 3600 + local.tm_min * 60 + local.tm_sec;
if (seconds <= 0) {
return 0;
}
return static_cast<uint64_t>(seconds) * TimeBaseContract::kTicksPerSecond;
}
const uint64_t g_timeBaseOrigin = InitialTimeBase();
uint64_t GetTimeBase() {
const auto elapsed = std::chrono::steady_clock::now() - g_timeBaseStart;
const auto nanoseconds =
std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count();
return TimeBaseContract::NanosecondsToTicks(static_cast<uint64_t>(nanoseconds));
return g_timeBaseOrigin + TimeBaseContract::NanosecondsToTicks(static_cast<uint64_t>(nanoseconds));
}
constexpr uint32_t kFpscrFx = 1u << 31;