Files
FlashGBX/FlashGBX/PocketCamera.py
Lesserkuma 1375159284 2.0
2021-05-01 00:03:56 +02:00

128 lines
3.9 KiB
Python

# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
from PIL import Image, ImageDraw
from PIL.PngImagePlugin import PngInfo
import os, hashlib
import email.utils
class PocketCamera:
DATA = None
PALETTES = [
[ 255, 255, 255, 176, 176, 176, 104, 104, 104, 0, 0, 0 ], # Grayscale
[ 208, 217, 60, 120, 164, 106, 84, 88, 84, 36, 70, 36 ], # Game Boy
#[ 196, 207, 161, 139, 149, 109, 77, 83, 60, 31, 31, 31 ], # Game Boy Pocket
[ 255, 255, 255, 181, 179, 189, 84, 83, 103, 9, 7, 19 ], # Super Game Boy
[ 240, 240, 240, 218, 196, 106, 112, 88, 52, 30, 30, 30 ], # Game Boy Color (JPN)
[ 240, 240, 240, 220, 160, 160, 136, 78, 78, 30, 30, 30 ], # Game Boy Color (USA Gold)
[ 240, 240, 240, 134, 200, 100, 58, 96, 132, 30, 30, 30 ], # Game Boy Color (USA/EUR)
]
PALETTE = [ 240, 240, 240, 218, 196, 106, 112, 88, 52, 30, 30, 30 ] # default
IMAGES = [None] * 31
IMAGES_DELETED = []
ORDER = None
def __init__(self):
pass
def LoadFile(self, savefile):
if os.path.getsize(savefile) != 128*1024: return False
with open(savefile, "rb") as file: self.DATA = file.read()
if self.DATA[0x1FFB1:0x1FFB6] != b'Magic':
self.DATA = None
return False
order_raw = self.DATA[0x11D7:0x11F5]
order = [None] * 30
deleted = []
for i in range(0, 30):
if order_raw[i] == 0xFF:
deleted.append(i)
else:
order[order_raw[i]] = i
while None in order: order.remove(None)
order.extend(deleted)
self.ORDER = order
self.IMAGES_DELETED = deleted
self.IMAGES[0] = self.ExtractGameFace()
for i in range(0, 30):
self.IMAGES[i+1] = self.ExtractPicture(i)
return True
def SetPalette(self, palette):
if isinstance(palette, int):
palette = self.PALETTES[palette]
for p in range (0, len(self.IMAGES)):
self.IMAGES[p].putpalette(palette)
self.PALETTE = palette
def GetPicture(self, index):
return self.IMAGES[index]
def IsEmpty(self, index):
return (hashlib.sha1(self.IMAGES[index].tobytes()).digest() == b'\xefX\xa8\x12\xa8\x1a\xb1EI\xd8\xf4\xfb\x86\xe9\xec\xb5J_\xb7#')
def IsDeleted(self, index):
index = self.ORDER[index-1]
return index in self.IMAGES_DELETED
def ConvertPicture(self, buffer):
tile_width = 16
tile_height = 14
img = Image.new(mode='P', size=(128, 112))
img.putpalette(self.PALETTE)
pixels = img.load()
for h in range(tile_height):
for w in range(tile_width):
tile_pos = 16 * ((h * tile_width) + w)
tile = buffer[tile_pos:tile_pos+16]
for i in range(8):
for j in range(8):
hi = (tile[i * 2] >> (7 - j)) & 1
lo = (tile[i * 2 + 1] >> (7 - j)) & 1
pixels[(w * 8) + j, (h * 8) + i] = (lo << 1 | hi)
return img
def ExtractGameFace(self):
offset = 0x11FC
imgbuffer = self.DATA[offset:offset+0x1000]
return self.ConvertPicture(imgbuffer)
def ExtractPicture(self, index):
index = self.ORDER[index]
offset = 0x2000 + (index * 0x1000)
imgbuffer = self.DATA[offset:offset+0x1000]
return self.ConvertPicture(imgbuffer)
def ExportPicture(self, index, path):
pnginfo = PngInfo()
pnginfo.add_text("Software", "FlashGBX")
pnginfo.add_text("Source", "Pocket Camera")
pnginfo.add_text("Creation Time", email.utils.formatdate())
if index == 0:
pic = self.GetPicture(0)
pnginfo.add_text("Title", "Game Face")
else:
pic = self.GetPicture(index)
pnginfo.add_text("Title", "Photo {:02d}".format(index))
ext = os.path.splitext(path)[1]
if ext.lower() == ".png":
outpic = pic
outpic.save(path, pnginfo=pnginfo)
elif ext.lower() == ".gif":
outpic = pic
outpic.save(path)
elif ext.lower() in (".jpg", ".jpeg"):
outpic = pic.convert("RGB")
outpic.save(path, quality=100, subsampling=0)
else:
outpic = pic.convert("RGB")
outpic.save(path)