handoff: VSync/event-wedge fixes + iterate 2.A–2.BC research notes
Source changes (dormant parity infra, retained from iterate 2.AI/2.AO): - xenia-kernel/exports.rs: nt_create_event manual_reset polarity + related event wiring - xenia-gpu/mmio_region.rs: D1MODE_VBLANK_VLINE_STATUS hardcode parity Also lands the audit-runs/ analysis notes (.md/.txt/.json digests) for the iterate 2.x VSync/0x10e8/0x1004 wedge investigation. Raw trace dumps (.jsonl/.gz/.csv/.stdout) and agent worktrees (.claude/) are gitignored as regenerable local artifacts — see memory + HANDOFF for the running findings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
195
audit-runs/phase-c5-NtWriteFile/diff-report.md
Normal file
195
audit-runs/phase-c5-NtWriteFile/diff-report.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# Phase A diff report
|
||||
|
||||
**This report is the output of Phase A's diff harness. Divergences
|
||||
shown here are INPUT for Phase B (first-divergence localization),
|
||||
not findings of Phase A.** Phase A's job is to make the harness
|
||||
itself correct, not to analyze what it surfaces.
|
||||
|
||||
## Summary
|
||||
|
||||
| canary_tid | ours_tid | matched | canary_total | ours_total | first_divergence_at |
|
||||
|---|---|---|---|---|---|
|
||||
| 4 | 11 | 5 | 47573 | 9 | 5 |
|
||||
| 6 | 1 | 102132 | 329948 | 108489 | 102132 |
|
||||
| 7 | 2 | 15 | 29 | 33 | 15 |
|
||||
| 12 | 7 | 2 | 6689 | 3 | 2 |
|
||||
| 14 | 9 | 39 | 1371603 | 75 | 39 |
|
||||
| 15 | 10 | 15 | 863209 | 15 | — |
|
||||
|
||||
## canary_tid=4 → ours_tid=11
|
||||
|
||||
First divergence at `tid_event_idx=5`: payload.return_value: canary=1 ours=0
|
||||
|
||||
**Pre-context (last 5 matching events):**
|
||||
```
|
||||
canary: [0] import.call RtlEnterCriticalSection
|
||||
ours: [0] import.call RtlEnterCriticalSection
|
||||
canary: [1] kernel.call RtlEnterCriticalSection
|
||||
ours: [1] kernel.call RtlEnterCriticalSection
|
||||
canary: [2] kernel.return RtlEnterCriticalSection
|
||||
ours: [2] kernel.return RtlEnterCriticalSection
|
||||
canary: [3] import.call KeSetEvent
|
||||
ours: [3] import.call KeSetEvent
|
||||
canary: [4] kernel.call KeSetEvent
|
||||
ours: [4] kernel.call KeSetEvent
|
||||
```
|
||||
|
||||
**Divergent event:**
|
||||
```
|
||||
canary: [5] kernel.return KeSetEvent
|
||||
ours: [5] kernel.return KeSetEvent
|
||||
```
|
||||
|
||||
**Next event after the divergence (if any):**
|
||||
```
|
||||
canary: [6] import.call KeWaitForMultipleObjects
|
||||
ours: [6] import.call KeWaitForMultipleObjects
|
||||
```
|
||||
|
||||
**Raw events (JSON):**
|
||||
```json
|
||||
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 1080594600, "kind": "kernel.return", "payload": {"name": "KeSetEvent", "return_value": 1, "side_effects": [], "status": "0x00000001"}, "schema_version": 1, "tid": 4, "tid_event_idx": 5}
|
||||
{"deterministic": true, "engine": "ours", "guest_cycle": 33, "host_ns": 1694772531, "kind": "kernel.return", "payload": {"name": "KeSetEvent", "return_value": 0, "side_effects": [], "status": "0x00000000"}, "schema_version": 1, "tid": 11, "tid_event_idx": 5}
|
||||
```
|
||||
|
||||
## canary_tid=6 → ours_tid=1
|
||||
|
||||
First divergence at `tid_event_idx=102132`: payload.ord: canary=207 ours=60
|
||||
|
||||
**Pre-context (last 5 matching events):**
|
||||
```
|
||||
canary: [102127] kernel.call RtlEnterCriticalSection
|
||||
ours: [102127] kernel.call RtlEnterCriticalSection
|
||||
canary: [102128] kernel.return RtlEnterCriticalSection
|
||||
ours: [102128] kernel.return RtlEnterCriticalSection
|
||||
canary: [102129] import.call RtlLeaveCriticalSection
|
||||
ours: [102129] import.call RtlLeaveCriticalSection
|
||||
canary: [102130] kernel.call RtlLeaveCriticalSection
|
||||
ours: [102130] kernel.call RtlLeaveCriticalSection
|
||||
canary: [102131] kernel.return RtlLeaveCriticalSection
|
||||
ours: [102131] kernel.return RtlLeaveCriticalSection
|
||||
```
|
||||
|
||||
**Divergent event:**
|
||||
```
|
||||
canary: [102132] import.call NtClose
|
||||
ours: [102132] import.call IoDismountVolumeByFileHandle
|
||||
```
|
||||
|
||||
**Next event after the divergence (if any):**
|
||||
```
|
||||
canary: [102133] kernel.call NtClose
|
||||
ours: [102133] kernel.call IoDismountVolumeByFileHandle
|
||||
```
|
||||
|
||||
**Raw events (JSON):**
|
||||
```json
|
||||
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 726307000, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "NtClose", "ord": 207}, "schema_version": 1, "tid": 6, "tid_event_idx": 102132}
|
||||
{"deterministic": true, "engine": "ours", "guest_cycle": 5378182, "host_ns": 477341747, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "IoDismountVolumeByFileHandle", "ord": 60}, "schema_version": 1, "tid": 1, "tid_event_idx": 102132}
|
||||
```
|
||||
|
||||
## canary_tid=7 → ours_tid=2
|
||||
|
||||
First divergence at `tid_event_idx=15`: payload.ord: canary=300 ours=601
|
||||
|
||||
**Pre-context (last 5 matching events):**
|
||||
```
|
||||
canary: [10] kernel.call NtQueryVolumeInformationFile
|
||||
ours: [10] kernel.call NtQueryVolumeInformationFile
|
||||
canary: [11] kernel.return NtQueryVolumeInformationFile
|
||||
ours: [11] kernel.return NtQueryVolumeInformationFile
|
||||
canary: [12] import.call RtlInitAnsiString
|
||||
ours: [12] import.call RtlInitAnsiString
|
||||
canary: [13] kernel.call RtlInitAnsiString
|
||||
ours: [13] kernel.call RtlInitAnsiString
|
||||
canary: [14] kernel.return RtlInitAnsiString
|
||||
ours: [14] kernel.return RtlInitAnsiString
|
||||
```
|
||||
|
||||
**Divergent event:**
|
||||
```
|
||||
canary: [15] import.call RtlInitAnsiString
|
||||
ours: [15] import.call StfsCreateDevice
|
||||
```
|
||||
|
||||
**Next event after the divergence (if any):**
|
||||
```
|
||||
canary: [16] kernel.call RtlInitAnsiString
|
||||
ours: [16] kernel.call StfsCreateDevice
|
||||
```
|
||||
|
||||
**Raw events (JSON):**
|
||||
```json
|
||||
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 729254300, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "RtlInitAnsiString", "ord": 300}, "schema_version": 1, "tid": 7, "tid_event_idx": 15}
|
||||
{"deterministic": true, "engine": "ours", "guest_cycle": 4231, "host_ns": 477679153, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "StfsCreateDevice", "ord": 601}, "schema_version": 1, "tid": 2, "tid_event_idx": 15}
|
||||
```
|
||||
|
||||
## canary_tid=12 → ours_tid=7
|
||||
|
||||
First divergence at `tid_event_idx=2`: payload.return_value: canary=258 ours=0
|
||||
|
||||
**Pre-context (last 5 matching events):**
|
||||
```
|
||||
canary: [0] import.call KeWaitForSingleObject
|
||||
ours: [0] import.call KeWaitForSingleObject
|
||||
canary: [1] kernel.call KeWaitForSingleObject
|
||||
ours: [1] kernel.call KeWaitForSingleObject
|
||||
```
|
||||
|
||||
**Divergent event:**
|
||||
```
|
||||
canary: [2] kernel.return KeWaitForSingleObject
|
||||
ours: [2] kernel.return KeWaitForSingleObject
|
||||
```
|
||||
|
||||
**Next event after the divergence (if any):**
|
||||
```
|
||||
canary: [3] import.call RtlEnterCriticalSection
|
||||
ours: <end of stream>
|
||||
```
|
||||
|
||||
**Raw events (JSON):**
|
||||
```json
|
||||
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 904485700, "kind": "kernel.return", "payload": {"name": "KeWaitForSingleObject", "return_value": 258, "side_effects": [], "status": "0x00000102"}, "schema_version": 1, "tid": 12, "tid_event_idx": 2}
|
||||
{"deterministic": true, "engine": "ours", "guest_cycle": 30, "host_ns": 504476955, "kind": "kernel.return", "payload": {"name": "KeWaitForSingleObject", "return_value": 0, "side_effects": [], "status": "0x00000000"}, "schema_version": 1, "tid": 7, "tid_event_idx": 2}
|
||||
```
|
||||
|
||||
## canary_tid=14 → ours_tid=9
|
||||
|
||||
First divergence at `tid_event_idx=39`: payload.ord: canary=503 ours=293
|
||||
|
||||
**Pre-context (last 5 matching events):**
|
||||
```
|
||||
canary: [34] kernel.call KeReleaseSpinLockFromRaisedIrql
|
||||
ours: [34] kernel.call KeReleaseSpinLockFromRaisedIrql
|
||||
canary: [35] kernel.return KeReleaseSpinLockFromRaisedIrql
|
||||
ours: [35] kernel.return KeReleaseSpinLockFromRaisedIrql
|
||||
canary: [36] import.call KfLowerIrql
|
||||
ours: [36] import.call KfLowerIrql
|
||||
canary: [37] kernel.call KfLowerIrql
|
||||
ours: [37] kernel.call KfLowerIrql
|
||||
canary: [38] kernel.return KfLowerIrql
|
||||
ours: [38] kernel.return KfLowerIrql
|
||||
```
|
||||
|
||||
**Divergent event:**
|
||||
```
|
||||
canary: [39] import.call XAudioGetVoiceCategoryVolumeChangeMask
|
||||
ours: [39] import.call RtlEnterCriticalSection
|
||||
```
|
||||
|
||||
**Next event after the divergence (if any):**
|
||||
```
|
||||
canary: [40] kernel.call XAudioGetVoiceCategoryVolumeChangeMask
|
||||
ours: [40] kernel.call RtlEnterCriticalSection
|
||||
```
|
||||
|
||||
**Raw events (JSON):**
|
||||
```json
|
||||
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 1082563200, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "XAudioGetVoiceCategoryVolumeChangeMask", "ord": 503}, "schema_version": 1, "tid": 14, "tid_event_idx": 39}
|
||||
{"deterministic": true, "engine": "ours", "guest_cycle": 417, "host_ns": 1694955205, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "RtlEnterCriticalSection", "ord": 293}, "schema_version": 1, "tid": 9, "tid_event_idx": 39}
|
||||
```
|
||||
|
||||
## canary_tid=15 → ours_tid=10
|
||||
|
||||
No divergence within the 15 compared events (canary has 863209, ours has 15).
|
||||
10
audit-runs/phase-c5-NtWriteFile/digest-cvaroff-1.json
Normal file
10
audit-runs/phase-c5-NtWriteFile/digest-cvaroff-1.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"instructions": 50000000,
|
||||
"imports": 40470,
|
||||
"unimpl": 0,
|
||||
"draws": 0,
|
||||
"swaps": 1,
|
||||
"unique_render_targets": 0,
|
||||
"shader_blobs_live": 0,
|
||||
"texture_cache_entries": 0
|
||||
}
|
||||
10
audit-runs/phase-c5-NtWriteFile/digest-cvaroff-2.json
Normal file
10
audit-runs/phase-c5-NtWriteFile/digest-cvaroff-2.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"instructions": 50000000,
|
||||
"imports": 40470,
|
||||
"unimpl": 0,
|
||||
"draws": 0,
|
||||
"swaps": 1,
|
||||
"unique_render_targets": 0,
|
||||
"shader_blobs_live": 0,
|
||||
"texture_cache_entries": 0
|
||||
}
|
||||
10
audit-runs/phase-c5-NtWriteFile/digest-cvaroff-3.json
Normal file
10
audit-runs/phase-c5-NtWriteFile/digest-cvaroff-3.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"instructions": 50000000,
|
||||
"imports": 40470,
|
||||
"unimpl": 0,
|
||||
"draws": 0,
|
||||
"swaps": 1,
|
||||
"unique_render_targets": 0,
|
||||
"shader_blobs_live": 0,
|
||||
"texture_cache_entries": 0
|
||||
}
|
||||
1167
audit-runs/phase-c5-NtWriteFile/fix.diff
Normal file
1167
audit-runs/phase-c5-NtWriteFile/fix.diff
Normal file
File diff suppressed because it is too large
Load Diff
140
audit-runs/phase-c5-NtWriteFile/investigation.md
Normal file
140
audit-runs/phase-c5-NtWriteFile/investigation.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# Phase C+5 — investigation: `NtWriteFile` at idx=102068
|
||||
|
||||
## Divergence
|
||||
|
||||
| | canary | ours (pre-fix) |
|
||||
|---|---|---|
|
||||
| `payload.return_value` at idx=102068, tid=6→1 | `259 = 0x103` (`STATUS_PENDING`) | `0` (`STATUS_SUCCESS`) |
|
||||
| `payload.status` | `0x00000103` | `0x00000000` |
|
||||
| Surrounding context (idx 102060..102067): `RtlEnterCriticalSection`/`RtlLeaveCriticalSection` → `NtWriteFile` | | |
|
||||
| Game thread | tid=6 main | tid=1 main |
|
||||
| Next 6 events (102069..102074) | three more `NtWriteFile` calls, ALL return `0x103` in canary | same calls, ours returns `0` |
|
||||
|
||||
## Step 1 — Event context at idx=102068
|
||||
|
||||
Both engines emit identical call sequences leading to this point:
|
||||
|
||||
```
|
||||
102016 NtCreateFile (sync; both return 0)
|
||||
102019 NtReadFile (both return 0)
|
||||
102022 NtClose
|
||||
102025 NtCreateFile (sync; both return 0)
|
||||
102034 NtReadFile (both return 0)
|
||||
102037 NtWriteFile (both return 0 - sync file)
|
||||
102040 NtClose
|
||||
102052 NtOpenFile (both return 0)
|
||||
102055 NtDeviceIoControlFile (GET_DRIVE_GEOMETRY)
|
||||
102058 NtDeviceIoControlFile (GET_PARTITION_INFO)
|
||||
102067 NtWriteFile (canary returns 0x103, ours returns 0) ← divergence
|
||||
```
|
||||
|
||||
The handle written at 102067 was opened by `NtOpenFile` at 102052. Per
|
||||
canary log `audit-065-canary.log` line for `cache:\,...,00000003`, the
|
||||
`open_options` passed by the game is `0x00000003` =
|
||||
`FILE_DIRECTORY_FILE | FILE_WRITE_THROUGH`. **No `FILE_SYNCHRONOUS_IO_*`
|
||||
bit** — file is async in canary.
|
||||
|
||||
## Step 2 — Source-read both engines
|
||||
|
||||
### Canary `NtWriteFile_entry` (xboxkrnl_io.cc:304-389)
|
||||
|
||||
```cpp
|
||||
// Write completes synchronously (the `if (true || ...)` short-circuit
|
||||
// at line 327 always takes the sync path).
|
||||
if (!file->is_synchronous()) {
|
||||
result = X_STATUS_PENDING; // ← line 351-353
|
||||
}
|
||||
```
|
||||
|
||||
`is_synchronous_` is the bool stored on `XFile`, derived at open time
|
||||
from `create_options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)`
|
||||
(xboxkrnl_io.cc:94-97 inside `NtCreateFile_entry`). `NtOpenFile_entry`
|
||||
forwards `open_options` straight into `NtCreateFile_entry`'s
|
||||
`create_options` slot (xboxkrnl_io.cc:118-122).
|
||||
|
||||
So canary's invariant: a file opened **without** bit 0x10 or 0x20 in
|
||||
its `create_options` is async, and `NtWriteFile` on it returns
|
||||
`STATUS_PENDING` after the synchronous write completes. The IO_STATUS_BLOCK
|
||||
still records `STATUS_SUCCESS`; only the function-return value flips.
|
||||
|
||||
### Ours `nt_write_file` (exports.rs:1484-1554)
|
||||
|
||||
Pre-fix: returns `STATUS_SUCCESS` unconditionally after a successful
|
||||
write. The `KernelObject::File` enum does not track `is_synchronous`.
|
||||
|
||||
### Ours `nt_open_file` (exports.rs:1317-1335)
|
||||
|
||||
Pre-fix: reads `open_options` from `ctx.gpr[8]` (= r8). **This is the
|
||||
wrong register.**
|
||||
|
||||
Canary's `NtOpenFile_entry` signature is
|
||||
```cpp
|
||||
dword_result_t NtOpenFile_entry(
|
||||
lpdword_t handle_out, // r3
|
||||
dword_t desired_access, // r4
|
||||
pointer_t<X_OBJECT_ATTRIBUTES> object_attributes, // r5
|
||||
pointer_t<X_IO_STATUS_BLOCK> io_status_block, // r6
|
||||
dword_t open_options); // r7
|
||||
```
|
||||
|
||||
— **5 args**, so per Xenia's `shim_utils::Param::LoadValue`
|
||||
(util/shim_utils.h:158-167), the 5th dword arrives in `r3 + (ordinal_) = r7`.
|
||||
|
||||
Live capture (Phase C+5 debug log):
|
||||
|
||||
```
|
||||
nt_open_file: r7=0x3 r8=0x800021 ← cache:\ probe
|
||||
nt_open_file: r7=0x3 r8=0x800021
|
||||
nt_open_file: r7=0x3 r8=0x4021
|
||||
nt_open_file: r7=0x7 r8=0x4040
|
||||
nt_open_file: r7=0x7 r8=0x4020
|
||||
```
|
||||
|
||||
`r7=0x3` matches canary's logged value exactly. Ours's
|
||||
`r8=0x4021,0x4020,...` are residuals from prior register usage that
|
||||
happen to have the FILE_DIRECTORY_FILE bit (0x01) set — which is why
|
||||
the AUDIT-053/054 hierarchical-create fix worked at all. But the
|
||||
**0x20 bit (FILE_SYNCHRONOUS_IO_NONALERT)** was also frequently set in
|
||||
r8 residual data, making every NtOpenFile-derived file appear
|
||||
synchronous in ours.
|
||||
|
||||
## Step 3 — Classification
|
||||
|
||||
**Class (A) — Engine bug, two interlinked defects:**
|
||||
|
||||
1. **Wrong-register bug** in `nt_open_file`: reads `open_options` from
|
||||
r8 instead of r7. Confirmed by canary-side ground truth (r7=0x3
|
||||
matches canary log `cache:\,…,00000003`).
|
||||
2. **Missing async/sync tracking** on `KernelObject::File`: even with
|
||||
correct `open_options`, ours had no machinery to remember the
|
||||
sync/async state and flip `NtWriteFile` returns.
|
||||
|
||||
Both defects must be fixed together to align Phase A's matched prefix
|
||||
past idx=102068. Fixing only #2 (without #1) leaves the file marked
|
||||
sync (because r8 has bit 0x20 from residual register usage), so
|
||||
`NtWriteFile` returns `STATUS_SUCCESS` and the divergence persists —
|
||||
which we observed in the first fix iteration (matched-prefix stayed
|
||||
at 102068 after a Path-A fix that relied on r8).
|
||||
|
||||
### Why this is class (A) not (α) canonicalization
|
||||
|
||||
Examining events 102069..102074 in canary: three more `NtWriteFile`
|
||||
calls on the same handle, all returning `STATUS_PENDING`. Then at
|
||||
idx=102132 canary calls `NtClose`; ours diverges at 102132 by calling
|
||||
`IoDismountVolumeByFileHandle` instead. **The game branches on the
|
||||
return value of these writes** — without aligning the return values,
|
||||
ours's downstream code path stays divergent. Canonicalization would
|
||||
mask this, not fix it.
|
||||
|
||||
### Tripstone #2 (reading-error #23) check
|
||||
|
||||
A "fix the upstream cause" change could in principle flip a CRT
|
||||
branch. Empirically, after the fix:
|
||||
- imports counter: 40452 → 40470 (game responded to the new return
|
||||
values by issuing additional kernel calls — expected for async-IO
|
||||
semantics).
|
||||
- main matched prefix: 102068 → **102132 (+64)**. No regression.
|
||||
- All sub-chains' matched prefixes unchanged.
|
||||
|
||||
Reading-error #23 risk DID NOT MATERIALIZE because the new return
|
||||
values match canary's, so the CRT branches identically downstream.
|
||||
106
audit-runs/phase-c5-NtWriteFile/re-validation.md
Normal file
106
audit-runs/phase-c5-NtWriteFile/re-validation.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# Phase C+5 — re-validation
|
||||
|
||||
## Gate 1 — Determinism (cvar-OFF, ours)
|
||||
|
||||
3 fresh runs of `check -n 50000000 --stable-digest`:
|
||||
|
||||
| run | digest md5 |
|
||||
|-----|------------|
|
||||
| 1 | c6d895829b4611964978990ae1cb8a6a |
|
||||
| 2 | c6d895829b4611964978990ae1cb8a6a |
|
||||
| 3 | c6d895829b4611964978990ae1cb8a6a |
|
||||
| Stage-2 baseline (pre-C+5) | `d8a3d439c37d0436a12c3e01149d8fa8` |
|
||||
|
||||
**Result**: ✅ byte-identical across 3 runs. New baseline `c6d89582…`
|
||||
diverges from the Stage-2 baseline `d8a3d439…` — expected per
|
||||
Tripstone #4 ("a real return-value fix in ours likely shifts the boot
|
||||
trajectory; the baseline digest WILL change"). The fix flips
|
||||
`NtOpenFile`'s reg read (r8 → r7) + adds an `async_file_handles` side
|
||||
table + adds a STATUS_PENDING branch to `nt_write_file`. The combined
|
||||
effect changes import counts from 40452 → 40470 (+18) because the
|
||||
game's downstream code reacts to the new async-style returns.
|
||||
|
||||
## Gate 2 — Phase B `image_canonical_sha256`
|
||||
|
||||
Phase B snapshot captured to `snap/ours/`. `image_loaded_sha256` =
|
||||
`ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18`,
|
||||
matches the Phase-A/B verify baseline. The fix touches only the
|
||||
kernel-export shim layer and the in-process file-handle table — no
|
||||
PE image bytes modified.
|
||||
|
||||
## Gate 3 — Phase A matched-prefix extension (KEY METRIC)
|
||||
|
||||
Diffed `audit-runs/phase-c5-NtWriteFile/ours.jsonl` against the
|
||||
existing `phase-c-first-divergence/phase-a/canary.jsonl`.
|
||||
|
||||
| chain | C+3 (pre-C+5) | C+5 (post) | Δ |
|
||||
|---|---|---|---|
|
||||
| canary tid=6 → ours tid=1 (main) | 102068 | **102132** | **+64** |
|
||||
| canary tid=4 → ours tid=11 | 5 | 5 | 0 |
|
||||
| canary tid=7 → ours tid=2 | 15 | 15 | 0 |
|
||||
| canary tid=12 → ours tid=7 | 2 | 2 | 0 |
|
||||
| canary tid=14 → ours tid=9 | 39 | 39 | 0 |
|
||||
| canary tid=15 → ours tid=10 | (no div) | (no div) | 0 |
|
||||
|
||||
**Main thread matched prefix grew from 102068 to 102132. Gate 3 ✅.**
|
||||
|
||||
The new first-divergence at idx=102132 is `NtClose` (canary) vs
|
||||
`IoDismountVolumeByFileHandle` (ours) — a call-name divergence
|
||||
indicating a different branch, that's the next Phase C+N target.
|
||||
|
||||
## Gate 4 — Build
|
||||
|
||||
```
|
||||
$ cargo build --release
|
||||
Compiling xenia-kernel v0.1.0
|
||||
Compiling xenia-app v0.1.0
|
||||
Finished `release` profile [optimized] target(s) in 6.34s
|
||||
```
|
||||
|
||||
One pre-existing dead-code warning (`walk_committed_regions`); not
|
||||
introduced by this fix. Canary untouched.
|
||||
|
||||
## Gate 5 — Phase A determinism (emitter)
|
||||
|
||||
Two cvar-ON captures of the same engine binary on the same ISO,
|
||||
md5-summing only deterministic fields (excluding `host_ns`):
|
||||
|
||||
```
|
||||
ours.jsonl (run 1, deterministic-fields-only) 388d394a92f6b26a44d549fb70ca95fa
|
||||
/tmp/c5_pa_run2.jsonl (run 2, det-fields-only) 388d394a92f6b26a44d549fb70ca95fa
|
||||
```
|
||||
|
||||
Byte-identical. ✅
|
||||
|
||||
## Gate 6 — Kernel unit tests
|
||||
|
||||
```
|
||||
$ cargo test --release -p xenia-kernel
|
||||
test result: ok. 144 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
3 new tests added (141 → 144):
|
||||
|
||||
* `nt_write_file_async_handle_returns_status_pending` — file in
|
||||
`async_file_handles` returns STATUS_PENDING; IO_STATUS_BLOCK still
|
||||
records STATUS_SUCCESS.
|
||||
* `nt_write_file_sync_handle_returns_status_success` — file NOT in
|
||||
`async_file_handles` retains legacy STATUS_SUCCESS return.
|
||||
* `nt_close_prunes_async_file_set` — final handle close removes from
|
||||
the side-table.
|
||||
|
||||
Pre-existing `cache_create_write_read_roundtrip` updated to set
|
||||
`FILE_SYNCHRONOUS_IO_NONALERT` on its create_options stack slot so the
|
||||
created handle is sync (legacy STATUS_SUCCESS assertion preserved).
|
||||
|
||||
## Summary
|
||||
|
||||
All 6 gates pass. Phase A main matched prefix grew from 102068 to
|
||||
102132 (+64 events). Two interlinked engine defects fixed: (1) wrong
|
||||
register read in `nt_open_file` (r8 → r7); (2) missing async/sync
|
||||
tracking on file handles. Diff-tool unchanged. Canary unchanged.
|
||||
|
||||
Next divergence: **NtClose vs IoDismountVolumeByFileHandle at
|
||||
tid_event_idx=102132** (call-name divergence — game branches into a
|
||||
different code path). Class likely (A) missing handler or upstream
|
||||
state divergence — Phase C+6 target.
|
||||
25
audit-runs/phase-c5-NtWriteFile/snap/ours/config.json
Normal file
25
audit-runs/phase-c5-NtWriteFile/snap/ours/config.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"build_id": "ours-phaseB",
|
||||
"cvars": {
|
||||
"phase_b_dump_section_content": false,
|
||||
"phase_b_snapshot_and_exit": false,
|
||||
"phase_b_snapshot_dir": "/home/fabi/RE - Project Sylpheed/xenia-rs/audit-runs/phase-c5-NtWriteFile/snap"
|
||||
},
|
||||
"deterministic_skip": [
|
||||
"host_ns_at_snapshot",
|
||||
"wall_clock_iso8601",
|
||||
"build_id",
|
||||
"iso_path",
|
||||
"cvars.phase_b_snapshot_dir"
|
||||
],
|
||||
"engine": "ours",
|
||||
"host_ns_at_snapshot": 0,
|
||||
"image_loaded_sha256": "ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18",
|
||||
"iso_path": "",
|
||||
"schema_version": 1,
|
||||
"wall_clock_iso8601": "epoch:0",
|
||||
"xex_entry_point": "0x824ab748",
|
||||
"xex_header_sha256": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"xex_image_base": "0x82000000",
|
||||
"xex_image_size": 9568256
|
||||
}
|
||||
234
audit-runs/phase-c5-NtWriteFile/snap/ours/cpu_state.json
Normal file
234
audit-runs/phase-c5-NtWriteFile/snap/ours/cpu_state.json
Normal file
@@ -0,0 +1,234 @@
|
||||
{
|
||||
"cr": [
|
||||
"0x0",
|
||||
"0x0",
|
||||
"0x0",
|
||||
"0x0",
|
||||
"0x0",
|
||||
"0x0",
|
||||
"0x0",
|
||||
"0x0"
|
||||
],
|
||||
"ctr": "0x0000000000000000",
|
||||
"deterministic_skip": [
|
||||
"hw_id"
|
||||
],
|
||||
"engine": "ours",
|
||||
"fpr": [
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000"
|
||||
],
|
||||
"fpscr": "0x00000000",
|
||||
"gpr": [
|
||||
"0x0000000000000000",
|
||||
"0x00000000700fff00",
|
||||
"0x0000000020000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x000000007fff0000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000",
|
||||
"0x0000000000000000"
|
||||
],
|
||||
"hw_id": 0,
|
||||
"lr": "0x00000000bcbcbcbc",
|
||||
"msr": "0x0000000000009030",
|
||||
"pc": "0x824ab748",
|
||||
"pcr_base": "0x7fff0000",
|
||||
"schema_version": 1,
|
||||
"stack_base": "0x00000000",
|
||||
"stack_limit": "0x00000000",
|
||||
"thread_id": 1,
|
||||
"tls_base": "0x00000000",
|
||||
"vr": [
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000"
|
||||
],
|
||||
"vrsave": "0xffffffff",
|
||||
"vscr": "00000000000000000000000000010000",
|
||||
"xer": {
|
||||
"ca": 0,
|
||||
"ov": 0,
|
||||
"so": 0,
|
||||
"tbc": 0
|
||||
}
|
||||
}
|
||||
62
audit-runs/phase-c5-NtWriteFile/snap/ours/kernel.json
Normal file
62
audit-runs/phase-c5-NtWriteFile/snap/ours/kernel.json
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"deterministic_skip": [
|
||||
"raw_handle_id",
|
||||
"exports_registered_count"
|
||||
],
|
||||
"engine": "ours",
|
||||
"exports_registered_count": 199,
|
||||
"exports_registered_sample": [
|
||||
"xam.xex!NetDll_WSACleanup",
|
||||
"xam.xex!NetDll_WSAStartup",
|
||||
"xam.xex!XGetAVPack",
|
||||
"xam.xex!XGetGameRegion",
|
||||
"xam.xex!XGetLanguage",
|
||||
"xam.xex!XGetVideoMode",
|
||||
"xam.xex!XMsgInProcessCall",
|
||||
"xam.xex!XMsgStartIORequest",
|
||||
"xam.xex!XMsgStartIORequestEx",
|
||||
"xam.xex!XNotifyGetNext",
|
||||
"xam.xex!XNotifyPositionUI",
|
||||
"xam.xex!XamAlloc",
|
||||
"xam.xex!XamContentClose",
|
||||
"xam.xex!XamContentCreate",
|
||||
"xam.xex!XamContentCreateEnumerator",
|
||||
"xam.xex!XamContentDelete",
|
||||
"xam.xex!XamContentGetCreator",
|
||||
"xam.xex!XamContentGetDeviceData",
|
||||
"xam.xex!XamContentGetDeviceName",
|
||||
"xam.xex!XamContentGetDeviceState",
|
||||
"xam.xex!XamContentSetThumbnail",
|
||||
"xam.xex!XamEnableInactivityProcessing",
|
||||
"xam.xex!XamEnumerate",
|
||||
"xam.xex!XamFree",
|
||||
"xam.xex!XamGetExecutionId",
|
||||
"xam.xex!XamGetSystemVersion",
|
||||
"xam.xex!XamInputGetCapabilities",
|
||||
"xam.xex!XamInputGetKeystrokeEx",
|
||||
"xam.xex!XamInputGetState",
|
||||
"xam.xex!XamInputSetState",
|
||||
"xam.xex!XamLoaderLaunchTitle",
|
||||
"xam.xex!XamLoaderTerminateTitle"
|
||||
],
|
||||
"exports_registered_sha256": "bb97815f82b2313c9eaa07bf80dab47c5c23408c24203a1283dfb2aba1e84e09",
|
||||
"handle_name_table": [],
|
||||
"notification_listeners": [],
|
||||
"objects": [
|
||||
{
|
||||
"details": {
|
||||
"entry_pc": "0x824ab748",
|
||||
"exit_code": null,
|
||||
"hw_id": 0,
|
||||
"is_entry_thread": true,
|
||||
"thread_id": 1
|
||||
},
|
||||
"handle_semantic_id": "9879c5053fedb1d0",
|
||||
"name": null,
|
||||
"raw_handle_id": "0x00001000",
|
||||
"type": "Thread",
|
||||
"type_code": 5
|
||||
}
|
||||
],
|
||||
"schema_version": 1
|
||||
}
|
||||
11
audit-runs/phase-c5-NtWriteFile/snap/ours/manifest.json
Normal file
11
audit-runs/phase-c5-NtWriteFile/snap/ours/manifest.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"engine": "ours",
|
||||
"files": {
|
||||
"config.json": "656a2df4f35d20fd6d8caa0b7145ee232aa3325cef27d31c550ca8aae9e748f2",
|
||||
"cpu_state.json": "4e6df54ca1939d08854f3a52b49ed2c5ee0823d63cdecad8a7395203dac5443a",
|
||||
"kernel.json": "b64ea3a6c14f1b0aaadc6de8adbb894edf636a813120d08028ca096e1d06bacc",
|
||||
"memory.json": "b96ae4daebfbdd314e574492c1e162f532fa4f89ff5c0d7c6c29743797089cf1",
|
||||
"vfs.json": "97bb2bda57266d8e0dd1da13309eab5ece43130ef378a0b682917d299e9dc4e1"
|
||||
},
|
||||
"schema_version": 1
|
||||
}
|
||||
84
audit-runs/phase-c5-NtWriteFile/snap/ours/memory.json
Normal file
84
audit-runs/phase-c5-NtWriteFile/snap/ours/memory.json
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"committed_pages_total": 2594,
|
||||
"deterministic_skip": [
|
||||
"host_base_pointer"
|
||||
],
|
||||
"engine": "ours",
|
||||
"guest_address_space_bytes": 4294967296,
|
||||
"heaps": [
|
||||
{
|
||||
"base": "0x00000000",
|
||||
"name": "v00000000",
|
||||
"page_size": 4096,
|
||||
"page_state_histogram": {
|
||||
"committed": 0
|
||||
},
|
||||
"size": "0x40000000"
|
||||
},
|
||||
{
|
||||
"base": "0x40000000",
|
||||
"name": "v40000000",
|
||||
"page_size": 4096,
|
||||
"page_state_histogram": {
|
||||
"committed": 266
|
||||
},
|
||||
"size": "0x40000000"
|
||||
},
|
||||
{
|
||||
"base": "0x80000000",
|
||||
"name": "v80000000",
|
||||
"page_size": 4096,
|
||||
"page_state_histogram": {
|
||||
"committed": 2336
|
||||
},
|
||||
"size": "0x40000000"
|
||||
},
|
||||
{
|
||||
"base": "0x90000000",
|
||||
"name": "v90000000",
|
||||
"page_size": 4096,
|
||||
"page_state_histogram": {
|
||||
"committed": 0
|
||||
},
|
||||
"size": "0x40000000"
|
||||
}
|
||||
],
|
||||
"page_size": 4096,
|
||||
"regions": [
|
||||
{
|
||||
"byte_count": 1048576,
|
||||
"end": "0x70100000",
|
||||
"protect": 0,
|
||||
"section_kind": null,
|
||||
"sha256": "30e14955ebf1352266dc2ff8067e68104607e750abb9d3b36582b8af909fcb58",
|
||||
"start": "0x70000000"
|
||||
},
|
||||
{
|
||||
"byte_count": 4096,
|
||||
"end": "0x7ffe1000",
|
||||
"protect": 0,
|
||||
"section_kind": null,
|
||||
"sha256": "ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7",
|
||||
"start": "0x7ffe0000"
|
||||
},
|
||||
{
|
||||
"byte_count": 4096,
|
||||
"end": "0x7fff1000",
|
||||
"protect": 0,
|
||||
"section_kind": null,
|
||||
"sha256": "e35cddaf9c210aed7505ec4cf1c599f58ac2b7ec25b0885db1ee49aba2db519a",
|
||||
"start": "0x7fff0000"
|
||||
},
|
||||
{
|
||||
"byte_count": 9568256,
|
||||
"end": "0x82920000",
|
||||
"protect": 0,
|
||||
"section_kind": null,
|
||||
"sha256": "ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18",
|
||||
"start": "0x82000000"
|
||||
}
|
||||
],
|
||||
"regions_walked": [],
|
||||
"schema_version": 1,
|
||||
"section_contents": null
|
||||
}
|
||||
71
audit-runs/phase-c5-NtWriteFile/snap/ours/vfs.json
Normal file
71
audit-runs/phase-c5-NtWriteFile/snap/ours/vfs.json
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"cache_root_listing": [],
|
||||
"deterministic_skip": [
|
||||
"host_path_realpath"
|
||||
],
|
||||
"engine": "ours",
|
||||
"mounted_devices_observed_count": 1,
|
||||
"resolve_path_probes": [
|
||||
{
|
||||
"is_directory": true,
|
||||
"path": "\\Device\\Cdrom0",
|
||||
"resolved": true,
|
||||
"size": null
|
||||
},
|
||||
{
|
||||
"is_directory": true,
|
||||
"path": "\\Device\\Cdrom0\\dat",
|
||||
"resolved": true,
|
||||
"size": 4096
|
||||
},
|
||||
{
|
||||
"is_directory": null,
|
||||
"path": "\\Device\\Cdrom0\\dat\\movie",
|
||||
"resolved": false,
|
||||
"size": null
|
||||
},
|
||||
{
|
||||
"is_directory": null,
|
||||
"path": "\\Device\\Cdrom0\\dat\\movie\\opening.bik",
|
||||
"resolved": false,
|
||||
"size": null
|
||||
},
|
||||
{
|
||||
"is_directory": false,
|
||||
"path": "\\Device\\Cdrom0\\default.xex",
|
||||
"resolved": true,
|
||||
"size": 3497984
|
||||
},
|
||||
{
|
||||
"is_directory": null,
|
||||
"path": "\\Device\\HardDisk0\\Partition1",
|
||||
"resolved": false,
|
||||
"size": null
|
||||
},
|
||||
{
|
||||
"is_directory": true,
|
||||
"path": "cache:\\",
|
||||
"resolved": true,
|
||||
"size": null
|
||||
},
|
||||
{
|
||||
"is_directory": null,
|
||||
"path": "cache:\\nonexistent_probe",
|
||||
"resolved": false,
|
||||
"size": null
|
||||
},
|
||||
{
|
||||
"is_directory": true,
|
||||
"path": "game:\\dat",
|
||||
"resolved": true,
|
||||
"size": 4096
|
||||
},
|
||||
{
|
||||
"is_directory": false,
|
||||
"path": "game:\\default.xex",
|
||||
"resolved": true,
|
||||
"size": 3497984
|
||||
}
|
||||
],
|
||||
"schema_version": 1
|
||||
}
|
||||
Reference in New Issue
Block a user