Skip to content

Commit

Permalink
Move the VariableDeclaration into the Script class. Apparently these …
Browse files Browse the repository at this point in the history
…don't just appear in the Context global parameters but can also appear in scripts - who knew!
  • Loading branch information
npjg committed Oct 24, 2024
1 parent 5c08e0b commit ee0c3bd
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 59 deletions.
64 changes: 63 additions & 1 deletion src/MediaStation/Assets/Script.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class OperandType(IntEnum):
DollarSignVariable = 155
AssetId = 156
Float = 157
VariableType = 158
VariableDeclaration = 158
Function = 160

class VariableScope(IntEnum):
Expand All @@ -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.
Expand Down Expand Up @@ -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)

Expand Down
60 changes: 2 additions & 58 deletions src/MediaStation/Context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand All @@ -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.
Expand Down

0 comments on commit ee0c3bd

Please sign in to comment.