forked from mirror/Archipelago
Some checks failed
Analyze modified files / flake8 (push) Failing after 2m28s
Build / build-win (push) Has been cancelled
Build / build-ubuntu2204 (push) Has been cancelled
ctest / Test C++ ubuntu-latest (push) Has been cancelled
ctest / Test C++ windows-latest (push) Has been cancelled
Analyze modified files / mypy (push) Has been cancelled
Build and Publish Docker Images / Push Docker image to Docker Hub (push) Successful in 5m4s
Native Code Static Analysis / scan-build (push) Failing after 5m2s
type check / pyright (push) Successful in 1m7s
unittests / Test Python 3.11.2 ubuntu-latest (push) Failing after 16m23s
unittests / Test Python 3.12 ubuntu-latest (push) Failing after 28m19s
unittests / Test Python 3.13 ubuntu-latest (push) Failing after 14m49s
unittests / Test hosting with 3.13 on ubuntu-latest (push) Successful in 5m0s
unittests / Test Python 3.13 macos-latest (push) Has been cancelled
unittests / Test Python 3.11 windows-latest (push) Has been cancelled
unittests / Test Python 3.13 windows-latest (push) Has been cancelled
160 lines
5.9 KiB
Python
160 lines
5.9 KiB
Python
"""Locate Hash images for displaying on the website."""
|
|
|
|
import base64
|
|
import io
|
|
import zlib
|
|
import js
|
|
|
|
from PIL import Image
|
|
|
|
from randomizer.Patching.Patcher import ROM, LocalROM
|
|
|
|
|
|
class ImageInfo:
|
|
"""Class to store parameters for an image in ROM."""
|
|
|
|
def __init__(self, name: str, format: str, table: int, index: int, width: int, height: int, mode: str):
|
|
"""Initialize with given parameters."""
|
|
self.name = name
|
|
self.format = format
|
|
self.table = table
|
|
self.index = index
|
|
self.width = width
|
|
self.height = height
|
|
self.mode = mode
|
|
|
|
|
|
def genGIFFrame(im: Image, cap=128):
|
|
"""Generate information necessary for a gif frame."""
|
|
alpha = im.getchannel("A")
|
|
im = im.convert("RGB").convert("P", palette=Image.Palette.ADAPTIVE, colors=255)
|
|
mask = Image.eval(alpha, lambda a: 255 if a <= cap else 0)
|
|
im.paste(255, mask)
|
|
im.info["transparency"] = 255
|
|
return im
|
|
|
|
|
|
def get_hash_images(type="local", mode="hash"):
|
|
"""Get and return a list of hash images for the website UI."""
|
|
images = [
|
|
ImageInfo("bongos", "rgba16", 25, 5548, 40, 40, "hash"),
|
|
ImageInfo("crown", "rgba16", 25, 5893, 44, 44, "hash"),
|
|
ImageInfo("dk_coin", "rgba16", 7, 500, 48, 44, "hash"),
|
|
ImageInfo("fairy", "rgba32", 25, 5869, 32, 32, "hash"),
|
|
ImageInfo("guitar", "rgba16", 25, 5547, 40, 40, "hash"),
|
|
ImageInfo("nin_coin", "rgba16", 25, 5912, 44, 44, "hash"),
|
|
ImageInfo("orange", "rgba16", 7, 309, 32, 32, "hash"),
|
|
ImageInfo("rainbow_coin", "rgba16", 25, 5963, 48, 44, "hash"),
|
|
ImageInfo("rw_coin", "rgba16", 25, 5905, 44, 44, "hash"),
|
|
ImageInfo("saxophone", "rgba16", 25, 5549, 40, 40, "hash"),
|
|
]
|
|
|
|
for x in range(0x8):
|
|
# Fairy
|
|
images.append(ImageInfo(f"Fairy Image {x}", "rgba32", 25, 0x16ED + x, 32, 32, "loading-fairy"))
|
|
for x in range(0x1B):
|
|
# Explosion
|
|
images.append(ImageInfo(f"Explosion Image {x}", "rgba32", 25, 0x1539 + x, 32, 32, "loading-dead"))
|
|
|
|
ptr_offset = 0x101C50
|
|
loaded_images = []
|
|
gif_frames = []
|
|
rom_type = None
|
|
if type == "browser":
|
|
if mode in ("loading-fairy", "loading-dead"):
|
|
rom_type = ROM(js.romFile)
|
|
else:
|
|
rom_type = ROM()
|
|
else:
|
|
rom_type = LocalROM()
|
|
filtered_list = [x for x in images if x.mode == mode]
|
|
for x in filtered_list:
|
|
rom_type.seek(ptr_offset + (x.table * 4))
|
|
ptr_table = ptr_offset + int.from_bytes(rom_type.readBytes(4), "big")
|
|
rom_type.seek(ptr_table + (x.index * 4))
|
|
img_start = ptr_offset + int.from_bytes(rom_type.readBytes(4), "big")
|
|
rom_type.seek(ptr_table + ((x.index + 1) * 4))
|
|
img_end = ptr_offset + int.from_bytes(rom_type.readBytes(4), "big")
|
|
img_size = img_end - img_start
|
|
rom_type.seek(img_start)
|
|
if x.table == 25:
|
|
dec = zlib.decompress(rom_type.readBytes(img_size), 15 + 32)
|
|
else:
|
|
dec = rom_type.readBytes(img_size)
|
|
im = Image.new(mode="RGBA", size=(x.width, x.height))
|
|
pix = im.load()
|
|
pix_count = x.width * x.height
|
|
for pixel in range(pix_count):
|
|
if x.format == "rgba16":
|
|
start = pixel * 2
|
|
end = start + 2
|
|
pixel_data = int.from_bytes(dec[start:end], "big")
|
|
red = (pixel_data >> 11) & 0x1F
|
|
green = (pixel_data >> 6) & 0x1F
|
|
blue = (pixel_data >> 1) & 0x1F
|
|
alpha = pixel_data & 1
|
|
red = int((red / 0x1F) * 0xFF)
|
|
green = int((green / 0x1F) * 0xFF)
|
|
blue = int((blue / 0x1F) * 0xFF)
|
|
alpha = alpha * 255
|
|
elif x.format == "rgba32":
|
|
start = pixel * 4
|
|
end = start + 4
|
|
pixel_data = int.from_bytes(dec[start:end], "big")
|
|
red = (pixel_data >> 24) & 0xFF
|
|
green = (pixel_data >> 16) & 0xFF
|
|
blue = (pixel_data >> 8) & 0xFF
|
|
alpha = pixel_data & 0xFF
|
|
if alpha == 0:
|
|
red = 0
|
|
green = 0
|
|
blue = 0
|
|
pix_x = pixel % x.width
|
|
pix_y = int(pixel / x.width)
|
|
pix[pix_x, pix_y] = (red, green, blue, alpha)
|
|
|
|
in_mem_file = io.BytesIO()
|
|
im = im.transpose(Image.FLIP_LEFT_RIGHT)
|
|
im.save(in_mem_file, format="PNG")
|
|
if mode == "hash":
|
|
in_mem_file.seek(0)
|
|
img_bytes = in_mem_file.read()
|
|
|
|
base64_encoded_result_bytes = base64.b64encode(img_bytes)
|
|
base64_encoded_result_str = base64_encoded_result_bytes.decode("ascii")
|
|
loaded_images.append(base64_encoded_result_str)
|
|
else:
|
|
im = im.transpose(Image.FLIP_TOP_BOTTOM)
|
|
gif_frames.append(genGIFFrame(im, 10 if mode == "loading-dead" else 128))
|
|
if mode in ("loading-fairy", "loading-dead"):
|
|
in_mem_file = io.BytesIO()
|
|
if mode == "loading-dead":
|
|
null_frame = Image.new(mode="RGBA", size=(32, 32))
|
|
gif_frames.append(genGIFFrame(im, 10))
|
|
gif_frames[0].save(
|
|
in_mem_file,
|
|
save_all=True,
|
|
append_images=gif_frames[1:],
|
|
duration=2 * len(gif_frames),
|
|
disposal=2,
|
|
format="GIF",
|
|
)
|
|
else:
|
|
gif_frames[0].save(
|
|
in_mem_file,
|
|
save_all=True,
|
|
append_images=gif_frames[1:],
|
|
loop=0,
|
|
duration=12 * len(gif_frames),
|
|
disposal=2,
|
|
format="GIF",
|
|
)
|
|
|
|
in_mem_file.seek(0)
|
|
img_bytes = in_mem_file.read()
|
|
|
|
base64_encoded_result_bytes = base64.b64encode(img_bytes)
|
|
base64_encoded_result_str = base64_encoded_result_bytes.decode("ascii")
|
|
loaded_images.append(base64_encoded_result_str)
|
|
return loaded_images
|