Crusader_Decomp/tools/hud_icon_match.py

107 lines
3.8 KiB
Python
Raw Normal View History

2026-04-12 14:45:08 +02:00
IN_BIN = r"K:\ghidra\Crusader_Decomp\binary\Crusader - No Remorse Memdump Weapons.bin"
W,H = 1024,512
# regions
HUD_BOX = (80,44,360,92) # left,top,right,bot
VSTRIP_X0, VSTRIP_X1 = 956, 1023
import os, sys
with open(IN_BIN,'rb') as f:
data = f.read()
count = min(len(data)//2, W*H)
# build RGB rows
rows = []
for y in range(H):
row = []
for x in range(W):
i = y*W + x
if i < count:
off = i*2
val = data[off] | (data[off+1]<<8)
b = (val & 0x1F) << 3
g = ((val >>5) & 0x1F) << 3
r = ((val >>10) & 0x1F) << 3
else:
r=g=b=0
row.append((r,g,b))
rows.append(row)
# extract HUD crop
lx,ty,rx,by = HUD_BOX
w = rx-lx; h = by-ty
hud = [[rows[y][x] for x in range(lx,rx)] for y in range(ty,by)]
# build mask for HUD
hud_mask = [[(1 if any(ch!=0 for ch in hud[y][x]) else 0) for x in range(w)] for y in range(h)]
# extract vstrip area and find blobs
vx0,vx1 = VSTRIP_X0, VSTRIP_X1
vw = vx1-vx0+1
vrows = [[rows[y][x] for x in range(vx0,vx1+1)] for y in range(H)]
# mask and flood-fill
mask = [[1 if any(ch!=0 for ch in vrows[y][x]) else 0 for x in range(vw)] for y in range(H)]
visited = [[0]*vw for _ in range(H)]
from collections import deque
blobs = []
for y in range(H):
for x in range(vw):
if mask[y][x] and not visited[y][x]:
q=deque([(x,y)])
visited[y][x]=1
xs=[]; ys=[]
while q:
cx,cy=q.popleft()
xs.append(cx); ys.append(cy)
for dx,dy in ((1,0),(-1,0),(0,1),(0,-1)):
nx,ny = cx+dx, cy+dy
if 0<=nx<vw and 0<=ny<H and mask[ny][nx] and not visited[ny][nx]:
visited[ny][nx]=1; q.append((nx,ny))
x0,x1 = min(xs), max(xs)
y0,y1 = min(ys), max(ys)
area = len(xs)
blobs.append((x0,y0,x1,y1,area))
# sort blobs by y (top to bottom)
blobs.sort(key=lambda b: b[1])
print('Found', len(blobs), 'blobs in vstrip')
for i,b in enumerate(blobs):
x0,y0,x1,y1,area = b
print(i, 'blob box (vstrip coords)=', (x0,y0,x1,y1), 'area=',area)
# extract blob images
def extract_from_rows(rr, x0,y0,x1,y1):
w = x1-x0+1; h = y1-y0+1
img = [[rr[y+y0][x+x0] for x in range(w)] for y in range(h)]
return img
blob_imgs = [extract_from_rows(vrows, *b[:4]) for b in blobs]
# template match each blob against hud with sliding window
import math
results = []
for bi, img in enumerate(blob_imgs):
bh = len(img); bw = len(img[0])
if bh<4 or bw<4: continue
best = (1e12, -1,-1)
# convert flattened arrays for speed
tmpl = [c for row in img for px in row for c in px]
for y in range(0, h-bh+1):
for x in range(0, w-bw+1):
ssd=0
for j in range(bh):
for i in range(bw):
r1,g1,b1 = img[j][i]
r2,g2,b2 = hud[j+y][i+x]
dr=r1-r2; dg=g1-g2; db=b1-b2
ssd += dr*dr + dg*dg + db*db
if ssd>best[0]: break
if ssd>best[0]: break
if ssd < best[0]: best = (ssd,x,y)
results.append((bi,bw,bh,best[0],best[1],best[2]))
# sort by score
results.sort(key=lambda x: x[3])
print('\nTop matches:')
for bi,bw,bh,ssd,x,y in results[:10]:
# compute HUD pixel coordinates and vram offsets
hud_x = lx + x; hud_y = ty + y
start_idx = hud_y*W + hud_x
end_idx = (hud_y+bh-1)*W + (hud_x + bw -1)
so = start_idx*2; eo = (end_idx+1)*2 -1
vb = blobs[bi]
v_x0 = vx0 + vb[0]; v_y0 = vb[1]; v_x1 = vx0 + vb[2]; v_y1 = vb[3]
print(f'blob#{bi} vbox=({v_x0},{v_y0})-({v_x1},{v_y1}) size={bw}x{bh} bestssd={ssd} hudpos=({hud_x},{hud_y}) bytes=0x{so:06x}-0x{eo:06x}')
# If results empty, report
if not results:
print('No matches found')