"""Fetch the official Dota 2 emblem icon (no wordmark) for the preview header.
Source (discovered, not guessed) from the dota2.com.cn site HTML:
That .ico is a multi-frame container whose largest frames are 256x256 PNGs of
the red Dota 2 map-marker emblem with a transparent background — i.e. the
brand icon alone, no "DOTA 2" text. We extract the first 256x256 PNG frame,
decode with OpenCV, and write it to assets/ui_icons/dota2_logo.png so it rides
the existing /ui-icon/ route + static-export glob pipeline.
"""
import struct
import sys
import cv2
import numpy as np
from common import UI_ICONS
from http_utils import http_bytes
FAVICON_URL = "https://www.dota2.com.cn/favicon.ico"
OUT = UI_ICONS / "dota2_logo.png"
TARGET_SIZE = 256
def _extract_largest_png_frame(data: bytes) -> bytes | None:
"""Return the bytes of the largest PNG-embedded frame in an ICO blob."""
if len(data) < 6 or data[:4] != b"\x00\x00\x01\x00":
return None
count = struct.unpack(" len(data):
break
w = data[off]
size = struct.unpack(" best[0]:
best = (width * width, blob)
return best[1] if best else None
def main() -> int:
if OUT.is_file() and OUT.stat().st_size >= 512:
print(f"exists: {OUT} ({OUT.stat().st_size} bytes)")
return 0
data = http_bytes(FAVICON_URL, timeout=30)
if not data or len(data) < 16:
print("empty favicon download", file=sys.stderr)
return 1
png = _extract_largest_png_frame(data)
if png is None:
print("no PNG frame in .ico", file=sys.stderr)
return 1
arr = cv2.imdecode(np.frombuffer(png, dtype=np.uint8), cv2.IMREAD_UNCHANGED)
if arr is None:
print("decode failed", file=sys.stderr)
return 1
UI_ICONS.mkdir(parents=True, exist_ok=True)
ok = cv2.imwrite(str(OUT), arr)
if not ok:
print("write failed", file=sys.stderr)
return 1
h, w = arr.shape[:2]
channels = arr.shape[2] if arr.ndim == 3 else 1
transparent = int(np.count_nonzero(arr[:, :, 3] == 0)) if channels == 4 else 0
print(f"saved {OUT} ({w}x{h}, {OUT.stat().st_size} bytes, {transparent} px transparent)")
return 0
if __name__ == "__main__":
raise SystemExit(main())