NihonDecompiler

Run Settings
LanguageLua
Language Version
Run Command
local function deserialize(bytecode) local reader do reader = {} pos = 1 function reader:pos() return pos end function reader:nextByte() local v = bytecode:byte(pos, pos) pos = pos + 1 return v end function reader:nextChar() return string.char(reader:nextByte()); end function reader:nextInt() local b = { reader:nextByte(), reader:nextByte(), reader:nextByte(), reader:nextByte() } return ( bit32.bor(bit32.lshift(b[4], 24), bit32.bor(bit32.lshift(b[3], 16), bit32.bor(bit32.lshift(b[2], 8), b[1]))) ) end function reader:nextVarInt() local c1, c2, b, r = 0, 0, 0, 0 repeat c1 = reader:nextByte() c2 = bit32.band(c1, 0x7F) r = bit32.bor(r, bit32.lshift(c2, b)) b = b + 7 until not bit32.btest(c1, 0x80) return r; end function reader:nextString() local result = "" local len = reader:nextVarInt(); for i = 1, len do result = result .. reader:nextChar(); end return result; end function reader:nextDouble() local b = {}; for i = 1, 8 do table.insert(b, reader:nextByte()); end local str = ''; for i = 1, 8 do str = str .. string.char(b[i]); end return string.unpack("<d", str) end end local status = reader:nextByte() if (status ~= 0) then local protoTable = {} local stringTable = {} local sizeStrings = reader:nextVarInt() for i = 1,sizeStrings do stringTable[i] = reader:nextString() end local sizeProtos = reader:nextVarInt(); for i = 1,sizeProtos do protoTable[i] = {} -- pre-initialize an entry protoTable[i].codeTable = {} protoTable[i].kTable = {} protoTable[i].pTable = {} protoTable[i].smallLineInfo = {} protoTable[i].largeLineInfo = {} end for i = 1,sizeProtos do local proto = protoTable[i] proto.maxStackSize = reader:nextByte() proto.numParams = reader:nextByte() proto.numUpValues = reader:nextByte() proto.isVarArg = reader:nextByte() proto.sizeCode = reader:nextVarInt() for j = 1,proto.sizeCode do proto.codeTable[j] = reader:nextInt() end proto.sizeConsts = reader:nextVarInt(); for j = 1,proto.sizeConsts do local k = {}; k.type = reader:nextByte(); if k.type == 1 then -- boolean k.value = (reader:nextByte() == 1 and true or false) elseif k.type == 2 then -- number k.value = reader:nextDouble() elseif k.type == 3 then -- string k.value = stringTable[reader:nextVarInt()] elseif k.type == 4 then -- cache k.value = reader:nextInt() elseif k.type == 5 then -- table k.value = { ["size"] = reader:nextVarInt(), ["ids"] = {} } for s = 1,k.value.size do table.insert(k.value.ids, reader:nextVarInt() + 1) end elseif k.type == 6 then -- closure k.value = reader:nextVarInt() + 1 -- closure id elseif k.type ~= 0 then error(string.format("Unrecognized constant type: %i", k.type)) end proto.kTable[j] = k end proto.sizeProtos = reader:nextVarInt(); for j = 1,proto.sizeProtos do proto.pTable[j] = protoTable[reader:nextVarInt() + 1] end proto.lineDefined = reader:nextVarInt() local protoSourceId = reader:nextVarInt() proto.source = stringTable[protoSourceId] if (reader:nextByte() == 1) then -- Has Line info? local compKey = reader:nextVarInt() for j = 1,proto.sizeCode do proto.smallLineInfo[j] = reader:nextByte() end local n = bit32.band(proto.sizeCode + 3, -4) local intervals = bit32.rshift(proto.sizeCode - 1, compKey) + 1 for j = 1,intervals do proto.largeLineInfo[j] = reader:nextInt() end end if (reader:nextByte() == 1) then -- Has Debug info? error'disassemble() can only be called on ROBLOX scripts' end end local mainProtoId = reader:nextVarInt() return protoTable[mainProtoId + 1], protoTable, stringTable; else error(string.format("Invalid bytecode (version: %i)", status)) return nil; end end local function getluauoptable() return { -- I could use case multiplier, but that depends only on how accurate -- our ordering of the opcodes are -- so if we really want to rely on -- the latest updated luau source, then we could do it that way. { ["name"] = "NOP", ["type"] = "none", ["case"] = 0, ["number"] = 0x00 }, { ["name"] = "BREAK", ["type"] = "none", ["case"] = 1, ["number"] = 0xE3 }, { ["name"] = "LOADNIL", ["type"] = "iA", ["case"] = 2, ["number"] = 0xC6 }, { ["name"] = "LOADB", ["type"] = "iABC", ["case"] = 3, ["number"] = 0xA9 }, { ["name"] = "LOADN", ["type"] = "iABx", ["case"] = 4, ["number"] = 0x8C }, { ["name"] = "LOADK", ["type"] = "iABx", ["case"] = 5, ["number"] = 0x6F }, { ["name"] = "MOVE", ["type"] = "iAB", ["case"] = 6, ["number"] = 0x52 }, { ["name"] = "GETGLOBAL", ["type"] = "iAC", ["case"] = 7, ["number"] = 0x35, ["aux"] = true }, { ["name"] = "SETGLOBAL", ["type"] = "iAC", ["case"] = 8, ["number"] = 0x18, ["aux"] = true }, { ["name"] = "GETUPVAL", ["type"] = "iAB", ["case"] = 9, ["number"] = 0xFB }, { ["name"] = "SETUPVAL", ["type"] = "iAB", ["case"] = 10, ["number"] = 0xDE }, { ["name"] = "CLOSEUPVALS", ["type"] = "iA", ["case"] = 11, ["number"] = 0xC1 }, { ["name"] = "GETIMPORT", ["type"] = "iABx", ["case"] = 12, ["number"] = 0xA4, ["aux"] = true }, { ["name"] = "GETTABLE", ["type"] = "iABC", ["case"] = 13, ["number"] = 0x87 }, { ["name"] = "SETTABLE", ["type"] = "iABC", ["case"] = 14, ["number"] = 0x6A }, { ["name"] = "GETTABLEKS", ["type"] = "iABC", ["case"] = 15, ["number"] = 0x4D, ["aux"] = true }, { ["name"] = "SETTABLEKS", ["type"] = "iABC", ["case"] = 16, ["number"] = 0x30, ["aux"] = true }, { ["name"] = "GETTABLEN", ["type"] = "iABC", ["case"] = 17, ["number"] = 0x13 }, { ["name"] = "SETTABLEN", ["type"] = "iABC", ["case"] = 18, ["number"] = 0xF6 }, { ["name"] = "NEWCLOSURE", ["type"] = "iABx", ["case"] = 19, ["number"] = 0xD9 }, { ["name"] = "NAMECALL", ["type"] = "iABC", ["case"] = 20, ["number"] = 0xBC, ["aux"] = true }, { ["name"] = "CALL", ["type"] = "iABC", ["case"] = 21, ["number"] = 0x9F }, { ["name"] = "RETURN", ["type"] = "iAB", ["case"] = 22, ["number"] = 0x82 }, { ["name"] = "JUMP", ["type"] = "isBx", ["case"] = 23, ["number"] = 0x65 }, { ["name"] = "JUMPBACK", ["type"] = "isBx", ["case"] = 24, ["number"] = 0x48 }, { ["name"] = "JUMPIF", ["type"] = "iAsBx", ["case"] = 25, ["number"] = 0x2B }, { ["name"] = "JUMPIFNOT", ["type"] = "iAsBx", ["case"] = 26, ["number"] = 0x0E }, { ["name"] = "JUMPIFEQ", ["type"] = "iAsBx", ["case"] = 27, ["number"] = 0xF1, ["aux"] = true }, { ["name"] = "JUMPIFLE", ["type"] = "iAsBx", ["case"] = 28, ["number"] = 0xD4, ["aux"] = true }, { ["name"] = "JUMPIFLT", ["type"] = "iAsBx", ["case"] = 29, ["number"] = 0xB7, ["aux"] = true }, { ["name"] = "JUMPIFNOTEQ", ["type"] = "iAsBx", ["case"] = 30, ["number"] = 0x9A, ["aux"] = true }, { ["name"] = "JUMPIFNOTLE", ["type"] = "iAsBx", ["case"] = 31, ["number"] = 0x7D, ["aux"] = true }, { ["name"] = "JUMPIFNOTLT", ["type"] = "iAsBx", ["case"] = 32, ["number"] = 0x60, ["aux"] = true }, { ["name"] = "ADD", ["type"] = "iABC", ["case"] = 33, ["number"] = 0x43 }, { ["name"] = "SUB", ["type"] = "iABC", ["case"] = 34, ["number"] = 0x26 }, { ["name"] = "MUL", ["type"] = "iABC", ["case"] = 35, ["number"] = 0x09 }, { ["name"] = "DIV", ["type"] = "iABC", ["case"] = 36, ["number"] = 0xEC }, { ["name"] = "MOD", ["type"] = "iABC", ["case"] = 37, ["number"] = 0xCF }, { ["name"] = "POW", ["type"] = "iABC", ["case"] = 38, ["number"] = 0xB2 }, { ["name"] = "ADDK", ["type"] = "iABC", ["case"] = 39, ["number"] = 0x95 }, { ["name"] = "SUBK", ["type"] = "iABC", ["case"] = 40, ["number"] = 0x78 }, { ["name"] = "MULK", ["type"] = "iABC", ["case"] = 41, ["number"] = 0x5B }, { ["name"] = "DIVK", ["type"] = "iABC", ["case"] = 42, ["number"] = 0x3E }, { ["name"] = "MODK", ["type"] = "iABC", ["case"] = 43, ["number"] = 0x21 }, { ["name"] = "POWK", ["type"] = "iABC", ["case"] = 44, ["number"] = 0x04 }, { ["name"] = "AND", ["type"] = "iABC", ["case"] = 45, ["number"] = 0xE7 }, { ["name"] = "OR", ["type"] = "iABC", ["case"] = 46, ["number"] = 0xCA }, { ["name"] = "ANDK", ["type"] = "iABC", ["case"] = 47, ["number"] = 0xAD }, { ["name"] = "ORK", ["type"] = "iABC", ["case"] = 48, ["number"] = 0x90 }, { ["name"] = "CONCAT", ["type"] = "iABC", ["case"] = 49, ["number"] = 0x73 }, { ["name"] = "NOT", ["type"] = "iAB", ["case"] = 50, ["number"] = 0x56 }, { ["name"] = "UNM", ["type"] = "iAB", ["case"] = 51, ["number"] = 0x39 }, { ["name"] = "LEN", ["type"] = "iAB", ["case"] = 52, ["number"] = 0x1C }, { ["name"] = "NEWTABLE", ["type"] = "iAB", ["case"] = 53, ["number"] = 0xFF, ["aux"] = true }, { ["name"] = "DUPTABLE", ["type"] = "iABx", ["case"] = 54, ["number"] = 0xE2 }, { ["name"] = "SETLIST", ["type"] = "iABC", ["case"] = 55, ["number"] = 0xC5, ["aux"] = true }, { ["name"] = "NFORPREP", ["type"] = "iABx", ["case"] = 56, ["number"] = 0xA8 }, { ["name"] = "NFORLOOP", ["type"] = "iABx", ["case"] = 57, ["number"] = 0x8B }, { ["name"] = "TFORLOOP", ["type"] = "iABx", ["case"] = 58, ["number"] = 0x6E, ["aux"] = true }, { ["name"] = "IPAIRSPREP", ["type"] = "none", ["case"] = 59, ["number"] = 0x51 }, { ["name"] = "IPAIRSLOOP", ["type"] = "none", ["case"] = 60, ["number"] = 0x34 }, { ["name"] = "PAIRSPREP", ["type"] = "none", ["case"] = 61, ["number"] = 0x17 }, { ["name"] = "PAIRSLOOP", ["type"] = "none", ["case"] = 62, ["number"] = 0xFA }, { ["name"] = "GETVARARGS", ["type"] = "iAB", ["case"] = 63, ["number"] = 0xDD }, { ["name"] = "DUPCLOSURE", ["type"] = "iABx", ["case"] = 64, ["number"] = 0xC0 }, { ["name"] = "PREPVARARGS", ["type"] = "iA", ["case"] = 65, ["number"] = 0xA3 }, { ["name"] = "LOADKX", ["type"] = "iA", ["case"] = 66, ["number"] = 0x86 }, { ["name"] = "JUMPX", ["type"] = "isAx", ["case"] = 67, ["number"] = 0x69 }, { ["name"] = "FASTCALL", ["type"] = "iAC", ["case"] = 68, ["number"] = 0x4C }, { ["name"] = "COVERAGE", ["type"] = "isAx", ["case"] = 69, ["number"] = 0x2F }, { ["name"] = "CAPTURE", ["type"] = "iAB", ["case"] = 70, ["number"] = 0x12 }, { ["name"] = "JUMPIFEQK", ["type"] = "iABx", ["case"] = 71, ["number"] = 0xF5, ["aux"] = true }, { ["name"] = "JUMPIFNOTEQK", ["type"] = "iABx", ["case"] = 72, ["number"] = 0xD8, ["aux"] = true }, { ["name"] = "FASTCALL1", ["type"] = "iABC", ["case"] = 73, ["number"] = 0xBB }, { ["name"] = "FASTCALL2", ["type"] = "iABC", ["case"] = 74, ["number"] = 0x9E, ["aux"] = true }, { ["name"] = "FASTCALL2K", ["type"] = "iABC", ["case"] = 75, ["number"] = 0x81, ["aux"] = true }, { ["name"] = "COUNT", ["type"] = "none", ["case"] = 76, ["number"] = 0x64 } }; end local luau = {}; luau.SIZE_A = 8 luau.SIZE_C = 8 luau.SIZE_B = 8 luau.SIZE_Bx = (luau.SIZE_C + luau.SIZE_B) luau.SIZE_OP = 8 luau.POS_OP = 0 luau.POS_A = (luau.POS_OP + luau.SIZE_OP) luau.POS_B = (luau.POS_A + luau.SIZE_A) luau.POS_C = (luau.POS_B + luau.SIZE_B) luau.POS_Bx = luau.POS_B luau.MAXARG_A = (bit32.lshift(1, luau.SIZE_A) - 1) luau.MAXARG_B = (bit32.lshift(1, luau.SIZE_B) - 1) luau.MAXARG_C = (bit32.lshift(1, luau.SIZE_C) - 1) luau.MAXARG_Bx = (bit32.lshift(1, luau.SIZE_Bx) - 1) luau.MAXARG_sBx = bit32.rshift(luau.MAXARG_Bx, 1) luau.BITRK = bit32.lshift(1, (luau.SIZE_B - 1)) luau.MAXINDEXRK = (luau.BITRK - 1) luau.ISK = function(x) return bit32.band(x, luau.BITRK) end luau.INDEXK = function(x) return bit32.band(x, bit32.bnot(luau.BITRK)) end luau.RKASK = function(x) return bit32.bor(x, luau.BITRK) end luau.MASK1 = function(n,p) return bit32.lshift(bit32.bnot(bit32.lshift(bit32.bnot(0), n)), p) end luau.MASK0 = function(n,p) return bit32.bnot(luau.MASK1(n, p)) end luau.GETARG_A = function(i) return bit32.band(bit32.rshift(i, luau.POS_A), luau.MASK1(luau.SIZE_A, 0)) end luau.GETARG_B = function(i) return bit32.band(bit32.rshift(i, luau.POS_B), luau.MASK1(luau.SIZE_B, 0)) end luau.GETARG_C = function(i) return bit32.band(bit32.rshift(i, luau.POS_C), luau.MASK1(luau.SIZE_C, 0)) end luau.GETARG_Bx = function(i) return bit32.band(bit32.rshift(i, luau.POS_Bx), luau.MASK1(luau.SIZE_Bx, 0)) end luau.GETARG_sBx = function(i) local Bx = luau.GETARG_Bx(i) local sBx = Bx + 1; if Bx > 0x7FFF and Bx <= 0xFFFF then sBx = -(0xFFFF - Bx); sBx = sBx - 1; end return sBx end luau.GETARG_sAx = function(i) return bit32.rshift(i, 8) end luau.GET_OPCODE = function(i) return bit32.band(bit32.rshift(i, luau.POS_OP), luau.MASK1(luau.SIZE_OP, 0)) end local function disassemble(a1, showOps) if (typeof(a1):lower() == "instance") then if not getscriptbytecode then error("Executor does not support getscriptbytecode") end a1 = getscriptbytecode(a1); end if type(a1) == "table" then -- I just prefer bytecode strings local t = a1; at = ""; for i = 1,#t do a1 = a1 .. string.char(t[i]); end end local output = "" local mainProto, protoTable, stringTable = deserialize(a1) local luauOpTable = getluauoptable(); local function getOpCode(opName) for _,v in pairs(luauOpTable) do if v.name == opName then return v.number; end end return 0; end mainProto.source = "main" mainScope = {}; -- scope control, coming soon local function readProto(proto, depth) local output = ""; local function addTabSpace(depth) output = output .. string.rep(" ", depth) end -- using function name (this will be removed & done outside of readProto) if proto.source then output = output .. proto.source .. " = function(" else output = output .. "function(" end for i = 1,proto.numParams do output = output .. "arg" .. (i - 1) -- args coincide with stack index if i < proto.numParams then output = output .. ", " end end if proto.isVarArg ~= 0 then if proto.numParams > 0 then output = output .. ", " end output = output .. "..." end output = output .. ")\n" depth = depth + 1 for i = 1,proto.numParams do addTabSpace(depth); output = output .. string.format("local v%i = arg%i\n", i - 1, i - 1); end local refData = {} local nameCall = nil local markedAux = false local codeIndex = 1 while codeIndex < proto.sizeCode do local i = proto.codeTable[codeIndex] local opc = luau.GET_OPCODE(i) local A = luau.GETARG_A(i) local B = luau.GETARG_B(i) local Bx = luau.GETARG_Bx(i) local C = luau.GETARG_C(i) local sBx = luau.GETARG_sBx(i) local sAx = luau.GETARG_sAx(i) local aux = proto.codeTable[codeIndex + 1] if markedAux then markedAux = false else addTabSpace(depth); local opinfo; for _,v in pairs(luauOpTable) do if v.number == opc then opinfo = v break; end end -- output = output .. tostring(codeIndex) .. ". " if showOps and opinfo then local str = opinfo.name .. string.rep(" ", 16 - string.len(opinfo.name)) if opinfo.type == "iA" then str = str .. string.format("%i", A) elseif opinfo.type == "iAB" then str = str .. string.format("%i %i", A, B) elseif opinfo.type == "iAC" then str = str .. string.format("%i %i", A, C) elseif opinfo.type == "iABx" then str = str .. string.format("%i %i", A, Bx) elseif opinfo.type == "iAsBx" then str = str .. string.format("%i %i", A, sBx) elseif opinfo.type == "isBx" then str = str .. string.format("%i", sBx) elseif opinfo.type == "iABC" then str = str .. string.format("%i %i %i", A, B, C) end if opinfo.aux then str = str .. " [aux]"; markedAux = true end output = output .. str .. string.rep(" ", 40 - string.len(str)) else if opinfo then if opinfo.aux then markedAux = true; end end end -- continue with disassembly (rough decompilation -- no scope/flow control) -- local varsDefined = {}; local function defineVar(index, name) table.insert(varsDefined, { ["name"] = name, ["stackIndex"] = index }); end local function isVarDefined(index) return true; --[[for _,v in pairs(varsDefined) do if v.stackIndex == index then return true end end return false; ]] end local function addReference(refStart, refEnd) for _,v in pairs(refData) do if v.codeIndex == refEnd then table.insert(v.refs, refStart); return; end end table.insert(refData, { ["codeIndex"] = refEnd, ["refs"] = { refStart } }); end local nilValue = { ["type"] = "nil", ["value"] = "nil" } --[[ TO-DO: we could make getOpCode faster by using the opcode numbers directly, or just getting it by table index and the case-to-opcode multiplier (op * 227) but tbh this runs just fine ]] if opc == getOpCode("LOADNIL") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = nil", A) elseif opc == getOpCode("BREAK") then output = output .. "break" elseif opc == getOpCode("LOADK") then local k = proto.kTable[Bx + 1] or nilValue; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = %s", A, (type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value)) elseif opc == getOpCode("LOADKX") then local k = proto.kTable[aux + 1] or nilValue; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = %s", A, (type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value)) elseif opc == getOpCode("LOADB") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = %s", A, tostring(B == 1)) elseif opc == getOpCode("LOADN") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = %s", A, tostring(Bx)) elseif opc == getOpCode("GETUPVAL") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = upvalues[%i]", A, B) elseif opc == getOpCode("SETUPVAL") then output = output .. string.format("upvalues[%i] = v%i", B, A) elseif opc == getOpCode("MOVE") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i", A, B) elseif opc == getOpCode("LEN") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = #v%i", A, B) elseif opc == getOpCode("UNM") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = -v%i", A, B) elseif opc == getOpCode("NOT") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = not v%i", A, B) elseif opc == getOpCode("GETVARARGS") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = ...", A) elseif opc == getOpCode("CONCAT") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i .. v%i", A, B, C) elseif opc == getOpCode("AND") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i and v%i", A, B, C) elseif opc == getOpCode("OR") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i or v%i", A, B, C) elseif opc == getOpCode("ANDK") then local k = proto.kTable[C + 1] or nilValue; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i and %s", A, B, tostring(k.value)) elseif opc == getOpCode("ORK") then local k = proto.kTable[C + 1] or nilValue; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i or %s", A, B, tostring(k.value)) elseif opc == getOpCode("FASTCALL") then output = output .. string.format("FASTCALL[id=%i]()", A, B); elseif opc == getOpCode("FASTCALL2") then output = output .. string.format("FASTCALL[id=%i]()", A, B); elseif opc == getOpCode("FASTCALL2K") then output = output .. string.format("FASTCALL[id=%i]()", A, B); elseif opc == getOpCode("GETIMPORT") then local indexCount = bit32.band(bit32.rshift(aux, 30), 0x3FF) -- 0x40000000 --> 1, 0x80000000 --> 2 local cacheIndex1 = bit32.band(bit32.rshift(aux, 20), 0x3FF) local cacheIndex2 = bit32.band(bit32.rshift(aux, 10), 0x3FF) local cacheIndex3 = bit32.band(bit32.rshift(aux, 0), 0x3FF) if indexCount == 1 then local k1 = proto.kTable[cacheIndex1 + 1]; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = %s", A, tostring(k1.value)) elseif indexCount == 2 then local k1 = proto.kTable[cacheIndex1 + 1]; local k2 = proto.kTable[cacheIndex2 + 1]; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = %s[\"%s\"]", A, k1.value, tostring(k2.value)) elseif indexCount == 3 then local k1 = proto.kTable[cacheIndex1 + 1]; local k2 = proto.kTable[cacheIndex2 + 1]; local k3 = proto.kTable[cacheIndex3 + 1]; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = %s[\"%s\"][\"%s\"]", A, k1.value, tostring(k2.value), tostring(k3.value)) else error("[GETIMPORT] Too many entries"); end elseif opc == getOpCode("GETGLOBAL") then local k = proto.kTable[aux + 1] or nilValue; output = output .. string.format("v%i = %s", A, tostring(k.value)) elseif opc == getOpCode("SETGLOBAL") then local k = proto.kTable[aux + 1] or nilValue; output = output .. string.format("%s = v%i", tostring(k.value), A) elseif opc == getOpCode("GETTABLE") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i[v%i]", A, B, C) elseif opc == getOpCode("SETTABLE") then output = output .. string.format("v%i[v%i] = v%i", B, C, A) elseif opc == getOpCode("GETTABLEN") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i[%i]", A, B, C - 1) elseif opc == getOpCode("SETTABLEN") then output = output .. string.format("v%i[%i] = v%i", B, C - 1, A) elseif opc == getOpCode("GETTABLEKS") then local k = proto.kTable[aux + 1] or nilValue; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i[%s]", A, B, (type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value)) elseif opc == getOpCode("SETTABLEKS") then local k = proto.kTable[aux + 1] or nilValue; output = output .. string.format("v%i[%s] = v%i", B, (type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value), A) elseif opc == getOpCode("NAMECALL") then local k = proto.kTable[aux + 1] or nilValue; nameCall = string.format("v%i:%s", B, tostring(k.value)) markedAux = true; elseif opc == getOpCode("NFORPREP") then output = output .. string.format("nforprep start - [escape at #%i] -- v%i = iterator", (codeIndex + sBx) + 1, A + 3); elseif opc == getOpCode("NFORLOOP") then output = output .. string.format("nforloop end - iterate + goto #%i", codeIndex + sBx); elseif opc == getOpCode("PAIRSPREP") then output = output .. string.format("pairsprep start - [escape at #%i] -- v%i = key, v%i = value", (codeIndex + sBx) + 1, A + 3, A + 4); elseif opc == getOpCode("PAIRSLOOP") then output = output .. string.format("pairsloop end - iterate + goto #%i", codeIndex + sBx); elseif opc == getOpCode("IPAIRSPREP") then output = output .. string.format("ipairsprep start [escape at #%i] -- v%i = key, v%i = value", (codeIndex + sBx) + 1, A + 3, A + 4); elseif opc == getOpCode("IPAIRSLOOP") then output = output .. string.format("ipairsloop end - iterate + goto #%i", codeIndex + sBx); elseif opc == getOpCode("TFORLOOP") then output = output .. string.format("gforloop - iterate + goto #%i", codeIndex + aux); elseif opc == getOpCode("JUMP") then addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i", codeIndex + sBx); elseif opc == getOpCode("JUMPBACK") then addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i", codeIndex + sBx); elseif opc == getOpCode("JUMPX") then addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i", codeIndex + sAx); elseif opc == getOpCode("JUMPIFEQK") then local k = proto.kTable[aux + 1] or nilValue; addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i if v%i == %s", codeIndex + sBx, A, (type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value)); elseif opc == getOpCode("JUMPIFNOTEQK") then local k = proto.kTable[aux + 1] or nilValue; addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i if v%i ~= %s", codeIndex + sBx, A, (type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value)); elseif opc == getOpCode("JUMPIF") then addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i if v%i", codeIndex + sBx, A); elseif opc == getOpCode("JUMPIFNOT") then addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i if not v%i", codeIndex + sBx, A); elseif opc == getOpCode("JUMPIFEQ") then addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i if v%i == v%i", codeIndex + sBx, A, aux); elseif opc == getOpCode("JUMPIFNOTEQ") then addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i if v%i ~= v%i", codeIndex + sBx, A, aux); elseif opc == getOpCode("JUMPIFLE") then addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i if v%i <= v%i", codeIndex + sBx, A, aux); elseif opc == getOpCode("JUMPIFNOTLE") then addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i if v%i > v%i", codeIndex + sBx, A, aux); elseif opc == getOpCode("JUMPIFLT") then addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i if v%i < v%i", codeIndex + sBx, A, aux); elseif opc == getOpCode("JUMPIFNOTLT") then addReference(codeIndex, codeIndex + sBx); output = output .. string.format("goto #%i if v%i >= v%i", codeIndex + sBx, A, aux); elseif opc == getOpCode("ADD") then output = output .. string.format("v%i = v%i + v%i", A, B, C); elseif opc == getOpCode("ADDK") then local k = proto.kTable[C + 1] or nilValue; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i + %s", A, B, tostring(k.value)); elseif opc == getOpCode("SUB") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i - v%i", A, B, C); elseif opc == getOpCode("SUBK") then local k = proto.kTable[C + 1] or nilValue; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i - %s", A, B, tostring(k.value)); elseif opc == getOpCode("MUL") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i * v%i", A, B, C); elseif opc == getOpCode("MULK") then local k = proto.kTable[C + 1] or nilValue; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i * %s", A, B, tostring(k.value)); elseif opc == getOpCode("DIV") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i / v%i", A, B, C); elseif opc == getOpCode("DIVK") then local k = proto.kTable[C + 1] or nilValue; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i / %s", A, B, tostring(k.value)); elseif opc == getOpCode("MOD") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i %% v%i", A, B, C); elseif opc == getOpCode("MODK") then local k = proto.kTable[C + 1] or nilValue; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i %% %s", A, B, tostring(k.value)); elseif opc == getOpCode("POW") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i ^ v%i", A, B, C); elseif opc == getOpCode("POWK") then local k = proto.kTable[C + 1] or nilValue; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = v%i ^ %s", A, B, tostring(k.value)); elseif opc == getOpCode("CALL") then if C > 1 then for j = 1, C - 1 do output = output .. string.format("v%i", A + j - 1) if j < C - 1 then output = output .. ", " end end output = output .. " = " elseif C == 0 then output = output .. string.format("v%i, ...", A); output = output .. " = " --for j = 1, proto.maxStackSize do -- output = output .. string.format("v%i", A + j - 1) -- if j < proto.maxStackSize - 1 then output = output .. ", " end --end end if nameCall then output = output .. nameCall .. "("; else output = output .. string.format("v%i(", A) end if B > 1 then if nameCall then for j = 1, B - 2 do output = output .. string.format("v%i", A + 1 + j) -- exclude self if j < B - 2 then output = output .. ", " end end else for j = 1, B - 1 do output = output .. string.format("v%i", A + j) if j < B - 1 then output = output .. ", " end end end elseif B == 0 then output = output .. string.format("v%i, ...", A + 1); --for j = 1, proto.maxStackSize do -- if nameCall then -- output = output .. string.format("v%i", A + 1 + j) -- exclude self -- else -- output = output .. string.format("v%i", A + j) -- end -- if j < proto.maxStackSize - 1 then output = output .. ", " end --end end nameCall = nil; output = output .. ")"; elseif opc == getOpCode("NEWTABLE") then output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = {}", A) elseif opc == getOpCode("DUPTABLE") then local t = proto.kTable[Bx + 1].value; output = output .. (isVarDefined(A) and "" or "local ") .. string.format("v%i = { ", A) for j = 1,t.size do local id = t.ids[j]; local k = proto.kTable[id]; output = output .. ((type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value)) if j < t.size then output = output .. ", "; end end output = output .. "}"; elseif opc == getOpCode("SETLIST") then local fieldSize = aux; output = output .. "\n" for j = 1, C do addTabSpace(depth); output = output .. string.format("v%i[%i] = v%i\n", A, j + fieldSize - 1, B + j - 1); end elseif opc == getOpCode("CAPTURE") then markedAux = true; elseif opc == getOpCode("NEWCLOSURE") then output = output .. "\n" local nCaptures = 0; for j = codeIndex + 1, proto.sizeCode do if luau.GET_OPCODE(proto.codeTable[j]) ~= getOpCode("CAPTURE") then break else local upvalueIndex = j - codeIndex - 1; local captureType = luau.GETARG_A(proto.codeTable[j]); local captureIndex = luau.GETARG_Bx(proto.codeTable[j]); nCaptures = nCaptures + 1; addTabSpace(depth); if captureType == 0 or captureType == 1 then output = output .. string.format("-- V nested upvalues[%i] = v%i\n", upvalueIndex, captureIndex) elseif captureType == 2 then output = output .. string.format("-- V nested upvalues[%i] = upvalues[%i]\n", upvalueIndex, captureIndex) else error("[NEWCLOSURE] Invalid capture type"); end end end codeIndex = codeIndex + nCaptures; addTabSpace(depth); local nextProto = proto.pTable[Bx + 1] if nextProto.source then output = output .. readProto(nextProto, depth) addTabSpace(depth); output = output .. string.format("v%i = ", A) .. nextProto.source else nextProto.source = nil; output = output .. string.format("v%i = ", A) .. readProto(nextProto, depth) end elseif opc == getOpCode("DUPCLOSURE") then output = output .. "\n" local nCaptures = 0; for j = codeIndex + 1, proto.sizeCode do if luau.GET_OPCODE(proto.codeTable[j]) ~= getOpCode("CAPTURE") then break else local upvalueIndex = j - codeIndex - 1; local captureType = luau.GETARG_A(proto.codeTable[j]); local captureIndex = luau.GETARG_Bx(proto.codeTable[j]); nCaptures = nCaptures + 1; addTabSpace(depth); if captureType == 0 or captureType == 1 then output = output .. string.format("-- V nested upvalues[%i] = v%i\n", upvalueIndex, captureIndex) elseif captureType == 2 then output = output .. string.format("-- V nested upvalues[%i] = upvalues[%i]\n", upvalueIndex, captureIndex) else error("[DUPCLOSURE] Invalid capture type"); end end end codeIndex = codeIndex + nCaptures; addTabSpace(depth); local nextProto = protoTable[proto.kTable[Bx + 1].value] if nextProto.source then output = output .. readProto(nextProto, depth) addTabSpace(depth); output = output .. string.format("v%i = ", A) .. nextProto.source else nextProto.source = nil; output = output .. string.format("v%i = ", A) .. readProto(nextProto, depth) end elseif opc == getOpCode("RETURN") then if B > 1 then output = output .. "return "; for j = 1, B - 1 do output = output .. string.format("v%i", A + j) if j < B - 1 then output = output .. ", " end end elseif B == 0 then output = output .. string.format("v%i, ...", A) end end for _,v in pairs(refData) do if v.codeIndex == codeIndex then output = output .. " -- referenced by " for j = 1,#v.refs do output = output .. "#" .. v.refs[j] if j < #v.refs then output = output .. ", " end end end end output = output .. "\n" end codeIndex = codeIndex + 1 end depth = depth - 1 addTabSpace(depth) output = output .. "end\n" return output; end local startDepth = 0; output = output .. readProto(mainProto, startDepth) return output end getgenv().decompile = newcclosure(function(script) return disassemble(getscriptbytecode(script), false) end)
Editor Settings
Theme
Key bindings
Full width
Lines