fxconv: PythonExtra support for bopti-fx

This commit is contained in:
Lephenixnoir 2024-02-01 17:46:30 +01:00
parent 975f29a471
commit 6e62fb7d6d
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
2 changed files with 54 additions and 4 deletions

View file

@ -23,6 +23,7 @@ When TYPE is specified (one-shot conversion), it should be one of:
--libimg-image Convert to the libimg image format --libimg-image Convert to the libimg image format
--custom Use a custom converter; you might want to specify an explicit --custom Use a custom converter; you might want to specify an explicit
type by adding "custom-type:your_custom_type" (see below) type by adding "custom-type:your_custom_type" (see below)
Custom converters can be specified by:
--converters Semicolon-separated list of custom converters (converters.py --converters Semicolon-separated list of custom converters (converters.py
in the current directory is detected as one per legacy) in the current directory is detected as one per legacy)
@ -31,8 +32,13 @@ syntax (names can contain dots). For example:
fxconv -f myfont.png -o myfont.o charset:ascii grid.padding:1 height:7 fxconv -f myfont.png -o myfont.o charset:ascii grid.padding:1 height:7
Some formats differ between platforms so you should specify it when possible: Some formats differ between platforms so you should specify it when possible:
--fx Casio fx-9860G family (black-and-white calculators) --fx CASIO fx-9860G family (black-and-white calculators)
--cg Casio fx-CG 50 family (16-bit color calculators) --cg CASIO fx-CG 50 family (16-bit color calculators)
Finally, there is some support (non-final) for PythonExtra, in which case the
output file is as Python file instead of an object file.
--py Convert for PythonExtra (some types supported)
--py-compact Use compact bytes notation (shorter, but non-printable)
""".strip() """.strip()
# Simple error-warnings system # Simple error-warnings system
@ -60,6 +66,7 @@ def main():
target = { 'toolchain': None, 'arch': None, 'section': None } target = { 'toolchain': None, 'arch': None, 'section': None }
use_custom = False use_custom = False
converter_paths = [] converter_paths = []
py = { 'enabled': False, 'compact': False }
# Parse command-line arguments # Parse command-line arguments
@ -69,7 +76,7 @@ def main():
try: try:
longs = ["help", "output=", "toolchain=", "arch=", "section=", "fx", longs = ["help", "output=", "toolchain=", "arch=", "section=", "fx",
"cg", "converters="] + types.split() "cg", "converters=", "py", "py-compact"] + types.split()
opts, args = getopt.gnu_getopt(sys.argv[1:], "hsbifo:", longs) opts, args = getopt.gnu_getopt(sys.argv[1:], "hsbifo:", longs)
except getopt.GetoptError as error: except getopt.GetoptError as error:
return err(error) return err(error)
@ -94,6 +101,10 @@ def main():
mode = "custom" mode = "custom"
elif name == "--converters": elif name == "--converters":
converter_paths = [path for path in value.split(";") if path] converter_paths = [path for path in value.split(";") if path]
elif name == "--py":
py['enabled'] = True
elif name == "--py-compact":
py['compact'] = True
# Other names are modes # Other names are modes
else: else:
mode = name[1] if len(name)==2 else name[2:] mode = name[1] if len(name)==2 else name[2:]
@ -144,6 +155,7 @@ def main():
spec.loader.exec_module(module) spec.loader.exec_module(module)
converters.append(module.convert) converters.append(module.convert)
params["py"] = py
fxconv.convert(input, params, target, output, model, converters) fxconv.convert(input, params, target, output, model, converters)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -513,6 +513,11 @@ def convert_bopti_fx(input, params):
data[n] = layer[4 * longword + i] data[n] = layer[4 * longword + i]
n += 1 n += 1
if params["py"]["enabled"]:
w, h = img.size
return ["import gint\n",
f"{params['name']} = gint.image({p.id}, {w}, {h}, ", data, ")\n"]
# Generate the object file # Generate the object file
o = ObjectData() o = ObjectData()
o += header o += header
@ -1155,8 +1160,12 @@ def convert(input, params, target, output=None, model=None, custom=None):
else: else:
raise FxconvError(f'unknown resource type \'{t}\'') raise FxconvError(f'unknown resource type \'{t}\'')
# PythonExtra conversion: output a file
if params["py"]["enabled"]:
pyout(o, output, params)
# Standard conversions: save to ELF in the natural way # Standard conversions: save to ELF in the natural way
elf(o, output, "_" + params["name"], **target) else:
elf(o, output, "_" + params["name"], **target)
def elf(data, output, symbol, toolchain=None, arch=None, section=None, def elf(data, output, symbol, toolchain=None, arch=None, section=None,
assembly=None): assembly=None):
@ -1308,6 +1317,35 @@ def elf_multi(vars, output, assembly=None, **kwargs):
return elf(None, output, None, assembly=asm, **kwargs) return elf(None, output, None, assembly=asm, **kwargs)
def pyout(bits, output, params):
# Compact into byte strings to avoid building tuples in the heap;
# MicroPython allows basically anything in literal strings (including
# NUL!), we just have to escape \, \n, \r, and ".
def byteify(c):
if c == ord('"'):
return b'\\"'
if c == ord('\n'):
return b'\\n'
if c == ord('\r'):
return b'\\r'
if c == ord('\\'):
return b'\\\\'
return bytes([c])
with open(output, "wb") as fp:
for section in bits:
if isinstance(section, bytearray):
section = bytes(section)
if isinstance(section, bytes):
if params["py"]["compact"]:
fp.write(b'b"')
fp.write(section)
fp.write(b'"')
else:
fp.write(repr(section).encode("utf-8"))
else:
fp.write(section.encode("utf-8"))
# #
# Meta API # Meta API
# #