132 lines
4.2 KiB
Python
132 lines
4.2 KiB
Python
"""
|
|
g3a_generator - Casio G1A file generator
|
|
"""
|
|
import os
|
|
import sys
|
|
|
|
from datetime import datetime
|
|
from PIL import Image
|
|
|
|
#---
|
|
# Internals
|
|
#---
|
|
|
|
def _u32(ptr, idx, data):
|
|
""" unsigned 32bits wrapper """
|
|
ptr[idx + 0] = (data & 0xff000000) >> 24
|
|
ptr[idx + 1] = (data & 0x00ff0000) >> 16
|
|
ptr[idx + 2] = (data & 0x0000ff00) >> 8
|
|
ptr[idx + 3] = (data & 0x000000ff) >> 0
|
|
|
|
def _u16(ptr, idx, data):
|
|
""" unsigned 16bits injection """
|
|
ptr[idx + 0] = (data & 0xff00) >> 8
|
|
ptr[idx + 1] = (data & 0x00ff) >> 0
|
|
|
|
def _u08(ptr, idx, data):
|
|
""" unsigned 8bits injection """
|
|
ptr[idx + 0] = (data & 0xff) >> 0
|
|
|
|
def _str(ptr, idx, data):
|
|
""" string copy """
|
|
for i, j in enumerate(data):
|
|
_u08(ptr, idx + i, ord(j))
|
|
|
|
def _generate_standar_header(addin):
|
|
""" generate Casio file standard header """
|
|
_str(addin, 0x000, "USBPower") # watermark
|
|
_u08(addin, 0x008, 0xf3) # addin file type
|
|
_u08(addin, 0x009, 0x00) # watermark
|
|
_u08(addin, 0x00a, 0x01) # watermark
|
|
_u08(addin, 0x00b, 0x00) # watermark
|
|
_u08(addin, 0x00c, 0x01) # watermark
|
|
_u08(addin, 0x00d, 0x00) # watermark
|
|
_u08(addin, 0x00e, 0x00) # LSB of file size + 0x41 (post)
|
|
_u08(addin, 0x00f, 0x01) # watermark
|
|
_u32(addin, 0x010, 0x00000000) # file size (MSB) (post)
|
|
_u08(addin, 0x014, 0x00) # LSB of file size + 0xb8 (post)
|
|
_u16(addin, 0x016, 0x0000) # sum of 8 words starting at 0x7100
|
|
|
|
def _generate_addin_subheader(addin):
|
|
""" generate the g1a addin subheader """
|
|
_str(addin, 0x020, "@VXGOS ") # checksum (post)
|
|
_u08(addin, 0x02b, 0x00) # number of estrips
|
|
_str(addin, 0x030, "01.00.0000") # addin version
|
|
_str(addin, 0x03c, datetime.now().strftime("%Y.%m%d.%M%S")) # addin date
|
|
|
|
for i in range(0, 44):
|
|
_u08(addin, 0x04c + i, 0x00) # icon
|
|
|
|
_str(addin, 0x1d4, "VXGOS ") # add name
|
|
_u32(addin, 0x002e, 0x00000000) # filesize (post)
|
|
|
|
def _generate_addin_icon(addin, icon_path):
|
|
""" encode mono icon in addin blob """
|
|
with Image.open(icon_path) as icon:
|
|
if icon.width != 30 and icon.height != 19:
|
|
print(f"{icon_path}: image size should be 30x19 pixels, ignored")
|
|
return
|
|
pixels = icon.getdata()
|
|
for _y in range(1, 18):
|
|
for _x in range(0, 30):
|
|
if pixels[(_y * 30) + _x] != (0, 0, 0, 255):
|
|
continue
|
|
_idx = 0x4c + ((_y * 4) + int((_x / 8)))
|
|
_u08(addin, _idx, addin[_idx] | (0x80 >> (_x & 7)))
|
|
|
|
|
|
def _generate_checksums(addin):
|
|
""" generate all checksums requested for the g3a format """
|
|
filesize = len(addin)
|
|
|
|
# standard header checksum
|
|
_u08(addin, 0x010, (filesize & 0xff000000) >> 24)
|
|
_u08(addin, 0x011, (filesize & 0x00ff0000) >> 16)
|
|
_u08(addin, 0x012, (filesize & 0x0000ff00) >> 8)
|
|
_u08(addin, 0x013, (filesize & 0x000000ff) >> 0)
|
|
_u08(addin, 0x00e, addin[0x013] + 0x41)
|
|
_u08(addin, 0x014, addin[0x013] + 0xb8)
|
|
checksum = 0x00000
|
|
for i in range(0, 8):
|
|
checksum += int(
|
|
addin[0x300 + (i * 2) : 0x302 + (i * 2)].hex(),
|
|
base=16
|
|
)
|
|
_u16(addin, 0x016, checksum)
|
|
|
|
# addin-specific header checksum
|
|
_u08(addin, 0x1f0, (filesize & 0xff000000) >> 24)
|
|
_u08(addin, 0x1f1, (filesize & 0x00ff0000) >> 16)
|
|
_u08(addin, 0x1f2, (filesize & 0x0000ff00) >> 8)
|
|
_u08(addin, 0x1f3, (filesize & 0x000000ff) >> 0)
|
|
|
|
def _main(argv):
|
|
""" main entry of the project """
|
|
if len(argv) != 3:
|
|
print('missing argument (./g1a_generator <output> <rawbin> <icon>)')
|
|
sys.exit(84)
|
|
|
|
rawbin = b''
|
|
with open(argv[1], 'rb') as rawfile:
|
|
rawbin = rawfile.read()
|
|
|
|
addin = bytearray(0x200)
|
|
_generate_standar_header(addin)
|
|
_generate_addin_subheader(addin)
|
|
_generate_addin_icon(addin, argv[2])
|
|
addin += rawbin
|
|
_generate_checksums(addin)
|
|
|
|
if os.path.exists(argv[0]):
|
|
os.remove(argv[0])
|
|
with open(argv[0], 'xb') as addinfile:
|
|
addinfile.write(addin)
|
|
return 0
|
|
|
|
#---
|
|
# Public
|
|
#---
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(_main(sys.argv[1:]))
|