Crusader_Decomp/tools/vram_to_bmp.py
2026-04-12 14:45:08 +02:00

60 lines
1.8 KiB
Python

import struct, os
IN_PATH = r"K:\ghidra\Crusader_Decomp\binary\Crusader - No Remorse Memdump Weapons.bin"
OUT_PATH = r"K:\ghidra\Crusader_Decomp\binary\vram_weapons.bmp"
W, H = 1024, 512
with open(IN_PATH, 'rb') as f:
data = f.read()
exp = W * H * 2
if len(data) < exp:
print(f"Warning: expected {exp} bytes, got {len(data)} bytes")
# read pixels (little-endian 16bpp)
count = min(len(data) // 2, W * H)
pixels = [None] * (W * H)
for i in range(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
pixels[i] = (r, g, b)
# fill remaining if any
for i in range(count, W*H):
pixels[i] = (0,0,0)
# BMP row padding
row_bytes_unpadded = 3 * W
row_size = (row_bytes_unpadded + 3) // 4 * 4
pixel_data = bytearray()
for y in range(H-1, -1, -1):
row_start = y * W
for x in range(W):
r,g,b = pixels[row_start + x]
pixel_data.extend(bytes((b, g, r)))
pad = row_size - row_bytes_unpadded
if pad:
pixel_data.extend(b"\x00" * pad)
# headers
bfType = b'BM'
bfSize = 14 + 40 + len(pixel_data)
bfReserved1 = 0
bfReserved2 = 0
bfOffBits = 14 + 40
bmp = bytearray()
bmp.extend(bfType)
bmp.extend(struct.pack('<IHHI', bfSize, bfReserved1, bfReserved2, bfOffBits))
# DIB header (BITMAPINFOHEADER)
biSize = 40
biWidth = W
biHeight = H
biPlanes = 1
biBitCount = 24
biCompression = 0
biSizeImage = len(pixel_data)
biXPelsPerMeter = 2835
biYPelsPerMeter = 2835
biClrUsed = 0
biClrImportant = 0
bmp.extend(struct.pack('<IIIHHIIIIII', biSize, biWidth, biHeight, biPlanes, biBitCount, biCompression, biSizeImage, biXPelsPerMeter, biYPelsPerMeter, biClrUsed, biClrImportant))
bmp.extend(pixel_data)
with open(OUT_PATH, 'wb') as f:
f.write(bmp)
print('Wrote', OUT_PATH, 'bytes=', len(bmp))