mirror of
https://github.com/MCKero6423/uv-k5-v3-emulator.git
synced 2026-10-02 11:07:31 +00:00
flash controller: store ACR/OPTKEYR instead of swallowing them, which is what stopped the factory bootloader from starting slots over the firmware's own serial protocol (0x0720 family); uvk5_socket/uvk5_testenv so a fresh checkout skips instead of failing; web UI slot table and Multiboot button; quick start, CONTRIBUTING, and stop tracking firmware images and radio dumps
87 lines
3.3 KiB
Python
87 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Fetch a released firmware build, so the tests are not anchored to one machine.
|
|
|
|
Several emulator tests need a real image to run against: the multi-system slot tests
|
|
need a build with ENABLE_FEAT_F4HWN_MULTIBOOT (the 0x0720..0x0727 commands are inside
|
|
that ifdef), and a plain application image is useful for the rest. Neither is in this
|
|
repository -- firmware is not ours to redistribute -- and pointing tests at one
|
|
developer's build directory is what makes a suite unreproducible.
|
|
|
|
So the tests read a documented directory, `assets/firmware/`, and skip with a message
|
|
naming this tool when it is empty. Run it once:
|
|
|
|
python3 tools/fetch_firmware.py # fieldops-v6.0.0, the multiboot one
|
|
python3 tools/fetch_firmware.py --list
|
|
python3 tools/fetch_firmware.py --all
|
|
|
|
Files come from the upstream project's own archive, through jsDelivr, and each one
|
|
prints its SHA-256 so a fetch can be compared with someone else's.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import os
|
|
import sys
|
|
import urllib.request
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
ROOT = os.path.dirname(HERE)
|
|
DEST_DIR = os.path.join(ROOT, "assets", "firmware")
|
|
|
|
# armel/uv-k1-k5v3-firmware-custom, the firmware the emulator is built for. The v6.0.0
|
|
# builds are the ones with the multi-system boot menu; the localised releases floating
|
|
# around are often built without it.
|
|
BASE = "https://cdn.jsdelivr.net/gh/armel/uv-k1-k5v3-firmware-custom@main/"
|
|
|
|
RELEASES = {
|
|
"fieldops-v6.0.0": ("archive/f4hwn.fieldops.v6.0.0.bin",
|
|
"application image with the multi-system boot menu"),
|
|
"fusion-v6.0.0": ("archive/f4hwn.fusion.v6.0.0.bin",
|
|
"application image with the multi-system boot menu"),
|
|
"fusion-v5.9.0": ("archive/f4hwn.fusion.v5.9.0.bin", "application image, no boot menu"),
|
|
}
|
|
|
|
|
|
def fetch(name: str, dest_dir: str = DEST_DIR) -> str:
|
|
if name not in RELEASES:
|
|
raise SystemExit("unknown release %r; --list shows what there is" % name)
|
|
path, _why = RELEASES[name]
|
|
os.makedirs(dest_dir, exist_ok=True)
|
|
out = os.path.join(dest_dir, os.path.basename(path))
|
|
url = BASE + path
|
|
print("fetching %s" % url)
|
|
with urllib.request.urlopen(url, timeout=120) as response:
|
|
blob = response.read()
|
|
if not blob:
|
|
raise SystemExit("empty response from %s" % url)
|
|
with open(out, "wb") as fh:
|
|
fh.write(blob)
|
|
print("wrote %s (%d bytes)" % (out, len(blob)))
|
|
print("sha256 %s" % hashlib.sha256(blob).hexdigest())
|
|
return out
|
|
|
|
|
|
def main(argv=None):
|
|
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
ap.add_argument("--list", action="store_true", help="show the known releases")
|
|
ap.add_argument("--all", action="store_true", help="fetch everything")
|
|
ap.add_argument("names", nargs="*", help="release names (default: fieldops-v6.0.0)")
|
|
args = ap.parse_args(argv)
|
|
|
|
if args.list:
|
|
for name, (path, why) in sorted(RELEASES.items()):
|
|
print("%-18s %-40s %s" % (name, path, why))
|
|
return 0
|
|
names = sorted(RELEASES) if args.all else (args.names or ["fieldops-v6.0.0"])
|
|
for name in names:
|
|
fetch(name)
|
|
print("")
|
|
print("tests look in %s now; UVK5_MULTIBOOT_IMAGE overrides the path"
|
|
% DEST_DIR)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|