Add Linux ARM64 preset, Raspberry Pi 5 docs and remote zip fetcher
- export_presets.cfg: new "Linux ARM64" preset (arch arm64, embedded PCK, ETC2/ASTC textures, S3TC/BPTC off since the Pi 5 V3D driver lacks it). - tools/zipfetch.py: pull a single member out of the remote export-templates tpz with HTTP Range requests instead of downloading the whole 1.28 GB. - docs/BUILD-RASPBERRY-PI.md: reproducible procedure, Pi 5 renderer expectations (GLES3 fallback, Forward+ downgraded to Mobile) and the pitfalls verified on the field. Verified: arm64 export produced (74.094.536 B, PCK embedded at the end of the official template); the x86_64 export from the same tree runs without missing resources.
This commit is contained in:
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Estrae singoli file da uno ZIP remoto via HTTP Range (senza scaricare 1,28 GB).
|
||||
|
||||
Il tpz dei template Godot e' uno zip: serve solo templates/linux_release.arm64.
|
||||
Legge EOCD + central directory con Range, poi scarica solo il membro richiesto.
|
||||
"""
|
||||
import binascii
|
||||
import struct
|
||||
import sys
|
||||
import urllib.request
|
||||
import zlib
|
||||
|
||||
URL = ("https://github.com/godotengine/godot-builds/releases/download/"
|
||||
"4.7.1-stable/Godot_v4.7.1-stable_export_templates.tpz")
|
||||
|
||||
|
||||
def get_range(url, start, end=None):
|
||||
if end is None:
|
||||
rng = f"bytes={start}-"
|
||||
else:
|
||||
rng = f"bytes={start}-{end}"
|
||||
req = urllib.request.Request(url, headers={"Range": rng})
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
return r.read(), r.status
|
||||
|
||||
|
||||
def main(wanted, dest_dir):
|
||||
# 1. dimensione totale
|
||||
req = urllib.request.Request(URL, method="HEAD")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
total = int(r.headers["Content-Length"])
|
||||
print(f"zip remoto: {total} byte")
|
||||
|
||||
# 2. EOCD negli ultimi 64 KiB
|
||||
tail_len = min(65536, total)
|
||||
tail, _ = get_range(URL, total - tail_len)
|
||||
pos = tail.rfind(b"PK\x05\x06")
|
||||
if pos < 0:
|
||||
sys.exit("EOCD non trovato (zip64?)")
|
||||
(_, _, _, _, n_entries, cd_size, cd_off, _) = struct.unpack(
|
||||
"<IHHHHIIH", tail[pos:pos + 22])
|
||||
print(f"voci: {n_entries}, central directory: {cd_off}+{cd_size}")
|
||||
|
||||
# 3. central directory
|
||||
cd, _ = get_range(URL, cd_off, cd_off + cd_size - 1)
|
||||
|
||||
entries = {}
|
||||
i = 0
|
||||
while i + 46 <= len(cd) and cd[i:i + 4] == b"PK\x01\x02":
|
||||
(method, csize, usize, name_len, extra_len, comment_len,
|
||||
local_off) = (struct.unpack("<H", cd[i + 10:i + 12])[0],
|
||||
struct.unpack("<I", cd[i + 20:i + 24])[0],
|
||||
struct.unpack("<I", cd[i + 24:i + 28])[0],
|
||||
struct.unpack("<H", cd[i + 28:i + 30])[0],
|
||||
struct.unpack("<H", cd[i + 30:i + 32])[0],
|
||||
struct.unpack("<H", cd[i + 32:i + 34])[0],
|
||||
struct.unpack("<I", cd[i + 42:i + 46])[0])
|
||||
name = cd[i + 46:i + 46 + name_len].decode("utf-8", "replace")
|
||||
entries[name] = (method, csize, usize, local_off)
|
||||
i += 46 + name_len + extra_len + comment_len
|
||||
|
||||
print(f"membri indicizzati: {len(entries)}")
|
||||
if wanted not in entries:
|
||||
candidati = [n for n in entries if "arm64" in n]
|
||||
sys.exit(f"'{wanted}' non presente. Candidati arm64: {candidati}")
|
||||
|
||||
method, csize, usize, local_off = entries[wanted]
|
||||
print(f"{wanted}: metodo={method} compresso={csize} originale={usize}")
|
||||
|
||||
# 4. header locale per sapere dove iniziano davvero i dati
|
||||
lh, _ = get_range(URL, local_off, local_off + 29)
|
||||
if lh[:4] != b"PK\x03\x04":
|
||||
sys.exit("header locale inatteso")
|
||||
name_len = struct.unpack("<H", lh[26:28])[0]
|
||||
extra_len = struct.unpack("<H", lh[28:30])[0]
|
||||
data_start = local_off + 30 + name_len + extra_len
|
||||
|
||||
# 5. dati compressi del solo membro
|
||||
print(f"scarico {csize} byte da offset {data_start} ...")
|
||||
data, status = get_range(URL, data_start, data_start + csize - 1)
|
||||
print(f"ricevuti {len(data)} byte (http {status})")
|
||||
|
||||
if method == 8:
|
||||
raw = zlib.decompress(data, -15)
|
||||
elif method == 0:
|
||||
raw = data
|
||||
else:
|
||||
sys.exit(f"metodo di compressione non gestito: {method}")
|
||||
|
||||
print(f"decompresso: {len(raw)} byte (atteso {usize})")
|
||||
if len(raw) != usize:
|
||||
sys.exit("dimensione non corrispondente")
|
||||
out = f"{dest_dir}/{wanted.split('/')[-1]}"
|
||||
with open(out, "wb") as fh:
|
||||
fh.write(raw)
|
||||
print(f"scritto {out}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
target = sys.argv[1] if len(sys.argv) > 1 else "templates/linux_release.arm64"
|
||||
dest = sys.argv[2] if len(sys.argv) > 2 else "/tmp/godot-dl"
|
||||
main(target, dest)
|
||||
Reference in New Issue
Block a user