Preserve formatted NAND boot and Wii Remote pairing, add DMA/SRAM conformance tests and a Wii comparison probe. Keep P1 profiling tools and native update A/B evidence; reject the unhelpful targeted SRAM shortcut.
173 lines
5.5 KiB
C
173 lines
5.5 KiB
C
// Copyright 2026 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <fat.h>
|
|
#include <gccore.h>
|
|
#include <ogc/es.h>
|
|
#include <ogc/ios.h>
|
|
#include <ogc/ipc.h>
|
|
#include <ogc/isfs.h>
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// The same DOL must be run on the console and in Dolphin. Do not depend on
|
|
// timings or on allocated IOS file descriptor numbers in the comparison.
|
|
#define SCHEMA_VERSION 1
|
|
#define ROUNDS 64
|
|
#define RECORDS_PER_ROUND 29
|
|
#define SKIPPED (-32768)
|
|
#define SD_TEST_FILE "sd:/wii-conformance-data.bin"
|
|
|
|
static FILE* output;
|
|
static unsigned int sequence;
|
|
|
|
static void record(const char* test, unsigned int round, int value, int extra)
|
|
{
|
|
if (output)
|
|
fprintf(output, "%u,%s,%u,%d,%d\n", sequence, test, round, value, extra);
|
|
++sequence;
|
|
}
|
|
|
|
static void probe_open(const char* name, const char* path, unsigned int round, unsigned int mode)
|
|
{
|
|
char test[48];
|
|
const s32 fd = IOS_Open(path, mode);
|
|
snprintf(test, sizeof(test), "open_%s_mode%u", name, mode);
|
|
// File descriptor allocation can legitimately differ between runs.
|
|
record(test, round, fd >= 0 ? 0 : fd, 0);
|
|
snprintf(test, sizeof(test), "close_%s_mode%u", name, mode);
|
|
record(test, round, fd >= 0 ? IOS_Close(fd) : SKIPPED, 0);
|
|
}
|
|
|
|
static void probe_sd(unsigned int round)
|
|
{
|
|
static u8 expected[4096] __attribute__((aligned(32)));
|
|
static u8 actual[4096] __attribute__((aligned(32)));
|
|
const size_t length = 1 + ((round * 67) % sizeof(expected));
|
|
for (size_t i = 0; i < length; ++i)
|
|
expected[i] = (u8)(round * 131 + i * 17);
|
|
|
|
FILE* file = fopen(SD_TEST_FILE, "wb");
|
|
record("sd_open_write", round, file ? 0 : -1, (int)length);
|
|
record("sd_write_bytes", round, file ? (int)fwrite(expected, 1, length, file) : SKIPPED,
|
|
(int)length);
|
|
record("sd_close_write", round, file ? fclose(file) : SKIPPED, 0);
|
|
|
|
file = fopen(SD_TEST_FILE, "rb");
|
|
record("sd_open_read", round, file ? 0 : -1, 0);
|
|
const size_t read_count = file ? fread(actual, 1, length, file) : 0;
|
|
record("sd_read_bytes", round, file ? (int)read_count : SKIPPED, (int)length);
|
|
u32 hash = 2166136261u;
|
|
for (size_t i = 0; i < read_count; ++i)
|
|
hash = (hash ^ actual[i]) * 16777619u;
|
|
record("sd_read_hash", round, file ? (int)hash : SKIPPED, 0);
|
|
record("sd_data_equal", round,
|
|
file ? (read_count == length && memcmp(expected, actual, length) == 0) : SKIPPED, 0);
|
|
record("sd_close_read", round, file ? fclose(file) : SKIPPED, 0);
|
|
record("sd_remove", round, remove(SD_TEST_FILE), 0);
|
|
}
|
|
|
|
static void init_display(void)
|
|
{
|
|
VIDEO_Init();
|
|
GXRModeObj* mode = VIDEO_GetPreferredMode(NULL);
|
|
void* framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(mode));
|
|
console_init(framebuffer, 20, 20, mode->fbWidth - 40, mode->xfbHeight - 40,
|
|
mode->fbWidth * VI_DISPLAY_PIX_SZ);
|
|
VIDEO_Configure(mode);
|
|
VIDEO_SetNextFramebuffer(framebuffer);
|
|
VIDEO_SetBlack(FALSE);
|
|
VIDEO_Flush();
|
|
VIDEO_WaitVSync();
|
|
if (mode->viTVMode & VI_NON_INTERLACE)
|
|
VIDEO_WaitVSync();
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
init_display();
|
|
printf("Wii IOS conformance probe v%d\n", SCHEMA_VERSION);
|
|
|
|
if (fatInitDefault())
|
|
output = fopen("sd:/wii-conformance.csv", "wb");
|
|
if (!output)
|
|
{
|
|
printf("Cannot write sd:/wii-conformance.csv\n");
|
|
printf("Insert a writable SD card and restart.\n");
|
|
for (int i = 0; i < 600; ++i)
|
|
VIDEO_WaitVSync();
|
|
return 1;
|
|
}
|
|
|
|
// Metadata is checked before comparing individual observations.
|
|
fprintf(output, "schema,%d\n", SCHEMA_VERSION);
|
|
fprintf(output, "rounds,%d\n", ROUNDS);
|
|
fprintf(output, "observations,%d\n", 4 + ROUNDS * RECORDS_PER_ROUND);
|
|
fprintf(output, "ios_version,%d\n", IOS_GetVersion());
|
|
fprintf(output, "ios_revision,%d\n", IOS_GetRevision());
|
|
fprintf(output, "seq,test,round,value,extra\n");
|
|
|
|
const s32 fs_init = ISFS_Initialize();
|
|
record("isfs_init", 0, fs_init, 0);
|
|
const s32 es_init = __ES_Init();
|
|
record("es_init", 0, es_init, 0);
|
|
|
|
for (unsigned int round = 0; round < ROUNDS; ++round)
|
|
{
|
|
// A missing resource is a useful stable IPC error-path test.
|
|
probe_open("missing", "/dev/wii_conformance_probe_absent", round, IPC_OPEN_NONE);
|
|
|
|
for (unsigned int mode = IPC_OPEN_NONE; mode <= IPC_OPEN_RW; ++mode)
|
|
{
|
|
probe_open("fs", "/dev/fs", round, mode);
|
|
probe_open("es", "/dev/es", round, mode);
|
|
}
|
|
|
|
if (fs_init >= 0)
|
|
{
|
|
u32 count __attribute__((aligned(32))) = 0;
|
|
const s32 result = ISFS_ReadDir("/", NULL, &count);
|
|
record("isfs_root_count", round, result, result >= 0 ? (int)count : 0);
|
|
}
|
|
else
|
|
{
|
|
record("isfs_root_count", round, SKIPPED, 0);
|
|
}
|
|
|
|
if (es_init >= 0)
|
|
{
|
|
u32 title_count __attribute__((aligned(32))) = 0;
|
|
const s32 title_result = ES_GetNumTitles(&title_count);
|
|
record("es_title_count", round, title_result,
|
|
title_result >= 0 ? (int)title_count : 0);
|
|
}
|
|
else
|
|
{
|
|
record("es_title_count", round, SKIPPED, 0);
|
|
}
|
|
|
|
probe_sd(round);
|
|
}
|
|
|
|
if (fs_init >= 0)
|
|
record("isfs_deinit", 0, ISFS_Deinitialize(), 0);
|
|
else
|
|
record("isfs_deinit", 0, SKIPPED, 0);
|
|
if (es_init >= 0)
|
|
record("es_deinit", 0, __ES_Close(), 0);
|
|
else
|
|
record("es_deinit", 0, SKIPPED, 0);
|
|
|
|
const int write_error = ferror(output);
|
|
const int close_error = fclose(output);
|
|
printf("%u observations. CSV: sd:/wii-conformance.csv\n", sequence);
|
|
printf("Write status: %d, close status: %d\n", write_error, close_error);
|
|
printf("Returning to loader in about 10 seconds.\n");
|
|
for (int i = 0; i < 600; ++i)
|
|
VIDEO_WaitVSync();
|
|
return write_error || close_error ? 1 : 0;
|
|
}
|