5 device="${2:-/dev/sr0}"
6 firmware_note="${3:-${FRIIDUMP_FIRMWARE_MODIFIED_NOTE:-}}"
8 source_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
9 build_root="$source_root/build-candidate21-linux"
10 expected_version="0.5.3.16-pf1-candidate21"
12 python3 - "$source_root/CANDIDATE.json" <<'PYEXPECTED'
16 from pathlib import Path
19 value = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))["candidate_implementation"]["commit"]
20 except (OSError, KeyError, TypeError, ValueError):
23 print(value.lower() if re.fullmatch(r"[0-9a-fA-F]{40}", value) else "")
27 if [[ ! "$expected_commit" =~ ^[0-9a-f]{40}$ ]]; then
28 echo "Unable to resolve candidate21 implementation commit from CANDIDATE.json." >&2
33 local command_name="$1"
34 local package_hint="$2"
36 if ! command -v "$command_name" >/dev/null 2>&1; then
37 echo "Missing required command: $command_name" >&2
38 echo "Ubuntu package hint: sudo apt-get install -y $package_hint" >&2
43 require_build_dependencies() {
44 require_command cmake cmake
45 require_command cc build-essential
46 require_command python3 python3
48 if ! python3 -c 'import jsonschema' >/dev/null 2>&1; then
49 echo "Missing Python module: jsonschema" >&2
50 echo "Ubuntu package hint: sudo apt-get install -y python3-jsonschema" >&2
56 local git_root git_commit
58 git_root="$(git -C "$source_root" rev-parse --show-toplevel 2>/dev/null || true)"
59 if [[ -n "$git_root" && "$(cd -- "$git_root" && pwd -P)" == "$(cd -- "$source_root" && pwd -P)" ]]; then
60 git_commit="$(git -C "$source_root" rev-parse HEAD 2>/dev/null || true)"
61 if [[ ! "$git_commit" =~ ^[0-9a-fA-F]{40}$ ]]; then
62 echo "Unable to resolve HEAD from the source-root Git repository." >&2
65 if [[ "${git_commit,,}" != "$expected_commit" ]]; then
66 echo "Candidate implementation commit mismatch." >&2
67 echo "Expected: $expected_commit" >&2
68 echo "Actual: ${git_commit,,}" >&2
73 printf '%s\n' "$expected_commit"
76 validate_report_directory() {
77 local report_root="$1"
79 python3 "$source_root/tests/validate_native_reports.py" \
80 --fixtures "$report_root" \
81 --schema "$source_root/tests/contracts/friidump-test-result.v1.schema.json" \
85 normalize_future_source_timestamps() {
86 python3 - "$source_root" <<'PYTIMESTAMPS'
90 from pathlib import Path
92 root = Path(sys.argv[1])
96 for path in root.rglob("*"):
97 if not path.is_file():
100 if stat.st_mtime > now + 1.0:
101 os.utime(path, (stat.st_atime, now))
104 print(f"Future source timestamp preflight: PASS (normalized={normalized})")
109 local commit fixtures
111 require_build_dependencies
112 commit="$(resolve_commit)"
113 normalize_future_source_timestamps
115 rm -rf -- "$build_root"
117 cmake -S "$source_root" -B "$build_root" \
118 -DBUILD_STATIC_BINARY=ON \
119 -DBUILD_NATIVE_REPORT_TESTS=ON \
120 -DFRIIDUMP_BUILD_COMMIT="$commit"
122 cmake --build "$build_root" --parallel
124 "$build_root/src/friidump-xbox-portable-metadata-test"
125 python3 "$source_root/tests/test_linux_xbox_portable_contract.py"
126 python3 "$source_root/tests/test_seed_diagnostics_contract.py"
127 "$build_root/src/friidump-linux-rawio-test"
128 python3 "$source_root/tests/test_linux_rawio_contract.py"
129 python3 "$source_root/tests/test_xbox_dvd_structure_contract.py" --source-root "$source_root"
131 fixtures="$(mktemp -d)"
132 trap 'rm -rf -- "$fixtures"' RETURN
134 "$build_root/src/friidump-report-fixtures" "$fixtures"
135 validate_report_directory "$fixtures"
137 test -f "$build_root/src/redump_dat/Nintendo - GameCube.dat"
138 test -f "$build_root/src/redump_dat/Nintendo - Wii.dat"
139 test -f "$build_root/src/redump_dat/Microsoft - Xbox.dat"
141 "$build_root/src/friidump-redump-dat-resolver-test" \
144 "Nintendo - GameCube.dat" \
145 "$build_root/src/redump_dat/Nintendo - GameCube.dat" \
148 "$build_root/src/friidump-redump-dat-verify-test" \
149 "$build_root/src/redump_dat/Nintendo - GameCube.dat"
151 resolver_tmp="$(mktemp -d)"
152 mkdir -p "$resolver_tmp/prefix/bin" \
153 "$resolver_tmp/prefix/share/friidump/redump_dat" \
154 "$resolver_tmp/env-dat" \
155 "$resolver_tmp/explicit-dat"
156 printf 'installed\n' > "$resolver_tmp/prefix/share/friidump/redump_dat/Test.dat"
157 printf 'environment\n' > "$resolver_tmp/env-dat/Test.dat"
158 printf 'explicit\n' > "$resolver_tmp/explicit-dat/Test.dat"
160 "$build_root/src/friidump-redump-dat-resolver-test" \
162 "$resolver_tmp/prefix/bin" \
164 "$resolver_tmp/prefix/bin/../share/friidump/redump_dat/Test.dat" \
167 FRIIDUMP_REDUMP_DAT_DIR="$resolver_tmp/env-dat" \
168 "$build_root/src/friidump-redump-dat-resolver-test" \
170 "$resolver_tmp/prefix/bin" \
172 "$resolver_tmp/env-dat/Test.dat" \
175 FRIIDUMP_REDUMP_DAT_DIR="$resolver_tmp/env-dat" \
176 "$build_root/src/friidump-redump-dat-resolver-test" \
177 "$resolver_tmp/explicit-dat" \
178 "$resolver_tmp/prefix/bin" \
180 "$resolver_tmp/explicit-dat/Test.dat" \
184 rm -rf -- "$resolver_tmp"
187 help_output="$("$build_root/src/friidump" --help 2>&1)"
191 if test "$help_status" -ne 1; then
192 echo "Unexpected FriiDump --help exit status: $help_status (expected 1)." >&2
196 grep -F "FriiDump $expected_version" <<<"$help_output"
197 grep -F "FRIIDUMP_REDUMP_DAT_DIR" <<<"$help_output"
198 grep -F "Linux raw-I/O requirement" <<<"$help_output"
201 echo "WARNING: this Linux build does not yet carry CAP_SYS_RAWIO."
202 echo "Vendor-command hardware operations will be refused until the exact"
203 echo "validated executable receives cap_sys_rawio=ep. Do not run FriiDump"
204 echo "itself as root. Rebuilding or replacing the executable clears the capability."
205 echo "Install and verify with:"
206 echo " bash $source_root/validation/friidump-v0.5.3.16-pf1-candidate21-linux-live-validation.sh capability"
208 echo "FRIIDUMP CANDIDATE21 LINUX BUILD: PASS"
212 require_command getcap libcap2-bin
213 require_command setcap libcap2-bin
214 require_command sudo sudo
217 bash "$source_root/validation/friidump-linux-rawio-capability.sh" install "$build_root/src/friidump"
219 bash "$source_root/validation/friidump-linux-rawio-capability.sh" status "$build_root/src/friidump"
221 echo "FRIIDUMP CANDIDATE21 LINUX CAPABILITY: PASS"
225 if ! test -x "$build_root/src/friidump"; then
231 local block_name model_path
233 block_name="$(basename -- "$device")"
234 model_path="/sys/class/block/$block_name/device/model"
236 if test -r "$model_path"; then
237 tr -d '\000' < "$model_path" | xargs
244 local output_root report_path output_path exit_code
246 require_build_dependencies
249 output_root="$source_root/_native-report-linux-validation/$(date -u +%Y%m%dT%H%M%SZ)-no-media"
250 mkdir -p "$output_root"
252 echo "Remove media from $device, close the tray, wait for the drive to settle, then press Enter."
255 output_path="$output_root/no-media-placeholder.iso"
256 report_path="$output_root/no-media-placeholder.iso.friidump.json"
261 "$build_root/src/friidump" \
263 -i "no-media-placeholder.iso"
268 test -f "$report_path" || {
269 echo "FAIL: expected native report was not created: $report_path" >&2
273 validate_report_directory "$output_root"
275 python3 - "$report_path" "$device" "$expected_version" "$expected_commit" <<'PYREPORT'
278 from pathlib import Path
280 report_path = Path(sys.argv[1])
281 expected_device = sys.argv[2]
282 expected_version = sys.argv[3]
283 expected_commit = sys.argv[4]
285 report = json.loads(report_path.read_text(encoding="utf-8"))
288 "schema": report.get("schema") == "friidump-test-result.v1",
289 "version": report.get("generator", {}).get("version") == expected_version,
290 "commit": report.get("generator", {}).get("build_commit") == expected_commit,
291 "device": report.get("drive", {}).get("device_path") == expected_device,
292 "stock_firmware": report.get("drive", {}).get("firmware_modified") is False,
293 "no_modification_note": report.get("drive", {}).get("modification_note") is None,
294 "test_type": report.get("run", {}).get("test_type") == "diagnostic_no_media",
295 "run_result": report.get("run", {}).get("result") == "not_applicable",
296 "unknown_platform": report.get("media", {}).get("platform") == "unknown",
297 "seed_not_attempted": report.get("seed", {}).get("attempted") is False,
298 "dump_not_attempted": report.get("dump", {}).get("attempted") is False,
299 "no_output_path": report.get("dump", {}).get("output_path") is None,
300 "no_hashes": all(value is None for value in report.get("hashes", {}).values()),
301 "no_artifacts": report.get("artifacts") == [],
304 failed = [name for name, ok in checks.items() if not ok]
305 for name, ok in checks.items():
306 print(f"{'PASS' if ok else 'FAIL'}: {name}")
309 raise SystemExit("No-media report contract failed: " + ", ".join(failed))
312 test ! -e "$output_path" || {
313 echo "FAIL: no-media validation unexpectedly created an image: $output_path" >&2
317 echo "FriiDump exit code: $exit_code (nonzero is expected for no media)"
318 echo "Report: $report_path"
319 echo "FRIIDUMP CANDIDATE21 LINUX NO-MEDIA: PASS"
322 xbox_cancel_phase() {
323 local model output_root
325 require_build_dependencies
328 model="$(device_model)"
329 if [[ "$model" != *GDR8050L* && "$model" != *GDR-8050L* ]]; then
330 echo "Refusing Xbox cancellation validation on this device." >&2
331 echo "Expected a GDR-8050L inquiry identity; found: ${model:-unknown}" >&2
335 if [[ -z "$firmware_note" ]]; then
336 echo "Xbox cancellation validation requires an explicit modified-firmware note." >&2
337 echo "Pass it as argument 3 or set FRIIDUMP_FIRMWARE_MODIFIED_NOTE." >&2
341 output_root="$source_root/_native-report-linux-validation/$(date -u +%Y%m%dT%H%M%SZ)-xbox-cancel"
342 mkdir -p "$output_root"
344 echo "Insert the Xbox disc in $device. Press Ctrl+C once after XISO progress begins."
348 "$build_root/src/friidump" \
351 -X "Red Faction II [TQ00501A].xiso" \
352 --firmware-modified "$firmware_note"
355 echo "Inspect the generated partial XISO, .friidump.json, and log under: $output_root"
361 block_name="$(basename -- "$device")"
363 echo "Device: $device"
364 for field in vendor model rev; do
365 printf '%-8s ' "$field:"
366 cat "/sys/class/block/$block_name/device/$field" 2>/dev/null || echo "unavailable"
369 if command -v sg_inq >/dev/null 2>&1; then
374 echo "Optional detailed inquiry: sudo apt-get install -y sg3-utils"
378 stat -c 'Access: %A Owner: %U Group: %G' "$device"
381 if test -x "$build_root/src/friidump"; then
383 bash "$source_root/validation/friidump-linux-rawio-capability.sh" status "$build_root/src/friidump" || true
388 build) build_phase ;;
389 capability) capability_phase ;;
390 identify) identify_phase ;;
391 no-media) no_media_phase ;;
392 xbox-cancel) xbox_cancel_phase ;;
394 echo "Usage: $0 {build|capability|identify|no-media|xbox-cancel} [/dev/sr0] [firmware-note]" >&2