185 lines
7.3 KiB
Bash
Executable file
185 lines
7.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
|
# See LICENSE.md for license information.
|
|
# SPDX-License-Identifier: MIT
|
|
#
|
|
# check-icons.sh - Host gate for programs' icons: on every port built here,
|
|
# DRAW (examples/draw) carries the icon make baked it (make/common.mk's
|
|
# programIcon, tools/iconBake.py), where its system looks for it. Read with
|
|
# the systems' own tools where they are installed, otherwise by the format:
|
|
#
|
|
# amiga Draw.info beside it: a DiskObject (magic $E310) for a tool,
|
|
# AMIGA_ICON_STACK bytes of stack, the image iconBake makes
|
|
# win32 a group icon, number HOSTED_ICON_ID, among its resources
|
|
# (llvm-readobj)
|
|
# win16 the same in its NE resource table
|
|
# sdl3 the window icon linked in (gHostedIconRgba)
|
|
# web icon.png beside the page, the page naming it its icon
|
|
# psp the EBOOT's ICON0, the baked PNG, 144x80
|
|
# haiku, haiku32, beos BEOS:L:STD_ICON and BEOS:M:STD_ICON resources
|
|
# (xres)
|
|
# dvx the icon32 resource (dvxres)
|
|
# iigs DRAW.ICONS in joey.2mg's Icons folder, type $CA: an icon file
|
|
# for an application named DRAW
|
|
#
|
|
# A port not built is left out; none built at all is a SKIP. (Tool output is
|
|
# taken whole and handed to grep as a here-string: under pipefail a writer
|
|
# that grep -q leaves behind dies of SIGPIPE and looks like a failure.) Each port's
|
|
# emulator gate (scripts/verify-<port>-icon.sh) checks that its system shows
|
|
# the icon.
|
|
#
|
|
# Usage: scripts/check-icons.sh
|
|
# Exit: 0 PASS, 1 FAIL, 2 SKIP (no port built)
|
|
set -uo pipefail
|
|
|
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
source "$repo/scripts/lib/joeyEnv.sh"
|
|
label=check-icons
|
|
fails=""
|
|
checked=""
|
|
|
|
fail() {
|
|
fails="${fails:+$fails; }$1: $2"
|
|
}
|
|
|
|
# makeValue PORT VARIABLE: a variable of make/PORT.mk.
|
|
makeValue() {
|
|
make -s -f "$repo/make/$1.mk" --eval "valueOf: ; @echo \$($2)" valueOf 2> /dev/null
|
|
}
|
|
|
|
for port in amiga win32 win16 sdl3 web psp haiku haiku32 beos dvx iigs; do
|
|
program=$(joeyExampleProgram draw "$port" 2> /dev/null) || continue
|
|
[ -f "$program" ] || continue
|
|
checked="${checked:+$checked }$port"
|
|
case "$port" in
|
|
amiga)
|
|
python3 - "$program.info" "$(makeValue amiga AMIGA_ICON_STACK)" "$repo/tools" <<'PY' || fail amiga "Draw.info"
|
|
import struct
|
|
import sys
|
|
|
|
info, stack, tools = sys.argv[1], int(sys.argv[2]), sys.argv[3]
|
|
sys.path.insert(0, tools)
|
|
import iconBake # noqa: E402
|
|
|
|
data = open(info, "rb").read()
|
|
magic, version = struct.unpack_from(">HH", data, 0)
|
|
doType = data[48]
|
|
doStack = struct.unpack_from(">I", data, 74)[0]
|
|
w, h, depth = struct.unpack_from(">hhh", data, 82)
|
|
want = (iconBake.WB_DISKMAGIC, iconBake.WBTOOL, stack, iconBake.AMIGA_W, iconBake.AMIGA_H, iconBake.AMIGA_DEPTH)
|
|
got = (magic, doType, doStack, w, h, depth)
|
|
if got != want:
|
|
print(" amiga: magic, type, stack, image %s, want %s" % (got, want))
|
|
sys.exit(1)
|
|
PY
|
|
;;
|
|
win32)
|
|
readobj=$(dirname "$WIN32_CC")/llvm-readobj
|
|
id=$(makeValue win32 HOSTED_ICON_ID)
|
|
listing=$("$readobj" --coff-resources "$program" 2> /dev/null)
|
|
awk -v want="Name: (ID $id)" '/Type: GROUP_ICON/ { group = 1; next } group && /Name:/ { found = index($0, want) > 0; exit } END { exit !found }' <<< "$listing" || fail win32 "no group icon $id among DRAW.EXE's resources"
|
|
;;
|
|
win16)
|
|
python3 - "$program" "$(makeValue win16 HOSTED_ICON_ID)" <<'PY' || fail win16 "no group icon in DRAW.EXE's NE resources"
|
|
import struct
|
|
import sys
|
|
|
|
RT_GROUP_ICON = 0x800E
|
|
INTEGER_ID = 0x8000
|
|
data = open(sys.argv[1], "rb").read()
|
|
wantId = INTEGER_ID | int(sys.argv[2])
|
|
ne = struct.unpack_from("<I", data, 0x3C)[0]
|
|
table = ne + struct.unpack_from("<H", data, ne + 0x24)[0]
|
|
if table == ne + struct.unpack_from("<H", data, ne + 0x26)[0]:
|
|
sys.exit(1)
|
|
at = table + 2
|
|
while True:
|
|
typeId, count = struct.unpack_from("<HH", data, at)
|
|
if typeId == 0:
|
|
sys.exit(1)
|
|
at += 8
|
|
for i in range(count):
|
|
if typeId == RT_GROUP_ICON and struct.unpack_from("<H", data, at + 6)[0] == wantId:
|
|
sys.exit(0)
|
|
at += 12
|
|
PY
|
|
;;
|
|
sdl3)
|
|
symbols=$(nm "$program" 2> /dev/null)
|
|
grep -q ' gHostedIconRgba$' <<< "$symbols" || fail sdl3 "DRAW links no window icon"
|
|
;;
|
|
web)
|
|
folder=$(dirname "$program")
|
|
grep -qE '<link[^>]*rel=("?)icon\1[^>]*>' "$program" && grep -qE 'href=("?)icon\.png' "$program" || fail web "DRAW.html names no icon.png"
|
|
python3 -c 'import sys; from PIL import Image; sys.exit(Image.open(sys.argv[1]).format != "PNG")' "$folder/icon.png" 2> /dev/null || fail web "no icon.png beside DRAW.html"
|
|
;;
|
|
psp)
|
|
python3 - "$program" "$repo/build/psp/icon/DRAW/icon0.png" "$(makeValue psp PSP_ICON0_SIZE)" <<'PY' || fail psp "the EBOOT's ICON0"
|
|
import io
|
|
import struct
|
|
import sys
|
|
|
|
from PIL import Image
|
|
|
|
eboot, baked, size = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
data = open(eboot, "rb").read()
|
|
if data[:4] != b"\0PBP":
|
|
sys.exit(1)
|
|
offsets = struct.unpack_from("<8I", data, 8)
|
|
icon = data[offsets[1]:offsets[2]]
|
|
picture = Image.open(io.BytesIO(icon))
|
|
if icon != open(baked, "rb").read() or "%dx%d" % picture.size != size:
|
|
print(" psp: ICON0 is %d bytes, %dx%d" % (len(icon), picture.size[0], picture.size[1]))
|
|
sys.exit(1)
|
|
PY
|
|
;;
|
|
haiku|haiku32|beos)
|
|
listing=$("$HAIKU_TOOLS/xres" -l "$program" 2> /dev/null)
|
|
for want in "'ICON' *101 .*BEOS:L:STD_ICON" "'MICN' *101 .*BEOS:M:STD_ICON"; do
|
|
grep -qE "$want" <<< "$listing" || fail "$port" "no $want resource"
|
|
done
|
|
;;
|
|
dvx)
|
|
listing=$("$DVX_SDK/bin/dvxres" list "$program" 2> /dev/null)
|
|
grep -qE '^ +icon32 +icon ' <<< "$listing" || fail dvx "no icon32 resource in draw.app"
|
|
;;
|
|
iigs)
|
|
disk=$repo/build/iigs/bin/joey.2mg
|
|
if [ ! -f "$disk" ]; then
|
|
fail iigs "no joey.2mg (make iigs-disk)"
|
|
continue
|
|
fi
|
|
out=$(mktemp -d -t joeylib-icons.XXXXXX)
|
|
"$CADIUS" EXTRACTFILE "$disk" /JOEYLIB/Icons/DRAW.ICONS "$out" > /dev/null 2>&1
|
|
iconFile=$(find "$out" -iname 'DRAW.ICONS*' -not -name '_FileInformation.txt' -print -quit)
|
|
listing=$("$CADIUS" CATALOG "$disk" 2> /dev/null)
|
|
grep -qE '^ +DRAW\.ICONS +ICN ' <<< "$listing" || fail iigs "no DRAW.ICONS of type \$CA in joey.2mg's Icons folder"
|
|
python3 - "$iconFile" <<'PY' || fail iigs "DRAW.ICONS is no icon file for DRAW"
|
|
import struct
|
|
import sys
|
|
|
|
ICON_FILE_ID = 0x0001
|
|
APPLICATION = 0x00B3
|
|
data = open(sys.argv[1], "rb").read()
|
|
fileId = struct.unpack_from("<H", data, 4)[0]
|
|
record = 26
|
|
name = data[record + 67:record + 67 + data[record + 66]].decode("ascii")
|
|
fileType = struct.unpack_from("<H", data, record + 82)[0]
|
|
if (fileId, name, fileType) != (ICON_FILE_ID, "DRAW", APPLICATION):
|
|
print(" iigs: id, name, type %s" % ((fileId, name, fileType),))
|
|
sys.exit(1)
|
|
PY
|
|
rm -rf "$out"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -z "$checked" ]; then
|
|
echo "$label: SKIP - no port with icons is built"
|
|
exit 2
|
|
fi
|
|
if [ -n "$fails" ]; then
|
|
echo "$label: FAIL - $fails"
|
|
exit 1
|
|
fi
|
|
echo "$label: PASS ($checked: DRAW carries its icon where the system looks)"
|