first commit

This commit is contained in:
2026-06-14 01:40:04 +02:00
commit 5511354783
4787 changed files with 103543 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+886
View File
@@ -0,0 +1,886 @@
'use strict';
// ─── Bytecode action opcodes (hex values) ───
const ACTION_END = 0x00;
const ACTION_NEXTFRAME = 0x04;
const ACTION_PREVFRAME = 0x05;
const ACTION_PLAY = 0x06;
const ACTION_STOP = 0x07;
const ACTION_TOGGLEQUALITY = 0x08;
const ACTION_STOPSOUNDS = 0x09;
const ACTION_ADD = 0x0A;
const ACTION_SUBTRACT = 0x0B;
const ACTION_MULTIPLY = 0x0C;
const ACTION_DIVIDE = 0x0D;
const ACTION_EQUAL = 0x0E;
const ACTION_LESSTHAN = 0x0F;
const ACTION_LOGICALAND = 0x10;
const ACTION_LOGICALOR = 0x11;
const ACTION_LOGICALNOT = 0x12;
const ACTION_STRINGEQ = 0x13;
const ACTION_STRINGLENGTH = 0x14;
const ACTION_SUBSTRING = 0x15;
const ACTION_POP = 0x17;
const ACTION_INT = 0x18;
const ACTION_GETVARIABLE = 0x1C;
const ACTION_SETVARIABLE = 0x1D;
const ACTION_SETTARGETEXPRESSION = 0x20;
const ACTION_STRINGCONCAT = 0x21;
const ACTION_GETPROPERTY = 0x22;
const ACTION_SETPROPERTY = 0x23;
const ACTION_DUPLICATECLIP = 0x24;
const ACTION_REMOVECLIP = 0x25;
const ACTION_TRACE = 0x26;
const ACTION_STARTDRAGMOVIE = 0x27;
const ACTION_STOPDRAGMOVIE = 0x28;
const ACTION_STRINGCOMPARE = 0x29;
const ACTION_THROW = 0x2A;
const ACTION_CASTOP = 0x2B;
const ACTION_IMPLEMENTSOP = 0x2C;
const ACTION_RANDOM = 0x30;
const ACTION_MBLENGTH = 0x31;
const ACTION_ORD = 0x32;
const ACTION_CHR = 0x33;
const ACTION_GETTIMER = 0x34;
const ACTION_MBSUBSTRING = 0x35;
const ACTION_MBORD = 0x36;
const ACTION_MBCHR = 0x37;
const ACTION_DELETE = 0x3A;
const ACTION_DELETE2 = 0x3B;
const ACTION_DEFINELOCAL = 0x3C;
const ACTION_CALLFUNCTION = 0x3D;
const ACTION_RETURN = 0x3E;
const ACTION_MODULO = 0x3F;
const ACTION_NEW = 0x40;
const ACTION_VAR = 0x41;
const ACTION_INITARRAY = 0x42;
const ACTION_INITOBJECT = 0x43;
const ACTION_TYPEOF = 0x44;
const ACTION_TARGETPATH = 0x45;
const ACTION_ENUMERATE = 0x46;
const ACTION_NEWADD = 0x47;
const ACTION_NEWLESSTHAN = 0x48;
const ACTION_NEWEQUALS = 0x49;
const ACTION_TONUMBER = 0x4A;
const ACTION_TOSTRING = 0x4B;
const ACTION_DUP = 0x4C;
const ACTION_SWAP = 0x4D;
const ACTION_GETMEMBER = 0x4E;
const ACTION_SETMEMBER = 0x4F;
const ACTION_INCREMENT = 0x50;
const ACTION_DECREMENT = 0x51;
const ACTION_CALLMETHOD = 0x52;
const ACTION_NEWMETHOD = 0x53;
const ACTION_INSTANCEOF = 0x54;
const ACTION_ENUM2 = 0x55;
const ACTION_BITWISEAND = 0x60;
const ACTION_BITWISEOR = 0x61;
const ACTION_BITWISEXOR = 0x62;
const ACTION_SHIFTLEFT = 0x63;
const ACTION_SHIFTRIGHT = 0x64;
const ACTION_SHIFTRIGHT2 = 0x65;
const ACTION_STRICTEQ = 0x66;
const ACTION_GREATER = 0x67;
const ACTION_STRINGGREATER = 0x68;
const ACTION_EXTENDS = 0x69;
const ACTION_WAITFORFRAME = 0x8A;
const ACTION_WAITFORFRAMEEXPRESSION = 0x8D;
const ACTION_SETREGISTER = 0x87;
const ACTION_CONSTANTPOOL = 0x88;
const ACTION_PUSHDATA = 0x96;
const ACTION_BRANCHALWAYS = 0x99;
const ACTION_GETURL2 = 0x9A;
const ACTION_DEFINEFUNCTION = 0x9B;
const ACTION_BRANCHIFTRUE = 0x9D;
const ACTION_CALLFRAME = 0x9E;
const ACTION_GOTOEXPRESSION = 0x9F;
const ACTION_GOTOFRAME = 0x81;
const ACTION_GETURL = 0x83;
const ACTION_SETTARGET = 0x8B;
const ACTION_GOTOLABEL = 0x8C;
const ACTION_DEFINEFUNCTION2 = 0x8E;
const ACTION_TRY = 0x8F;
const ACTION_WITH = 0x94;
// EA-specific opcodes
const EA_ACTION56 = 0x56;
const EA_ACTION58 = 0x58;
const EA_PUSHZERO = 0x59;
const EA_PUSHONE = 0x5A;
const EA_CALLFUNCTIONPOP = 0x5B;
const EA_CALLFUNCTION = 0x5C;
const EA_CALLMETHODPOP = 0x5D;
const EA_CALLMETHOD = 0x5E;
const EA_PUSHTHIS = 0x70;
const EA_PUSHGLOBAL = 0x71;
const EA_ZEROVARIABLE = 0x72;
const EA_PUSHTRUE = 0x73;
const EA_PUSHFALSE = 0x74;
const EA_PUSHNULL = 0x75;
const EA_PUSHUNDEFINED = 0x76;
const EA_ACTION77 = 0x77;
const EA_PUSHSTRING = 0xA1;
const EA_PUSHCONSTANT = 0xA2;
const EA_PUSHWORDCONSTANT = 0xA3;
const EA_GETSTRINGVAR = 0xA4;
const EA_GETSTRINGMEMBER = 0xA5;
const EA_SETSTRINGVAR = 0xA6;
const EA_SETSTRINGMEMBER = 0xA7;
const EA_PUSHVALUEOFVAR = 0xAE;
const EA_GETNAMEDMEMBER = 0xAF;
const EA_CALLNAMEDFUNCTIONPOP = 0xB0;
const EA_CALLNAMEDFUNCTION = 0xB1;
const EA_CALLNAMEDMETHODPOP = 0xB2;
const EA_CALLNAMEDMETHOD = 0xB3;
const EA_PUSHFLOAT = 0xB4;
const EA_PUSHBYTE = 0xB5;
const EA_PUSHSHORT = 0xB6;
const EA_PUSHLONG = 0xB7;
const EA_BRANCHIFFALSE = 0xB8;
const EA_PUSHREGISTER = 0xB9;
// ─── Flag enums ───
const PlaceObjectFlags = {
Move: 1,
HasCharacter: 2,
HasMatrix: 4,
HasColorTransform: 8,
HasRatio: 16,
HasName: 32,
HasClipDepth: 64,
HasClipAction: 128
};
const ClipEventFlags = {
KeyUp: 0x800000,
KeyDown: 0x400000,
MouseUp: 0x200000,
MouseDown: 0x100000,
MouseMove: 0x080000,
Unload: 0x040000,
EnterFrame: 0x020000,
Load: 0x010000,
DragOver: 0x008000,
RollOut: 0x004000,
RollOver: 0x002000,
ReleaseOutside: 0x001000,
Release: 0x000800,
Press: 0x000400,
DragOut: 0x000200,
Data: 0x000100,
Construct: 0x000004,
KeyPress: 0x000002,
Initialize: 0x000001
};
const ButtonRecordFlags = {
None: 0,
StateUp: 1,
StateOver: 2,
StateDown: 4,
StateHit: 8
};
const ButtonActionFlags = {
IdleToOverUp: 1,
OverUpToIdle: 2,
OverUpToOverDown: 4,
OverDownToOverUp: 8,
OverDownToOutDown: 16,
OutDownToOverDown: 32,
IdleToOverDown: 128,
OverDownToIdle: 256
};
const ButtonInput = {
MouseButton0: 0,
Left: 1,
Right: 2,
Home: 3,
End: 4,
Insert: 5,
Delete: 6,
BackSpace: 8,
Enter: 13,
Unknown: 240
};
const FunctionPreloadFlags = {
PreloadExtern: 0x010000,
PreloadParent: 0x008000,
PreloadRoot: 0x004000,
SupressSuper: 0x002000,
PreloadSuper: 0x001000,
SupressArguments: 0x000800,
PreloadArguments: 0x000400,
SupressThis: 0x000200,
PreloadThis: 0x000100,
PreloadGlobal: 0x000001
};
// ─── Action name tables ───
// New/shorter style names (default)
const ActionNames = [
"actionEnd", // 0x00
"", // 0x01
"", // 0x02
"", // 0x03
"ActionNextFrame", // 0x04
"ActionPrevFrame", // 0x05
"play", // 0x06
"stop", // 0x07
"ActionToggleQuality", // 0x08
"ActionStopSounds", // 0x09
"ActionAdd", // 0x0A
"ActionSubtract", // 0x0B
"ActionMultiply", // 0x0C
"ActionDivide", // 0x0D
"ActionEqual", // 0x0E
"ActionLessThan", // 0x0F
"ActionLogicalAnd", // 0x10
"ActionLogicalOr", // 0x11
"ActionLogicalNot", // 0x12
"ActionStringEq", // 0x13
"ActionStringLength", // 0x14
"ActionSubString", // 0x15
"", // 0x16
"ActionPop", // 0x17
"ActionInt", // 0x18
"", // 0x19
"", // 0x1A
"", // 0x1B
"ActionGetVariable", // 0x1C
"ActionSetVariable", // 0x1D
"", // 0x1E
"", // 0x1F
"ActionSetTargetExpression", // 0x20
"ActionStringConcat", // 0x21
"ActionGetProperty", // 0x22
"ActionSetProperty", // 0x23
"ActionDuplicateClip", // 0x24
"ActionRemoveClip", // 0x25
"ActionTrace", // 0x26
"ActionStartDrag", // 0x27
"ActionStopDrag", // 0x28
"ActionStringCompare", // 0x29
"ActionThrow", // 0x2A
"ActionCastOp", // 0x2B
"ActionImplementsOp", // 0x2C
"", // 0x2D
"", // 0x2E
"", // 0x2F
"ActionRandom", // 0x30
"ActionMBLength", // 0x31
"ActionOrd", // 0x32
"ActionChr", // 0x33
"ActionGetTimer", // 0x34
"ActionMBSubString", // 0x35
"ActionMBOrd", // 0x36
"ActionMBChr", // 0x37
"", // 0x38
"", // 0x39
"ActionDelete", // 0x3A
"ActionDelete2", // 0x3B
"ActionDefineLocal", // 0x3C
"ActionCallFunction", // 0x3D
"ActionReturn", // 0x3E
"ActionModulo", // 0x3F
"ActionNew", // 0x40
"ActionVar", // 0x41
"ActionInitArray", // 0x42
"ActionInitObject", // 0x43
"ActionTypeOf", // 0x44
"ActionTargetPath", // 0x45
"ActionEnumerate", // 0x46
"ActionNewAdd", // 0x47
"ActionNewLessThan", // 0x48
"ActionNewEquals", // 0x49
"ActionToNumber", // 0x4A
"ActionToString", // 0x4B
"ActionDup", // 0x4C
"ActionSwap", // 0x4D
"ActionGetMember", // 0x4E
"ActionSetMember", // 0x4F
"ActionIncrement", // 0x50
"ActionDecrement", // 0x51
"ActionCallMethod", // 0x52
"ActionNewMethod", // 0x53
"ActionInstanceOf", // 0x54
"ActionEnum2", // 0x55
"EAUnknownAction56", // 0x56
"", // 0x57
"EAUnknownAction58", // 0x58
"EAPushZero", // 0x59
"EAPushOne", // 0x5A
"EACallFunctionPop", // 0x5B
"callset", // 0x5C
"callmethodpop", // 0x5D
"callmethodset", // 0x5E
"", // 0x5F
"bitand", // 0x60
"bitor", // 0x61
"xor", // 0x62
"lsh", // 0x63
"rsh", // 0x64
"unsignedrsh", // 0x65
"stricteq", // 0x66
"gt", // 0x67
"strgt", // 0x68
"extends", // 0x69
"", // 0x6A
"", // 0x6B
"", // 0x6C
"", // 0x6D
"", // 0x6E
"", // 0x6F
"pushthis", // 0x70
"pushglobal", // 0x71
"zerovar", // 0x72
"pushtrue", // 0x73
"pushfalse", // 0x74
"pushnull", // 0x75
"pushundefined", // 0x76
"", // 0x77
"", // 0x78
"", // 0x79
"", // 0x7A
"", // 0x7B
"", // 0x7C
"", // 0x7D
"", // 0x7E
"", // 0x7F
"", // 0x80
"gotoframe", // 0x81
"", // 0x82
"geturl", // 0x83
"", // 0x84
"", // 0x85
"", // 0x86
"setreg", // 0x87
"ActionConstantPool", // 0x88
"", // 0x89
"waitforframe", // 0x8A
"settarget", // 0x8B
"gotolabel", // 0x8C
"waitforframeexpr", // 0x8D
"ActionDefineFunction2", // 0x8E
"try", // 0x8F
"", // 0x90
"", // 0x91
"", // 0x92
"", // 0x93
"with", // 0x94
"", // 0x95
"pushdata", // 0x96
"", // 0x97
"", // 0x98
"jump", // 0x99
"geturl2", // 0x9A
"ActionDefineFunction", // 0x9B
"", // 0x9C
"jumptrue", // 0x9D
"callframe", // 0x9E
"gotoexpr", // 0x9F
"", // 0xA0
"pushstr", // 0xA1
"pushconst", // 0xA2
"pushwconst", // 0xA3
"getstrvar", // 0xA4
"getstrmember", // 0xA5
"setstrvar", // 0xA6
"setstrmember", // 0xA7
"", // 0xA8
"", // 0xA9
"", // 0xAA
"", // 0xAB
"", // 0xAC
"", // 0xAD
"pushvar", // 0xAE
"getnamedmember", // 0xAF
"callnamedpop", // 0xB0
"callnamedset", // 0xB1
"callnamedmethodpop", // 0xB2
"callnamedmethodset", // 0xB3
"pushfloat", // 0xB4
"pushbyte", // 0xB5
"pushshort", // 0xB6
"pushlong", // 0xB7
"jumpfalse", // 0xB8
"pushreg" // 0xB9
];
// Old/verbose style names
const ActionNames2 = [
"ActionEnd", // 0x00
"", // 0x01
"", // 0x02
"", // 0x03
"ActionNextFrame", // 0x04
"ActionPrevFrame", // 0x05
"ActionPlay", // 0x06
"ActionStop", // 0x07
"ActionToggleQuality", // 0x08
"ActionStopSounds", // 0x09
"ActionAdd", // 0x0A
"ActionSubtract", // 0x0B
"ActionMultiply", // 0x0C
"ActionDivide", // 0x0D
"ActionEqual", // 0x0E
"ActionLessThan", // 0x0F
"ActionLogicalAnd", // 0x10
"ActionLogicalOr", // 0x11
"ActionLogicalNot", // 0x12
"ActionStringEq", // 0x13
"ActionStringLength", // 0x14
"ActionSubString", // 0x15
"", // 0x16
"ActionPop", // 0x17
"ActionInt", // 0x18
"", // 0x19
"", // 0x1A
"", // 0x1B
"ActionGetVariable", // 0x1C
"ActionSetVariable", // 0x1D
"", // 0x1E
"", // 0x1F
"ActionSetTargetExpression", // 0x20
"ActionStringConcat", // 0x21
"ActionGetProperty", // 0x22
"ActionSetProperty", // 0x23
"ActionDuplicateClip", // 0x24
"ActionRemoveClip", // 0x25
"ActionTrace", // 0x26
"ActionStartDrag", // 0x27
"ActionStopDrag", // 0x28
"ActionStringCompare", // 0x29
"ActionThrow", // 0x2A
"ActionCastOp", // 0x2B
"ActionImplementsOp", // 0x2C
"", // 0x2D
"", // 0x2E
"", // 0x2F
"ActionRandom", // 0x30
"ActionMBLength", // 0x31
"ActionOrd", // 0x32
"ActionChr", // 0x33
"ActionGetTimer", // 0x34
"ActionMBSubString", // 0x35
"ActionMBOrd", // 0x36
"ActionMBChr", // 0x37
"", // 0x38
"", // 0x39
"ActionDelete", // 0x3A
"ActionDelete2", // 0x3B
"ActionDefineLocal", // 0x3C
"ActionCallFunction", // 0x3D
"ActionReturn", // 0x3E
"ActionModulo", // 0x3F
"ActionNew", // 0x40
"ActionVar", // 0x41
"ActionInitArray", // 0x42
"ActionInitObject", // 0x43
"ActionTypeOf", // 0x44
"ActionTargetPath", // 0x45
"ActionEnumerate", // 0x46
"ActionNewAdd", // 0x47
"ActionNewLessThan", // 0x48
"ActionNewEquals", // 0x49
"ActionToNumber", // 0x4A
"ActionToString", // 0x4B
"ActionDup", // 0x4C
"ActionSwap", // 0x4D
"ActionGetMember", // 0x4E
"ActionSetMember", // 0x4F
"ActionIncrement", // 0x50
"ActionDecrement", // 0x51
"ActionCallMethod", // 0x52
"ActionNewMethod", // 0x53
"ActionInstanceOf", // 0x54
"ActionEnum2", // 0x55
"EAUnknownAction56", // 0x56
"", // 0x57
"EAUnknownAction58", // 0x58
"EAPushZero", // 0x59
"EAPushOne", // 0x5A
"EACallFunctionPop", // 0x5B
"EACallFunction", // 0x5C
"EACAllMethodPop", // 0x5D
"EACallMethod", // 0x5E
"", // 0x5F
"ActionBitwiseAnd", // 0x60
"ActionBitwiseOr", // 0x61
"ActionBitwiseXor", // 0x62
"ActionShiftLeft", // 0x63
"ActionShiftRight", // 0x64
"ActionShiftRight2", // 0x65
"ActionStrictEq", // 0x66
"ActionGreater", // 0x67
"ActionStringGreater", // 0x68
"ActionExtends", // 0x69
"", // 0x6A
"", // 0x6B
"", // 0x6C
"", // 0x6D
"", // 0x6E
"", // 0x6F
"EAPushThis", // 0x70
"EAPushGlobal", // 0x71
"EAZeroVariable", // 0x72
"EAPushTrue", // 0x73
"EAPushFalse", // 0x74
"EAPushNull", // 0x75
"EAPushUndefined", // 0x76
"", // 0x77
"", // 0x78
"", // 0x79
"", // 0x7A
"", // 0x7B
"", // 0x7C
"", // 0x7D
"", // 0x7E
"", // 0x7F
"", // 0x80
"ActionGotoFrame", // 0x81
"", // 0x82
"ActionGetURL", // 0x83
"", // 0x84
"", // 0x85
"", // 0x86
"ActionSetRegister", // 0x87
"ActionConstantPool", // 0x88
"", // 0x89
"ActionWaitForFrame", // 0x8A
"ActionSetTarget", // 0x8B
"ActionGotoLabel", // 0x8C
"ActionWaitForFrameExpression", // 0x8D
"ActionDefineFunction2", // 0x8E
"ActionTry", // 0x8F
"", // 0x90
"", // 0x91
"", // 0x92
"", // 0x93
"ActionWith", // 0x94
"", // 0x95
"ActionPushData", // 0x96
"", // 0x97
"", // 0x98
"ActionBranchAlways", // 0x99
"ActionGetURL2", // 0x9A
"ActionDefineFunction", // 0x9B
"", // 0x9C
"ActionBranchIfTrue", // 0x9D
"ActionCallFrame", // 0x9E
"ActionGotoExpression", // 0x9F
"", // 0xA0
"EAPushString", // 0xA1
"EAPushConstant", // 0xA2
"EAPushWordConstant", // 0xA3
"EAGetStringVar", // 0xA4
"EAGetStringMember", // 0xA5
"EASetStringVar", // 0xA6
"EASetStringMember", // 0xA7
"", // 0xA8
"", // 0xA9
"", // 0xAA
"", // 0xAB
"", // 0xAC
"", // 0xAD
"EAPushValueOfVar", // 0xAE
"EAGetNamedMember", // 0xAF
"EACallNamedFunctionPop", // 0xB0
"EACallNamedFunction", // 0xB1
"EACallNamedMethodPop", // 0xB2
"EACallNamedMethod", // 0xB3
"EAPushFloat", // 0xB4
"EAPushByte", // 0xB5
"EAPushShort", // 0xB6
"EAPushLong", // 0xB7
"EABranchIfFalse", // 0xB8
"EAPushRegister" // 0xB9
];
// ─── Action alignment table ───
// 1 = this action's body needs alignment to 4-byte boundary
const ActionAligns = new Array(0xB9 + 1).fill(0);
ActionAligns[ACTION_BRANCHALWAYS] = 1;
ActionAligns[ACTION_BRANCHIFTRUE] = 1;
ActionAligns[EA_BRANCHIFFALSE] = 1;
ActionAligns[ACTION_GOTOFRAME] = 1;
ActionAligns[ACTION_SETREGISTER] = 1;
ActionAligns[ACTION_WITH] = 1;
ActionAligns[ACTION_GOTOEXPRESSION] = 1;
ActionAligns[ACTION_GETURL] = 1;
ActionAligns[ACTION_CONSTANTPOOL] = 1;
ActionAligns[ACTION_PUSHDATA] = 1;
ActionAligns[ACTION_DEFINEFUNCTION2] = 1;
ActionAligns[ACTION_DEFINEFUNCTION] = 1;
ActionAligns[EA_PUSHSTRING] = 1;
ActionAligns[EA_GETSTRINGVAR] = 1;
ActionAligns[EA_GETSTRINGMEMBER] = 1;
ActionAligns[EA_SETSTRINGVAR] = 1;
ActionAligns[EA_SETSTRINGMEMBER] = 1;
ActionAligns[ACTION_SETTARGET] = 1;
ActionAligns[ACTION_GOTOLABEL] = 1;
// ─── Action size table ───
// Fixed byte size of each action (0 = unknown/empty, 1 = no additional data)
const ActionSizes = [
1, // ActionEnd 0x00
0, // (empty) 0x01
0, // (empty) 0x02
0, // (empty) 0x03
1, // ActionNextFrame 0x04
1, // ActionPrevFrame 0x05
1, // ActionPlay 0x06
1, // ActionStop 0x07
1, // ActionToggleQuality 0x08
1, // ActionStopSounds 0x09
1, // ActionAdd 0x0A
1, // ActionSubtract 0x0B
1, // ActionMultiply 0x0C
1, // ActionDivide 0x0D
1, // ActionEqual 0x0E
1, // ActionLessThan 0x0F
1, // ActionLogicalAnd 0x10
1, // ActionLogicalOr 0x11
1, // ActionLogicalNot 0x12
1, // ActionStringEq 0x13
1, // ActionStringLength 0x14
1, // ActionSubString 0x15
0, // (empty) 0x16
1, // ActionPop 0x17
1, // ActionInt 0x18
0, // 0x19
0, // 0x1A
0, // 0x1B
1, // ActionGetVariable 0x1C
1, // ActionSetVariable 0x1D
0, // 0x1E
0, // 0x1F
1, // ActionSetTargetExpression 0x20
1, // ActionStringConcat 0x21
1, // ActionGetProperty 0x22
1, // ActionSetProperty 0x23
1, // ActionDuplicateClip 0x24
1, // ActionRemoveClip 0x25
1, // ActionTrace 0x26
1, // ActionStartDrag 0x27
1, // ActionStopDrag 0x28
1, // ActionStringCompare 0x29
1, // ActionThrow 0x2A
1, // ActionCastOp 0x2B
1, // ActionImplementsOp 0x2C
0, // 0x2D
0, // 0x2E
0, // 0x2F
1, // ActionRandom 0x30
1, // ActionMBLength 0x31
1, // ActionOrd 0x32
1, // ActionChr 0x33
1, // ActionGetTimer 0x34
1, // ActionMBSubString 0x35
1, // ActionMBOrd 0x36
1, // ActionMBChr 0x37
0, // 0x38
0, // 0x39
1, // ActionDelete 0x3A
1, // ActionDelete2 0x3B
1, // ActionDefineLocal 0x3C
1, // ActionCallFunction 0x3D
1, // ActionReturn 0x3E
1, // ActionModulo 0x3F
1, // ActionNew 0x40
1, // ActionVar 0x41
1, // ActionInitArray 0x42
1, // ActionInitObject 0x43
1, // ActionTypeOf 0x44
1, // ActionTargetPath 0x45
1, // ActionEnumerate 0x46
1, // ActionNewAdd 0x47
1, // ActionNewLessThan 0x48
1, // ActionNewEquals 0x49
1, // ActionToNumber 0x4A
1, // ActionToString 0x4B
1, // ActionDup 0x4C
1, // ActionSwap 0x4D
1, // ActionGetMember 0x4E
1, // ActionSetMember 0x4F
1, // ActionIncrement 0x50
1, // ActionDecrement 0x51
1, // ActionCallMethod 0x52
1, // ActionNewMethod 0x53
1, // ActionInstanceOf 0x54
1, // ActionEnum2 0x55
1, // EAUnknownAction56 0x56
0, // 0x57
1, // EAUnknownAction58 0x58
1, // EAPushZero 0x59
1, // EAPushOne 0x5A
1, // EACallFunctionPop 0x5B
1, // EACallFunction 0x5C
1, // EACAllMethodPop 0x5D
1, // EACallMethod 0x5E
0, // 0x5F
1, // ActionBitwiseAnd 0x60
1, // ActionBitwiseOr 0x61
1, // ActionBitwiseXor 0x62
1, // ActionShiftLeft 0x63
1, // ActionShiftRight 0x64
1, // ActionShiftRight2 0x65
1, // ActionStrictEq 0x66
1, // ActionGreater 0x67
1, // ActionStringGreater 0x68
1, // ActionExtends 0x69
0, // 0x6A
0, // 0x6B
0, // 0x6C
0, // 0x6D
0, // 0x6E
0, // 0x6F
1, // EAPushThis 0x70
1, // EAPushGlobal 0x71
1, // EAZeroVariable 0x72
1, // EAPushTrue 0x73
1, // EAPushFalse 0x74
1, // EAPushNull 0x75
1, // EAPushUndefined 0x76
0, // 0x77
0, // 0x78
0, // 0x79
0, // 0x7A
0, // 0x7B
0, // 0x7C
0, // 0x7D
0, // 0x7E
0, // 0x7F
0, // 0x80
5, // ActionGotoFrame 0x81
0, // 0x82
9, // ActionGetURL 0x83
0, // 0x84
0, // 0x85
0, // 0x86
5, // ActionSetRegister 0x87
9, // ActionConstantPool 0x88
0, // 0x89
1, // ActionWaitForFrame 0x8A
5, // ActionSetTarget 0x8B
5, // ActionGotoLabel 0x8C
1, // ActionWaitForFrameExpression 0x8D
29, // ActionDefineFunction2 0x8E
1, // ActionTry 0x8F
0, // 0x90
0, // 0x91
0, // 0x92
0, // 0x93
5, // ActionWith 0x94
0, // 0x95
9, // ActionPushData 0x96
0, // 0x97
0, // 0x98
5, // ActionBranchAlways 0x99
1, // ActionGetURL2 0x9A
25, // ActionDefineFunction 0x9B
0, // 0x9C
5, // ActionBranchIfTrue 0x9D
1, // ActionCallFrame 0x9E
5, // ActionGotoExpression 0x9F
0, // 0xA0
5, // EAPushString 0xA1
2, // EAPushConstant 0xA2
3, // EAPushWordConstant 0xA3
5, // EAGetStringVar 0xA4
5, // EAGetStringMember 0xA5
5, // EASetStringVar 0xA6
5, // EASetStringMember 0xA7
0, // 0xA8
0, // 0xA9
0, // 0xAA
0, // 0xAB
0, // 0xAC
0, // 0xAD
2, // EAPushValueOfVar 0xAE
2, // EAGetNamedMember 0xAF
2, // EACallNamedFunctionPop 0xB0
2, // EACallNamedFunction 0xB1
2, // EACallNamedMethodPop 0xB2
2, // EACallNamedMethod 0xB3
5, // EAPushFloat 0xB4
2, // EAPushByte 0xB5
3, // EAPushShort 0xB6
5, // EAPushLong 0xB7
5, // EABranchIfFalse 0xB8
2 // EAPushRegister 0xB9
];
// ─── Composed helpers ───
// Selector string for action nodes that reference constants
const CONST_ACTION_SELECTOR = '[action=' + EA_PUSHCONSTANT + '],' +
'[action=' + EA_CALLNAMEDFUNCTIONPOP + '],' +
'[action=' + EA_CALLNAMEDFUNCTION + '],' +
'[action=' + EA_CALLNAMEDMETHODPOP + '],' +
'[action=' + EA_CALLNAMEDMETHOD + '],' +
'[action=' + EA_PUSHVALUEOFVAR + '],' +
'[action=' + EA_GETNAMEDMEMBER + '],' +
'[action=' + EA_PUSHWORDCONSTANT + ']';
// Selector for function-splitting tag names (used in expand)
const FUNCTION_SPLITTERS = 'definefunction2,definefunction,constantpool,end';
// System argument order for definefunction2
const SYSTEM_ARGUMENTS = ['this', 'arguments', 'super', '_root', '_parent', '_global', '_extern'];
// ─── Exports ───
module.exports = {
// opcodes
ACTION_END, ACTION_NEXTFRAME, ACTION_PREVFRAME, ACTION_PLAY, ACTION_STOP,
ACTION_TOGGLEQUALITY, ACTION_STOPSOUNDS, ACTION_ADD, ACTION_SUBTRACT,
ACTION_MULTIPLY, ACTION_DIVIDE, ACTION_EQUAL, ACTION_LESSTHAN,
ACTION_LOGICALAND, ACTION_LOGICALOR, ACTION_LOGICALNOT, ACTION_STRINGEQ,
ACTION_STRINGLENGTH, ACTION_SUBSTRING, ACTION_POP, ACTION_INT,
ACTION_GETVARIABLE, ACTION_SETVARIABLE, ACTION_SETTARGETEXPRESSION,
ACTION_STRINGCONCAT, ACTION_GETPROPERTY, ACTION_SETPROPERTY,
ACTION_DUPLICATECLIP, ACTION_REMOVECLIP, ACTION_TRACE,
ACTION_STARTDRAGMOVIE, ACTION_STOPDRAGMOVIE, ACTION_STRINGCOMPARE,
ACTION_THROW, ACTION_CASTOP, ACTION_IMPLEMENTSOP, ACTION_RANDOM,
ACTION_MBLENGTH, ACTION_ORD, ACTION_CHR, ACTION_GETTIMER,
ACTION_MBSUBSTRING, ACTION_MBORD, ACTION_MBCHR, ACTION_DELETE,
ACTION_DELETE2, ACTION_DEFINELOCAL, ACTION_CALLFUNCTION, ACTION_RETURN,
ACTION_MODULO, ACTION_NEW, ACTION_VAR, ACTION_INITARRAY, ACTION_INITOBJECT,
ACTION_TYPEOF, ACTION_TARGETPATH, ACTION_ENUMERATE, ACTION_NEWADD,
ACTION_NEWLESSTHAN, ACTION_NEWEQUALS, ACTION_TONUMBER, ACTION_TOSTRING,
ACTION_DUP, ACTION_SWAP, ACTION_GETMEMBER, ACTION_SETMEMBER,
ACTION_INCREMENT, ACTION_DECREMENT, ACTION_CALLMETHOD, ACTION_NEWMETHOD,
ACTION_INSTANCEOF, ACTION_ENUM2, ACTION_BITWISEAND, ACTION_BITWISEOR,
ACTION_BITWISEXOR, ACTION_SHIFTLEFT, ACTION_SHIFTRIGHT, ACTION_SHIFTRIGHT2,
ACTION_STRICTEQ, ACTION_GREATER, ACTION_STRINGGREATER, ACTION_EXTENDS,
ACTION_GOTOFRAME, ACTION_GETURL, ACTION_WAITFORFRAME, ACTION_SETTARGET,
ACTION_GOTOLABEL, ACTION_WAITFORFRAMEEXPRESSION, ACTION_PUSHDATA,
ACTION_BRANCHALWAYS, ACTION_GETURL2, ACTION_DEFINEFUNCTION,
ACTION_BRANCHIFTRUE, ACTION_CALLFRAME, ACTION_GOTOEXPRESSION,
ACTION_CONSTANTPOOL, ACTION_DEFINEFUNCTION2, ACTION_TRY, ACTION_WITH,
ACTION_SETREGISTER,
EA_ACTION56, EA_ACTION58, EA_PUSHZERO, EA_PUSHONE,
EA_CALLFUNCTIONPOP, EA_CALLFUNCTION, EA_CALLMETHODPOP, EA_CALLMETHOD,
EA_PUSHTHIS, EA_PUSHGLOBAL, EA_ZEROVARIABLE, EA_PUSHTRUE, EA_PUSHFALSE,
EA_PUSHNULL, EA_PUSHUNDEFINED, EA_ACTION77,
EA_PUSHSTRING, EA_PUSHCONSTANT, EA_PUSHWORDCONSTANT,
EA_GETSTRINGVAR, EA_GETSTRINGMEMBER, EA_SETSTRINGVAR, EA_SETSTRINGMEMBER,
EA_PUSHVALUEOFVAR, EA_GETNAMEDMEMBER,
EA_CALLNAMEDFUNCTIONPOP, EA_CALLNAMEDFUNCTION,
EA_CALLNAMEDMETHODPOP, EA_CALLNAMEDMETHOD,
EA_PUSHFLOAT, EA_PUSHBYTE, EA_PUSHSHORT, EA_PUSHLONG,
EA_BRANCHIFFALSE, EA_PUSHREGISTER,
// flag enums
PlaceObjectFlags, ClipEventFlags, ButtonRecordFlags,
ButtonActionFlags, ButtonInput, FunctionPreloadFlags,
// name tables
ActionNames, ActionNames2, ActionAligns, ActionSizes,
// composed helpers
CONST_ACTION_SELECTOR, FUNCTION_SPLITTERS, SYSTEM_ARGUMENTS
};
+433
View File
@@ -0,0 +1,433 @@
'use strict';
const fs = require('fs');
const path = require('path');
const cheerio = require('cheerio');
const C = require('./constants');
const U = require('./utils');
// ─── Main expand function ───
function expand(input, output, opts) {
opts = opts || {};
const { filedir, aptName, baseName } = U.resolveFilePaths(input);
const tmpdir = U.createTempDir();
let success = false;
try {
// 1. Copy files to temp
const { manifestData, datData } = U.copyInputToTemp(filedir, tmpdir, baseName, aptName);
// 2. Run apt2xml.exe
U.runApt2Xml(opts.toolsDir, tmpdir, baseName);
// 3. Read raw XML
const rawXml = U.readRawXml(tmpdir, baseName);
// 4. Parse manifest if available (for texture name resolution)
const manifest = manifestData ? cheerio.load(manifestData, { xmlMode: true, decodeEntities: false }) : null;
// 5. Parse .dat if available
const dats = parseDat(datData);
// 6. Run xmladdhint() transformation
const humanXml = xmladdhint(rawXml, baseName, aptName, manifest, dats, filedir, opts.oldStyle);
// 7. Write output
const outPath = output || (filedir + baseName + '_edit.xml');
fs.writeFileSync(outPath, humanXml, 'utf-8');
success = true;
return outPath;
} finally {
if (!opts.keepTemp) {
U.cleanupTempDir(tmpdir, false);
} else {
console.warn('Temporary files kept in: ' + tmpdir);
}
}
}
// ─── Parse .dat file ───
function parseDat(datData) {
const dats = {};
if (!datData) return dats;
const lines = datData.split(/[\r\n]+/);
for (let i = 0; i < lines.length; i++) {
let m;
if ((m = lines[i].match(/([0-9]+)\-\>([0-9]+)/))) {
dats[m[1]] = m[2];
} else if ((m = lines[i].match(/([0-9]+)\=([0-9\s]+)/))) {
dats[m[1]] = m[2].split(/\s+/);
}
}
return dats;
}
// ─── xmladdhint(): raw apt XML → human-readable XML ───
function xmladdhint(rawXml, baseName, aptName, manifest, dats, filedir, oldStyle) {
U.resetLabels();
const $ = cheerio.load(rawXml, { xmlMode: true, decodeEntities: false });
const ActionNames = oldStyle ? C.ActionNames2 : C.ActionNames;
const xmldoc = $;
// Remove duplicated exports
const exp = {};
$('export').each(function () {
const nm = $(this).attr('name');
if (exp[nm]) {
$(this).remove();
} else {
exp[nm] = true;
}
});
// Calculate aligned positions for each action container
const actionContainers = $('initaction, buttonaction, action, clipaction');
actionContainers.each(function () {
const list = $(this).children('[action]');
U.calcaln(list);
});
// Add end-of-function markers
const functions = $(C.FUNCTION_SPLITTERS);
functions.each(function () {
const v = $(this);
if (v[0].tagName.indexOf('definefunction') === 0) {
const size = parseInt(v.attr('size'), 10);
const headaln = U.thisaln(v.next());
let c = null;
let sz = 0;
let cur = v;
for (; sz <= size && cur.length; ) {
c = cur;
sz = U.nextaln(c) - headaln;
if (sz === size) {
break;
}
cur = cur.next();
c = null;
}
if (c) {
const endNode = U.el('end', $);
endNode.insertAfter(c);
endNode.attr('aln', U.nextaln(c));
U.copyindent(endNode, c);
}
}
});
// Resolve constants (expand constant pool references)
const actionConsts = $(C.CONST_ACTION_SELECTOR);
actionConsts.each(function () {
const v = $(this);
const val = parseInt(v.attr('val'), 10);
const c = v.parent().find('constantpool').children().eq(val);
if (c.attr('str') !== undefined) {
v.attr('str', c.attr('str'));
} else {
v.attr('num', c.attr('num'));
}
});
// Resolve jump targets
const gotos = $('goto');
gotos.each(function () {
const v = $(this);
const action = parseInt(v.attr('action'), 10);
if (action === C.ACTION_BRANCHALWAYS || action === C.EA_BRANCHIFFALSE || action === C.ACTION_BRANCHIFTRUE) {
let pos = parseInt(v.attr('pos'), 10);
let targetlabel = v.attr('targetlabel');
if (!targetlabel) {
const naln = U.nextaln(v);
const jumpaln = naln + pos;
const filter = '[aln=' + jumpaln + ']:first';
const target = pos >= 0 ? v.nextAll(filter) : v.prevAll(filter);
if (target.length) {
let label = target.attr('mylabel');
if (!label) {
label = U.makelabel();
target.attr('mylabel', label);
}
v.attr('targetlabel', label);
}
}
}
});
// Convert actions to text
actionContainers.each(function () {
const v = $(this);
let astr = '';
const actions = v.children().not('constantpool');
const indent = [U.indentstr(actions.first())];
actions.each(function () {
const a = $(this);
if (a.attr('mylabel')) {
astr += indent[0];
astr += a.attr('mylabel') + ':';
}
if (a[0].tagName === 'end') {
indent.pop();
}
astr += indent.join('');
astr += actiontostring(a, ActionNames, $);
if (a[0].tagName.indexOf('definefunction') === 0) {
indent.push('\t');
}
});
astr += U.indentstr(v);
// Replace CDATA with text node (entity-encoded)
v.empty().text(astr);
});
// Resolve flags to symbolic names
$('placeobject').each(function () {
$(this).attr('flags', U.valuetoflags($(this).attr('flags'), C.PlaceObjectFlags));
});
$('buttonrecord').each(function () {
$(this).attr('flags', U.valuetoflags($(this).attr('flags'), C.ButtonRecordFlags));
});
$('clipaction').each(function () {
const $v = $(this);
const flags = parseInt($v.attr('flags'), 10);
const realflags = U.endian(flags & 0xffffff, 3);
const keycode = (flags >> 24) & 0xff;
$v.attr('flags', U.valuetoflags(realflags, C.ClipEventFlags));
$v.attr('keycode', keycode);
});
$('buttonaction').each(function () {
const $v = $(this);
const flags = parseInt($v.attr('flags'), 10);
$v.attr('flags', U.valuetoflags(flags, C.ButtonActionFlags));
});
// Image → texture name resolution (from manifest)
if (manifest) {
$('image').each(function () {
const v = $(this);
const id = v.attr('id');
const dd = dats[id];
if (typeof dd === 'string') {
const tex = manifest(`Texture[id=apt_${baseName}_${dd}]`).attr('File');
if (tex) v.attr('image', tex);
} else if (dd) {
const tex = manifest(`Texture[id=apt_${baseName}_${id}]`).attr('File');
if (tex) v.attr('image', tex + ',' + dd.join(' '));
}
});
}
// Shape → embed .ru geometry
$('shape').each(function () {
const $v = $(this);
const id = $v.attr('id');
let ff = null;
if (manifest) {
ff = manifest(`AptGeometryData[id=${baseName}_${id}]`).attr('File');
} else {
ff = $v.attr('geometry');
}
if (ff) {
let ruData = '';
try {
ruData = fs.readFileSync(filedir + ff, 'utf-8');
} catch (e) {
// ru file might not exist
}
if (ruData) {
let ru = '\n' + ruData;
ru = ru.split(/[\r\n]+/);
for (let i = 0; i < ru.length; i++) {
ru[i] = ru[i].trim();
if (ru[i].indexOf('tc') > 0) {
const ts = ru[i].split(':');
const img = $('image[id=' + ts[5] + ']');
ts[5] = img.length ? (img.attr('image') || ts[5]) : ts[5];
ru[i] = ts.join(':');
}
}
ru = ru.join('\n\t\t\t') + '\n\t\t';
$v.attr('geometry', ff);
const ruNode = U.el('ru', $);
ruNode.text(ru);
$v.append('\n\t\t');
$v.append(ruNode);
$v.append('\n\t');
}
}
});
// Clean up placeobject attributes based on flags
$('placeobject').each(function () {
const $v = $(this);
const flags = $v.attr('flags') || '';
if (flags.indexOf('HasCharacter') < 0 || $v.attr('character') === '-1') {
$v.removeAttr('character');
}
if (flags.indexOf('HasMatrix') < 0) {
$v.removeAttr('matrix');
}
if (flags.indexOf('HasColorTransform') < 0) {
$v.removeAttr('rgbamul rgbaadd');
}
if (flags.indexOf('HasRatio') < 0) {
$v.removeAttr('ratio');
}
if (flags.indexOf('HasClipDepth') < 0) {
$v.removeAttr('clipdepth');
}
if (flags.indexOf('HasName') < 0) {
$v.find('poname').remove();
}
if (flags.indexOf('HasClipAction') < 0) {
$v.find('clipactions').remove();
}
});
// Clean up: remove extra attributes from the final output
$('edittext').each(function () {
const $v = $(this);
// Override for xmladdhint: store original color info
// (the original code swaps red/blue for edittext display - preserve that)
});
// Clean internal attributes
$('*').each(function () {
const $v = $(this);
$v.removeAttr('filtered mylabel targetlabel aln');
});
// Serialize
let output = $.xml();
// Wrap action container text content in CDATA sections
// This prevents cheerio from entity-encoding & in the action assembly text
output = output.replace(
/<(initaction|buttonaction|action|clipaction)(\s[^>]*)?>([\s\S]*?)<\/\1>/g,
(match, tag, attrs, content) => {
const trimmed = content.trim();
if (!trimmed) return match;
return '<' + tag + (attrs || '') + '><![CDATA[' + content + ']]></' + tag + '>';
}
);
// Clean up excessive whitespace
output = output.replace(/^[\t]+$/gm, '').replace(/[\r\n]+/g, '\n');
return output;
}
// ─── Decode XML entities in attribute values ───
// With decodeEntities: false in cheerio load, attribute values preserve XML entities
// (e.g. &amp; stays as &amp;). We need to decode them for human-readable output.
function decodeAttrEntities(s) {
if (typeof s !== 'string') return s;
return s.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&#39;/g, "'");
}
// ─── actiontostring(): XML action node → text assembly ───
function actiontostring(a, ActionNames, $) {
const action = parseInt(a.attr('action'), 10);
let aname = ActionNames[action] || ('unknown_' + action);
// Fix sign extension for pushbyte/pushshort
if (action === C.EA_PUSHBYTE) {
a.attr('val', (a.attr('val') << 24) >> 24);
} else if (action === C.EA_PUSHSHORT) {
a.attr('val', (a.attr('val') << 16) >> 16);
}
// Branch instructions: jump label_name
if (action === C.ACTION_BRANCHALWAYS || action === C.ACTION_BRANCHIFTRUE || action === C.EA_BRANCHIFFALSE) {
return aname + ' ' + a.attr('targetlabel');
}
// Constant-referencing instructions: pushconst "str" or pushconst 123
if (action === C.EA_PUSHCONSTANT || action === C.EA_CALLNAMEDFUNCTIONPOP ||
action === C.EA_CALLNAMEDFUNCTION || action === C.EA_CALLNAMEDMETHODPOP ||
action === C.EA_CALLNAMEDMETHOD || action === C.EA_PUSHVALUEOFVAR ||
action === C.EA_GETNAMEDMEMBER || action === C.EA_PUSHWORDCONSTANT) {
return aname + ' ' + (a.attr('str') !== undefined ? U.mkstr(decodeAttrEntities(a.attr('str'))) : a.attr('num'));
}
// goto: ActionGotoFrame pos
if (a[0].tagName === 'goto') {
return aname + ' ' + a.attr('pos');
}
// geturl
if (a[0].tagName === 'geturl') {
return aname + ' ' + U.mkstr(decodeAttrEntities(a.attr('str1'))) + ', ' + U.mkstr(decodeAttrEntities(a.attr('str2')));
}
// definefunction
if (a[0].tagName === 'definefunction') {
let s1 = 'function ' + a.attr('name') + '(';
const args = [];
a.children().each(function () {
args.push($(this).attr('name'));
});
s1 += args.join(', ') + ')';
return s1;
}
// definefunction2
if (a[0].tagName === 'definefunction2') {
const flags = parseInt(a.attr('flags'), 10);
const nregs = flags & 0xff;
const realflags = U.endian((flags >> 8) & 0xffffff, 3);
let s1 = 'function ' + a.attr('name') + '(';
const aa = [];
const aa2 = ['regs:' + nregs];
let r = 1;
const fp = C.FunctionPreloadFlags;
if (realflags & fp.PreloadThis) aa2.push('reg' + (r++) + ':this');
if (realflags & fp.PreloadArguments) aa2.push('reg' + (r++) + ':arguments');
if (realflags & fp.PreloadSuper) aa2.push('reg' + (r++) + ':super');
if (realflags & fp.PreloadRoot) aa2.push('reg' + (r++) + ':_root');
if (realflags & fp.PreloadParent) aa2.push('reg' + (r++) + ':_parent');
if (realflags & fp.PreloadGlobal) aa2.push('reg' + (r++) + ':_global');
if (realflags & fp.PreloadExtern) aa2.push('reg' + (r++) + ':_extern');
a.children().each(function () {
aa.push('reg' + $(this).attr('reg') + ':' + $(this).attr('name'));
});
s1 += aa.join(', ') + ')(' + aa2.join(', ') + ')';
return s1;
}
// end
if (a[0].tagName === 'end') {
return 'end';
}
// noarg (single opcode with no arguments)
if (a[0].tagName === 'noarg') {
return aname;
}
// string instruction: pushstr "hello"
if (a[0].tagName === 'string') {
return aname + ' ' + U.mkstr(decodeAttrEntities(a.attr('str')));
}
// pushdata: pushdata val1, val2, ...
if (a[0].tagName === 'pushdata') {
const vals = [];
a.children().each(function () {
const v = $(this);
vals.push(v.attr('str') !== undefined ? U.mkstr(decodeAttrEntities(v.attr('str'))) : v.attr('num'));
});
return aname + ' ' + vals.join(', ');
}
// Generic: actionname value
if (a.attr('val') !== undefined) {
return aname + ' ' + a.attr('val');
}
return 'unknownaction_' + action;
}
// ─── Exports ───
module.exports = { expand };
+266
View File
@@ -0,0 +1,266 @@
'use strict';
const path = require('path');
const os = require('os');
const fs = require('fs');
const cp = require('child_process');
const {
ActionSizes, ActionAligns, PlaceObjectFlags, ClipEventFlags,
ButtonRecordFlags, ButtonActionFlags, FunctionPreloadFlags
} = require('./constants');
// ─── Byte order ───
function endian(n, bytes) {
bytes = bytes || 4;
let v = 0;
for (let i = 0; i < bytes; i++) {
const b = n & 0xff;
v |= b << (8 * (bytes - i - 1));
n >>>= 8;
}
return v;
}
// ─── Flag converters ───
function valuetoflags(v, e) {
v = parseInt(v, 10);
let v0 = v;
const keys = [];
for (const k in e) {
const ek = e[k];
if (v & ek) {
keys.push(k);
v0 &= ~ek;
}
}
if (v0) {
throw new Error('unknown bit flags ' + v0 + ' in ' + v);
}
return keys.join('|');
}
function flagstovalue(f, e) {
if (!f || f === '') return 0;
const parts = f.split('|');
let v = 0;
for (let i = 0; i < parts.length; i++) {
const k = e[parts[i]];
if (k !== undefined) {
v |= k;
} else {
throw new Error('invalid bit flag: ' + parts[i]);
}
}
return v;
}
// ─── String encoding ───
function encodeXML(s) {
const a = new Array(s.length);
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (c === '\r') a[i] = '\\r';
else if (c === '\n') a[i] = '\\n';
else if (c === '"') a[i] = '\\"';
else a[i] = c;
}
return a.join('');
}
function mkstr(s) {
return '"' + encodeXML(s) + '"';
}
// ─── Cheerio element creation (works with a cheerio $ instance) ───
function el(tagName, $) {
return $('<' + tagName + '>');
}
function bad(t, $) {
return $('<bad>').attr('text', encodeURIComponent(t));
}
// ─── Label generation ───
let labelID = 0;
function makelabel() {
return 'lxnx' + labelID++;
}
function resetLabels() {
labelID = 0;
}
// ─── Indentation helpers ───
function indentstr(n) {
const t = n[0].prev;
if (t && t.nodeType === 3) {
return t.data;
}
return '';
}
function copyindent(ndest, nsrc) {
const indent = nsrc[0].prev;
if (indent && indent.nodeType === 3) {
const indent2 = ndest[0].prev;
if (indent2 && indent2.nodeType === 3) {
$(indent2).remove();
}
ndest.before(indent.cloneNode());
}
}
// ─── RGBA helpers ───
function rgba(c) {
c = parseInt(c, 10);
return [
c & 0xff,
(c >> 8) & 0xff,
(c >> 16) & 0xff,
(c >> 24) & 0xff
].join(',');
}
function getrgba($v) {
return $v.attr('red') + ',' + $v.attr('green') + ',' + $v.attr('blue') + ',' + $v.attr('alpha');
}
function cleanrgba($v) {
$v.removeAttr('red green blue alpha');
}
function getmatrix($v) {
return [
$v.attr('rotm00'), $v.attr('rotm01'),
$v.attr('rotm10'), $v.attr('rotm11'),
$v.attr('tx'), $v.attr('ty')
].join(',');
}
function cleanmatrix($v) {
$v.removeAttr('rotm00 rotm01 rotm10 rotm11 tx ty');
}
// ─── Action alignment helpers ───
function alnsize(l, aln) {
if (l.is('end')) return 0;
const la = parseInt(l.attr('action'), 10);
const aa = ActionAligns[la];
let sz = ActionSizes[la];
if (aa && ((aln + 1) % 4)) {
sz += 4 - ((aln + 1) % 4);
}
return sz;
}
function calcaln(list) {
let aln = 0;
for (let i = 0; i < list.length; i++) {
const l = list.eq(i);
const sz = alnsize(l, aln);
l.attr('aln', aln);
aln += sz;
}
}
function thisaln(l) {
return parseInt(l.attr('aln'), 10);
}
function nextaln(l) {
const aln = thisaln(l);
return aln + alnsize(l, aln);
}
// ─── Temporary directory management ───
function createTempDir() {
return fs.mkdtempSync(os.tmpdir() + path.sep) + path.sep;
}
function cleanupTempDir(tmpdir, keepTemp) {
if (!keepTemp && tmpdir) {
try {
fs.rmSync(tmpdir, { recursive: true, force: true });
} catch (e) {
// ignore cleanup errors
}
}
}
// ─── Run cc3tools converter ───
function runApt2Xml(toolsDir, tmpdir, baseName) {
cp.execFileSync(toolsDir + 'apt2xml', [tmpdir + baseName]);
}
function runXml2Apt(toolsDir, tmpdir, baseName) {
cp.execFileSync(toolsDir + 'xml2apt', [tmpdir + baseName]);
}
// ─── Resolve file paths ───
function resolveFilePaths(aptFilePath) {
const aptFullPath = path.resolve(aptFilePath);
const filedir = aptFullPath.substring(0, aptFullPath.lastIndexOf(path.sep)) + path.sep;
const aptName = aptFullPath.substring(aptFullPath.lastIndexOf(path.sep) + 1);
const baseName = aptName.replace(/\.apt$/i, '');
return { filedir, aptName, baseName };
}
// ─── Copy input files to temp dir ───
function copyInputToTemp(filedir, tmpdir, baseName, aptName) {
// Always copy .apt and .const
fs.copyFileSync(filedir + aptName, tmpdir + aptName);
try {
fs.copyFileSync(filedir + baseName + '.const', tmpdir + baseName + '.const');
} catch (e) {
// .const file might not exist
}
// Also copy .xml (manifest) if it exists
let manifestData = null;
try {
manifestData = fs.readFileSync(filedir + baseName + '.xml', 'utf-8');
} catch (e) {
// manifest doesn't exist, that's ok
}
// Also copy .dat if it exists
let datData = null;
try {
datData = fs.readFileSync(filedir + baseName + '.dat', 'utf-8');
} catch (e) {
// .dat doesn't exist
}
return { manifestData, datData };
}
// ─── Copy output files from temp to filedir ───
function copyOutputFromTemp(filedir, tmpdir, baseName, aptName) {
// Copy .apt and .const back
fs.copyFileSync(tmpdir + aptName, filedir + aptName);
try {
fs.copyFileSync(tmpdir + baseName + '.const', filedir + baseName + '.const');
} catch (e) {
// .const may not have been generated
}
}
// ─── Read raw XML from temp dir ───
function readRawXml(tmpdir, baseName) {
return fs.readFileSync(tmpdir + baseName + '.xml', 'utf-8');
}
function writeRawXml(tmpdir, baseName, xmlStr) {
fs.writeFileSync(tmpdir + baseName + '.xml', xmlStr, 'utf-8');
}
// ─── Exports ───
module.exports = {
endian, valuetoflags, flagstovalue,
encodeXML, mkstr,
el, bad, makelabel, resetLabels,
indentstr, copyindent,
rgba, getrgba, cleanrgba, getmatrix, cleanmatrix,
alnsize, calcaln, thisaln, nextaln,
createTempDir, cleanupTempDir,
runApt2Xml, runXml2Apt,
resolveFilePaths, copyInputToTemp, copyOutputFromTemp,
readRawXml, writeRawXml
};