Advertisement
vxste

Untitled

Jul 31st, 2024 (edited)
16
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 53.51 KB | None | 0 0
  1. --!optimize 2
  2.  
  3. --TODO: add --optimize hotcomment support if possible even?
  4. --TODO: stop listing nested upvalues and use them directly
  5. --TODO: use letter "u" instead of "v" for upvalues
  6.  
  7. ;;CONSTANTS HERE;;
  8.  
  9. -- TEMPORARY
  10. local POINT_TYPE_END = 0
  11. local POINT_TYPE_ELSE = 1
  12. local POINT_TYPE_ELSEIF = 2
  13.  
  14. -- maybe move this somewhere? its just faster bit32 funcs
  15. local lshift, rshift
  16. function lshift(x, disp)
  17. if disp < 0 then return rshift(x,-disp) end
  18. return (x * 2^disp) % 2^32
  19. end
  20. function rshift(x, disp)
  21. if disp < 0 then return lshift(x,-disp) end
  22. return (x % 2^32) // (2^disp)
  23. end
  24.  
  25. local function LoadFromUrl(x)
  26. local BASE_USER = "w-a-e"
  27. local BASE_BRANCH = "main"
  28. local BASE_URL = "https://raw.githubusercontent.com/%s/Advanced-Decompiler-V3/%s/%s.lua"
  29.  
  30. local loadSuccess, loadResult = pcall(function()
  31. local formattedUrl = string.format(BASE_URL, BASE_USER, BASE_BRANCH, x)
  32. return game:HttpGet(formattedUrl, true)
  33. end)
  34.  
  35. if not loadSuccess then
  36. warn(`({math.random()}) MОDULE FАILЕD ТO LOАD FRОM URL: {loadResult}.`)
  37. return
  38. end
  39.  
  40. local success, result = pcall(loadstring, loadResult)
  41. if not success then
  42. warn(`({math.random()}) MОDULE FАILЕD ТO LOАDSТRING: {result}.`)
  43. return
  44. end
  45.  
  46. if type(result) ~= "function" then
  47. warn(`MОDULE IS {tostring(result)} (function expected)`)
  48. return
  49. end
  50.  
  51. return result()
  52. end
  53. local Implementations = LoadFromUrl("Implementations")
  54. local Reader = LoadFromUrl("Reader")
  55. local Strings = LoadFromUrl("Strings")
  56. local Luau = LoadFromUrl("Luau")
  57.  
  58. local LuauOpCode = Luau.OpCode
  59. local LuauBytecodeTag = Luau.BytecodeTag
  60. local LuauBytecodeType = Luau.BytecodeType
  61. local LuauCaptureType = Luau.CaptureType
  62. local LuauBuiltinFunction = Luau.BuiltinFunction
  63. local LuauProtoFlag = Luau.ProtoFlag
  64.  
  65. local toboolean = Implementations.toboolean
  66. local toEscapedString = Implementations.toEscapedString
  67. local formatIndexString = Implementations.formatIndexString
  68. local isGlobal = Implementations.isGlobal
  69.  
  70. Reader:Set(READER_FLOAT_PRECISION)
  71.  
  72. local function Decompile(bytecode)
  73. local bytecodeVersion, typeEncodingVersion
  74. --
  75. local reader = Reader.new(bytecode)
  76. --
  77. -- collects all information from the bytecode and organizes it
  78. local function disassemble()
  79. if bytecodeVersion >= 4 then
  80. -- type encoding did not exist before this version
  81. typeEncodingVersion = reader:nextByte()
  82. end
  83.  
  84. local stringTable = {}
  85. local function readStringTable()
  86. local sizeStringTable = reader:nextVarInt()
  87. for i = 1, sizeStringTable do
  88. stringTable[i] = reader:nextString()
  89. end
  90. end
  91. readStringTable()
  92.  
  93. local protoTable = {}
  94. local function readProtoTable()
  95. local sizeProtoTable = reader:nextVarInt()
  96. for i = 1, sizeProtoTable do
  97. local protoId = i - 1 -- account for main proto
  98.  
  99. local proto = {
  100. id = protoId,
  101.  
  102. insnTable = {},
  103. constsTable = {},
  104. innerProtoTable = {},
  105.  
  106. smallLineInfo = {},
  107. largeLineInfo = {},
  108. -- stores information about the first instruction to help detect inlining
  109. firstInstruction = nil
  110. }
  111. protoTable[protoId] = proto
  112.  
  113. -- read header
  114. proto.maxStackSize = reader:nextByte()
  115. proto.numParams = reader:nextByte()
  116. proto.numUpvalues = reader:nextByte()
  117. proto.isVarArg = toboolean(reader:nextByte())
  118.  
  119. -- prepare a table for upvalue references for further use if there are any
  120. if proto.numUpvalues > 0 then
  121. proto.nestedUpvalues = table.create(proto.numUpvalues)
  122. end
  123.  
  124. -- read flags and typeinfo if bytecode version includes that information
  125. if bytecodeVersion >= 4 then
  126. proto.flags = reader:nextByte()
  127. proto.typeinfo = reader:nextBytes(reader:nextVarInt()) -- array of uint8
  128. end
  129.  
  130. proto.sizeInsns = reader:nextVarInt() -- total number of instructions
  131. for i = 1, proto.sizeInsns do
  132. local encodedInsn = reader:nextUInt32()
  133. proto.insnTable[i] = encodedInsn
  134. end
  135.  
  136. -- this might be confusing but just read into it
  137. proto.sizeConsts = reader:nextVarInt() -- total number of constants
  138. for i = 1, proto.sizeConsts do
  139. local constType = reader:nextByte()
  140. local constValue
  141.  
  142. if constType == LuauBytecodeTag.LBC_CONSTANT_BOOLEAN then
  143. -- 1 = true, 0 = false
  144. constValue = toboolean(reader:nextByte())
  145. elseif constType == LuauBytecodeTag.LBC_CONSTANT_NUMBER then
  146. constValue = reader:nextDouble()
  147. elseif constType == LuauBytecodeTag.LBC_CONSTANT_STRING then
  148. local stringId = reader:nextVarInt()
  149. constValue = stringTable[stringId]
  150. elseif constType == LuauBytecodeTag.LBC_CONSTANT_IMPORT then
  151. local id = reader:nextUInt32()
  152.  
  153. local indexCount = rshift(id, 30)
  154.  
  155. local cacheIndex1 = bit32.band(rshift(id, 20), 0x3FF)
  156. local cacheIndex2 = bit32.band(rshift(id, 10), 0x3FF)
  157. local cacheIndex3 = bit32.band(rshift(id, 0), 0x3FF)
  158.  
  159. local importTag = "("
  160.  
  161. if indexCount == 1 then
  162. local k1 = proto.constsTable[cacheIndex1 + 1]
  163. importTag ..= tostring(k1.value)
  164. elseif indexCount == 2 then
  165. local k1 = proto.constsTable[cacheIndex1 + 1]
  166. local k2 = proto.constsTable[cacheIndex2 + 1]
  167. importTag ..= tostring(k1.value) .. ", "
  168. importTag ..= tostring(k2.value)
  169. elseif indexCount == 3 then
  170. local k1 = proto.constsTable[cacheIndex1 + 1]
  171. local k2 = proto.constsTable[cacheIndex2 + 1]
  172. local k3 = proto.constsTable[cacheIndex3 + 1]
  173. importTag ..= tostring(k1.value) .. ", "
  174. importTag ..= tostring(k2.value) .. ", "
  175. importTag ..= tostring(k3.value)
  176. end
  177.  
  178. importTag ..= ")"
  179.  
  180. constValue = "import - " .. importTag
  181. elseif constType == LuauBytecodeTag.LBC_CONSTANT_TABLE then
  182. local sizeTable = reader:nextVarInt()
  183. local tableKeys = {}
  184.  
  185. for _ = 1, sizeTable do
  186. local keyStringId = reader:nextVarInt() + 1
  187. table.insert(tableKeys, keyStringId)
  188. end
  189.  
  190. constValue = { ["size"] = sizeTable, ["keys"] = tableKeys }
  191. elseif constType == LuauBytecodeTag.LBC_CONSTANT_CLOSURE then
  192. local closureId = reader:nextVarInt() + 1
  193. constValue = closureId
  194. elseif constType == LuauBytecodeTag.LBC_CONSTANT_VECTOR then
  195. local x, y, z, w = reader:nextFloat(), reader:nextFloat(), reader:nextFloat(), reader:nextFloat()
  196. if w ~= 0 then
  197. constValue = `Vector3.new({x}, {y}, {z}, {w})`
  198. else
  199. constValue = `Vector3.new({x}, {y}, {z})`
  200. end
  201. elseif constType ~= LuauBytecodeTag.LBC_CONSTANT_NIL then
  202. -- handle unknown constant type later
  203. end
  204.  
  205. proto.constsTable[i] = { ["type"] = constType, ["value"] = constValue }
  206. end
  207.  
  208. proto.sizeInnerProtos = reader:nextVarInt() -- total number of protos inside this proto
  209. for i = 1, proto.sizeInnerProtos do
  210. local protoId = reader:nextVarInt()
  211. proto.innerProtoTable[i] = protoTable[protoId]
  212. end
  213.  
  214. -- lineDefined is the line function starts on
  215. proto.lineDefined = reader:nextVarInt()
  216. -- protoSourceId is the string id of the function's name if it is not unnamed
  217. local protoSourceId = reader:nextVarInt()
  218. proto.source = stringTable[protoSourceId]
  219.  
  220. -- smallLineInfo contains lines for each instruction
  221. -- largeLineInfo contains lines for each 256 line chunk proto uses
  222. local hasLineInfo = toboolean(reader:nextByte())
  223. if hasLineInfo then
  224. -- this code is confusing
  225. local logspan = reader:nextByte() -- uint8
  226.  
  227. local intervals = rshift(proto.sizeInsns - 1, logspan) + 1
  228.  
  229. local lastOffset = 0
  230. local lastLine = 0
  231.  
  232. local added = {}
  233. local smallLineInfo = {}
  234. local largeLineInfo = {}
  235.  
  236. for i, insn in proto.insnTable do
  237. local val = reader:nextByte()
  238. local prevInsn = proto.insnTable[i - 1]
  239. if prevInsn then
  240. local insnOP = Luau:INSN_OP(prevInsn)
  241. local opInfo = LuauOpCode[insnOP]
  242. if opInfo and opInfo.aux then
  243. -- ignore aux lines
  244. val = 0
  245. end
  246. end
  247. local currOP = Luau:INSN_OP(insn)
  248. local currOPInfo = LuauOpCode[currOP]
  249. if currOPInfo then
  250. if string.find(currOPInfo.type, "sD") then
  251. -- works in most cases but still gotta replace this
  252. local sD = Luau:INSN_sD(insn)
  253. if sD < -1 and val ~= 0 then
  254. val -= (0xFF + 1)
  255. end
  256. elseif currOPInfo.name == "CALL" and val > 0 then
  257. -- TODO: replace later. i dont trust this. check if there is a pointer to an instruction less than the current one
  258. val -= (0xFF + 1)
  259. end
  260. end
  261. smallLineInfo[i] = val
  262. end
  263.  
  264. for i = 1, intervals do
  265. local val = reader:nextUInt32()
  266. largeLineInfo[i - 1] = val
  267. end
  268.  
  269. for i, offset in smallLineInfo do
  270. -- HELP HELP HELP HELP HELP HELP HELP HELP HELP HELP HELP HELP HELP HELP HELP HELP HELP HELP HELP HELP
  271. local largeLineIndex = rshift(i - 0.1, logspan)
  272. local largeLine
  273. if not added[largeLineIndex] then
  274. added[largeLineIndex] = true
  275. largeLine = largeLineInfo[largeLineIndex]
  276. lastLine += largeLine
  277. if largeLineIndex ~= 0 then
  278. offset = 0
  279. lastOffset = offset
  280. end
  281. end
  282. local lineInsn = proto.insnTable[i]
  283. local lineOP = lineInsn and Luau:INSN_OP(lineInsn)
  284. local lineOPInfo = LuauOpCode[lineOP]
  285. lastOffset += offset
  286. if i == 1 then
  287. proto.firstInstruction = {lineOPInfo and lineOPInfo.name, largeLine}
  288. end
  289. proto.smallLineInfo[i] = lastOffset
  290. proto.largeLineInfo[i] = bit32.band(lastLine, 0xFFFF)
  291. end
  292. end
  293.  
  294. -- debug info is local and function parameter names, all that
  295. local hasDebugInfo = toboolean(reader:nextByte())
  296. if hasDebugInfo then
  297. -- script does not use Roblox bytecode
  298. return ":("
  299. end
  300. end
  301. end
  302. readProtoTable()
  303.  
  304. local mainProtoId = reader:nextVarInt()
  305. return protoTable[mainProtoId], protoTable, stringTable
  306. end
  307. local function roughDecompilation()
  308. local output = ""
  309.  
  310. local mainProto, protoTable, stringTable = disassemble()
  311.  
  312. local inlineRemarks = {}
  313. local function handleInlinedCalls()
  314. -- its either my lineinfo implementation is faulty or roblox's inlining
  315. -- is so bad they mess up line numbers. i hate it so much
  316. -- also this is not trustworthy, if anyone comes up with a better method
  317. -- try it out
  318. for i, proto in protoTable do
  319. local smallLineInfo = proto.smallLineInfo
  320. local largeLineInfo = proto.largeLineInfo
  321.  
  322. local lineDefined = proto.lineDefined
  323.  
  324. local insnTable = proto.insnTable
  325.  
  326. local validProtos = {}
  327. for i, otherProto in protoTable do
  328. local protoName = otherProto.source
  329. if protoName and otherProto.lineDefined < lineDefined then
  330. validProtos[i] = otherProto
  331. end
  332. end
  333.  
  334. local lineOffset = 0
  335. local queuedInsns = {}
  336.  
  337. for i, insn in insnTable do
  338. local instructionLine = smallLineInfo[i]
  339. local largeInstructionLine = largeLineInfo[i]
  340.  
  341. if queuedInsns[i] then
  342. queuedInsns[i] = nil
  343. else
  344. local prevInstructionLine = smallLineInfo[i - 1]
  345. local largePrevInstructionLine = largeLineInfo[i - 1]
  346.  
  347. if not prevInstructionLine or ((instructionLine + largeInstructionLine) - (prevInstructionLine + largePrevInstructionLine)) >= 30 then
  348. local actualInstructionLine = largeInstructionLine + (instructionLine - (0xFF + 1))
  349.  
  350. local insnOP = Luau:INSN_OP(insn)
  351. local opInfo = LuauOpCode[insnOP]
  352.  
  353. if opInfo then
  354. for _, otherProto in validProtos do
  355. local protoName = otherProto.source
  356. local firstInstructionInfo = otherProto.firstInstruction
  357. if opInfo.name == firstInstructionInfo[1] and actualInstructionLine == firstInstructionInfo[2] then
  358. -- this instruction was used in another function previously defined
  359. if ENABLED_REMARKS.INLINE_REMARK then
  360. inlineRemarks[i..insn] = protoName
  361. end
  362. lineOffset += (0xFF + 1)
  363. for x = i, i + otherProto.sizeInsns - 1 do
  364. queuedInsns[x] = true
  365. end
  366. break
  367. end
  368. end
  369. end
  370. end
  371. end
  372.  
  373. smallLineInfo[i] = instructionLine - lineOffset
  374. end
  375. end
  376. end
  377. handleInlinedCalls()
  378.  
  379. local globalData = {}
  380.  
  381. local totalParams = 0
  382. local totalVars = 0
  383.  
  384. local function baseProto(proto, depth, isMainProto)
  385. local localData = {}
  386. local refData = {}
  387. --local upvalRefData = {}
  388.  
  389. local ifLoopPoints = {}
  390. local promotedJumps = {}
  391. local function createLoopPoint(jumpId, pointId)
  392. --TODO: fix this system. it only works for relatively simplistic if thens and idk why I called it loop point
  393.  
  394. if promotedJumps[jumpId] then
  395. -- promoted to elseif, end is not needed
  396. return
  397. end
  398.  
  399. local pointData = ifLoopPoints[pointId] or table.create(1)
  400.  
  401. local pointInsn = proto.insnTable[pointId]
  402. local pointOP = pointInsn and Luau:INSN_OP(pointInsn)
  403. local pointInfo = LuauOpCode[pointOP]
  404. if pointInfo and pointInfo.name == "JUMP" then
  405. local promote = false
  406.  
  407. local jumpEndPoint = pointId + Luau:INSN_sD(pointInsn)
  408. -- analyze closest jump in range
  409. for i = pointId + 1, jumpEndPoint do
  410. local insn = proto.insnTable[i]
  411. local op = Luau:INSN_OP(insn)
  412. local opInfo = LuauOpCode[op]
  413. if opInfo and string.find(opInfo.name, "JUMP") then
  414. -- check if matches initial jump point
  415. local endPoint = i + Luau:INSN_sD(insn)
  416. if endPoint == jumpEndPoint then
  417. promotedJumps[i] = true
  418. promote = true
  419. break
  420. else
  421. break
  422. end
  423. end
  424. end
  425.  
  426. if promote then
  427. table.insert(pointData, POINT_TYPE_ELSEIF)
  428. else
  429. table.insert(pointData, POINT_TYPE_ELSE)
  430. end
  431. else
  432. table.insert(pointData, POINT_TYPE_END)
  433. end
  434.  
  435. ifLoopPoints[pointId] = pointData
  436. end
  437.  
  438. local protoId = proto.id
  439. local protoNumParams = proto.numParams
  440. local protoTypeInfo = proto.typeinfo
  441. local protoFlags = proto.flags
  442.  
  443. local protoVars = 0
  444.  
  445. local function logRegister(t, register)
  446. local dataTable
  447. if t == "local" then
  448. dataTable = localData
  449. protoVars += 1
  450. elseif t == "global" then
  451. dataTable = globalData
  452. end
  453. local isLogged = table.find(dataTable, register)
  454. if not isLogged then
  455. table.insert(dataTable, register)
  456. end
  457. return isLogged
  458. end
  459. local function modifyRegister(register, isUpvalue)
  460. -- parameter registers are preallocated
  461. if register < protoNumParams then
  462. return `p{(totalParams - protoNumParams) + register + 1}`
  463. else
  464. local starterCount
  465. if isUpvalue then
  466. starterCount = 0
  467. else
  468. starterCount = totalVars
  469. end
  470. return `v{starterCount + depth + register - protoNumParams}`, true
  471. end
  472. end
  473.  
  474. local function baseHotComments()
  475. -- not really needed but I feel like it
  476. local isNative = false
  477. local isCold = true
  478.  
  479. if protoFlags then
  480. isNative = toboolean(bit32.band(protoFlags, LuauProtoFlag.LPF_NATIVE_MODULE))
  481. isCold = toboolean(bit32.band(protoFlags, LuauProtoFlag.LPF_NATIVE_COLD))
  482. end
  483.  
  484. local output = ""
  485.  
  486. if isNative then
  487. output ..= "--!native\n"
  488.  
  489. if ENABLED_REMARKS.NATIVE_REMARK and isCold then
  490. output ..= string.format(Strings.DECOMPILER_REMARK, "This function is cold and is not compiled natively")
  491. end
  492. end
  493.  
  494. return output
  495. end
  496. local function baseLocal(register, value)
  497. local prefix = "local "
  498. -- previously logged
  499. if logRegister("local", register) then
  500. prefix = ""
  501. end
  502.  
  503. local register, isVar = modifyRegister(register)
  504. if not isVar then
  505. prefix = ""
  506. end
  507.  
  508. return `{prefix}{register} = {value}`
  509. end
  510. local function baseLocals(register, count, value)
  511. if count > 0 then
  512. local output = `local `
  513.  
  514. for i = 0, count - 1 do
  515. local usedRegister = register + i
  516. logRegister("local", usedRegister)
  517. output ..= modifyRegister(usedRegister)
  518. if i ~= count - 1 then
  519. output ..= ", "
  520. end
  521. end
  522.  
  523. output ..= ` = {value}`
  524.  
  525. return output
  526. else
  527. return baseLocal(register, value)
  528. end
  529. end
  530. local function baseGlobal(key, value)
  531. logRegister("global", key)
  532.  
  533. return `{key} = {value}`
  534. end
  535. local function baseFunc()
  536. local prefix = ""
  537. local postfix = "function"
  538.  
  539. local output
  540.  
  541. if proto.source then
  542. prefix = "local "
  543. -- has a name
  544. postfix ..= ` {proto.source}`
  545. end
  546. postfix ..= "("
  547.  
  548. output = prefix .. postfix
  549.  
  550. -- handle type info
  551. local hasTypedParameters = false
  552. if protoTypeInfo and #protoTypeInfo > 0 then
  553. local encodedType = table.remove(protoTypeInfo, 1)
  554. if (encodedType ~= LuauBytecodeType.LBC_TYPE_FUNCTION) then
  555. -- this shouldn't happen? but it happened so i had to do this
  556. else
  557. local numparams = table.remove(protoTypeInfo, 1)
  558.  
  559. hasTypedParameters = true
  560. end
  561. end
  562.  
  563. -- complex parameter handling
  564. for i = 1, proto.numParams do
  565. -- params coincide with stack index
  566. local paramRef = totalParams + i
  567.  
  568. local typeSetString = ""
  569. if hasTypedParameters then
  570. local paramType = protoTypeInfo[i]
  571. if paramType then
  572. typeSetString ..= `: {Luau:GetBaseTypeString(paramType, true)}`
  573. end
  574. end
  575.  
  576. output ..= `p{paramRef}{typeSetString}`
  577.  
  578. if i < proto.numParams then
  579. output ..= ", "
  580. end
  581. end
  582. totalParams = totalParams + proto.numParams
  583.  
  584. if proto.isVarArg then
  585. if proto.numParams > 0 then
  586. output ..= ", "
  587. end
  588.  
  589. output ..= "..."
  590. end
  591.  
  592. output ..= `) {`-- [line {proto.lineDefined}]`}\n`
  593. output ..= baseHotComments()
  594.  
  595. return output
  596. end
  597.  
  598. --
  599.  
  600. local protoOutput = ""
  601.  
  602. local function addTab(depth)
  603. protoOutput ..= string.rep(" ", depth)
  604. end
  605. local function addNewLine()
  606. protoOutput ..= "\n"
  607. end
  608.  
  609. if isMainProto then
  610. protoOutput ..= baseHotComments()
  611. else
  612. protoOutput ..= baseFunc()
  613.  
  614. depth += 1
  615. end
  616.  
  617. -- instruction handling here
  618. local expectation
  619. local nextIsAux = false
  620.  
  621. for insnIndex, insn in proto.insnTable do
  622. if nextIsAux then
  623. nextIsAux = false
  624. else
  625. addTab(depth)
  626.  
  627. local aux = proto.insnTable[insnIndex + 1]
  628.  
  629. local OP = Luau:INSN_OP(insn)
  630. local A, B, C = Luau:INSN_A(insn), Luau:INSN_B(insn), Luau:INSN_C(insn)
  631. local D, sD = Luau:INSN_D(insn), Luau:INSN_sD(insn)
  632. local E = Luau:INSN_E(insn)
  633.  
  634. local opInfo = LuauOpCode[OP]
  635. if not opInfo then
  636. protoOutput ..= `UNKNOWN OP: {OP}`
  637. addNewLine()
  638. continue
  639. end
  640.  
  641. if opInfo.aux then
  642. nextIsAux = true
  643. end
  644.  
  645. local lineStr = ""
  646.  
  647. local remarkProtoName = inlineRemarks[insnIndex..insn]
  648. if remarkProtoName then
  649. lineStr ..= string.format(Strings.DECOMPILER_REMARK, `Function "{remarkProtoName}" was inlined here (LINE IS NOT VALID)`) .. " "
  650. end
  651.  
  652. if SHOW_INSTRUCTION_LINES then
  653. local instructionLine = proto.smallLineInfo[insnIndex]
  654. local instructionLargeLine = proto.largeLineInfo[insnIndex]
  655. lineStr ..= `[line {instructionLargeLine + instructionLine}] `
  656. end
  657.  
  658. protoOutput ..= lineStr .. '--[[# ' .. tostring(insnIndex) .. "]] "
  659.  
  660. addTab(1)
  661.  
  662. if SHOW_OPERATION_NAMES then
  663. protoOutput ..= opInfo.name or "[UNNAMED]"
  664. addTab(1)
  665. end
  666.  
  667. -- no scope/flow control
  668.  
  669. --local upvalRefs = {}
  670. --upvalRefData[protoId] = upvalRefs
  671.  
  672. local function addReference(refStart, refEnd)
  673. for _, v in refData do
  674. if v.insnIndex == refEnd then
  675. table.insert(v.refs, refStart)
  676. return
  677. end
  678. end
  679.  
  680. table.insert(refData, { ["insnIndex"] = refEnd, ["refs"] = {refStart} })
  681. end
  682.  
  683. local nilValue = { ["type"] = "nil", ["value"] = nil }
  684.  
  685. --
  686. local function handleConstantValue(k)
  687. if k["type"] == LuauBytecodeTag.LBC_CONSTANT_VECTOR then
  688. return k.value
  689. else
  690. if type(tonumber(k.value)) == "number" then
  691. return tonumber(string.format(`%0.{READER_FLOAT_PRECISION}f`, k.value))
  692. else
  693. return toEscapedString(k.value)
  694. end
  695. end
  696. end
  697. --
  698.  
  699. local opConstructors = {} do
  700. opConstructors["LOADNIL"] = function()
  701. protoOutput ..= baseLocal(A, "nil")
  702. end
  703. opConstructors["NOP"] = function()
  704. protoOutput ..= "[NOP]"
  705. end
  706. opConstructors["BREAK"] = function()
  707. protoOutput ..= "break (debugger)"
  708. end
  709. opConstructors["LOADK"] = function()
  710. local k = proto.constsTable[D + 1] or nilValue
  711. protoOutput ..= baseLocal(A, handleConstantValue(k))
  712. end
  713. opConstructors["LOADKX"] = function()
  714. local k = proto.constsTable[aux + 1] or nilValue
  715. protoOutput ..= baseLocal(A, handleConstantValue(k))
  716. end
  717. opConstructors["LOADB"] = function()
  718. local value = toboolean(B)
  719. protoOutput ..= baseLocal(A, toEscapedString(value))
  720. if C ~= 0 then
  721. protoOutput ..= string.format(" +%i", C) -- skip over next LOADB?
  722. end
  723. end
  724. opConstructors["LOADN"] = function()
  725. protoOutput ..= baseLocal(A, sD)
  726. end
  727. opConstructors["GETUPVAL"] = function()
  728. --local upvalRefs = upvalRefData[protoId - 1] or {}
  729.  
  730. --local var = upvalRefs[B]
  731. --if var then
  732. -- protoOutput ..= baseLocal(A, toEscapedString(var))
  733. --else
  734. -- protoOutput ..= baseLocal(A, `upvalues[{B}]`)
  735. --end
  736. protoOutput ..= baseLocal(A, `{proto.nestedUpvalues[B]} -- get upval`)
  737. end
  738. opConstructors["SETUPVAL"] = function()
  739. protoOutput ..= `{proto.nestedUpvalues[B]} = {modifyRegister(A)} -- set upval`
  740. end
  741. opConstructors["CLOSEUPVALS"] = function()
  742. protoOutput ..= `[CLOSEUPVALS]: clear captures from back until: {A}`
  743. end
  744. opConstructors["MOVE"] = function()
  745. protoOutput ..= baseLocal(A, modifyRegister(B))
  746. end
  747. opConstructors["MINUS"] = function()
  748. protoOutput ..= baseLocal(A, `-{modifyRegister(B)}`)
  749. end
  750. opConstructors["LENGTH"] = function()
  751. protoOutput ..= baseLocal(A, `#{modifyRegister(B)}`)
  752. end
  753. opConstructors["NOT"] = function()
  754. protoOutput ..= baseLocal(A, `not {modifyRegister(B)}`)
  755. end
  756. opConstructors["GETVARARGS"] = function()
  757. protoOutput ..= baseLocals(A, B - 1, "...")
  758. end
  759. opConstructors["CONCAT"] = function()
  760. local value = modifyRegister(B)
  761. local totalStrings = C - B
  762. for i = 1, totalStrings do
  763. value ..= ` .. {modifyRegister(B + i)}`
  764. end
  765. protoOutput ..= baseLocal(A, value)
  766. end
  767. opConstructors["AND"] = function()
  768. protoOutput ..= baseLocal(A, `{modifyRegister(B)} and {modifyRegister(C)}`)
  769. end
  770. opConstructors["OR"] = function()
  771. protoOutput ..= baseLocal(A, `{modifyRegister(B)} or {modifyRegister(C)}`)
  772. end
  773. opConstructors["ANDK"] = function()
  774. local k = proto.constsTable[C + 1] or nilValue
  775. protoOutput ..= baseLocal(A, `{modifyRegister(B)} and {handleConstantValue(k)}`)
  776. end
  777. opConstructors["ORK"] = function()
  778. local k = proto.constsTable[C + 1] or nilValue
  779. protoOutput ..= baseLocal(A, `{modifyRegister(B)} or {handleConstantValue(k)}`)
  780. end
  781. opConstructors["FASTCALL"] = function()
  782. protoOutput ..= `FASTCALL[{Luau:GetBuiltinInfo(A)}]()`
  783. end
  784. opConstructors["FASTCALL1"] = function()
  785. protoOutput ..= `FASTCALL[{Luau:GetBuiltinInfo(A)}]({modifyRegister(B)})`
  786. end
  787. opConstructors["FASTCALL2"] = function()
  788. protoOutput ..= `FASTCALL[{Luau:GetBuiltinInfo(A)}]({modifyRegister(B)}, {modifyRegister(aux)})`
  789. end
  790. opConstructors["FASTCALL2K"] = function()
  791. local k = proto.constsTable[aux + 1] or nilValue
  792. protoOutput ..= `FASTCALL[{Luau:GetBuiltinInfo(A)}]({modifyRegister(B)}, {handleConstantValue(k)})`
  793. end
  794. opConstructors["GETIMPORT"] = function()
  795. local indexCount = rshift(aux, 30) -- 0x40000000 --> 1, 0x80000000 --> 2
  796.  
  797. local cacheIndex1 = bit32.band(rshift(aux, 20), 0x3FF)
  798. local cacheIndex2 = bit32.band(rshift(aux, 10), 0x3FF)
  799. local cacheIndex3 = bit32.band(rshift(aux, 0), 0x3FF)
  800.  
  801. if indexCount == 1 then
  802. local k1 = tostring(proto.constsTable[cacheIndex1 + 1].value)
  803. if not isGlobal(k1) then
  804. logRegister("global", k1)
  805. end
  806. protoOutput ..= baseLocal(A, k1)
  807. elseif indexCount == 2 then
  808. local k1 = tostring(proto.constsTable[cacheIndex1 + 1].value)
  809. local k2 = tostring(proto.constsTable[cacheIndex2 + 1].value)
  810. protoOutput ..= baseLocal(A, `{k1}{formatIndexString(k2)}`)
  811. elseif indexCount == 3 then
  812. local k1 = tostring(proto.constsTable[cacheIndex1 + 1].value)
  813. local k2 = tostring(proto.constsTable[cacheIndex2 + 1].value)
  814. local k3 = tostring(proto.constsTable[cacheIndex3 + 1].value)
  815. protoOutput ..= baseLocal(A, `{k1}{formatIndexString(k2)}{formatIndexString(k3)}`)
  816. else
  817. error("[GETIMPORT] Too many entries")
  818. end
  819. end
  820. opConstructors["GETGLOBAL"] = function()
  821. local k = proto.constsTable[aux + 1] or nilValue
  822. local key = tostring(k.value) -- escaping not required here
  823. logRegister("global", key)
  824. protoOutput ..= baseLocal(A, key)
  825. end
  826. opConstructors["SETGLOBAL"] = function()
  827. local k = proto.constsTable[aux + 1] or nilValue
  828. local key = tostring(k.value)
  829. protoOutput ..= baseGlobal(key, modifyRegister(A))
  830. end
  831. opConstructors["GETTABLE"] = function()
  832. protoOutput ..= baseLocal(A, `{modifyRegister(B)}[{modifyRegister(C)}]`)
  833. end
  834. opConstructors["SETTABLE"] = function()
  835. protoOutput ..= `{modifyRegister(B)}[{modifyRegister(C)}] = {modifyRegister(A)}`
  836. end
  837. opConstructors["GETTABLEN"] = function()
  838. protoOutput ..= baseLocal(A, `{modifyRegister(B)}[{C - 1}]`)
  839. end
  840. opConstructors["SETTABLEN"] = function()
  841. protoOutput ..= `{modifyRegister(B)}[{C - 1}] = {modifyRegister(A)}`
  842. end
  843. opConstructors["GETTABLEKS"] = function()
  844. local k = proto.constsTable[aux + 1] or nilValue
  845. protoOutput ..= baseLocal(A, `{modifyRegister(B)}{formatIndexString(k.value)}`)
  846. end
  847. opConstructors["SETTABLEKS"] = function()
  848. local k = proto.constsTable[aux + 1] or nilValue
  849. protoOutput ..= `{modifyRegister(B)}{formatIndexString(k.value)} = {modifyRegister(A)}`
  850. end
  851. opConstructors["NAMECALL"] = function()
  852. local k = proto.constsTable[aux + 1] or nilValue
  853. expectation = { ["type"] = "NAMECALL", ["value"] = `{modifyRegister(B)}:{tostring(k.value)}` }
  854. --nextIsAux = true
  855. end
  856. opConstructors["FORNPREP"] = function() -- prepare numeric loop
  857. --TODO: read instructions before fornprep and show their values (and clear them away)
  858. -- also remove step if its 1
  859. protoOutput ..= `for {modifyRegister(A + 2)} = {modifyRegister(A + 2)}, {modifyRegister(A)}, {modifyRegister(A + 1)} do -- [escape at #{insnIndex + sD}]`
  860. end
  861. opConstructors["FORNLOOP"] = function()
  862. protoOutput ..= "end" .. ` -- FORNLOOP end - iterate + goto #{insnIndex + sD}`
  863. end
  864. opConstructors["FORGPREP"] = function() -- prepare generic loop
  865. local endInsnIndex = insnIndex + sD + 1
  866. local endInsnAuxIndex = endInsnIndex + 1
  867. local endInsnAux = proto.insnTable[endInsnAuxIndex]
  868.  
  869. local numRegs = bit32.band(endInsnAux, 0xFF)
  870.  
  871. local regStr = ""
  872. for regIndex = 1, numRegs do
  873. regStr = regStr .. modifyRegister(A + 2 + regIndex)
  874. if regIndex ~= numRegs then
  875. regStr = regStr .. ", "
  876. end
  877. end
  878.  
  879. protoOutput ..= `for {regStr} in {modifyRegister(A)} do -- [escape at #{endInsnIndex}]`
  880. end
  881. opConstructors["FORGLOOP"] = function()
  882. local respectsArrayOrder = toboolean(rshift(aux, 0x1F))
  883. protoOutput ..= "end" .. string.format(" -- FORGLOOP - iterate + goto #%i", insnIndex + sD) .. (respectsArrayOrder and " (ipairs)" or "")
  884. end
  885. opConstructors["FORGPREP_INEXT"] = function()
  886. local endInsnIndex = insnIndex + sD + 1
  887. local endInsnAuxIndex = endInsnIndex + 1
  888. local endInsnAux = proto.insnTable[endInsnAuxIndex]
  889.  
  890. local numRegs = bit32.band(endInsnAux, 0xFF)
  891.  
  892. local regStr = ""
  893. for regIndex = 1, numRegs do
  894. regStr = regStr .. modifyRegister(A + 2 + regIndex)
  895. if regIndex ~= numRegs then
  896. regStr = regStr .. ", "
  897. end
  898. end
  899.  
  900. protoOutput ..= `for {regStr} in {modifyRegister(A)}({modifyRegister(A + 1)}) do -- [escape at #{endInsnIndex}] (ipairs)`
  901. end
  902. opConstructors["DEP_FORGLOOP_INEXT"] = function()
  903. local endInsnIndex = insnIndex + sD + 1
  904. local endInsnAuxIndex = endInsnIndex + 1
  905. local endInsnAux = proto.insnTable[endInsnAuxIndex]
  906.  
  907. local numRegs = bit32.band(endInsnAux, 0xFF)
  908.  
  909. local regStr = ""
  910. for regIndex = 1, numRegs do
  911. regStr = regStr .. modifyRegister(A + 2 + regIndex)
  912. if regIndex ~= numRegs then
  913. regStr = regStr .. ", "
  914. end
  915. end
  916.  
  917. protoOutput ..= `for {regStr} in {modifyRegister(A)}({modifyRegister(A + 1)}) do -- [escape at #{endInsnIndex}] (ipairs) DEPRECATED`
  918. end
  919. opConstructors["FORGPREP_NEXT"] = function()
  920. local endInsnIndex = insnIndex + sD + 1
  921. local endInsnAuxIndex = endInsnIndex + 1
  922. local endInsnAux = proto.insnTable[endInsnAuxIndex]
  923.  
  924. local numRegs = bit32.band(endInsnAux, 0xFF)
  925.  
  926. local regStr = ""
  927. for regIndex = 1, numRegs do
  928. regStr = regStr .. modifyRegister(A + 2 + regIndex)
  929. if regIndex ~= numRegs then
  930. regStr = regStr .. ", "
  931. end
  932. end
  933.  
  934. protoOutput ..= `for {regStr} in {modifyRegister(A)}({modifyRegister(A + 1)}) do -- [escape at #{endInsnIndex}] (pairs/next)`
  935. end
  936. opConstructors["DEP_FORGLOOP_NEXT"] = function()
  937. local endInsnIndex = insnIndex + sD + 1
  938. local endInsnAuxIndex = endInsnIndex + 1
  939. local endInsnAux = proto.insnTable[endInsnAuxIndex]
  940.  
  941. local numRegs = bit32.band(endInsnAux, 0xFF)
  942.  
  943. local regStr = ""
  944. for regIndex = 1, numRegs do
  945. regStr = regStr .. modifyRegister(A + 2 + regIndex)
  946. if regIndex ~= numRegs then
  947. regStr = regStr .. ", "
  948. end
  949. end
  950.  
  951. protoOutput ..= `for {regStr} in {modifyRegister(A)}({modifyRegister(A + 1)}) do -- [escape at #{endInsnIndex}] (pairs/next) DEPRECATED`
  952. end
  953. opConstructors["JUMP"] = function()
  954. local endPoint = insnIndex + sD
  955. createLoopPoint(insnIndex, endPoint)
  956. addReference(insnIndex, endPoint)
  957. protoOutput ..= string.format("goto #%i", endPoint)
  958. end
  959. opConstructors["JUMPBACK"] = function()
  960. local endPoint = insnIndex + sD
  961. createLoopPoint(insnIndex, endPoint)
  962. addReference(insnIndex, endPoint)
  963. protoOutput ..= string.format("go back to #%i -- might be a repeating loop", endPoint + 1)
  964. end
  965. opConstructors["JUMPIF"] = function(ignoreJump) -- inverse
  966. local nextInsn = proto.insnTable[insnIndex + 2]
  967. local nextOP = Luau:INSN_OP(nextInsn)
  968. if not ignoreJump and LuauOpCode[nextOP] and LuauOpCode[nextOP].name == "JUMPBACK" then
  969. return opConstructors["JUMPIFNOT"](true)
  970. end
  971. local endPoint = insnIndex + sD
  972. createLoopPoint(insnIndex, endPoint)
  973. addReference(insnIndex, endPoint)
  974. protoOutput ..= string.format("if not %s then goto #%i", modifyRegister(A), endPoint)
  975. end
  976. opConstructors["JUMPIFNOT"] = function(ignoreJump) -- inverse
  977. local nextInsn = proto.insnTable[insnIndex + 2]
  978. local nextOP = Luau:INSN_OP(nextInsn)
  979. if not ignoreJump and LuauOpCode[nextOP] and LuauOpCode[nextOP].name == "JUMPBACK" then
  980. return opConstructors["JUMPIF"](true)
  981. end
  982. local endPoint = insnIndex + sD
  983. createLoopPoint(insnIndex, endPoint)
  984. addReference(insnIndex, endPoint)
  985. protoOutput ..= string.format("if %s then goto #%i", modifyRegister(A), endPoint)
  986. end
  987. opConstructors["JUMPX"] = function()
  988. addReference(insnIndex, insnIndex + E)
  989. protoOutput ..= string.format("goto #%i [X]", insnIndex + E)
  990. end
  991. opConstructors["JUMPIFEQ"] = function(ignoreJump) -- inverse
  992. local nextInsn = proto.insnTable[insnIndex + 2]
  993. local nextOP = Luau:INSN_OP(nextInsn)
  994. if not ignoreJump and LuauOpCode[nextOP] and LuauOpCode[nextOP].name == "JUMPBACK" then
  995. return opConstructors["JUMPIFNOTEQ"](true)
  996. end
  997. local endPoint = insnIndex + sD
  998. createLoopPoint(insnIndex, endPoint)
  999. addReference(insnIndex, endPoint)
  1000. protoOutput ..= string.format("if %s ~= %s then goto #%i", modifyRegister(A), modifyRegister(aux), endPoint)
  1001. end
  1002. opConstructors["JUMPIFNOTEQ"] = function(ignoreJump) -- inverse
  1003. if not ignoreJump then
  1004. local nextInsn = proto.insnTable[insnIndex + 2]
  1005. local nextOP = Luau:INSN_OP(nextInsn)
  1006. local nextOPName = LuauOpCode[nextOP] and LuauOpCode[nextOP].name
  1007. if nextOPName == "JUMPBACK" or nextOPName == "CALL" then
  1008. return opConstructors["JUMPIFEQ"](true)
  1009. end
  1010. end
  1011. local endPoint = insnIndex + sD
  1012. createLoopPoint(insnIndex, endPoint)
  1013. addReference(insnIndex, endPoint)
  1014. protoOutput ..= string.format("if %s == %s then goto #%i", modifyRegister(A), modifyRegister(aux), endPoint)
  1015. end
  1016. opConstructors["JUMPIFLE"] = function(ignoreJump) -- inverse
  1017. local nextInsn = proto.insnTable[insnIndex + 2]
  1018. local nextOP = Luau:INSN_OP(nextInsn)
  1019. if not ignoreJump and LuauOpCode[nextOP] and LuauOpCode[nextOP].name == "JUMPBACK" then
  1020. return opConstructors["JUMPIFNOTLE"](true)
  1021. end
  1022. local endPoint = insnIndex + sD
  1023. createLoopPoint(insnIndex, endPoint)
  1024. addReference(insnIndex, endPoint)
  1025. protoOutput ..= string.format("if %s > %s then goto #%i", modifyRegister(A), modifyRegister(aux), endPoint)
  1026. end
  1027. opConstructors["JUMPIFNOTLE"] = function(ignoreJump) -- inverse
  1028. local nextInsn = proto.insnTable[insnIndex + 2]
  1029. local nextOP = Luau:INSN_OP(nextInsn)
  1030. if not ignoreJump and LuauOpCode[nextOP] and LuauOpCode[nextOP].name == "JUMPBACK" then
  1031. return opConstructors["JUMPIFLE"](true)
  1032. end
  1033. local endPoint = insnIndex + sD
  1034. createLoopPoint(insnIndex, endPoint)
  1035. addReference(insnIndex, endPoint)
  1036. protoOutput ..= string.format("if %s <= %s then goto #%i", modifyRegister(A), modifyRegister(aux), endPoint)
  1037. end
  1038. opConstructors["JUMPIFLT"] = function(ignoreJump) -- inverse
  1039. local nextInsn = proto.insnTable[insnIndex + 2]
  1040. local nextOP = Luau:INSN_OP(nextInsn)
  1041. if not ignoreJump and LuauOpCode[nextOP] and LuauOpCode[nextOP].name == "JUMPBACK" then
  1042. return opConstructors["JUMPIFNOTLT"](true)
  1043. end
  1044. local endPoint = insnIndex + sD
  1045. createLoopPoint(insnIndex, endPoint)
  1046. addReference(insnIndex, endPoint)
  1047. protoOutput ..= string.format("if %s >= %s then goto #%i", modifyRegister(A), modifyRegister(aux), endPoint)
  1048. end
  1049. opConstructors["JUMPIFNOTLT"] = function(ignoreJump) -- inverse
  1050. local nextInsn = proto.insnTable[insnIndex + 2]
  1051. local nextOP = Luau:INSN_OP(nextInsn)
  1052. if not ignoreJump and LuauOpCode[nextOP] and LuauOpCode[nextOP].name == "JUMPBACK" then
  1053. return opConstructors["JUMPIFLT"](true)
  1054. end
  1055. local endPoint = insnIndex + sD
  1056. createLoopPoint(insnIndex, endPoint)
  1057. addReference(insnIndex, endPoint)
  1058. protoOutput ..= string.format("if %s > %s then goto #%i", modifyRegister(aux), modifyRegister(A), endPoint)
  1059. end
  1060. opConstructors["JUMPXEQKNIL"] = function() -- inverse
  1061. addReference(insnIndex, insnIndex + sD)
  1062. local NOTFlag = rshift(aux, 0x1F) ~= 1
  1063. local nextInsn = proto.insnTable[insnIndex + 2]
  1064. local nextOP = Luau:INSN_OP(nextInsn)
  1065. if LuauOpCode[nextOP] and LuauOpCode[nextOP].name == "JUMPBACK" then
  1066. NOTFlag = not NOTFlag
  1067. end
  1068. local v = if NOTFlag then "~=" else "=="
  1069. local endPoint = insnIndex + sD
  1070. createLoopPoint(insnIndex, endPoint)
  1071. protoOutput ..= string.format("if %s %s nil then goto #%i", modifyRegister(A), v, endPoint)
  1072. end
  1073. opConstructors["JUMPXEQKB"] = function() -- inverse
  1074. addReference(insnIndex, insnIndex + sD)
  1075. local NOTFlag = rshift(aux, 0x1F) ~= 1
  1076. local nextInsn = proto.insnTable[insnIndex + 2]
  1077. local nextOP = Luau:INSN_OP(nextInsn)
  1078. if LuauOpCode[nextOP] and LuauOpCode[nextOP].name == "JUMPBACK" then
  1079. NOTFlag = not NOTFlag
  1080. end
  1081. local v = if NOTFlag then "~=" else "=="
  1082. local endPoint = insnIndex + sD
  1083. createLoopPoint(insnIndex, endPoint)
  1084. protoOutput ..= string.format("if %s %s %s then goto #%i", modifyRegister(A), v, tostring(bit32.band(aux, 1) == 1), endPoint)
  1085. end
  1086. opConstructors["JUMPXEQKN"] = function() -- inverse
  1087. addReference(insnIndex, insnIndex + sD)
  1088. local NOTFlag = rshift(aux, 0x1F) ~= 1
  1089. local nextInsn = proto.insnTable[insnIndex + 2]
  1090. local nextOP = Luau:INSN_OP(nextInsn)
  1091. if LuauOpCode[nextOP] and LuauOpCode[nextOP].name == "JUMPBACK" then
  1092. NOTFlag = not NOTFlag
  1093. end
  1094. local v = if NOTFlag then "~=" else "=="
  1095. local k = proto.constsTable[bit32.band(aux, 0xFFFFFF) + 1] or nilValue
  1096. local endPoint = insnIndex + sD
  1097. createLoopPoint(insnIndex, endPoint)
  1098. protoOutput ..= string.format("if %s %s %s then goto #%i", modifyRegister(A), v, tostring(k.value), endPoint)
  1099. end
  1100. opConstructors["JUMPXEQKS"] = function() -- inverse
  1101. addReference(insnIndex, insnIndex + sD)
  1102. local NOTFlag = rshift(aux, 0x1F) ~= 1
  1103. local nextInsn = proto.insnTable[insnIndex + 2]
  1104. local nextOP = Luau:INSN_OP(nextInsn)
  1105. local nextOPName = LuauOpCode[nextOP] and LuauOpCode[nextOP].name
  1106. if nextOPName == "JUMPBACK" or string.find(nextOPName,"JUMPXEQ%w+") then
  1107. NOTFlag = not NOTFlag
  1108. end
  1109. local v = if NOTFlag then "~=" else "=="
  1110. local k = proto.constsTable[bit32.band(aux, 0xFFFFFF) + 1] or nilValue
  1111. local endPoint = insnIndex + sD
  1112. createLoopPoint(insnIndex, endPoint)
  1113. protoOutput ..= string.format("if %s %s %s then goto #%i", modifyRegister(A), v, '"' .. tostring(k.value) .. '"', endPoint)
  1114. end
  1115. opConstructors["ADD"] = function()
  1116. protoOutput ..= baseLocal(A, `{modifyRegister(B)} + {modifyRegister(C)}`)
  1117. end
  1118. opConstructors["ADDK"] = function()
  1119. local k = proto.constsTable[C + 1] or nilValue
  1120. protoOutput ..= baseLocal(A, `{modifyRegister(B)} + {handleConstantValue(k)}`)
  1121. end
  1122. opConstructors["SUB"] = function()
  1123. protoOutput ..= baseLocal(A, `{modifyRegister(B)} - {modifyRegister(C)}`)
  1124. end
  1125. opConstructors["SUBK"] = function()
  1126. local k = proto.constsTable[C + 1] or nilValue
  1127. protoOutput ..= baseLocal(A, `{modifyRegister(B)} - {handleConstantValue(k)}`)
  1128. end
  1129. opConstructors["MUL"] = function()
  1130. protoOutput ..= baseLocal(A, `{modifyRegister(B)} * {modifyRegister(C)}`)
  1131. end
  1132. opConstructors["MULK"] = function()
  1133. local k = proto.constsTable[C + 1] or nilValue
  1134. protoOutput ..= baseLocal(A, `{modifyRegister(B)} * {handleConstantValue(k)}`)
  1135. end
  1136. opConstructors["DIV"] = function()
  1137. protoOutput ..= baseLocal(A, `{modifyRegister(B)} / {modifyRegister(C)}`)
  1138. end
  1139. opConstructors["DIVK"] = function()
  1140. local k = proto.constsTable[C + 1] or nilValue
  1141. protoOutput ..= baseLocal(A, `{modifyRegister(B)} / {handleConstantValue(k)}`)
  1142. end
  1143. opConstructors["MOD"] = function()
  1144. protoOutput ..= baseLocal(A, `{modifyRegister(B)} % {modifyRegister(C)}`)
  1145. end
  1146. opConstructors["MODK"] = function()
  1147. local k = proto.constsTable[C + 1] or nilValue
  1148. protoOutput ..= baseLocal(A, `{modifyRegister(B)} % {handleConstantValue(k)}`)
  1149. end
  1150. opConstructors["POW"] = function()
  1151. protoOutput ..= baseLocal(A, `{modifyRegister(B)} ^ {modifyRegister(C)}`)
  1152. end
  1153. opConstructors["POWK"] = function()
  1154. local k = proto.constsTable[C + 1] or nilValue
  1155. protoOutput ..= baseLocal(A, `{modifyRegister(B)} ^ {handleConstantValue(k)}`)
  1156. end
  1157. opConstructors["CALL"] = function()
  1158. if C == 0 then -- MULTRET (Results)
  1159. protoOutput ..= modifyRegister(A - 1)
  1160. else
  1161. for j = 1, C - 1 do
  1162. protoOutput ..= modifyRegister(A + j - 1)
  1163. if j < C - 1 then protoOutput ..= ", " end
  1164. end
  1165. end
  1166. if C ~= 1 then
  1167. protoOutput ..= " = "
  1168. end
  1169. local nameCallExpected = expectation and expectation.type == "NAMECALL"
  1170. if nameCallExpected then
  1171. protoOutput ..= expectation.value .. "("
  1172. else
  1173. protoOutput ..= modifyRegister(A) .. "("
  1174. end
  1175. if (B - 1) == 0 then -- MULTRET (Arguments)
  1176. protoOutput ..= modifyRegister(A + 1)
  1177. else
  1178. if nameCallExpected then
  1179. for j = 1, B - 2 do
  1180. protoOutput ..= modifyRegister(A + 1 + j) -- exclude self
  1181. if j < B - 2 then protoOutput ..= ", " end
  1182. end
  1183. else
  1184. for j = 1, B - 1 do
  1185. protoOutput ..= modifyRegister(A + j)
  1186. if j < B - 1 then protoOutput ..= ", " end
  1187. end
  1188. end
  1189. end
  1190. expectation = nil
  1191. protoOutput ..= ")"
  1192. end
  1193. opConstructors["NATIVECALL"] = function()
  1194. protoOutput ..= "NATIVECALL()"
  1195. end
  1196. opConstructors["NEWTABLE"] = function()
  1197. local arraySize = aux
  1198. if arraySize ~= 0 then
  1199. protoOutput ..= baseLocal(A, string.format("{} -- this array has (%s)indexes by default", arraySize))
  1200. else
  1201. protoOutput ..= baseLocal(A, "{} -- this array is empty")
  1202. end
  1203. end
  1204. opConstructors["DUPTABLE"] = function()
  1205. local t = proto.constsTable[D + 1].value
  1206. protoOutput ..= baseLocal(A, "{")
  1207. for i = 1, t.size do
  1208. local id = t.keys[i]
  1209. local k = proto.constsTable[id]
  1210. protoOutput ..= handleConstantValue(k)
  1211. if i < t.size then
  1212. protoOutput ..= ", "
  1213. end
  1214. end
  1215. --addTab(depth)
  1216. protoOutput ..= "}"
  1217. end
  1218. opConstructors["SETLIST"] = function()
  1219. local reg = A
  1220. local arrayChunkReg = B
  1221. local count = C
  1222.  
  1223. local arrayIndex = aux
  1224.  
  1225. if count == 0 then -- MULTRET
  1226. -- TODO: learn more and fix this
  1227. protoOutput ..= string.format("%s[%i] = ...", modifyRegister(reg), arrayIndex)
  1228. else
  1229. for i = 0, count - 2 do
  1230. protoOutput ..= string.format("%s[%i] = %s\n", modifyRegister(reg), arrayIndex + i, modifyRegister(arrayChunkReg + i))
  1231. end
  1232. end
  1233. end
  1234. opConstructors["COVERAGE"] = function()
  1235. protoOutput ..= string.format("COVERAGE (%i)", E)
  1236. end
  1237. opConstructors["CAPTURE"] = function()
  1238. local captureType = ""
  1239. if A == LuauCaptureType.LCT_VAL then
  1240. -- value is immutable
  1241. captureType = "VAL"
  1242. elseif A == LuauCaptureType.LCT_UPVAL then
  1243. -- upvalues are confirmed locals and not temporary registers
  1244. captureType = "UPVAL"
  1245. elseif A == LuauCaptureType.LCT_REF then
  1246. captureType = "REF"
  1247. end
  1248. protoOutput ..= string.format("CAPTURE %s %s%d\n", captureType, if captureType == "UPVAL" then "U" else "R", B)
  1249. end
  1250. opConstructors["SUBRK"] = function()
  1251. local k = proto.constsTable[B + 1] or nilValue
  1252. protoOutput ..= baseLocal(A, `{handleConstantValue(k)} - {modifyRegister(C)}`)
  1253. end
  1254. opConstructors["DIVRK"] = function()
  1255. local k = proto.constsTable[B + 1] or nilValue
  1256. protoOutput ..= baseLocal(A, `{handleConstantValue(k)} / {modifyRegister(C)}`)
  1257. end
  1258. opConstructors["NEWCLOSURE"] = function()
  1259. if SHOW_MISC_OPERATIONS then
  1260. protoOutput ..= "[NEWCLOSURE]\n"
  1261. end
  1262.  
  1263. local nextProto = proto.innerProtoTable[D + 1]
  1264. if not nextProto then
  1265. warn("no next proto?")
  1266. end
  1267.  
  1268. local nCaptures = 0
  1269. for j = insnIndex + 1, proto.sizeInsns do
  1270. local insn = proto.insnTable[j]
  1271. local op = Luau:INSN_OP(insn)
  1272. local opInfo = LuauOpCode[op]
  1273. if opInfo and opInfo.name == "CAPTURE" then
  1274. local upvalueIndex = j - insnIndex - 1
  1275.  
  1276. local captureType = Luau:INSN_A(insn)
  1277. local captureIndex = Luau:INSN_B(insn)
  1278.  
  1279. nCaptures += 1
  1280.  
  1281. addTab(depth)
  1282. if captureType == LuauCaptureType.LCT_VAL or captureType == LuauCaptureType.LCT_REF then
  1283. local varRef = modifyRegister(captureIndex, true)
  1284. --upvalRefs[upvalueIndex] = varRef
  1285. protoOutput ..= string.format("-- V nested upvalues[%i] = %s\n", upvalueIndex, varRef)
  1286. nextProto.nestedUpvalues[upvalueIndex] = varRef
  1287. elseif captureType == LuauCaptureType.LCT_UPVAL then
  1288. protoOutput ..= string.format("-- V nested upvalues[%i] = upvalues[%i]\n", upvalueIndex, captureIndex)
  1289. -- temporary
  1290. nextProto.nestedUpvalues[upvalueIndex] = `upvalues[{captureIndex}]`
  1291. else
  1292. error("[NEWCLOSURE] Invalid capture type")
  1293. end
  1294. else
  1295. break
  1296. end
  1297. end
  1298. insnIndex += nCaptures
  1299.  
  1300. addTab(depth)
  1301. if nextProto then
  1302. if nextProto.source then
  1303. protoOutput ..= baseProto(nextProto, depth, false)
  1304. addTab(depth)
  1305. protoOutput ..= string.format("[NEWCLOSURE] %s = ", modifyRegister(A)) .. nextProto.source
  1306. else
  1307. protoOutput ..= string.format("[NEWCLOSURE] %s = ", modifyRegister(A)) .. baseProto(nextProto, depth, false)
  1308. end
  1309.  
  1310. --TODO: idk what to do with this. causes issues sometimes
  1311. totalVars += nextProto.numVars
  1312. end
  1313. end
  1314. opConstructors["DUPCLOSURE"] = function()
  1315. -- shared upvalues >= 0
  1316.  
  1317. if SHOW_MISC_OPERATIONS then
  1318. protoOutput ..= "[DUPCLOSURE]\n"
  1319. end
  1320.  
  1321. local nextProto = protoTable[proto.constsTable[D + 1].value - 1]
  1322. if not nextProto then
  1323. warn("no next proto?")
  1324. end
  1325.  
  1326. local nCaptures = 0
  1327. for j = insnIndex + 1, proto.sizeInsns do
  1328. local insn = proto.insnTable[j]
  1329. local op = Luau:INSN_OP(insn)
  1330. local opInfo = LuauOpCode[op]
  1331. if opInfo and opInfo.name == "CAPTURE" then
  1332. local upvalueIndex = j - insnIndex - 1
  1333.  
  1334. local captureType = Luau:INSN_A(insn)
  1335. local captureIndex = Luau:INSN_B(insn)
  1336.  
  1337. nCaptures += 1
  1338.  
  1339. addTab(depth)
  1340. if captureType == LuauCaptureType.LCT_VAL or captureType == LuauCaptureType.LCT_REF then
  1341. local varRef = modifyRegister(captureIndex)
  1342. --upvalRefs[upvalueIndex] = varRef
  1343. protoOutput ..= string.format("-- V nested upvalues[%i] = %s\n", upvalueIndex, varRef)
  1344. nextProto.nestedUpvalues[upvalueIndex] = varRef
  1345. elseif captureType == LuauCaptureType.LCT_UPVAL then
  1346. protoOutput ..= string.format("-- V nested upvalues[%i] = upvalues[%i]\n", upvalueIndex, captureIndex)
  1347. -- temporary
  1348. nextProto.nestedUpvalues[upvalueIndex] = `upvalues[{captureIndex}]`
  1349. else
  1350. error("[DUPCLOSURE] Invalid capture type")
  1351. end
  1352. else
  1353. break
  1354. end
  1355. end
  1356. insnIndex += nCaptures
  1357.  
  1358. addTab(depth)
  1359. if nextProto then
  1360. if nextProto.source then
  1361. protoOutput ..= baseProto(nextProto, depth, false)
  1362. addTab(depth)
  1363. protoOutput ..= string.format("[DUPCLOSURE] %s = ", modifyRegister(A)) .. nextProto.source
  1364. else
  1365. protoOutput ..= string.format("[DUPCLOSURE] %s = ", modifyRegister(A)) .. baseProto(nextProto, depth, false)
  1366. end
  1367.  
  1368. --TODO: idk what to do with this. causes issues sometimes
  1369. totalVars += nextProto.numVars
  1370. end
  1371. end
  1372. opConstructors["PREPVARARGS"] = function()
  1373. if SHOW_MISC_OPERATIONS then
  1374. protoOutput ..= string.format("[PREPVARARGS] (%i) -- number of fixed args", A)
  1375. end
  1376. end
  1377. opConstructors["RETURN"] = function()
  1378. protoOutput ..= "return"
  1379. --if B == 1 then return doesn't return any values
  1380. if B == 0 then -- MULTRET
  1381. protoOutput ..= string.format(" %s, ...", modifyRegister(A))
  1382. elseif B > 1 then
  1383. local numValues = B - 2
  1384. for i = 0, numValues do
  1385. protoOutput ..= string.format(" %s", modifyRegister(A + i))
  1386. if i ~= numValues then
  1387. protoOutput ..= ","
  1388. end
  1389. end
  1390. end
  1391. end
  1392. opConstructors["IDIV"] = function()
  1393. protoOutput ..= baseLocal(A, `{modifyRegister(B)} // {modifyRegister(C)}`)
  1394. end
  1395. opConstructors["IDIVK"] = function()
  1396. local k = proto.constsTable[C + 1] or nilValue
  1397. protoOutput ..= baseLocal(A, `{modifyRegister(B)} // {handleConstantValue(k)}`)
  1398. end
  1399. end
  1400.  
  1401. local ctor = opConstructors[opInfo.name]
  1402. if ctor then
  1403. ctor()
  1404. else
  1405. warn(`OP '{opInfo.name}' went unhandled: missing constructor`)
  1406. end
  1407.  
  1408. if SHOW_REFERENCES then
  1409. for _, v in refData do
  1410. if v.insnIndex == insnIndex then
  1411. protoOutput ..= " -- referenced by "
  1412. for i = 1, #v.refs do
  1413. protoOutput ..= "#" .. v.refs[i]
  1414. if i < #v.refs then
  1415. protoOutput ..= ", "
  1416. end
  1417. end
  1418. end
  1419. end
  1420. end
  1421.  
  1422. addNewLine()
  1423. end
  1424.  
  1425. local loopPointData = ifLoopPoints[insnIndex]
  1426. if loopPointData then
  1427. local hasElse = false
  1428. for i, pointType in loopPointData do
  1429. local pointResult = "end"
  1430. if pointType == POINT_TYPE_ELSE then
  1431. if hasElse then
  1432. -- can't handle it right now, mark as an addition to previous if then
  1433. pointResult = "and (else)"
  1434. else
  1435. hasElse = true
  1436. pointResult = "else"
  1437. end
  1438. elseif pointType == POINT_TYPE_ELSEIF then
  1439. pointResult = "elseif"
  1440. end
  1441. addTab(depth + 4)
  1442. protoOutput ..= pointResult
  1443. addNewLine()
  1444. end
  1445. end
  1446. end
  1447.  
  1448. if not isMainProto then
  1449. depth -= 1
  1450.  
  1451. addTab(depth)
  1452. protoOutput ..= "end"
  1453. addNewLine()
  1454. end
  1455.  
  1456. proto.numVars = protoVars
  1457.  
  1458. return protoOutput
  1459. end
  1460.  
  1461. local decompiledOutput = baseProto(mainProto, 0, true)
  1462.  
  1463. if LIST_USED_GLOBALS then
  1464. if #globalData > 0 then
  1465. output ..= string.format(Strings.USED_GLOBALS, table.concat(globalData, ", "))
  1466. end
  1467. end
  1468.  
  1469. output ..= decompiledOutput
  1470.  
  1471. return output
  1472. end
  1473. -- supposed to cleanup temporary registers
  1474. local function optimize(code)
  1475. return code
  1476. end
  1477. local function manager(proceed, issue)
  1478. if proceed then
  1479. local startTime
  1480. local elapsedTime
  1481.  
  1482. local result
  1483. local function processingTask()
  1484. startTime = os.clock()
  1485. result = optimize(roughDecompilation())
  1486. elapsedTime = os.clock() - startTime
  1487. end
  1488. task.spawn(processingTask)
  1489.  
  1490. -- wait for yielding task
  1491. while not result and (os.clock() - startTime) < DECOMPILER_TIMEOUT do
  1492. task.wait()
  1493. end
  1494.  
  1495. if result then
  1496. return string.format(Strings.SUCCESS, result), elapsedTime
  1497. else
  1498. return Strings.TIMEOUT
  1499. end
  1500. else
  1501. if issue == "COMPILATION_FAILURE" then
  1502. local errorMessageLength = reader:len() - 1
  1503. local errorMessage = reader:nextString(errorMessageLength)
  1504. return string.format(Strings.COMPILATION_FAILURE, errorMessage)
  1505. elseif issue == "UNSUPPORTED_LBC_VERSION" then
  1506. return Strings.UNSUPPORTED_LBC_VERSION
  1507. end
  1508. end
  1509. end
  1510. --
  1511. bytecodeVersion = reader:nextByte()
  1512. if bytecodeVersion == 0 then
  1513. -- script errored
  1514. return manager(false, "COMPILATION_FAILURE")
  1515. elseif bytecodeVersion <= LuauBytecodeTag.LBC_VERSION_MAX and bytecodeVersion >= LuauBytecodeTag.LBC_VERSION_MIN then
  1516. -- script uses supported bytecode version
  1517. return manager(true)
  1518. else
  1519. return manager(false, "UNSUPPORTED_LBC_VERSION")
  1520. end
  1521. end
  1522.  
  1523. local _ENV = (getgenv or getrenv or getfenv)()
  1524. _ENV.decompile = function(script)
  1525. if typeof(script) ~= "Instance" then
  1526. error("invalid argument #1 to 'decompile' (Instance expected)", 2)
  1527. return
  1528. end
  1529.  
  1530. local function isScriptValid()
  1531. local class = script.ClassName
  1532. if class == "Script" then
  1533. return script.RunContext == Enum.RunContext.Client
  1534. else
  1535. return class == "LocalScript" or class == "ModuleScript"
  1536. end
  1537. end
  1538. if not isScriptValid() then
  1539. error("invalid argument #1 to 'decompile' (Instance<LocalScript, ModuleScript> expected)", 2)
  1540. return
  1541. end
  1542.  
  1543. if not getscriptbytecode then
  1544. error("decompile is not enabled. (getscriptbytecode is missing)", 2)
  1545. return
  1546. end
  1547.  
  1548. local success, result = pcall(getscriptbytecode, script)
  1549. if not success or type(result) ~= "string" then
  1550. error(`decompile failed to grab script bytecode: {tostring(result)}`, 2)
  1551. return
  1552. end
  1553.  
  1554. local output, elapsedTime = Decompile(result)
  1555.  
  1556. if RETURN_ELAPSED_TIME then
  1557. return output, elapsedTime
  1558. else
  1559. return output
  1560. end
  1561. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement