From ee0c3bd8678a47fff94dc5bb441a5106e0def1da Mon Sep 17 00:00:00 2001 From: Nathanael Gentry Date: Thu, 24 Oct 2024 08:07:29 -0400 Subject: [PATCH] Move the VariableDeclaration into the Script class. Apparently these don't just appear in the Context global parameters but can also appear in scripts - who knew! --- src/MediaStation/Assets/Script.py | 64 ++++++++++++++++++++++++++++++- src/MediaStation/Context.py | 60 +---------------------------- 2 files changed, 65 insertions(+), 59 deletions(-) diff --git a/src/MediaStation/Assets/Script.py b/src/MediaStation/Assets/Script.py index 52a006f..1528cd2 100644 --- a/src/MediaStation/Assets/Script.py +++ b/src/MediaStation/Assets/Script.py @@ -152,7 +152,7 @@ class OperandType(IntEnum): DollarSignVariable = 155 AssetId = 156 Float = 157 - VariableType = 158 + VariableDeclaration = 158 Function = 160 class VariableScope(IntEnum): @@ -178,6 +178,62 @@ def pprint_debug(object): debugging_string = pprint.pformat(object) global_variables.application.logger.debug(debugging_string) +class VariableDeclaration: + class Type(IntEnum): + # This is an "array", but the IMT sources + # use the term "collection". + COLLECTION = 0x0007 + STRING = 0x0006 + ASSET_ID = 0x0005 + # These seem to be used in Dalmatians, but I don't know what they are + # used for. + UNK1 = 0x0004 + # These seem to be constants of some sort? This is what some of these + # IDs look like in PROFILE._ST: + # - $downEar 10026 + # - $sitDown 10027 + # Seems like these can also reference variables: + # - var_6c14_bool_FirstThingLev3 315 + # - var_6c14_NextEncouragementSound 316 + UNK2 = 0x0003 + BOOLEAN = 0x0002 + LITERAL = 0x0001 + + def __init__(self, stream, read_id = True): + if read_id: + self.id = Datum(stream, Datum.Type.UINT16_1).d + # These variables don't seem to appear in the variables section of + # PROFILE._ST. They seem to be internal to each context. + self.type = maybe_cast_to_enum(Datum(stream, Datum.Type.UINT8).d, VariableDeclaration.Type) + self.value = None + + # Some of these seem to be the asset IDs that are groups + # of images, hilites, and outlines. For example, + # - img_6c16_ArmorHilite 2385 2672 + # - img_6c16_ArmorOutline 2383 2673 + # - img_6c16_ArmorHelp 2384 2674 + if VariableDeclaration.Type.COLLECTION == self.type: + size = Datum(stream).d + self.value = [] + for _ in range(size): + collection = VariableDeclaration(stream, read_id = False) + self.value.append(collection) + + elif VariableDeclaration.Type.STRING == self.type: + size = Datum(stream).d + string = stream.read(size).decode('latin-1') + self.value = string + + elif (VariableDeclaration.Type.ASSET_ID == self.type) or \ + (VariableDeclaration.Type.BOOLEAN == self.type) or \ + (VariableDeclaration.Type.LITERAL == self.type): + self.value = Datum(stream).d + + else: + global_variables.application.logger.warning(f'Got unknown variable type: 0x{self.type:04x}') + self.value = Datum(stream).d + global_variables.application.logger.warning(f' > Value: {self.value}') + ## A compiled function that executes in the Media Station bytecode interpreter. class Function: ## Reads a compiled script from a binary stream at is current position. @@ -481,6 +537,12 @@ def read_statement(self, stream): # instance where the function is an expression? value = maybe_cast_to_enum(self.read_statement(stream), BuiltInFunction) + elif OperandType.VariableDeclaration == operand_type: + # TODO: This is a bit of a special case. There isn't a statement + # for this, becuase we jump right away into the variable + # declaration. Is there a way to make this more intuitive? + value = VariableDeclaration(stream, read_id = False) + else: value = self.read_statement(stream) diff --git a/src/MediaStation/Context.py b/src/MediaStation/Context.py index dd9dbdf..8ef0c0e 100644 --- a/src/MediaStation/Context.py +++ b/src/MediaStation/Context.py @@ -11,7 +11,7 @@ from .Assets.Bitmap import Bitmap from .Assets.BitmapSet import BitmapSet from .Assets.Asset import Asset -from .Assets.Script import Function, EventHandler, maybe_cast_to_enum +from .Assets.Script import Function, EventHandler, VariableDeclaration from .Primitives.Datum import Datum from .Riff.DataFile import DataFile @@ -79,7 +79,7 @@ def __init__(self, stream): # READ THE VARIABLE DECLARATION. # Any dclared variables seem to always need a value. - variable = Variable(stream) + variable = VariableDeclaration(stream) self.variables.update({variable.id: variable}) elif section_type == Parameters.SectionType.BYTECODE: @@ -91,62 +91,6 @@ def __init__(self, stream): section_type: int = Datum(stream, Datum.Type.UINT16_1).d -class Variable: - class Type(IntEnum): - # This is an "array", but the IMT sources - # use the term "collection". - COLLECTION = 0x0007 - STRING = 0x0006 - ASSET_ID = 0x0005 - # These seem to be used in Dalmatians, but I don't know what they are - # used for. - UNK1 = 0x0004 - # These seem to be constants of some sort? This is what some of these - # IDs look like in PROFILE._ST: - # - $downEar 10026 - # - $sitDown 10027 - # Seems like these can also reference variables: - # - var_6c14_bool_FirstThingLev3 315 - # - var_6c14_NextEncouragementSound 316 - UNK2 = 0x0003 - BOOLEAN = 0x0002 - LITERAL = 0x0001 - - def __init__(self, stream, read_id = True): - if read_id: - self.id = Datum(stream, Datum.Type.UINT16_1).d - # These variables don't seem to appear in the variables section of - # PROFILE._ST. They seem to be internal to each context. - self.type = maybe_cast_to_enum(Datum(stream, Datum.Type.UINT8).d, Variable.Type) - self.value = None - - # Some of these seem to be the asset IDs that are groups - # of images, hilites, and outlines. For example, - # - img_6c16_ArmorHilite 2385 2672 - # - img_6c16_ArmorOutline 2383 2673 - # - img_6c16_ArmorHelp 2384 2674 - if Variable.Type.COLLECTION == self.type: - size = Datum(stream).d - self.value = [] - for _ in range(size): - collection = Variable(stream, read_id = False) - self.value.append(collection) - - elif Variable.Type.STRING == self.type: - size = Datum(stream).d - string = stream.read(size).decode('latin-1') - self.value = string - - elif (Variable.Type.ASSET_ID == self.type) or \ - (Variable.Type.BOOLEAN == self.type) or \ - (Variable.Type.LITERAL == self.type): - self.value = Datum(stream).d - - else: - global_variables.application.logger.warning(f'Got unknown variable type: 0x{self.type:04x}') - self.value = Datum(stream).d - global_variables.application.logger.warning(f' > Value: {self.value}') - ## I don't know what this structure is, but it's in every old-style game. ## The fields aside from the file numbers are constant. ## \param[in] stream - A binary stream that supports the read method.