mirror of
https://git.planet-casio.com/Lephenixnoir/fxsdk.git
synced 2024-12-28 04:23:37 +01:00
fxconv: expose fxconv-metadata.txt parsing functions in API
This commit is contained in:
parent
6dae13007e
commit
4d46661d3b
2 changed files with 116 additions and 58 deletions
|
@ -3,8 +3,6 @@
|
|||
import getopt
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import fnmatch
|
||||
import fxconv
|
||||
import importlib.util
|
||||
|
||||
|
@ -54,52 +52,6 @@ try:
|
|||
except ImportError:
|
||||
converters = []
|
||||
|
||||
def parse_parameters(params):
|
||||
"""Parse parameters of the form "NAME:VALUE" into a dictionary."""
|
||||
d = dict()
|
||||
|
||||
def insert(d, path, value):
|
||||
if len(path) == 1:
|
||||
d[path[0]] = value
|
||||
else:
|
||||
if not path[0] in d:
|
||||
d[path[0]] = dict()
|
||||
insert(d[path[0]], path[1:], value)
|
||||
|
||||
for decl in params:
|
||||
if ":" not in decl:
|
||||
raise FxconvError(f"invalid parameter {decl}, ignoring")
|
||||
else:
|
||||
name, value = decl.split(":", 1)
|
||||
value = value.strip()
|
||||
if name == "name_regex":
|
||||
value = value.split(" ", 1)
|
||||
insert(d, name.split("."), value)
|
||||
|
||||
return d
|
||||
|
||||
def parse_parameters_metadata(contents):
|
||||
"""Parse parameters from a metadata file contents."""
|
||||
|
||||
RE_COMMENT = re.compile(r'#.*$', re.MULTILINE)
|
||||
contents = re.sub(RE_COMMENT, "", contents)
|
||||
|
||||
RE_WILDCARD = re.compile(r'^(\S(?:[^:\s]|\\:|\\ )*)\s*:\s*$', re.MULTILINE)
|
||||
lead, *elements = [ s.strip() for s in re.split(RE_WILDCARD, contents) ]
|
||||
|
||||
if lead:
|
||||
raise FxconvError(f"invalid metadata: {lead} appears before wildcard")
|
||||
|
||||
# Group elements by pairs (left: wildcard, right: list of properties)
|
||||
elements = list(zip(elements[::2], elements[1::2]))
|
||||
|
||||
metadata = []
|
||||
for (wildcard, params) in elements:
|
||||
params = [ s.strip() for s in params.split("\n") if s.strip() ]
|
||||
metadata.append((wildcard, parse_parameters(params)))
|
||||
|
||||
return metadata
|
||||
|
||||
def main():
|
||||
types = "binary image font bopti-image libimg-image custom"
|
||||
mode = ""
|
||||
|
@ -155,21 +107,15 @@ def main():
|
|||
# In automatic mode, look for information in fxconv-metadata.txt
|
||||
if mode == "":
|
||||
metadata_file = os.path.dirname(input) + "/fxconv-metadata.txt"
|
||||
basename = os.path.basename(input)
|
||||
|
||||
if not os.path.exists(metadata_file):
|
||||
return err(f"using auto mode but {metadata_file} does not exist")
|
||||
|
||||
with open(metadata_file, "r") as fp:
|
||||
metadata = parse_parameters_metadata(fp.read())
|
||||
metadata = fxconv.Metadata(path=metadata_file)
|
||||
params = metadata.rules_for(input)
|
||||
|
||||
params = dict()
|
||||
for (wildcard, p) in metadata:
|
||||
if fnmatch.fnmatchcase(basename, wildcard):
|
||||
params.update(**p)
|
||||
|
||||
if "section" in params:
|
||||
target["section"] = params["section"]
|
||||
if "section" in params:
|
||||
target["section"] = params["section"]
|
||||
|
||||
# In manual conversion modes, read parameters from the command-line
|
||||
else:
|
||||
|
|
112
fxconv/fxconv.py
112
fxconv/fxconv.py
|
@ -6,6 +6,7 @@ import os
|
|||
import tempfile
|
||||
import subprocess
|
||||
import collections
|
||||
import fnmatch
|
||||
import re
|
||||
|
||||
from PIL import Image
|
||||
|
@ -23,6 +24,8 @@ __all__ = [
|
|||
"convert_bopti_fx", "convert_bopti_cg",
|
||||
"convert_topti",
|
||||
"convert_libimg_fx", "convert_libimg_cg",
|
||||
# Meta API to use fxconv-metadata.txt files
|
||||
"Metadata",
|
||||
]
|
||||
|
||||
#
|
||||
|
@ -1349,3 +1352,112 @@ def elf(data, output, symbol, toolchain=None, arch=None, section=None,
|
|||
fp_obj.close()
|
||||
if assembly:
|
||||
fp_asm.close()
|
||||
|
||||
#
|
||||
# Meta API
|
||||
#
|
||||
|
||||
def _parse_parameters(params):
|
||||
"""Parse parameters of the form "NAME:VALUE" into a dictionary."""
|
||||
d = dict()
|
||||
|
||||
def insert(d, path, value):
|
||||
if len(path) == 1:
|
||||
d[path[0]] = value
|
||||
else:
|
||||
if not path[0] in d:
|
||||
d[path[0]] = dict()
|
||||
insert(d[path[0]], path[1:], value)
|
||||
|
||||
for decl in params:
|
||||
if ":" not in decl:
|
||||
raise FxconvError(f"invalid parameter {decl}, ignoring")
|
||||
else:
|
||||
name, value = decl.split(":", 1)
|
||||
value = value.strip()
|
||||
if name == "name_regex":
|
||||
value = value.split(" ", 1)
|
||||
insert(d, name.split("."), value)
|
||||
|
||||
return d
|
||||
|
||||
def _parse_metadata(contents):
|
||||
"""
|
||||
Parse the contents of an fxconv-metadata.txt file. Comments start with '#'
|
||||
anywhere on a line and extend to the end of the line.
|
||||
|
||||
The file is divided in blocks that start with a "<wildcard>:" pattern at
|
||||
the first column of a line (no leading spaces) followed by zero or more
|
||||
properties declared as "key: value" (with at least one leading space).
|
||||
|
||||
The key can contain dots (eg. "category.field"), in which case the value
|
||||
for the main component ("category") is itself a dictionary.
|
||||
"""
|
||||
|
||||
RE_COMMENT = re.compile(r'#.*$', re.MULTILINE)
|
||||
contents = re.sub(RE_COMMENT, "", contents)
|
||||
|
||||
RE_WILDCARD = re.compile(r'^(\S(?:[^:\s]|\\:|\\ )*)\s*:\s*$', re.MULTILINE)
|
||||
lead, *elements = [ s.strip() for s in re.split(RE_WILDCARD, contents) ]
|
||||
|
||||
if lead:
|
||||
raise FxconvError(f"invalid metadata: {lead} appears before wildcard")
|
||||
|
||||
# Group elements by pairs (left: wildcard, right: list of properties)
|
||||
elements = list(zip(elements[::2], elements[1::2]))
|
||||
|
||||
metadata = []
|
||||
for (wildcard, params) in elements:
|
||||
params = [ s.strip() for s in params.split("\n") if s.strip() ]
|
||||
metadata.append((wildcard, _parse_parameters(params)))
|
||||
|
||||
return metadata
|
||||
|
||||
class Metadata:
|
||||
def __init__(self, path=None, text=None):
|
||||
"""
|
||||
Load either an fxconv-metadata.txt file (if path is not None) or the
|
||||
contents of such a file (if text is not None).
|
||||
"""
|
||||
|
||||
if (path is not None) == (text is not None):
|
||||
raise ValueError("Metadata must have exactly one of path and text")
|
||||
|
||||
if path is not None:
|
||||
self._path = path
|
||||
with open(path, "r") as fp:
|
||||
self._rules = _parse_metadata(fp.read())
|
||||
elif text is not None:
|
||||
self._path = None
|
||||
self._rules = _parse_metadata(text)
|
||||
|
||||
def path(self):
|
||||
"""
|
||||
Returns the path of the file from which the metadata was parsed, or
|
||||
None if the metadata was parsed from string.
|
||||
"""
|
||||
return self._path
|
||||
|
||||
def rules(self):
|
||||
"""
|
||||
Returns a list of pairs (wildcard, rules) where the wildcard is a
|
||||
string and the rules are a nested dictionary.
|
||||
"""
|
||||
return self._rules
|
||||
|
||||
def rules_for(self, path):
|
||||
"""
|
||||
Returns the parameters that apply to the specified path, or None if no
|
||||
wildcard matches it.
|
||||
"""
|
||||
|
||||
basename = os.path.basename(path)
|
||||
params = dict()
|
||||
matched = False
|
||||
|
||||
for (wildcard, p) in self._rules:
|
||||
if fnmatch.fnmatchcase(basename, wildcard):
|
||||
params.update(**p)
|
||||
matched = True
|
||||
|
||||
return params if matched else None
|
||||
|
|
Loading…
Reference in a new issue