Last few object bits implemented enough to parse Pop'n Music, a few more annotations.
This commit is contained in:
parent
d26f3e0238
commit
0bfec7655c
@ -162,7 +162,7 @@ class Point:
|
|||||||
return f"x: {round(self.x, 5)}, y: {round(self.y, 5)}"
|
return f"x: {round(self.x, 5)}, y: {round(self.y, 5)}"
|
||||||
|
|
||||||
|
|
||||||
class Tag:
|
class AP2Tag:
|
||||||
END = 0x0
|
END = 0x0
|
||||||
SHOW_FRAME = 0x1
|
SHOW_FRAME = 0x1
|
||||||
DEFINE_SHAPE = 0x2
|
DEFINE_SHAPE = 0x2
|
||||||
@ -486,6 +486,101 @@ class AP2Action:
|
|||||||
return resources.get(tagid, "UNKNOWN")
|
return resources.get(tagid, "UNKNOWN")
|
||||||
|
|
||||||
|
|
||||||
|
class AP2ObjectType:
|
||||||
|
UNDEFINED = 0x0
|
||||||
|
NAN = 0x1
|
||||||
|
BOOLEAN = 0x2
|
||||||
|
INTEGER = 0x3
|
||||||
|
S64 = 0x4
|
||||||
|
FLOAT = 0x5
|
||||||
|
DOUBLE = 0x6
|
||||||
|
STRING = 0x7
|
||||||
|
POINTER = 0x8
|
||||||
|
OBJECT = 0x9
|
||||||
|
INFINITY = 0xa
|
||||||
|
CONST_STRING = 0xb
|
||||||
|
BUILT_IN_FUNCTION = 0xc
|
||||||
|
|
||||||
|
|
||||||
|
class AP2PointerType:
|
||||||
|
# The type of the object if it is an AP2ObjectType.POINTER or AP2ObjectType.OBJECT
|
||||||
|
UNDEFINED = 0x0
|
||||||
|
AFP_TEXT = 0x1
|
||||||
|
AFP_RECT = 0x2
|
||||||
|
AFP_SHAPE = 0x3
|
||||||
|
DRAG = 0x4
|
||||||
|
MATRIX = 0x5
|
||||||
|
POINT = 0x6
|
||||||
|
GETTER_SETTER_PROPERTY = 0x7
|
||||||
|
FUNCTION_WITH_PROTOTYPE = 0x8
|
||||||
|
ROW_DATA = 0x20
|
||||||
|
|
||||||
|
object_W = 0x50
|
||||||
|
movieClip_W = 0x51
|
||||||
|
sound_W = 0x52
|
||||||
|
color_W = 0x53
|
||||||
|
date_W = 0x54
|
||||||
|
array_W = 0x55
|
||||||
|
xml_W = 0x56
|
||||||
|
xmlNode_W = 0x57
|
||||||
|
textFormat_W = 0x58
|
||||||
|
sharedObject_W = 0x59
|
||||||
|
sharedObjectData_W = 0x5a
|
||||||
|
textField_W = 0x5b
|
||||||
|
xmlAttrib_W = 0x5c
|
||||||
|
bitmapdata_W = 0x5d
|
||||||
|
matrix_W = 0x5e
|
||||||
|
point_W = 0x5f
|
||||||
|
ColorMatrixFilter_W = 0x60
|
||||||
|
String_W = 0x61
|
||||||
|
Boolean_W = 0x62
|
||||||
|
Number_W = 0x63
|
||||||
|
function_W = 0x64
|
||||||
|
prototype_W = 0x65
|
||||||
|
super_W = 0x66
|
||||||
|
transform_W = 0x68
|
||||||
|
colorTransform_W = 0x69
|
||||||
|
rectangle_W = 0x6a
|
||||||
|
|
||||||
|
# All of these can have prototypes, not sure what the "C" stands for.
|
||||||
|
Object_C = 0x78
|
||||||
|
MovieClip_C = 0x79
|
||||||
|
Sound_C = 0x7a
|
||||||
|
Color_C = 0x7b
|
||||||
|
Date_C = 0x7c
|
||||||
|
Array_C = 0x7d
|
||||||
|
XML_C = 0x7e
|
||||||
|
XMLNode_C = 0x7f
|
||||||
|
TextFormat_C = 0x80
|
||||||
|
TextField_C = 0x83
|
||||||
|
BitmapData_C = 0x85
|
||||||
|
matrix_C = 0x86
|
||||||
|
point_C = 0x87
|
||||||
|
String_C = 0x89
|
||||||
|
Boolean_C = 0x8a
|
||||||
|
Number_C = 0x8b
|
||||||
|
Function_C = 0x8c
|
||||||
|
aplib_C = 0x8f
|
||||||
|
transform_C = 0x90
|
||||||
|
colorTransform_C = 0x91
|
||||||
|
rectangle_C = 0x92
|
||||||
|
asdlib_C = 0x93
|
||||||
|
XMLController_C = 0x94
|
||||||
|
eManager_C = 0x95
|
||||||
|
|
||||||
|
stage_O = 0xa0
|
||||||
|
math_O = 0xa1
|
||||||
|
key_O = 0xa2
|
||||||
|
mouse_O = 0xa3
|
||||||
|
system_O = 0xa4
|
||||||
|
sharedObject_O = 0xa5
|
||||||
|
flash_O = 0xa6
|
||||||
|
global_O = 0xa7
|
||||||
|
display_P = 0xb4
|
||||||
|
geom_P = 0xb5
|
||||||
|
filtesr_P = 0xb6
|
||||||
|
|
||||||
|
|
||||||
class SWF:
|
class SWF:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -557,7 +652,7 @@ class SWF:
|
|||||||
def add_coverage(*args: Any, **kwargs: Any) -> None: # type: ignore
|
def add_coverage(*args: Any, **kwargs: Any) -> None: # type: ignore
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if tagid == Tag.AP2_SHAPE:
|
if tagid == AP2Tag.AP2_SHAPE:
|
||||||
if size != 4:
|
if size != 4:
|
||||||
raise Exception(f"Invalid shape size {size}")
|
raise Exception(f"Invalid shape size {size}")
|
||||||
|
|
||||||
@ -566,7 +661,7 @@ class SWF:
|
|||||||
|
|
||||||
shape_reference = f"{self.exported_name}_shape{shape_id}"
|
shape_reference = f"{self.exported_name}_shape{shape_id}"
|
||||||
vprint(f"{prefix} Tag ID: {shape_id}, AFP Reference: {shape_reference}, IFS GEO Filename: {md5(shape_reference.encode('utf-8')).hexdigest()}")
|
vprint(f"{prefix} Tag ID: {shape_id}, AFP Reference: {shape_reference}, IFS GEO Filename: {md5(shape_reference.encode('utf-8')).hexdigest()}")
|
||||||
elif tagid == Tag.AP2_DEFINE_SPRITE:
|
elif tagid == AP2Tag.AP2_DEFINE_SPRITE:
|
||||||
sprite_flags, sprite_id = struct.unpack("<HH", ap2data[dataoffset:(dataoffset + 4)])
|
sprite_flags, sprite_id = struct.unpack("<HH", ap2data[dataoffset:(dataoffset + 4)])
|
||||||
add_coverage(dataoffset, 4)
|
add_coverage(dataoffset, 4)
|
||||||
|
|
||||||
@ -580,14 +675,14 @@ class SWF:
|
|||||||
|
|
||||||
vprint(f"{prefix} Tag ID: {sprite_id}")
|
vprint(f"{prefix} Tag ID: {sprite_id}")
|
||||||
self.__parse_tags(ap2_version, afp_version, ap2data, subtags_offset, prefix=" " + prefix, verbose=verbose)
|
self.__parse_tags(ap2_version, afp_version, ap2data, subtags_offset, prefix=" " + prefix, verbose=verbose)
|
||||||
elif tagid == Tag.AP2_DEFINE_FONT:
|
elif tagid == AP2Tag.AP2_DEFINE_FONT:
|
||||||
wat, font_id = struct.unpack("<HH", ap2data[dataoffset:(dataoffset + 4)])
|
wat, font_id = struct.unpack("<HH", ap2data[dataoffset:(dataoffset + 4)])
|
||||||
vprint(f"{prefix} Tag ID: {font_id}")
|
vprint(f"{prefix} Tag ID: {font_id}")
|
||||||
elif tagid == Tag.AP2_DO_ACTION:
|
elif tagid == AP2Tag.AP2_DO_ACTION:
|
||||||
# TODO: This is wrong, this is only for defined functions.
|
# TODO: This is wrong, this is only for defined functions.
|
||||||
flags, unk1, nameoffset, unk2, _, unk3 = struct.unpack(">BHHHBH", ap2data[dataoffset:(dataoffset + 10)])
|
flags, unk1, nameoffset, unk2, _, unk3 = struct.unpack(">BHHHBH", ap2data[dataoffset:(dataoffset + 10)])
|
||||||
vprint(f"{prefix} Flags: {hex(flags)}, Unk1: {hex(unk1)}, Name: {hex(nameoffset)}, Unk2: {hex(unk2)}, Unk3: {hex(unk3)}")
|
vprint(f"{prefix} Flags: {hex(flags)}, Unk1: {hex(unk1)}, Name: {hex(nameoffset)}, Unk2: {hex(unk2)}, Unk3: {hex(unk3)}")
|
||||||
elif tagid == Tag.AP2_PLACE_OBJECT:
|
elif tagid == AP2Tag.AP2_PLACE_OBJECT:
|
||||||
# Allow us to keep track of what we've consumed.
|
# Allow us to keep track of what we've consumed.
|
||||||
datachunk = ap2data[dataoffset:(dataoffset + size)]
|
datachunk = ap2data[dataoffset:(dataoffset + size)]
|
||||||
flags, depth, object_id = struct.unpack("<IHH", datachunk[0:8])
|
flags, depth, object_id = struct.unpack("<IHH", datachunk[0:8])
|
||||||
@ -713,19 +808,47 @@ class SWF:
|
|||||||
acolor.a = float(rgba & 0xFF) * 0.003921569
|
acolor.a = float(rgba & 0xFF) * 0.003921569
|
||||||
vprint(f"{prefix} AColor: {color}")
|
vprint(f"{prefix} AColor: {color}")
|
||||||
|
|
||||||
# Completely unsure what this is
|
|
||||||
if flags & 0x80:
|
if flags & 0x80:
|
||||||
raise Exception("Unhandled flag!")
|
# Some sort of event data? I dunno, but it changes the running pointer for data
|
||||||
|
# following it. Not sure what the data itself contains. This looks like it contains
|
||||||
|
# some lengths such as length of bytecode contained, and length of some values
|
||||||
|
# contained.
|
||||||
|
flags, size = struct.unpack("<II", datachunk[running_pointer:(running_pointer + 8)])
|
||||||
|
add_coverage(dataoffset + running_pointer, 8)
|
||||||
|
running_pointer += size
|
||||||
|
|
||||||
|
# TODO: This is basically not understood at all, I can't make heads or tails of
|
||||||
|
# the code. This definitely contains bytecode in some circumstances, maybe sprite
|
||||||
|
# init portion of the SWF spec?
|
||||||
|
vprint(f"{prefix} Unknown Event data Flags: {hex(flags)}, Size: {size}")
|
||||||
|
|
||||||
# Completely unsure what this is
|
|
||||||
if flags & 0x10000:
|
if flags & 0x10000:
|
||||||
raise Exception("Unhandled flag!")
|
# Some sort of filter data? Not sure what this is either. Needs more investigation
|
||||||
|
# if I encounter files with it.
|
||||||
|
count, size = struct.unpack("<HH", datachunk[running_pointer:(running_pointer + 4)])
|
||||||
|
add_coverage(dataoffset + running_pointer, 4)
|
||||||
|
running_pointer += size
|
||||||
|
|
||||||
|
vprint(f"{prefix} Unknown Filter data Count: {count}, Size: {size}")
|
||||||
|
|
||||||
|
# running_pointer + 4 starts a series of shorts (exactly count of them) which are
|
||||||
|
# all in the range of 0-7, corresponding to some sort of filter. They get sizes
|
||||||
|
# looked up and I presume there's data following this corresponding to those sizes.
|
||||||
|
# I don't know however as I've not encountered data with this bit.
|
||||||
|
|
||||||
if flags & 0x1000000:
|
if flags & 0x1000000:
|
||||||
raise Exception("Unhandled flag!")
|
# Some sort of point, perhaps an x, y offset for the object?
|
||||||
|
x, y = struct.unpack("<ff", datachunk[running_pointer:(running_pointer + 8)])
|
||||||
|
add_coverage(dataoffset + running_pointer, 8)
|
||||||
|
running_pointer += 8
|
||||||
|
|
||||||
|
point = Point(x / 20.0, y / 20.0)
|
||||||
|
vprint(f"{prefix} Point: {point}")
|
||||||
|
|
||||||
if flags & 0x2000000:
|
if flags & 0x2000000:
|
||||||
raise Exception("Unhandled flag!")
|
# Same as above, but initializing to 0, 0 instead of from data.
|
||||||
|
point = Point(0.0, 0.0)
|
||||||
|
vprint(f"{prefix} Point: {point}")
|
||||||
|
|
||||||
# This flag states whether we are creating a new object on this depth, or updating one.
|
# This flag states whether we are creating a new object on this depth, or updating one.
|
||||||
if flags & 0x1:
|
if flags & 0x1:
|
||||||
@ -736,7 +859,7 @@ class SWF:
|
|||||||
if running_pointer < size:
|
if running_pointer < size:
|
||||||
raise Exception(f"Did not consume {size - running_pointer} bytes in object instantiation!")
|
raise Exception(f"Did not consume {size - running_pointer} bytes in object instantiation!")
|
||||||
|
|
||||||
elif tagid == Tag.AP2_REMOVE_OBJECT:
|
elif tagid == AP2Tag.AP2_REMOVE_OBJECT:
|
||||||
if size != 4:
|
if size != 4:
|
||||||
raise Exception(f"Invalid shape size {size}")
|
raise Exception(f"Invalid shape size {size}")
|
||||||
|
|
||||||
@ -781,7 +904,7 @@ class SWF:
|
|||||||
if size > 0x200000:
|
if size > 0x200000:
|
||||||
raise Exception(f"Invalid tag size {size}")
|
raise Exception(f"Invalid tag size {size}")
|
||||||
|
|
||||||
vprint(f"{prefix} Tag: {hex(tagid)} ({Tag.tag_to_name(tagid)}), Size: {hex(size)}, Offset: {hex(tags_offset + 4)}")
|
vprint(f"{prefix} Tag: {hex(tagid)} ({AP2Tag.tag_to_name(tagid)}), Size: {hex(size)}, Offset: {hex(tags_offset + 4)}")
|
||||||
self.__parse_tag(ap2_version, afp_version, ap2data, tagid, size, tags_offset + 4, prefix=prefix, verbose=verbose)
|
self.__parse_tag(ap2_version, afp_version, ap2data, tagid, size, tags_offset + 4, prefix=prefix, verbose=verbose)
|
||||||
tags_offset += size + 4 # Skip past tag header and data.
|
tags_offset += size + 4 # Skip past tag header and data.
|
||||||
|
|
||||||
@ -906,7 +1029,8 @@ class SWF:
|
|||||||
|
|
||||||
if flags & 0x1:
|
if flags & 0x1:
|
||||||
# I have no idea what this is, but its treated as 4 bytes and something
|
# I have no idea what this is, but its treated as 4 bytes and something
|
||||||
# happens if they aren't all 0xFF.
|
# happens if they aren't all 0xFF. It looks like this is an animation
|
||||||
|
# background color.
|
||||||
unknown_bytes = struct.unpack("<4B", data[28:32])
|
unknown_bytes = struct.unpack("<4B", data[28:32])
|
||||||
else:
|
else:
|
||||||
unknown_bytes = None
|
unknown_bytes = None
|
||||||
|
Loading…
Reference in New Issue
Block a user