mirror of https://github.com/zeldaret/tp.git
1865 lines
52 KiB
Python
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()
|