From 4ebe7a83cb2f5a792867bbf02568d7c20096466a Mon Sep 17 00:00:00 2001 From: Francis Herne Date: Sat, 31 Oct 2020 18:32:47 +0000 Subject: [PATCH] WIP: handle labels more readably --- nml/actions/action0.py | 10 +-- nml/actions/action0properties.py | 5 +- nml/actions/action11.py | 4 +- nml/actions/action2var.py | 2 +- nml/actions/action7.py | 22 +++---- nml/actions/actionD.py | 12 +--- nml/actions/actionE.py | 2 +- nml/ast/cargotable.py | 2 +- nml/ast/deactivate.py | 3 +- nml/ast/grf.py | 6 +- nml/ast/override.py | 12 ++-- nml/ast/tracktypetable.py | 4 +- nml/expression/__init__.py | 3 +- nml/expression/functioncall.py | 44 +++++++------ nml/expression/label.py | 65 +++++++++++++++++++ nml/expression/parameter.py | 37 +---------- nml/expression/storage_op.py | 4 +- nml/global_constants.py | 2 +- regression/expected/002_sounds.nfo | 4 +- regression/expected/004_deactivate.nfo | 6 +- .../expected/014_read_special_param.nfo | 2 +- regression/expected/023_engine_override.nfo | 4 +- regression/expected/030_house.nfo | 4 +- regression/expected/example_railtype.nfo | 2 +- regression/expected/example_road_vehicle.nfo | 16 ++--- 25 files changed, 155 insertions(+), 122 deletions(-) create mode 100644 nml/expression/label.py diff --git a/nml/actions/action0.py b/nml/actions/action0.py index d5918911..393faeaf 100644 --- a/nml/actions/action0.py +++ b/nml/actions/action0.py @@ -722,11 +722,11 @@ def get_tracktypelist_action(table_prop_id, cond_tracktype_not_defined, tracktyp id_table.append(tracktype) offset+=4 continue - param, extra_actions = actionD.get_tmp_parameter(expression.ConstantNumeric(expression.parse_string_to_dword(tracktype[-1]))) + param, extra_actions = actionD.get_tmp_parameter(expression.Label(tracktype[-1])) action_list.extend(extra_actions) for idx in range(len(tracktype)-2, -1, -1): - val = expression.ConstantNumeric(expression.parse_string_to_dword(tracktype[idx])) - action_list.append(action7.SkipAction(0x09, 0x00, 4, (cond_tracktype_not_defined, None), val.value, 1)) + val = expression.Label(tracktype[idx]) + action_list.append(action7.SkipAction(0x09, 0x00, 4, (cond_tracktype_not_defined, None), val, 1)) action_list.append(actionD.ActionD(expression.ConstantNumeric(param), expression.ConstantNumeric(0xFF), nmlop.ASSIGN, expression.ConstantNumeric(0xFF), val)) act6.modify_bytes(param, 4, offset) id_table.append(expression.StringLiteral(r"\00\00\00\00", None)) @@ -958,8 +958,8 @@ def __init__(self, source, target): def write(self, file): file.print_bytex(0x11) - file.print_dwordx(self.source) - file.print_dwordx(self.target) + self.source.write(file, 4) + self.target.write(file, 4) file.newline() def get_size(self): diff --git a/nml/actions/action0properties.py b/nml/actions/action0properties.py index 399a7282..38ac9345 100644 --- a/nml/actions/action0properties.py +++ b/nml/actions/action0properties.py @@ -16,7 +16,7 @@ import itertools from nml import generic, nmlop from nml.expression import (BinOp, ConstantNumeric, ConstantFloat, Array, StringLiteral, - Identifier, ProduceCargo, AcceptCargo, parse_string_to_dword) + Identifier, ProduceCargo, AcceptCargo, Label) tilelayout_names = {} @@ -1036,8 +1036,7 @@ def write(self, file): file.print_bytex(self.prop_num) file.print_byte(len(self.labels)) for label in self.labels: - parse_string_to_dword(label) # Error if the wrong length or not ASCII - label.write(file, 4) + Label(label).write(file, 4) file.newline() def get_size(self): diff --git a/nml/actions/action11.py b/nml/actions/action11.py index 15f08b87..1120e3ae 100644 --- a/nml/actions/action11.py +++ b/nml/actions/action11.py @@ -58,7 +58,7 @@ class ImportSound(base_action.BaseAction): * FE 00 @ivar grfid: ID of the other grf. - @type grfid: C{int} + @type grfid: L{Label} @ivar number: Sound number to load. @type number: C{int} @@ -76,7 +76,7 @@ def write(self, file): file.start_sprite(8) file.print_bytex(0xfe) file.print_bytex(0) - file.print_dwordx(self.grfid) + self.grfid.write(file, 4) file.print_wordx(self.number) file.end_sprite() if self.last: file.newline() diff --git a/nml/actions/action2var.py b/nml/actions/action2var.py index 4d4763f1..4d923c36 100644 --- a/nml/actions/action2var.py +++ b/nml/actions/action2var.py @@ -432,7 +432,7 @@ def preprocess_storageop(self, expr): if expr.info['perm'] and self.feature == 0x08: # store grfid in register 0x100 for town persistent storage - grfid = expression.ConstantNumeric(0xFFFFFFFF if expr.grfid is None else expression.parse_string_to_dword(expr.grfid)) + grfid = expression.ConstantNumeric(0xFFFFFFFF) if expr.grfid is None else expression.Label(expr.grfid) store_op = nmlop.STO_TMP(grfid, 0x100, expr.pos) ret = nmlop.VAL2(store_op, ret) elif expr.grfid is not None: diff --git a/nml/actions/action7.py b/nml/actions/action7.py index 5dea2b98..7a82ac1c 100644 --- a/nml/actions/action7.py +++ b/nml/actions/action7.py @@ -46,10 +46,10 @@ def write(self, file): file.print_bytex(self.condtype[0], self.condtype[1]) if self.varsize == 8: #grfid + mask - file.print_dwordx(self.value & 0xFFFFFFFF) - file.print_dwordx(self.value >> 32) + file.print_dwordx(self.value.value & 0xFFFFFFFF) + file.print_dwordx(self.value.value >> 32) else: - file.print_varx(self.value, self.varsize) + self.value.write(file, self.varsize) file.print_bytex(self.label) file.newline() file.end_sprite() @@ -62,7 +62,7 @@ def skip_action9(self): class UnconditionalSkipAction(SkipAction): def __init__(self, action_type, label): - SkipAction.__init__(self, action_type, 0x9A, 1, (0, r'\71'), 0, label) + SkipAction.__init__(self, action_type, 0x9A, 1, (0, r'\71'), expression.ConstantNumeric(0), label) def op_to_cond_op(op): #The operators are reversed as we want to skip if the expression is true @@ -84,7 +84,7 @@ def parse_conditional(expr): - The size of the value (as integer) ''' if expr is None: - return (None, [], (2, r'\7='), 0, 4) + return (None, [], (2, r'\7='), expression.ConstantNumeric(0), 4) if isinstance(expr, expression.BinOp): if expr.op == nmlop.HASBIT or expr.op == nmlop.NOTHASBIT: if isinstance(expr.expr1, expression.Parameter) and isinstance(expr.expr1.num, expression.ConstantNumeric): @@ -93,7 +93,7 @@ def parse_conditional(expr): else: param, actions = actionD.get_tmp_parameter(expr.expr1) if isinstance(expr.expr2, expression.ConstantNumeric): - bit_num = expr.expr2.value + bit_num = expr.expr2 else: if isinstance(expr.expr2, expression.Parameter) and isinstance(expr.expr2.num, expression.ConstantNumeric): param = expr.expr2.num.value @@ -103,7 +103,7 @@ def parse_conditional(expr): act6 = action6.Action6() act6.modify_bytes(param, 1, 4) actions.append(act6) - bit_num = 0 + bit_num = expression.ConstantNumeric(0) comp_type = (1, r'\70') if expr.op == nmlop.HASBIT else (0, r'\71') return (param, actions, comp_type, bit_num , 1) elif expr.op in (nmlop.CMP_EQ, nmlop.CMP_NEQ, nmlop.CMP_LE, nmlop.CMP_GE) \ @@ -114,17 +114,17 @@ def parse_conditional(expr): else: param, actions = actionD.get_tmp_parameter(expr.expr1) op = op_to_cond_op(expr.op) - return (param, actions, op, expr.expr2.value, 4) + return (param, actions, op, expr.expr2, 4) if isinstance(expr, expression.Boolean): expr = expr.expr if isinstance(expr, expression.Not): param, actions = actionD.get_tmp_parameter(expr.expr) - return (param, actions, (3, r'\7!'), 0, 4) + return (param, actions, (3, r'\7!'), expression.ConstantNumeric(0), 4) param, actions = actionD.get_tmp_parameter(expr) - return (param, actions, (2, r'\7='), 0, 4) + return (param, actions, (2, r'\7='), expression.ConstantNumeric(0), 4) def cond_skip_actions(action_list, param, condtype, value, value_size, pos): if len(action_list) == 0: return [] @@ -239,7 +239,7 @@ def parse_conditional_block(cond_list): param = block['param_dst'] if i == 0: action_list.extend(block['cond_actions']) else: - action_list.extend(cond_skip_actions(block['cond_actions'], param_skip_all, (2, r'\7='), 0, 4, cond_list.pos)) + action_list.extend(cond_skip_actions(block['cond_actions'], param_skip_all, (2, r'\7='), expression.ConstantNumeric(0), 4, cond_list.pos)) if param is None: param = param_skip_all else: diff --git a/nml/actions/actionD.py b/nml/actions/actionD.py index bb152533..32aa504a 100644 --- a/nml/actions/actionD.py +++ b/nml/actions/actionD.py @@ -177,15 +177,7 @@ def parse_special_check(assignment): check = assignment.value assert isinstance(check, expression.SpecialCheck) actions = parse_actionD(ParameterAssignment(assignment.param, expression.ConstantNumeric(check.results[0]))) - - value = check.value - if check.mask is not None: - value &= check.mask - value += check.mask << 32 - assert check.varsize == 8 - else: - assert check.varsize <= 4 - actions.append(nml.actions.action7.SkipAction(9, check.varnum, check.varsize, check.op, value, 1)) + actions.append(nml.actions.action7.SkipAction(9, check.varnum, check.varsize, check.op, check.value, 1)) actions.extend(parse_actionD(ParameterAssignment(assignment.param, expression.ConstantNumeric(check.results[1])))) return actions @@ -396,7 +388,7 @@ def parse_actionD(assignment): action_list.extend(tmp_param_actions) param1 = expression.ConstantNumeric(0) param2 = expression.ConstantNumeric(0xFE) - data = expression.ConstantNumeric(expression.parse_string_to_dword(assignment.value.grfid)) + data = expression.Label(assignment.value.grfid) elif isinstance(assignment.value, expression.PatchVariable): op = nmlop.ASSIGN param1 = expression.ConstantNumeric(assignment.value.num) diff --git a/nml/actions/actionE.py b/nml/actions/actionE.py index 2f97035f..0dc33b91 100644 --- a/nml/actions/actionE.py +++ b/nml/actions/actionE.py @@ -26,7 +26,7 @@ def write(self, file): file.print_byte(len(self.grfid_list)) for grfid in self.grfid_list: file.newline() - file.print_dwordx(grfid) + grfid.write(file, 4) file.newline() file.end_sprite() diff --git a/nml/ast/cargotable.py b/nml/ast/cargotable.py index 658f7cfe..349e1da2 100644 --- a/nml/ast/cargotable.py +++ b/nml/ast/cargotable.py @@ -27,7 +27,7 @@ def register_names(self): for i, cargo in enumerate(self.cargo_list): if isinstance(cargo, expression.Identifier): self.cargo_list[i] = expression.StringLiteral(cargo.value, cargo.pos) - expression.parse_string_to_dword(self.cargo_list[i]) # we don't care about the result, only validate the input + expression.Label(self.cargo_list[i]) # we don't care about the result, only validate the input if self.cargo_list[i].value in global_constants.cargo_numbers: generic.print_warning("Duplicate entry in cargo table: {}".format(self.cargo_list[i].value), cargo.pos) else: diff --git a/nml/ast/deactivate.py b/nml/ast/deactivate.py index 3909f63b..f8263d81 100644 --- a/nml/ast/deactivate.py +++ b/nml/ast/deactivate.py @@ -23,8 +23,7 @@ def __init__(self, grfid_list, pos): self.grfid_list = grfid_list def pre_process(self): - # Parse (string-)expressions to integers - self.grfid_list = [expression.parse_string_to_dword(grfid.reduce()) for grfid in self.grfid_list] + self.grfid_list = [expression.Label(grfid.reduce()) for grfid in self.grfid_list] def debug_print(self, indentation): generic.print_dbg(indentation, 'Deactivate other newgrfs:') diff --git a/nml/ast/grf.py b/nml/ast/grf.py index 2cdc41de..f38a2e34 100644 --- a/nml/ast/grf.py +++ b/nml/ast/grf.py @@ -27,6 +27,9 @@ """ param_stats = [0, 0x40] +"""The grfid set in the `grf` block, if present""" +GRFID = None + def print_stats(): """ Print statistics about used ids. @@ -105,7 +108,8 @@ def pre_process(self): raise generic.ScriptError("A GRF-block requires the 'name', 'desc', 'grfid', 'version' and 'min_compatible_version' properties to be set.", self.pos) self.grfid = self.grfid.reduce() - global_constants.constant_numbers['GRFID'] = expression.parse_string_to_dword(self.grfid) + global GRFID + GRFID = expression.Label(self.grfid) self.name = self.name.reduce() if not isinstance(self.name, expression.String): raise generic.ScriptError("GRF-name must be a string", self.name.pos) diff --git a/nml/ast/override.py b/nml/ast/override.py index 5b6e0a93..2c3a5ca7 100644 --- a/nml/ast/override.py +++ b/nml/ast/override.py @@ -14,7 +14,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.""" from nml import generic, expression, global_constants -from nml.ast import base_statement +from nml.ast import base_statement, grf from nml.actions import action0 class EngineOverride(base_statement.BaseStatement): @@ -36,15 +36,13 @@ def pre_process(self): raise generic.ScriptError("engine_override expects 1 or 2 parameters", self.pos) if len(self.args) == 1: - try: - self.source_grfid = expression.Identifier('GRFID').reduce(global_constants.const_list).value - assert isinstance(self.source_grfid, int) - except generic.ScriptError: + self.source_grfid = grf.GRFID + if self.source_grfid is None: raise generic.ScriptError("GRFID of this grf is required, but no grf-block is defined.", self.pos) else: - self.source_grfid = expression.parse_string_to_dword(self.args[0].reduce(global_constants.const_list)) + self.source_grfid = expression.Label(self.args[0].reduce(global_constants.const_list)) - self.grfid = expression.parse_string_to_dword(self.args[-1].reduce(global_constants.const_list)) + self.grfid = expression.Label(self.args[-1].reduce(global_constants.const_list)) def debug_print(self, indentation): generic.print_dbg(indentation, 'Engine override') diff --git a/nml/ast/tracktypetable.py b/nml/ast/tracktypetable.py index a3f3e2d8..b31edd42 100644 --- a/nml/ast/tracktypetable.py +++ b/nml/ast/tracktypetable.py @@ -37,13 +37,13 @@ def register_names(self): val_list.append(expression.StringLiteral(rt.value, rt.pos)) else: val_list.append(rt) - expression.parse_string_to_dword(val_list[-1]) # we don't care about the result, only validate the input + expression.Label(val_list[-1]) # we don't care about the result, only validate the input self.tracktype_list[i] = val_list if len(val_list) > 1 else val_list[0] else: name = tracktype if isinstance(tracktype, expression.Identifier): self.tracktype_list[i] = expression.StringLiteral(tracktype.value, tracktype.pos) - expression.parse_string_to_dword(self.tracktype_list[i]) # we don't care about the result, only validate the input + expression.Label(self.tracktype_list[i]) # we don't care about the result, only validate the input self.tracktype_table[name.value] = i def pre_process(self): diff --git a/nml/expression/__init__.py b/nml/expression/__init__.py index 1a89d6e2..782390a1 100644 --- a/nml/expression/__init__.py +++ b/nml/expression/__init__.py @@ -25,8 +25,9 @@ from .functioncall import FunctionCall, SpecialCheck, GRMOp from .functionptr import FunctionPtr from .identifier import Identifier +from .label import Label from .patch_variable import PatchVariable -from .parameter import Parameter, OtherGRFParameter, parse_string_to_dword +from .parameter import Parameter, OtherGRFParameter from .special_parameter import SpecialParameter from .spritegroup_ref import SpriteGroupRef from .storage_op import StorageOp diff --git a/nml/expression/functioncall.py b/nml/expression/functioncall.py index f9bc22ab..ab2062b7 100644 --- a/nml/expression/functioncall.py +++ b/nml/expression/functioncall.py @@ -22,7 +22,7 @@ from .binop import BinOp from .bitmask import BitMask from .cargo import ProduceCargo, AcceptCargo -from .parameter import parse_string_to_dword +from .label import Label from .storage_op import StorageOp from .string_literal import StringLiteral from .ternaryop import TernaryOp @@ -83,7 +83,7 @@ class SpecialCheck(Expression): @type results: (C{int}, C{int})-tuple @ivar value: Value to test - @type value: C{int} + @type value: L{ConstantNumeric} @ivar varsize: Varsize for the action7/9 check @type varsize: C{int} @@ -96,13 +96,17 @@ class SpecialCheck(Expression): """ def __init__(self, op, varnum, results, value, to_string, varsize = 4, mask = None, pos = None): Expression.__init__(self, pos) + if mask is not None: + value = ConstantNumeric((value.value & mask) + (mask << 32), value.pos) + assert varsize == 8 + else: + assert varsize <= 4 self.op = op self.varnum = varnum self.results = results self.value = value self.to_string = to_string self.varsize = varsize; - self.mask = mask def reduce(self, id_dicts = [], unknown_id_fatal = True): return self @@ -290,8 +294,8 @@ def builtin_cargotype_available(name, args, pos): """ if len(args) != 1: raise generic.ScriptError(name + "() must have exactly 1 parameter", pos) - label = args[0].reduce() - return SpecialCheck((0x0B, r'\7c'), 0, (0, 1), parse_string_to_dword(label), "{}({})".format(name, str(label)), pos = args[0].pos) + label = Label(args[0].reduce()) + return SpecialCheck((0x0B, r'\7c'), 0, (0, 1), label, "{}({})".format(name, label), pos=args[0].pos) def builtin_railtype_available(name, args, pos): """ @@ -301,8 +305,8 @@ def builtin_railtype_available(name, args, pos): """ if len(args) != 1: raise generic.ScriptError(name + "() must have exactly 1 parameter", pos) - label = args[0].reduce() - return SpecialCheck((0x0D, None), 0, (0, 1), parse_string_to_dword(label), "{}({})".format(name, str(label)), pos = args[0].pos) + label = Label(args[0].reduce()) + return SpecialCheck((0x0D, None), 0, (0, 1), label, "{}({})".format(name, label), pos=args[0].pos) def builtin_roadtype_available(name, args, pos): """ @@ -312,8 +316,8 @@ def builtin_roadtype_available(name, args, pos): """ if len(args) != 1: raise generic.ScriptError(name + "() must have exactly 1 parameter", pos) - label = args[0].reduce() - return SpecialCheck((0x0F, None), 0, (0, 1), parse_string_to_dword(label), "{}({})".format(name, str(label)), pos = args[0].pos) + label = Label(args[0].reduce()) + return SpecialCheck((0x0F, None), 0, (0, 1), label, "{}({})".format(name, label), pos=args[0].pos) def builtin_tramtype_available(name, args, pos): """ @@ -323,8 +327,8 @@ def builtin_tramtype_available(name, args, pos): """ if len(args) != 1: raise generic.ScriptError(name + "() must have exactly 1 parameter", pos) - label = args[0].reduce() - return SpecialCheck((0x11, None), 0, (0, 1), parse_string_to_dword(label), "{}({})".format(name, str(label)), pos = args[0].pos) + label = Label(args[0].reduce()) + return SpecialCheck((0x11, None), 0, (0, 1), label, "{}({})".format(name, label), pos=args[0].pos) def builtin_grf_status(name, args, pos): """ @@ -334,8 +338,6 @@ def builtin_grf_status(name, args, pos): """ if len(args) not in (1, 2): raise generic.ScriptError(name + "() must have 1 or 2 parameters", pos) - labels = [label.reduce() for label in args] - mask = parse_string_to_dword(labels[1]) if len(labels) > 1 else None if name == 'grf_current_status': op = (0x06, r'\7G') results = (1, 0) @@ -347,13 +349,17 @@ def builtin_grf_status(name, args, pos): results = (0, 1) else: assert False, "Unknown grf status function" - if mask is None: - string = "{}({})".format(name, str(labels)) + + label = Label(args[0].reduce()) + mask = None + if len(args) == 1: + string = "{}({})".format(name, label) varsize = 4 else: - string = "{}({}, {})".format(name, str(labels), str(mask)) + mask = Label(args[1]).value + string = "{}({}, {})".format(name, label, mask) varsize = 8 - return SpecialCheck(op, 0x88, results, parse_string_to_dword(labels[0]), string, varsize, mask, args[0].pos) + return SpecialCheck(op, 0x88, results, label, string, varsize, mask, label.pos) def builtin_visual_effect_and_powered(name, args, pos): """ @@ -404,7 +410,7 @@ def builtin_create_effect(name, args, pos): def builtin_str2number(name, args, pos): if len(args) != 1: raise generic.ScriptError(name + "() must have 1 parameter", pos) - return ConstantNumeric(parse_string_to_dword(args[0])) + return Label(args[0]) def builtin_cargotype(name, args, pos): if len(args) != 1: @@ -519,7 +525,7 @@ def builtin_sound_file(name, args, pos): def builtin_sound_import(name, args, pos): if len(args) not in (2, 3): raise generic.ScriptError(name + "() must have 2 or 3 parameters", pos) - grfid = parse_string_to_dword(args[0].reduce()) + grfid = Label(args[0].reduce()) sound_num = args[1].reduce_constant().value volume = args[2].reduce_constant().value if len(args) >= 3 else 100 generic.check_range(volume, 0, 100, "sound volume", pos) diff --git a/nml/expression/label.py b/nml/expression/label.py new file mode 100644 index 00000000..947bc9d4 --- /dev/null +++ b/nml/expression/label.py @@ -0,0 +1,65 @@ +from nml import generic, grfstrings + +from .base_expression import ConstantNumeric +from .string_literal import StringLiteral +from .identifier import Identifier + +class Label(ConstantNumeric): # lgtm[py/missing-equals] + """ + A 4-byte string (e.g. grfids, cargo/railtype labels) that's equivalent to a dword. + + This allows us to produce more-readable NFO when the value isn't modified, without + upsetting code that expects a ConstantNumeric. + + (lgtm tag: ConstantNumeric's __eq__ ignores string_value, which is intended) + """ + def __init__(self, value, pos=None): + if isinstance(value, (Identifier, StringLiteral)): + pos = pos or value.pos + value = value.value + super().__init__(parse_string_to_dword(value, pos), pos) + self.string_value = value + + def debug_print(self, indentation): + generic.print_dbg(indentation, 'Label: "{}"'.format(self.string_value)) + + def __str__(self): + return '"{}"'.format(self.string_value) + + def write(self, file, size): + assert size == 4 + file.print_string(self.string_value, final_zero=False, force_ascii=True) + + +def parse_string_to_dword(string, pos): + """ + Convert a 4-byte string to its equivalent 32 bit number. + + @param string: String to convert. + @type string: C{str} + + @param pos: Position of the original string in the file + @type pos: L{Position} or C{None} + + @return: Value of the converted expression (a 32 bit integer number, little endian). + @rtype: C{int} + """ + + # FIXME get_string_size is all wrong, and there must be a tidier way to solve this + if grfstrings.get_string_size(string, final_zero=False, force_ascii=True) != 4: + raise generic.ScriptError("Expected a string literal of length 4", pos) + + bytes = [] + i = 0 + try: + while len(bytes) < 4: + if string[i] == '\\': + bytes.append(int(string[i+1:i+3], 16)) + i += 3 + else: + bytes.append(ord(string[i])) + i += 1 + except ValueError: + raise generic.ScriptError("Cannot convert string to integer id", pos) + + return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24) diff --git a/nml/expression/parameter.py b/nml/expression/parameter.py index 7086d94e..8be898df 100644 --- a/nml/expression/parameter.py +++ b/nml/expression/parameter.py @@ -13,9 +13,9 @@ with NML; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.""" -from nml import generic, grfstrings +from nml import generic from .base_expression import Type, Expression, ConstantNumeric -from .string_literal import StringLiteral +from .label import Label class Parameter(Expression): def __init__(self, num, pos = None, by_user = False): @@ -71,8 +71,7 @@ def __str__(self): def reduce(self, id_dicts = [], unknown_id_fatal = True): grfid = self.grfid.reduce(id_dicts) - #Test validity - parse_string_to_dword(grfid) + Label(grfid) # Test validity num = self.num.reduce(id_dicts) if num.type() != Type.INTEGER: raise generic.ScriptError("Parameter number must be an integer.", num.pos) @@ -85,33 +84,3 @@ def supported_by_action2(self, raise_error): def supported_by_actionD(self, raise_error): return True - -def parse_string_to_dword(string): - """ - Convert string literal expression of length 4 to it's equivalent 32 bit number. - - @param string: Expression to convert. - @type string: L{Expression} - - @return: Value of the converted expression (a 32 bit integer number, little endian). - @rtype: C{int} - """ - if not isinstance(string, StringLiteral) or grfstrings.get_string_size(string.value, False, True) != 4: - raise generic.ScriptError("Expected a string literal of length 4", string.pos) - - pos = string.pos - string = string.value - bytes = [] - i = 0 - try: - while len(bytes) < 4: - if string[i] == '\\': - bytes.append(int(string[i+1:i+3], 16)) - i += 3 - else: - bytes.append(ord(string[i])) - i += 1 - except ValueError: - raise generic.ScriptError("Cannot convert string to integer id", pos) - - return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24) diff --git a/nml/expression/storage_op.py b/nml/expression/storage_op.py index ba5d2598..e4396470 100644 --- a/nml/expression/storage_op.py +++ b/nml/expression/storage_op.py @@ -15,7 +15,7 @@ from nml import generic from .base_expression import ConstantNumeric, Expression, Type -from .parameter import parse_string_to_dword +from .label import Label storage_op_info = { 'STORE_PERM' : {'store': True, 'perm': True, 'grfid': False, 'max': 0xFF}, @@ -109,7 +109,7 @@ def reduce(self, id_dicts = [], unknown_id_fatal = True): if self.grfid is not None: grfid = self.grfid.reduce(id_dicts) # Test validity - parse_string_to_dword(grfid) + Label(grfid) args.append(grfid) return StorageOp(self.name, args, self.pos) diff --git a/nml/global_constants.py b/nml/global_constants.py index 32715b61..acec818b 100644 --- a/nml/global_constants.py +++ b/nml/global_constants.py @@ -1167,7 +1167,7 @@ def patch_variable(name, info, pos): } def config_flag_read(bit, pos): - return expression.SpecialCheck((0x01, r'\70'), 0x85, (0, 1), bit, "PatchFlag({})".format(bit), varsize = 1, pos = pos) + return expression.SpecialCheck((0x01, r'\70'), 0x85, (0, 1), expression.ConstantNumeric(bit), "PatchFlag({})".format(bit), varsize=1, pos=pos) def config_flag(name, info, pos): return expression.SpecialParameter(name, info, None, config_flag_read, True, pos) diff --git a/regression/expected/002_sounds.nfo b/regression/expected/002_sounds.nfo index 6a47d89b..5bd6fe76 100644 --- a/regression/expected/002_sounds.nfo +++ b/regression/expected/002_sounds.nfo @@ -25,9 +25,9 @@ 6 * 3 11 \w4 7 ** beef.wav -8 * 8 FE 00 \dx78563412 \wx0003 +8 * 8 FE 00 "\12\34\56\78" \wx0003 9 ** beef.wav -10 * 8 FE 00 \dx78563412 \wx0003 +10 * 8 FE 00 "\12\34\56\78" \wx0003 11 * 9 00 0C \b1 01 FF \wx0049 08 66 diff --git a/regression/expected/004_deactivate.nfo b/regression/expected/004_deactivate.nfo index 356f1992..51f0cb56 100644 --- a/regression/expected/004_deactivate.nfo +++ b/regression/expected/004_deactivate.nfo @@ -6,9 +6,9 @@ // Format: spritenum imagefile depth xpos ypos xsize ysize xrel yrel zoom flags 0 * 6 0E \b1 -\dx44434241 +"ABCD" 1 * 10 0E \b2 -\dx04030201 -\dx48474645 +"\01\02\03\04" +"EFGH" diff --git a/regression/expected/014_read_special_param.nfo b/regression/expected/014_read_special_param.nfo index 8eebd544..2821f6c0 100644 --- a/regression/expected/014_read_special_param.nfo +++ b/regression/expected/014_read_special_param.nfo @@ -55,7 +55,7 @@ 9 * 9 0D 12 \D= FF 00 \dx00000001 // param[19] = param[0] -10 * 9 0D 13 \D= 00 FE \dx014C4D4E +10 * 9 0D 13 \D= 00 FE "NML\01" // param[158] = (param[158] | 2) 11 * 9 0D 9E \D| 9E FF \dx00000002 diff --git a/regression/expected/023_engine_override.nfo b/regression/expected/023_engine_override.nfo index 2ad2df84..d097f170 100644 --- a/regression/expected/023_engine_override.nfo +++ b/regression/expected/023_engine_override.nfo @@ -17,8 +17,8 @@ 00 2 * 52 08 08 "NML\23" "NML regression test" 00 "A test newgrf testing NML" 00 3 * 16 00 08 \b1 01 FF \wx0000 -11 \dx74736574 \dx44434241 +11 "test" "ABCD" 4 * 16 00 08 \b1 01 FF \wx0000 -11 \dx234C4D4E \dx78563412 +11 "NML\23" "\12\34\56\78" diff --git a/regression/expected/030_house.nfo b/regression/expected/030_house.nfo index a1458527..1d8c7b3c 100644 --- a/regression/expected/030_house.nfo +++ b/regression/expected/030_house.nfo @@ -254,7 +254,7 @@ FF // param[125] = 0 39 * 9 0D 7D \D= FF 00 \dx00000000 -40 * 9 09 00 04 \7c \dx49415247 01 +40 * 9 09 00 04 \7c "GRAI" 01 // param[125] = 1 41 * 9 0D 7D \D= FF 00 \dx00000001 @@ -262,7 +262,7 @@ FF // param[124] = 0 42 * 9 0D 7C \D= FF 00 \dx00000000 -43 * 9 09 00 04 \7c \dx41454857 01 +43 * 9 09 00 04 \7c "WHEA" 01 // param[124] = 1 44 * 9 0D 7C \D= FF 00 \dx00000001 diff --git a/regression/expected/example_railtype.nfo b/regression/expected/example_railtype.nfo index d6499071..8612fdc5 100644 --- a/regression/expected/example_railtype.nfo +++ b/regression/expected/example_railtype.nfo @@ -22,7 +22,7 @@ // param[126] = 0 4 * 9 0D 7E \D= FF 00 \dx00000000 -5 * 9 09 88 04 \7gG \dx01544A44 01 +5 * 9 09 88 04 \7gG "DJT\01" 01 // param[126] = 1 6 * 9 0D 7E \D= FF 00 \dx00000001 diff --git a/regression/expected/example_road_vehicle.nfo b/regression/expected/example_road_vehicle.nfo index a47498e9..95aa852b 100644 --- a/regression/expected/example_road_vehicle.nfo +++ b/regression/expected/example_road_vehicle.nfo @@ -33,18 +33,18 @@ "RFPR" "GOOD" "ENSP" "FMSP" "MNSP" "PAPR" "STEL" "VEHI" "COPR" "WOOD" -// param[127] = 1145130834 -9 * 9 0D 7F \D= FF 00 \dx44414F52 +// param[127] = "ROAD" +9 * 9 0D 7F \D= FF 00 "ROAD" -10 * 9 09 00 04 0F \dx52444552 01 +10 * 9 09 00 04 0F "REDR" 01 -// param[127] = 1380205906 -11 * 9 0D 7F \D= FF FF \dx52444552 +// param[127] = "REDR" +11 * 9 0D 7F \D= FF FF "REDR" -12 * 9 09 00 04 0F \dx5F444552 01 +12 * 9 09 00 04 0F "RED_" 01 -// param[127] = 1598309714 -13 * 9 0D 7F \D= FF FF \dx5F444552 +// param[127] = "RED_" +13 * 9 0D 7F \D= FF FF "RED_" 14 * 7 06 7F 04 FF \wx0014