tp/tools/libstage/libstage.py

1865 lines
52 KiB
Python

import struct
from sys import argv
import json
import traceback
# struct dStage_Elst_data {
# /* 0x0 */ u8 m_layerTable[15];
# };
def extractEVLY(m_entrynum, m_offset, data):
offset = m_offset
entries = []
for i in range(m_entrynum):
layerTable = []
for val in data[offset : offset + 15]:
layerTable.append(val)
entry = {"layerTable": layerTable}
# print(f"Layer Table Entry {i}: {entry}")
entries.append(entry)
offset += 15
return entries
def extractRPPN(m_entrynum, m_offset, data):
entries = []
offset = m_offset
for i in range(m_entrynum):
_0, _1, _2, _3, x, y, z = struct.unpack(
">BBBBfff", data[offset : offset + 0x10]
)
entry = {
"field_0x0": _0,
"field_0x1": _1,
"field_0x2": _2,
"field_0x3": _3,
"Position X": x,
"Position Y": y,
"Position Z": z,
}
entries.append(entry)
offset += 0x10
return entries
def extractRPAT(m_entrynum, m_offset, data):
entries = []
offset = m_offset
for i in range(m_entrynum):
numPoints, pathIndex, _4, isLoop, _6, firstEntryOffset = struct.unpack(
">HhBBHI", data[offset : offset + 12]
)
entry = {
"Number of Points": numPoints,
"Path Index": pathIndex,
"field_0x4": _4,
"Looped": isLoop,
"field_0x6": _6,
"RPPN Entry Index": firstEntryOffset // 0x10,
}
entries.append(entry)
offset += 12
return entries
# struct dStage_Mult_info {
# /* 0x0 */ f32 mTransX;
# /* 0x4 */ f32 mTransY;
# /* 0x8 */ s16 mAngle;
# /* 0xA */ u8 mRoomNo;
# }; // Size: 0xC
def extractMULT(m_entrynum, m_offset, data):
entires = []
offset = m_offset
for i in range(m_entrynum):
x, y, angle, roomNo, _b = struct.unpack(">ffhBB", data[offset : offset + 12])
entry = {"x": x, "y": y, "Angle": angle, "roomNo": roomNo, "field_0xb": _b}
# print(f"MULT Entry {i}: {entry}")
entires.append(entry)
offset += 12
return entires
# class stage_actor_data_class {
# public:
# /* 0x00 */ char mName[8];
# /* 0x08 */ u32 mParameter;
# /* 0x0C */ cXyz mSpawnPos;
# /* 0x18 */ csXyz mAngle;
# /* 0x1E */ u16 mEnemyNo;
# }; // Size: 0x20
def extractACTR(m_entrynum, m_offset, data):
entries = []
offset = m_offset
for i in range(m_entrynum):
name = str(data[offset : offset + 8], "ascii").rstrip("\x00")
param, x, y, z, ax, ay, az, enemyNo = struct.unpack(
">Ifffhhhh", data[offset + 8 : offset + 0x20]
)
entry = {
"Name": name,
"param": param,
"x": x,
"y": y,
"z": z,
"Angle X": ax,
"Angle Y": ay,
"Angle Z": az,
"EnemyNo": enemyNo,
}
# print(f"PLYR Entry {i}: {entry}")
entries.append(entry)
offset += 0x20
return entries
# struct stage_camera2_data_class {
# /* 0x00 */ int field_0x0;
# /* 0x04 */ f32 field_0x4;
# /* 0x08 */ f32 field_0x8;
# /* 0x0C */ f32 field_0xc;
# /* 0x10 */ u8 field_0x10;
# /* 0x11 */ u8 field_0x11;
# /* 0x12 */ u8 field_0x12;
# /* 0x13 */ u8 field_0x13;
# /* 0x14 */ u16 field_0x14;
# /* 0x16 */ u16 field_0x16;
# }; // Size: 0x18
def extractCAM(m_entrynum, m_offset, data):
entries = []
offset = m_offset
for i in range(m_entrynum):
camera_type = str(data[offset : offset + 16], "ascii").rstrip("\x00")
_10, _11, _12, _13, _14, _16 = struct.unpack(
">BBBBHH", data[offset + 16 : offset + 0x18]
)
# Beginning value seems to be a string, TODO check
entry = {
"Camera Type": camera_type,
"field_0x10": _10,
"field_0x11": _11,
"field_0x12": _12,
"field_0x13": _13,
"field_0x14": _14,
"field_0x16": _16,
}
# print(f"CAMERA Entry {i}: {entry}")
entries.append(entry)
offset += 0x18
return entries
# struct roomRead_class {
# /* 0x0 */ int field_0x0;
# /* 0x4 */ roomRead_data_class** field_0x4;
# };
# struct roomRead_data_class {
# /* 0x0 */ u8 field_0x0;
# /* 0x1 */ u8 field_0x1;
# /* 0x2 */ u8 field_0x2;
# /* 0x4 */ u8* field_0x4;
# };
def extractRTBL(m_entrynum, m_offset, data):
entries = []
offset = m_offset
for i in range(m_entrynum):
room_data_offset = struct.unpack(">I", data[offset : offset + 4])[0]
offset += 4
infoTblLen, _1, _2, _pad, infoTblOffset = struct.unpack(
">BBBBI", data[room_data_offset : room_data_offset + 8]
)
infoTbl = []
for j in range(infoTblLen):
infoTbl.append(
struct.unpack(">B", data[infoTblOffset : infoTblOffset + 1])[0]
)
infoTblOffset += 1
# These contain room properties such as time pass, vrbox, sound info
# See dStage_roomRead_dt_c_GetReverb for field1
# See dStage_roomRead_dt_c_GetTimePass and dStage_roomRead_dt_c_GetVrboxswitch for field2
# See dStage_roomRead_dt_c_ChkBg and dStage_roomRead_dt_c_GetLoadRoomIndex for infoTbl
# The entries in the tables are room numbers and the first entry is usually ORd by 0x80
# If an entry & 0x80 > 0, dStage_roomRead_dt_c_ChkBg will be true
# Note: The table will always have as many entries as there is the maximum number of rooms
# For example, if only rooms 0, 1, and 3 exist, there will be 4 entries in the info table
entry = {"field_0x1": _1, "field_0x2": _2, "Table": infoTbl}
# print(f"RTBL Entry {i}: {entry}")
entries.append(entry)
return entries
# struct stage_arrow_data_class {
# /* 0x00 */ cXyz mPosition;
# /* 0x0C */ csXyz mAngle;
# }; // Size: 0x14
def extractRARO(m_entrynum, m_offset, data):
offset = m_offset
entries = []
for i in range(m_entrynum):
x, y, z, ax, ay, az, _12, _13 = struct.unpack(
">fffhhhBB", data[offset : offset + 0x14]
)
entry = {
"x": x,
"y": y,
"z": z,
"Angle X": ax,
"Angle Y": ay,
"Angle Z": az,
"field_0x12": _12,
"field_0x13": _13,
}
# print(f"Arrow Entry {i}: {entry}")
entries.append(entry)
offset += 0x14
return entries
# struct stage_scls_info_class {
# /* 0x0 */ char mStage[8];
# /* 0x8 */ u8 mStart;
# /* 0x9 */ s8 mRoom;
# /* 0xA */ u8 field_0xa;
# /* 0xB */ u8 field_0xb;
# /* 0xC */ s8 mWipe;
# }; // Size: 0xD
def extractSCLS(m_entrynum, m_offset, data):
offset = m_offset
entries = []
for i in range(m_entrynum):
stage = str(data[offset : offset + 8], "ascii").rstrip("\x00")
start, room, _a, _b, wipe = struct.unpack(
">BbBBb", data[offset + 8 : offset + 13]
)
entry = {
"Stage": stage,
"Start": start,
"Room": room,
"field_0xa": _a,
"field_0xb": _b,
"Wipe": wipe,
}
entries.append(entry)
# print(f"SCLS Entry {i}: {entry}")
offset += 0xD
return entries
def extractTGSC(m_entrynum, m_offset, data):
entries = []
offset = m_offset
for i in range(m_entrynum):
name = str(data[offset : offset + 8], "ascii").rstrip("\x00")
(
param,
x,
y,
z,
ax,
ay,
az,
enemyNo,
scaleX,
scaleY,
scaleZ,
_23,
) = struct.unpack(">IfffhhhhBBBB", data[offset + 8 : offset + 0x24])
entry = {
"Name": name,
"param": param,
"x": x,
"y": y,
"z": z,
"Angle X": ax,
"Angle Y": ay,
"Angle Z": az,
"EnemyNo": enemyNo,
"Scale X": scaleX,
"Scale Y": scaleY,
"Scale Z": scaleZ,
"field_0x23": _23,
}
entries.append(entry)
offset += 0x24
return entries
# Any stage with the format room%d.dzs should use FileList2
# class dStage_FileList_dt_c {
# public:
# /* 0x00 */ u32 mParameters;
# /* 0x04 */ f32 mSeaLevel;
# /* 0x08 */ f32 field_0x8;
# /* 0x0C */ f32 field_0xc;
# /* 0x10 */ u8 field_0x10[10];
# /* 0x1A */ u8 mDefaultCamera;
# /* 0x1B */ u8 mBitSw;
# /* 0x1C */ u16 mMsg;
# }; // Size: 0x20
# class dStage_FileList2_dt_c {
# public:
# /* 0x00 */ f32 mLeftRmX;
# /* 0x04 */ f32 mInnerRmZ;
# /* 0x08 */ f32 mRightRmX;
# /* 0x0C */ f32 mFrontRmZ;
# /* 0x10 */ u8 mMinFloorNo;
# /* 0x11 */ u8 mMaxFloorNo;
# /* 0x12 */ u8 field_0x12;
# /* 0x13 */ u8 field_0x13;
# /* 0x14 */ f32 field_0x14;
# /* 0x18 */ f32 field_0x18;
# /* 0x1C */ s16 field_0x1c;
# }; // Size: 0x20
def extractFILI(m_entrynum, m_offset, data):
offset = m_offset
entries = []
for i in range(m_entrynum):
parameters, sea_level, _8, _c = struct.unpack(
">Ifff", data[offset : offset + 0x10]
)
table = []
for val in data[offset + 0x10 : offset + 0x1A]:
table.append(val)
default_camera, bit_sw, msg = struct.unpack(
">BBH", data[offset + 0x1A : offset + 0x1E]
)
entry = {
"Parameters": parameters,
"Sea Level": sea_level,
"field_0x8": _8,
"field_0xc": _c,
"field_0x10": table,
"Default Camera": default_camera,
"Bit Sw": bit_sw,
"Msg": msg,
}
# print(f"FileList Entry {i}: {entry}")
entries.append(entry)
offset += 0x20
return entries
def dStage_filiInfo2Init(m_entrynum, m_offset, data):
offset = m_offset
entries = []
for i in range(m_entrynum):
(
leftRmX,
innerRmZ,
RightRmX,
FrontRmZ,
MinFloorNo,
MaxFloorNo,
_12,
_13,
_14,
_18,
_1c,
) = struct.unpack(">ffffBBBBffh", data[offset : offset + 30])
entry = {
"Left Room X": leftRmX,
"Inner Room Z": innerRmZ,
"Right Room X": RightRmX,
"Front Room Z": FrontRmZ,
"Min Floor No.": MinFloorNo,
"Max Floor No.": MaxFloorNo,
"field_0x12": _12,
"field_0x13": _13,
"field_0x14": _14,
"field_0x18": _18,
"field_0x1c": _1c,
}
entries.append(entry)
offset += 0x20
return entries
# struct dStage_MapEvent_dt_c {
# /* 0x00 */ u8 mType;
# /* 0x01 */ u8 field_0x1;
# /* 0x02 */ u8 field_0x2;
# /* 0x03 */ u8 field_0x3;
# /* 0x04 */ u8 field_0x4;
# /* 0x05 */ u8 field_0x5;
# /* 0x06 */ u8 mPriority;
# /* 0x07 */ u8 field_0x7;
# /* 0x08 */ u8 field_0x8;
# /* 0x09 */ u8 field_0x9;
# /* 0x0A */ u8 field_0xA;
# /* 0x0B */ u8 field_0xB;
# /* 0x0C */ u8 field_0xC;
# /* 0x0D */ char mName[7];
# /* 0x14 */ u16 field_0x14;
# /* 0x16 */ u8 field_0x16;
# /* 0x17 */ u8 field_0x17;
# /* 0x18 */ u8 mSeType; // 1: RIDDLE_A, 2: RIDDLE_B
# /* 0x19 */ u8 field_0x19[0x1B - 0x19];
# /* 0x1B */ u8 mSwitch;
# }; // SIZE = 0x1C
def extractREVT(m_entrynum, m_offset, data):
offset = m_offset
entries = []
for i in range(m_entrynum):
_type, _1, _2, _3, _4, _5, priority, _7, _8, _9, _a, _b, _c = struct.unpack(
">BBBBBBBBBBBBB", data[offset : offset + 0xD]
)
name = None
try:
name = str(data[offset + 0xD : offset + 0x18], "ascii").rstrip("\x00")
except Exception:
name = list(data[offset + 0xD : offset + 0x18])
# TODO: Check name length
seType, _1a, _1b, switch = struct.unpack(
">BBBB", data[offset + 0x18 : offset + 0x1C]
)
entry = {
"Type": _type,
"field_0x1": _1,
"field_0x2": _2,
"field_0x3": _3,
"field_0x4": _4,
"field_0x5": _5,
"Priority": priority,
"field_0x7": _7,
"field_0x8": _8,
"field_0x9": _9,
"field_0xa": _a,
"field_0xb": _b,
"field_0xc": _c,
"Name": name,
"seType": seType,
"field_0x1a": _1a,
"field_0x1b": _1b,
"switch": switch,
}
# print(f"MapEvent {i}: {entry}")
offset += 0x1C
entries.append(entry)
return entries
# struct stage_sound_data {
# /* 0x00 */ char field_0x0[8];
# /* 0x08 */ Vec field_0x8;
# /* 0x14 */ u8 field_0x14;
# /* 0x15 */ u8 field_0x15;
# /* 0x16 */ u8 field_0x16;
# /* 0x17 */ u8 field_0x17;
# /* 0x18 */ u8 field_0x18;
# /* 0x19 */ u8 field_0x19;
# /* 0x1A */ u8 field_0x1a;
# }; // Size: 0x1C
def extractSOND(m_entrynum, m_offset, data):
entries = []
offset = m_offset
for i in range(m_entrynum):
name = str(data[offset : offset + 8], "ascii").rstrip("\x00")
# print(name)
x, y, z, _14, _15, _16, _17, _18, _19, _1a, _1b = struct.unpack(
">fffBBBBBBBB", data[offset + 8 : offset + 0x1C]
)
entry = {
"Name": name,
"x": x,
"y": y,
"z": z,
"field_0x14": _14,
"field_0x15": _15,
"field_0x16": _16,
"field_0x17": _17,
"field_0x18": _18,
"field_0x19": _19,
"field_0x1a": _1a,
"field_0x1b": _1b,
}
entries.append(entry)
offset += 0x1C
return entries
# struct stage_pure_lightvec_info_class {
# // LGT
# /* 0x00 */ Vec m_position;
# /* 0x0C */ f32 m_radius;
# /* 0x10 */ f32 m_directionX;
# /* 0x14 */ f32 m_directionY;
# /* 0x18 */ f32 m_spotCutoff;
# /* 0x1C */ u8 field_0x1c;
# /* 0x1D */ u8 field_0x1d;
# /* 0x1E */ u8 field_0x1e;
# /* 0x1F */ u8 field_0x1f;
# }; // Size: 0x20
def extractLGTV(m_entrynum, m_offset, data):
entries = []
offset = m_offset
for i in range(m_entrynum):
x, y, z, rad, dirX, dirY, spot_cutoff, _1c, _1d, _1e, _1f = struct.unpack(
">fffffffBBBB", data[offset : offset + 0x20]
)
entry = {
"x": x,
"y": y,
"z": z,
"Radius": rad,
"Direction X": dirX,
"Direction Y": dirY,
"Spotlight Cutoff": spot_cutoff,
"field_0x1c": _1c,
"field_0x1d": _1d,
"field_0x1e": _1e,
"field_0x1f": _1f,
}
# print(f"LGTV Entry {i}: {entry}")
entries.append(entry)
offset += 0x20
return entries
# struct stage_envr_info_class {
# /* 0x0 */ u8 m_pselectID[65];
# }; // Size: 0x41
def extractENVR(m_entrynum, m_offset, data):
offset = m_offset
entries = []
for i in range(m_entrynum):
dataTable = []
for val in data[offset : offset + 65]:
dataTable.append(val)
entry = {"Pselect ID Table": dataTable}
# print(f"envrInfo Entry {i}: {entry}")
entries.append(entry)
offset += 0x41
return entries
# struct stage_pselect_info_class {
# /* 0x0 */ u8 mPalIdx[8];
# /* 0x8 */ f32 mChangeRate;
# }; // Size: 0xC
def extractCol(m_entrynum, m_offset, data):
entries = []
offset = m_offset
for i in range(m_entrynum):
palIds = []
for val in data[offset : offset + 8]:
palIds.append(val)
change_rate = struct.unpack(">f", data[offset + 8 : offset + 12])[0]
entry = {"Palette Ids": palIds, "Change Rate": change_rate}
entries.append(entry)
# print(f"Col Info Entry {i}: {entry}")
offset += 0xC
return entries
def parse_RGB_class(data):
return struct.unpack(">BBB", data)
# struct stage_palette_info_class {
# /* 0x00 */ color_RGB_class mActorAmbColor;
# /* 0x03 */ color_RGB_class mBgAmbColor[4];
# /* 0x0F */ color_RGB_class mPlightColor[6];
# /* 0x21 */ color_RGB_class mFogColor;
# /* 0x24 */ f32 mFogStartZ;
# /* 0x28 */ f32 mFogEndZ;
# /* 0x2C */ u8 mVirtIdx;
# /* 0x2D */ u8 mTerrainLightInfluence;
# /* 0x2E */ u8 mCloudShadowDensity;
# /* 0x2F */ u8 field_0x2f;
# /* 0x30 */ u8 mBloomTblIdx;
# /* 0x31 */ u8 mBgAmbColor1A;
# /* 0x32 */ u8 mBgAmbColor2A;
# /* 0x33 */ u8 mBgAmbColor3A;
# }; // Size: 0x34
def extractPAL(m_entrynum, m_offset, data):
entries = []
offset = m_offset
for i in range(m_entrynum):
# print(f"Data {data[offset:offset+0x34]}")
actor_amb_color = parse_RGB_class(data[offset : offset + 3])
offset += 3
bg_amb_colors = []
for j in range(4):
bg_amb_colors.append(parse_RGB_class(data[offset : offset + 3]))
offset += 3
p_light_colors = []
for j in range(6):
p_light_colors.append(parse_RGB_class(data[offset : offset + 3]))
offset += 3
fog_color = parse_RGB_class(data[offset : offset + 3])
offset += 3
(
fog_start_z,
fog_end_z,
virt_idx,
terrain_light_influence,
cloud_shadow_density,
_2f,
bloom_tbl_idx,
bg_amb_color1a,
bg_amb_color2a,
bg_amb_color3a,
) = struct.unpack(">ffBBBBBBBB", data[offset : offset + 16])
offset += 16
entry = {
"Actor Ambient Color": actor_amb_color,
"BG Ambient Colors": bg_amb_colors,
"P Light Colors": p_light_colors,
"Fog Color": fog_color,
"Fog Start Z": fog_start_z,
"Fog End Z": fog_end_z,
"Virt Idx": virt_idx,
"Terrain Light Influence": terrain_light_influence,
"Cloud Shadow Density": cloud_shadow_density,
"field_0x2f": _2f,
"Bloom Table Idx": bloom_tbl_idx,
"BG Ambient Color 1a": bg_amb_color1a,
"BG Ambient Color 2a": bg_amb_color2a,
"BG Ambient Color 3a": bg_amb_color3a,
}
entries.append(entry)
# print(f"Palette Entry {i}: {entry}")
return entries
# struct stage_vrboxcol_info_class {
# // VRB
# }; // Size: 0x18
def extractVRB(m_entrynum, m_offset, data):
entries = []
offset = m_offset
for i in range(m_entrynum):
skyColor = parse_RGB_class(data[offset : offset + 3])
cloudColor = parse_RGB_class(data[offset + 3 : offset + 6])
colorTable = []
for j in range(5):
colorTable.append(
parse_RGB_class(data[offset + 6 + (j * 3) : offset + 6 + ((j + 1) * 3)])
)
offset += 0x15
entry = {
"Sky Color": skyColor,
"Cloud Color": cloudColor,
"Other Colors": colorTable,
}
# print(f"vrboxcolinfo {i}: {entry}")
entries.append(entry)
return entries
# class dStage_Lbnk_dt_c {
# public:
# /* 0x0 */ u8 field_0x0[0x2 - 0x0];
# /* 0x2 */ u8 field_0x2;
# };
def extractLBNK(m_entrynum, m_offset, data):
# TODO: Check
offset = m_offset
entries = []
for i in range(m_entrynum):
_0, _1, _2 = struct.unpack(">BBB", data[offset : offset + 3])
entry = {"field_0x0": _0, "field_0x1": _1, "field_0x2": _2}
# print(f"LBNK Entry {i}: {entry}")
entries.append(entry)
offset += 3
return entries
# class stage_tresure_class {
# public:
# /* 0x00 */ char mName[8];
# /* 0x08 */ u8 field_0x8;
# /* 0x09 */ u8 mTypeFlag;
# /* 0x0A */ u8 field_0xa; // part of flag
# /* 0x0B */ u8 mAppearType;
# /* 0x0C */ Vec mPosition;
# /* 0x18 */ s16 mRoomNo;
# /* 0x1A */ s16 mRotation;
# /* 0x1C */ u8 mItem;
# /* 0x1D */ u8 mFlagID;
# }; // Size: 0x20
def extractTRES(m_entrynum, m_offset, data):
offset = m_offset
entries = []
for i in range(m_entrynum):
name = str(data[offset : offset + 8], "ascii").rstrip("\x00")
(
_8,
type_flag,
_a,
appear_type,
x,
y,
z,
room,
rotation,
item,
flagId,
_1e,
_1f,
) = struct.unpack(">BBBBfffhhBBBB", data[offset + 8 : offset + 32])
entry = {
"Name": name,
"field_0x8": _8,
"Type Flag": type_flag,
"field_0xa": _a,
"Appear Type": appear_type,
"Position X": x,
"Position Y": y,
"Position Z": z,
"Room No.": room,
"Rotation": rotation,
"Item": item,
"Flag ID": flagId,
"field_0x1e": _1e,
"field_0x1f": _1f,
}
entries.append(entry)
offset += 0x20
return entries
# struct data_s {
# /* 0x00 */ u8 mNo;
# /* 0x01 */ s8 mRoomNo;
# /* 0x02 */ u8 mStatus;
# /* 0x03 */ u8 mArg1;
# /* 0x04 */ Vec mPos;
# /* 0x10 */ u8 mSwBit;
# /* 0x11 */ u8 mType;
# /* 0x12 */ u8 mArg2;
# /* 0x13 */ s8 mAngleY;
# };
def dStage_stageKeepTresureInit(m_entrynum, m_offset, data):
offset = m_offset
entries = []
for i in range(m_entrynum):
num, roomNo, status, arg1, x, y, z, swBit, _type, arg2, angleY = struct.unpack(
">BbBBfffBBBb", data[offset : offset + 0x14]
)
entry = {
"Number": num,
"Room Number": roomNo,
"Status": status,
"Argument 1": arg1,
"Position X": x,
"Position Y": y,
"Position Z": z,
"Sw Bit": swBit,
"Type": _type,
"Argument 2": arg2,
"Angle Y": angleY,
}
entries.append(entry)
offset += 0x14
return entries
# struct stage_stag_info_class {
# /* 0x00 */ f32 field_0x0;
# /* 0x04 */ f32 field_0x4;
# /* 0x08 */ u8 mCameraType;
# /* 0x09 */ u8 field_0x09;
# /* 0x0A */ u16 field_0x0a;
# /* 0x0C */ u32 field_0x0c;
# /* 0x10 */ u32 field_0x10;
# /* 0x14 */ u8 field_0x14[6]; // usually all 0xFF
# /* 0x1A */ s16 mGapLevel;
# /* 0x1C */ s16 mRangeUp;
# /* 0x1E */ s16 mRangeDown;
# /* 0x20 */ f32 field_0x20;
# /* 0x24 */ f32 field_0x24;
# /* 0x28 */ u8 mMsgGroup;
# /* 0x2A */ u16 mStageTitleNo;
# /* 0x2C */ u8 mParticleNo[16];
# }; // SIZE: 0x3C
def extractSTAG(m_entrynum, m_offset, data):
offset = m_offset
entries = []
for i in range(m_entrynum):
_0, _4, camera_type, _9, _a, _c, _10 = struct.unpack(
">ffBBHII", data[offset : offset + 0x14]
)
_14 = []
for j in range(6):
_14.append(data[offset + 0x14 + j])
(
gap_level,
range_up,
range_down,
_20,
_24,
msg_group,
_29,
stage_title_no,
) = struct.unpack(">hhhffBBH", data[offset + 0x1A : offset + 0x2C])
particle_no = []
for j in range(16):
particle_no.append(data[offset + 0x2C + j])
entry = {
"field_0x0": _0,
"field_0x4": _4,
"Camera Type": camera_type,
"field_0x9": _9,
"field_0xa": _a,
"field_0xc": _c,
"field_0x10": _10,
"field_0x14": _14,
"Gap Level": gap_level,
"Range Up": range_up,
"Range Down": range_down,
"field_0x20": _20,
"field_0x24": _24,
"Msg Group": msg_group,
"field_0x29": _29,
"Stage Title No": stage_title_no,
"Particle No": particle_no,
}
entries.append(entry)
# print(f"STAGE Entry {i}: {entry}")
offset += 0x3C
return entries
def extractMEMA(m_entrynum, m_offset, data):
offset = m_offset
entries = []
for i in range(m_entrynum):
entries.append(struct.unpack(">I", data[offset : offset + 4])[0])
offset += 4
return entries
# struct dStage_MemoryConfig_data {
# /* 0x0 */ u8 m_roomNo;
# /* 0x1 */ u8 m_blockID;
# }; // Size: 0x2
def extractMECO(m_entrynum, m_offset, data):
offset = m_offset
entries = []
for i in range(m_entrynum):
roomNo, blockId = struct.unpack(">BB", data[offset : offset + 2])
entry = {"Room Number": roomNo, "Block Id": blockId}
entries.append(entry)
offset += 2
return entries
# struct line_class {
# /* 0x00 */ u8 field_0x0;
# /* 0x01 */ u8 field_0x1;
# /* 0x02 */ u8 mDataNum; //Number of entries in mpData
# /* 0x03 */ u8 field_0x3;
# /* 0x04 */ u16* mpData;
# }; // Size: 0x8
# struct poly_class {
# /* 0x00 */ u8 field_0x0;
# /* 0x01 */ u8 mDataNum; //Number of entries in mpData
# /* 0x04 */ u16* mpData;
# }; // Size: 0x8
# struct group_class {
# /* 0x00 */ u8 field_0x0;
# /* 0x01 */ u8 field_0x1;
# /* 0x02 */ u8 mLineNum; //Number of Lines
# /* 0x03 */ u8 field_0x3;
# /* 0x04 */ u8 mPolyNum; //Number of Polygons
# /* 0x08 */ dDrawPath_c::line_class* mpLine;
# /* 0x0C */ u8 field_0xc[4];
# /* 0x10 */ dDrawPath_c::poly_class* mpPoly;
# }; // Size: 0x14
# struct floor_class {
# /* 0x0 */ s8 mFloorNo; //Floor Index
# /* 0x1 */ u8 mGroupNum; //Number of Groups
# /* 0x4 */ dDrawPath_c::group_class* mpGroup;
# }; // Size: 0x8
# struct room_class {
# /* 0x0 */ u8 mFloorNum;
# /* 0x4 */ dDrawPath_c::floor_class* mpFloor;
# /* 0x8 */ f32* mpFloatData; // vec2
# };
def extractMPAT(m_entrynum, m_offset, data):
# Formatted In The Order: Main Struct, Floor Structs, Float Data, Group Structs, Line than Polygon, one after another, Then The Data inside of lines and polygons
offset = m_offset
entry = {}
floor_entries, _1, xy_entries, floor_offset, floatData_offset = struct.unpack(
">BBHII", data[offset : offset + 12]
)
entry["field_0x1"] = _1
entry["Entry Num"] = m_entrynum
floor_offset += offset
floatData_offset += offset
# print(f"Entry Numbers: {m_entrynum}")
# print(f"Float Offset: {floatData_offset}")
floatData = []
for j in range(xy_entries):
x, z = struct.unpack(">ff", data[floatData_offset : floatData_offset + 8])
# print(f"X:{x} Y:{y}")
floatData.append({"x": x, "z": z})
floatData_offset += 8
entry["Vertices"] = floatData
# print(f"Floor Offset: {floor_offset}")
floors = []
for j in range(floor_entries):
floor_id, group_entries, _2, _3, group_offset = struct.unpack(
">BBBBI", data[floor_offset : floor_offset + 8]
)
floor = {"Id": floor_id, "field_0x2": _2, "field_0x3": _3}
group_offset += offset
# print(f" Group Offset: {group_offset}")
# print(f" Group Entries: {group_entries}")
groups = []
for k in range(group_entries):
(
_0,
_1,
line_entries,
_3,
poly_entries,
_5,
_6,
_7,
line_offset,
_c,
_d,
_e,
_f,
polygon_offset,
) = struct.unpack(
">BBBBBBBBIBBBBI", data[group_offset : group_offset + 0x14]
)
group = {
"field_0x0": _0,
"field_0x1": _1,
"field_0x3": _3,
"field_0x5": _5,
"field_0x6": _6,
"field_0x7": _7,
"field_0xc": _c,
"field_0xd": _d,
"field_0xe": _e,
"field_0xf": _f,
}
line_offset += offset
polygon_offset += offset
# print(f" Line Offset: {line_offset}")
lines = []
for l in range(line_entries):
_0, _1, data_entries, _3, data_offset = struct.unpack(
">BBBBI", data[line_offset : line_offset + 8]
)
line = {"field_0x0": _0, "field_0x1": _1, "field_0x3": _3}
data_offset += offset
# print(f" {l}: Data Offset: {data_offset}")
# print(f" {l}: Data Entries: {data_entries}")
data_list = []
for m in range(data_entries):
data_list.append(
struct.unpack(
">H",
data[data_offset + (m * 2) : data_offset + ((m + 1) * 2)],
)[0]
)
line["Vertex Indexes"] = data_list
lines.append(line)
line_offset += 8
# print(f" Polygon Offset: {polygon_offset}")
polygons = []
for l in range(poly_entries):
_0, data_entries, _2, _3, data_offset = struct.unpack(
">BBBBI", data[polygon_offset : polygon_offset + 8]
)
polygon = {"field_0x0": _0, "field_0x2": _2, "field_0x3": _3}
data_offset += offset
# print(f" {l}: Data Offset: {data_offset}")
# print(f" {l}: Data Entries: {data_entries}")
data_list = []
for m in range(data_entries):
data_list.append(
struct.unpack(
">H",
data[data_offset + (m * 2) : data_offset + ((m + 1) * 2)],
)[0]
)
polygon["Vertex Indexes"] = data_list
polygons.append(polygon)
polygon_offset += 8
group["Lines"] = lines
group["Polygons"] = polygons
groups.append(group)
group_offset += 0x14
floor["Groups"] = groups
floors.append(floor)
floor_offset += 8
entry["Floors"] = floors
return entry
decodeFuncTbl = {
"STAG": extractSTAG,
"EVLY": extractEVLY,
"RPPN": extractRPPN,
"RPAT": extractRPAT,
"MULT": extractMULT,
"PLYR": extractACTR,
"CAMR": extractCAM,
"RCAM": extractCAM,
"ACTR": extractACTR,
"TGOB": extractACTR,
"RTBL": extractRTBL,
"AROB": extractRARO,
"RARO": extractRARO,
# "Virt": dStage_vrboxInfoInit, #Unused
"SCLS": extractSCLS,
"TGSC": extractTGSC,
# "LGHT": dStage_plightInfoInit, #Unused
"PPNT": extractRPPN,
# "PATH": dStage_pathInfoInit, #Unused
"SCOB": extractTGSC,
"FILI": extractFILI,
"Door": extractTGSC,
# "FLOR": dStage_floorInfoInit, #Unused
"TGDR": extractTGSC,
# "DMAP": dStage_dmapInfoInit, #Unused
"REVT": extractREVT,
"SOND": extractSOND,
# Layer Functions
"SON0": extractSOND,
"LGT0": extractLGTV,
"LGTV": extractLGTV,
"Env0": extractENVR,
"Col0": extractCol,
"PAL0": extractPAL,
"VRB0": extractVRB,
"Doo0": extractTGSC,
"SCO0": extractTGSC,
"ACT0": extractACTR,
"TRE0": extractACTR,
# Room Functions
"LBNK": extractLBNK,
"TRES": extractTRES,
"MEM0": extractMEMA,
"MEC0": extractMECO,
"MPAT": extractMPAT,
"MPA0": extractMPAT,
}
def packageSTAG(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">ffBBHII",
e["field_0x0"],
e["field_0x4"],
e["Camera Type"],
e["field_0x9"],
e["field_0xa"],
e["field_0xc"],
e["field_0x10"],
)
for i in e["field_0x14"]:
data.append(i)
data += struct.pack(
">hhhffBBH",
e["Gap Level"],
e["Range Up"],
e["Range Down"],
e["field_0x20"],
e["field_0x24"],
e["Msg Group"],
e["field_0x29"],
e["Stage Title No"],
)
for i in e["Particle No"]:
data.append(i)
return data
def packageRTBL(entries, offset):
enddata = bytearray()
middledata = bytearray()
data = bytearray()
middle_offset = offset + (4 * len(entries))
end_offset = middle_offset + (len(entries) * 8)
for i, e in enumerate(entries):
data += struct.pack(">I", middle_offset + (i * 8))
middledata += struct.pack(
">BBBBI",
len(e["Table"]),
e["field_0x1"],
e["field_0x2"],
0,
end_offset + len(enddata),
)
for b in e["Table"]:
enddata.append(b)
data += middledata + enddata
return data
def packageMULT(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">ffhBB", e["x"], e["y"], e["Angle"], e["roomNo"], e["field_0xb"]
)
return data
def packageCAM(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">16sBBBBHH",
bytes(e["Camera Type"], "ascii"),
e["field_0x10"],
e["field_0x11"],
e["field_0x12"],
e["field_0x13"],
e["field_0x14"],
e["field_0x16"],
)
return data
def packageRARO(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">fffhhhBB",
e["x"],
e["y"],
e["z"],
e["Angle X"],
e["Angle Y"],
e["Angle Z"],
e["field_0x12"],
e["field_0x13"],
)
return data
def packageEVLY(entries, offset):
data = bytearray()
for e in entries:
for v in e["layerTable"]:
data.append(v)
return data
def packageVRB(entries, offset):
data = bytearray()
for e in entries:
data += packRGB(e["Sky Color"])
data += packRGB(e["Cloud Color"])
for col in e["Other Colors"]:
data += packRGB(col)
return data
def packageEnv(entries, offset):
data = bytearray()
for e in entries:
for v in e["Pselect ID Table"]:
data.append(v)
return data
def packageCol(entries, offset):
data = bytearray()
for e in entries:
for v in e["Palette Ids"]:
data.append(v)
data += struct.pack(">f", e["Change Rate"])
return data
def packRGB(arr):
return struct.pack(">BBB", arr[0], arr[1], arr[2])
def packagePAL(entries, offset):
data = bytearray()
for e in entries:
data += packRGB(e["Actor Ambient Color"])
for col in e["BG Ambient Colors"]:
data += packRGB(col)
for col in e["P Light Colors"]:
data += packRGB(col)
data += packRGB(e["Fog Color"])
data += struct.pack(
">ffBBBBBBBB",
e["Fog Start Z"],
e["Fog End Z"],
e["Virt Idx"],
e["Terrain Light Influence"],
e["Cloud Shadow Density"],
e["field_0x2f"],
e["Bloom Table Idx"],
e["BG Ambient Color 1a"],
e["BG Ambient Color 2a"],
e["BG Ambient Color 3a"],
)
return data
def packageFILI(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">Ifff", e["Parameters"], e["Sea Level"], e["field_0x8"], e["field_0xc"]
)
for i in e["field_0x10"]:
data.append(i)
data += struct.pack(">BBH", e["Default Camera"], e["Bit Sw"], e["Msg"])
return data
def packageFILI2(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">ffffBBBBffh",
e["Left Room X"],
e["Inner Room Z"],
e["Right Room X"],
e["Front Room Z"],
e["Min Floor No."],
e["Max Floor No."],
e["field_0x12"],
e["field_0x13"],
e["field_0x14"],
e["field_0x18"],
e["field_0x1c"],
)
return data
def packageSCLS(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">8sBbBBb",
bytes(e["Stage"], "ascii"),
e["Start"],
e["Room"],
e["field_0xa"],
e["field_0xb"],
e["Wipe"],
)
return data
def packageLBNK(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(">BBB", e["field_0x0"], e["field_0x1"], e["field_0x2"])
return data
def packageREVT(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">BBBBBBBBBBBBB",
e["Type"],
e["field_0x1"],
e["field_0x2"],
e["field_0x3"],
e["field_0x4"],
e["field_0x5"],
e["Priority"],
e["field_0x7"],
e["field_0x8"],
e["field_0x9"],
e["field_0xa"],
e["field_0xb"],
e["field_0xc"],
)
if isinstance(e["Name"], str):
data += struct.pack(">11s", bytes(e["Name"], "ascii"))
else:
for i in e["Name"]:
data.append(i)
data += struct.pack(
">BBBB", e["seType"], e["field_0x1a"], e["field_0x1b"], e["switch"]
)
return data
def packageACTR(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">8sIfffhhhh",
bytes(e["Name"], "ascii"),
e["param"],
e["x"],
e["y"],
e["z"],
e["Angle X"],
e["Angle Y"],
e["Angle Z"],
e["EnemyNo"],
)
return data
def packageLGT(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">fffffffBBBB",
e["x"],
e["y"],
e["z"],
e["Radius"],
e["Direction X"],
e["Direction Y"],
e["Spotlight Cutoff"],
e["field_0x1c"],
e["field_0x1d"],
e["field_0x1e"],
e["field_0x1f"],
)
return data
def packageTRES(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">8sBBBBfffhhBBBB",
bytes(e["Name"], "ascii"),
e["field_0x8"],
e["Type Flag"],
e["field_0xa"],
e["Appear Type"],
e["Position X"],
e["Position Y"],
e["Position Z"],
e["Room No."],
e["Rotation"],
e["Item"],
e["Flag ID"],
e["field_0x1e"],
e["field_0x1f"],
)
return data
def packageRPAT(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">HhBBHI",
e["Number of Points"],
e["Path Index"],
e["field_0x4"],
e["Looped"],
e["field_0x6"],
e["RPPN Entry Index"] * 0x10,
)
return data
def packageRPPN(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">BBBBfff",
e["field_0x0"],
e["field_0x1"],
e["field_0x2"],
e["field_0x3"],
e["Position X"],
e["Position Y"],
e["Position Z"],
)
return data
def packageTGSC(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">8sIfffhhhhBBBB",
bytes(e["Name"], "ascii"),
e["param"],
e["x"],
e["y"],
e["z"],
e["Angle X"],
e["Angle Y"],
e["Angle Z"],
e["EnemyNo"],
e["Scale X"],
e["Scale Y"],
e["Scale Z"],
e["field_0x23"],
)
return data
def packageStageTRES(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">BbBBfffBBBb",
e["Number"],
e["Room Number"],
e["Status"],
e["Argument 1"],
e["Position X"],
e["Position Y"],
e["Position Z"],
e["Sw Bit"],
e["Type"],
e["Argument 2"],
e["Angle Y"],
)
return data
def packageMPAT(entry, offset):
data = bytearray()
o = 12 + (len(entry["Floors"]) * 8)
data += struct.pack(
">BBHII",
len(entry["Floors"]),
entry["field_0x1"],
len(entry["Vertices"]),
12,
o,
)
o += 8 * len(entry["Vertices"])
group_length = 0
poly_line_length = 0
for floor in entry["Floors"]:
for g in floor["Groups"]:
group_length += 1
poly_line_length += len(g["Lines"]) + len(g["Polygons"])
poly_line_offset = o + (group_length * 20)
index_data_offset = poly_line_offset + (poly_line_length * 8)
float_data = bytearray()
for vertex in entry["Vertices"]:
float_data += struct.pack(">ff", vertex["x"], vertex["z"])
group_data = bytearray()
line_poly_data = bytearray()
index_data = bytearray()
for floor in entry["Floors"]:
data += struct.pack(
">BBBBI",
floor["Id"],
len(floor["Groups"]),
floor["field_0x2"],
floor["field_0x3"],
o + len(group_data),
)
for g in floor["Groups"]:
line_offset = poly_line_offset + len(line_poly_data)
poly_offset = line_offset + (len(g["Lines"]) * 8)
group_data += struct.pack(
">BBBBBBBBIBBBBI",
g["field_0x0"],
g["field_0x1"],
len(g["Lines"]),
g["field_0x3"],
len(g["Polygons"]),
g["field_0x5"],
g["field_0x6"],
g["field_0x7"],
line_offset,
g["field_0xc"],
g["field_0xd"],
g["field_0xe"],
g["field_0xf"],
poly_offset,
)
for l in g["Lines"]:
line_poly_data += struct.pack(
">BBBBI",
l["field_0x0"],
l["field_0x1"],
len(l["Vertex Indexes"]),
l["field_0x3"],
index_data_offset + len(index_data),
)
for i in l["Vertex Indexes"]:
index_data += struct.pack(">H", i)
for p in g["Polygons"]:
line_poly_data += struct.pack(
">BBBBI",
p["field_0x0"],
len(p["Vertex Indexes"]),
p["field_0x2"],
p["field_0x3"],
index_data_offset + len(index_data),
)
for i in p["Vertex Indexes"]:
index_data += struct.pack(">H", i)
return data + float_data + group_data + line_poly_data + index_data
def packageSOND(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(
">8sfffBBBBBBBB",
bytes(e["Name"], "ascii"),
e["x"],
e["y"],
e["z"],
e["field_0x14"],
e["field_0x15"],
e["field_0x16"],
e["field_0x17"],
e["field_0x18"],
e["field_0x19"],
e["field_0x1a"],
e["field_0x1b"],
)
return data
def packageMECO(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(">BB", e["Room Number"], e["Block Id"])
return data
def packageMEMO(entries, offset):
data = bytearray()
for e in entries:
data += struct.pack(">I", e)
return data
def extract(data, roomStage, roomFile):
chunkCount = struct.unpack(">I", data[:4])[0]
offset = 4
chunk_table = []
# struct dStage_nodeHeader {
# /* 0x0 */ u32 m_tag;
# /* 0x4 */ int m_entryNum;
# /* 0x8 */ u32 m_offset;
# };
tbl = dict(decodeFuncTbl)
if roomStage:
tbl["FILI"] = dStage_filiInfo2Init
tbl["TRES"] = dStage_stageKeepTresureInit
tbl["TRE0"] = dStage_stageKeepTresureInit
for i in range(chunkCount):
m_entrynum, m_offset = struct.unpack(">iI", data[offset + 4 : offset + 12])
tag = str(data[offset : offset + 4], "ascii").rstrip("\x00")
offset += 12
real_tag = tag # Used for writing correct layer tags
# print(f"{real_tag}:{m_offset}")
if not tag in tbl:
if tag[-1] in [
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"a",
"b",
"c",
"d",
"e",
]:
real_tag = tag
tag = tag[:-1] + "0"
if not tag in tbl:
print(f"Unkown layer tag: {real_tag}")
continue
else:
print(f"Unkown tag: {tag}")
continue
entries = tbl[tag](m_entrynum, m_offset, data)
chunk_table.append({"Tag": real_tag, "Offset": m_offset, "Entries": entries})
# Here, we sort the entries by their original offsets, so we can rebuild their data in the correct order
chunk_table.sort(key=lambda x: x["Offset"])
for chunk in chunk_table:
del chunk["Offset"]
return json.dumps(chunk_table, indent=2)
encodeFuncTbl = {
"STAG": packageSTAG,
"RTBL": packageRTBL,
"MULT": packageMULT,
"RCAM": packageCAM,
"RARO": packageRARO,
"AROB": packageRARO,
"EVLY": packageEVLY,
"VRB0": packageVRB,
"Env0": packageEnv,
"Col0": packageCol,
"PAL0": packagePAL,
"SCLS": packageSCLS,
"FILI": packageFILI,
"LBNK": packageLBNK,
"REVT": packageREVT,
"PLYR": packageACTR,
"ACTR": packageACTR,
"TGOB": packageACTR,
"TRES": packageTRES,
"ACT0": packageACTR,
"TRE0": packageACTR,
"LGT0": packageLGT,
"LGTV": packageLGT,
"RPAT": packageRPAT,
"RPPN": packageRPPN,
"SCOB": packageTGSC,
"TGSC": packageTGSC,
"TGDR": packageTGSC,
"SCO0": packageTGSC,
"MPAT": packageMPAT,
"MPA0": packageMPAT,
"SOND": packageSOND,
"SON0": packageSOND,
"MEC0": packageMECO,
"MEM0": packageMEMO,
"Door": packageTGSC,
"Doo0": packageTGSC,
}
package_order = [
"STAG",
"RTBL",
"SCLS",
"REVT",
"MULT",
"FILI",
"LBNK",
"RPAT",
"RPPN",
"RCAM",
"RARO",
"EVLY",
"PLYR",
"ACTR",
"SCOB",
"SOND",
"LGTV",
"TRES",
"MPAT",
"Door",
]
layer_order = [
"ACT",
"SCO",
"SON",
"MEC",
"MEM",
"LGT",
"MPA",
"VRB",
"Env",
"Col",
"PAL",
"Doo",
"TRE",
]
# PACKAGE
def package(chunk_table, roomStage, roomFile):
tbl = dict(encodeFuncTbl)
if roomStage:
tbl["FILI"] = packageFILI2
tbl["TRES"] = packageStageTRES
tbl["TRE0"] = packageStageTRES
data = bytearray()
header_table = []
header_size = 4 + (len(chunk_table) * 12)
for chunk in chunk_table:
tag = chunk["Tag"]
real_tag = tag
if not tag in tbl:
if tag[-1] in [
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"a",
"b",
"c",
"d",
"e",
]:
real_tag = tag
tag = tag[:-1] + "0"
if not tag in tbl:
print(f"Unkown layer tag: {real_tag}")
continue
else:
print(f"Unkown tag: {tag}")
continue
offset = header_size + len(data)
# print(f"{real_tag}:{offset}")
num_entries = 0
if isinstance(chunk["Entries"], dict):
num_entries = chunk["Entries"]["Entry Num"]
else:
num_entries = len(chunk["Entries"])
header_data = {"Tag": real_tag, "Offset": offset, "Entries": num_entries}
data += tbl[tag](chunk["Entries"], offset)
if len(data) % 4 != 0:
data += bytearray([0xFF] * (4 - (len(data) % 4)))
header_table.append(header_data)
order = []
order += package_order
for i in range(15):
for j in layer_order:
order.append(j + (f"{i:x}"))
tag_to_index = {tag: index for index, tag in enumerate(order)}
header_table.sort(key=lambda item: tag_to_index[item["Tag"]])
header_data = bytearray()
header_data += struct.pack(">I", len(header_table))
for item in header_table:
# print(f"{item['Tag']}:{item['Offset']}")
t = item["Tag"]
header_data += struct.pack(
">BBBBiI",
ord(t[0]),
ord(t[1]),
ord(t[2]),
ord(t[3]),
item["Entries"],
item["Offset"],
)
data = header_data + data
if roomFile:
data += struct.pack(">IIIIIIII", 0, 0x10, 0x10, 0x10, 0, 0x10, 0x10, 0x10)
if len(data) % 0x20 != 0:
data += bytearray([0xFF] * (0x20 - (len(data) % 0x20)))
return data
# To package, we first must write the entry data in the order
# given by the JSON, then we write Chunk data in the correct order, pointing
# to the right data.
def extract_to_json(name, data, writeFunc):
name = str(name)
roomFile = name.rfind(".dzr") != -1
roomStage = False
if not roomFile:
roomStage = name.find("room") != -1
print(f"Converting {name} to {name+'.json'}")
out = extract(data, roomStage, roomFile)
writeFunc(name + ".json", bytes(out, "ascii"))
return name + ".json"
def package_from_json(name, convertFunc):
name = str(name)
roomFile = name.rfind(".dzr") != -1
roomStage = False
if not roomFile:
roomStage = name.find("room") != -1
data = None
with open(name, "r") as f:
data = json.loads(f.read())
out = package(data, roomStage, roomFile)
return out
def main():
if len(argv) < 3:
print(
"Usage: 'python3 libstage.py input.dzs output.json' or 'python3 libstage.py input.json output.dzs'"
)
if argv[1] == "test":
testFiles = argv[2:]
for file in testFiles:
room_file = file.rfind(".dzr") != -1
room_stage = False
if not room_file:
room_stage = file.find("room") != -1
data = None
with open(file, "rb") as f:
data = f.read()
print(f"Testing {file}")
out = ""
data2 = bytearray()
try:
print(" Extract:")
out = extract(data, room_stage, room_file)
print(" Package:")
data2 = package(json.loads(out), room_stage, room_file)
if data != data2:
print("Diff Found")
raise Exception
except Exception:
print("Dumping")
ext = ".dzs" if not room_file else ".dzr"
with open("in" + ext, "wb") as f:
f.write(data)
with open("out.json", "w") as f:
f.write(out)
with open("out" + ext, "wb") as f:
f.write(data2)
print(traceback.format_exc())
return
print("Done!")
elif argv[2].rfind(".json") == -1:
chunk_table = None
with open(argv[1], "r") as f:
chunk_table = json.load(f)
out = None
room_file = argv[2].rfind(".dzr") != -1
room_stage = False
if not room_file:
room_stage = argv[2].find("room") != -1
out = package(chunk_table, room_stage, room_file)
with open(argv[2], "wb") as f:
f.write(out)
else:
data = None
with open(argv[1], "rb") as f:
data = f.read()
out = None
room_file = argv[1].rfind(".dzr") != -1
room_stage = False
if not room_file:
room_stage = argv[1].find("room") != -1
out = extract(data, room_stage, room_file)
with open(argv[2], "w") as f:
f.write(out)
if __name__ == "__main__":
main()