unidecompiler-plugin-lua 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unidecompiler_plugin_lua/__init__.py +2 -0
- unidecompiler_plugin_lua/chunk54.py +587 -0
- unidecompiler_plugin_lua/lifter.py +1631 -0
- unidecompiler_plugin_lua/luac.py +558 -0
- unidecompiler_plugin_lua/normalize.py +241 -0
- unidecompiler_plugin_lua/plugin.py +108 -0
- unidecompiler_plugin_lua/simulation.py +78 -0
- unidecompiler_plugin_lua/support.py +15 -0
- unidecompiler_plugin_lua-0.1.1.dist-info/METADATA +25 -0
- unidecompiler_plugin_lua-0.1.1.dist-info/RECORD +13 -0
- unidecompiler_plugin_lua-0.1.1.dist-info/WHEEL +5 -0
- unidecompiler_plugin_lua-0.1.1.dist-info/entry_points.txt +2 -0
- unidecompiler_plugin_lua-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from unidecompiler_plugin_lua.luac import LuaFunctionListing, LuaInstructionListing
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
BranchKind = Literal["conditional-pair", "unconditional"]
|
|
11
|
+
|
|
12
|
+
BRANCH_OPCODES = {
|
|
13
|
+
"EQ",
|
|
14
|
+
"LT",
|
|
15
|
+
"LE",
|
|
16
|
+
"TEST",
|
|
17
|
+
"TESTSET",
|
|
18
|
+
}
|
|
19
|
+
UNCONDITIONAL_JUMP_OPCODES = {"JMP"}
|
|
20
|
+
TERMINATOR_OPCODES = {
|
|
21
|
+
"RETURN",
|
|
22
|
+
"RETURN0",
|
|
23
|
+
"RETURN1",
|
|
24
|
+
"RETURN2",
|
|
25
|
+
"RETURNI",
|
|
26
|
+
"RETURNK",
|
|
27
|
+
}
|
|
28
|
+
UNSUPPORTED_FOR_LINEAR_LIFTING = {
|
|
29
|
+
"FORPREP",
|
|
30
|
+
"FORLOOP",
|
|
31
|
+
"TFORPREP",
|
|
32
|
+
"TFORCALL",
|
|
33
|
+
"TFORLOOP",
|
|
34
|
+
"JMP",
|
|
35
|
+
"TEST",
|
|
36
|
+
"TESTSET",
|
|
37
|
+
"NEWTABLE",
|
|
38
|
+
"SETLIST",
|
|
39
|
+
"LEN",
|
|
40
|
+
}
|
|
41
|
+
TARGET_COMMENT_RE = re.compile(r"\bto\s+(?P<target>\d+)\b")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class NormalizedLuaBranch:
|
|
46
|
+
pc: int
|
|
47
|
+
opcode: str
|
|
48
|
+
target_pc: int
|
|
49
|
+
kind: BranchKind
|
|
50
|
+
fallthrough_pc: int | None
|
|
51
|
+
|
|
52
|
+
def to_metadata(self) -> dict:
|
|
53
|
+
data = {
|
|
54
|
+
"pc": self.pc,
|
|
55
|
+
"opcode": self.opcode,
|
|
56
|
+
"target_pc": self.target_pc,
|
|
57
|
+
"kind": self.kind,
|
|
58
|
+
}
|
|
59
|
+
if self.fallthrough_pc is not None:
|
|
60
|
+
data["fallthrough_pc"] = self.fallthrough_pc
|
|
61
|
+
return data
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass(frozen=True)
|
|
65
|
+
class NormalizedLuaInstruction:
|
|
66
|
+
pc: int
|
|
67
|
+
line: int | None
|
|
68
|
+
opcode: str
|
|
69
|
+
operands: tuple[str, ...]
|
|
70
|
+
next_pc: int | None
|
|
71
|
+
explicit_target_pc: int | None
|
|
72
|
+
|
|
73
|
+
def to_metadata(self) -> dict:
|
|
74
|
+
return {
|
|
75
|
+
"pc": self.pc,
|
|
76
|
+
"line": self.line,
|
|
77
|
+
"opcode": self.opcode,
|
|
78
|
+
"operands": list(self.operands),
|
|
79
|
+
"next_pc": self.next_pc,
|
|
80
|
+
"explicit_target_pc": self.explicit_target_pc,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(frozen=True)
|
|
85
|
+
class NormalizedLuaFunction:
|
|
86
|
+
name: str
|
|
87
|
+
source: str
|
|
88
|
+
line_start: int | None
|
|
89
|
+
line_end: int | None
|
|
90
|
+
entry_pc: int | None
|
|
91
|
+
instructions: tuple[NormalizedLuaInstruction, ...]
|
|
92
|
+
branches: tuple[NormalizedLuaBranch, ...]
|
|
93
|
+
basic_block_leaders: tuple[int, ...]
|
|
94
|
+
unsupported_opcodes: tuple[str, ...]
|
|
95
|
+
|
|
96
|
+
def to_metadata(self) -> dict:
|
|
97
|
+
return {
|
|
98
|
+
"name": self.name,
|
|
99
|
+
"source": self.source,
|
|
100
|
+
"line_start": self.line_start,
|
|
101
|
+
"line_end": self.line_end,
|
|
102
|
+
"entry_pc": self.entry_pc,
|
|
103
|
+
"instruction_pcs": [instruction.pc for instruction in self.instructions],
|
|
104
|
+
"instructions": [
|
|
105
|
+
instruction.to_metadata() for instruction in self.instructions
|
|
106
|
+
],
|
|
107
|
+
"branch_targets": [branch.to_metadata() for branch in self.branches],
|
|
108
|
+
"basic_block_leaders": list(self.basic_block_leaders),
|
|
109
|
+
"unsupported_opcodes": list(self.unsupported_opcodes),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def normalize_lua_functions(
|
|
114
|
+
functions: tuple[LuaFunctionListing, ...],
|
|
115
|
+
) -> tuple[NormalizedLuaFunction, ...]:
|
|
116
|
+
return tuple(normalize_lua_function(function) for function in functions)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def normalize_lua_function(function: LuaFunctionListing) -> NormalizedLuaFunction:
|
|
120
|
+
instructions = _normalize_instructions(function.instructions)
|
|
121
|
+
branches = _normalize_branches(function.instructions)
|
|
122
|
+
leaders = _basic_block_leaders(function.instructions, branches)
|
|
123
|
+
unsupported_opcodes = tuple(
|
|
124
|
+
sorted(
|
|
125
|
+
{
|
|
126
|
+
instruction.opcode
|
|
127
|
+
for instruction in function.instructions
|
|
128
|
+
if instruction.opcode in UNSUPPORTED_FOR_LINEAR_LIFTING
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return NormalizedLuaFunction(
|
|
134
|
+
name=function.inferred_name or "<function>",
|
|
135
|
+
source=function.source,
|
|
136
|
+
line_start=function.line_start,
|
|
137
|
+
line_end=function.line_end,
|
|
138
|
+
entry_pc=instructions[0].pc if instructions else None,
|
|
139
|
+
instructions=instructions,
|
|
140
|
+
branches=branches,
|
|
141
|
+
basic_block_leaders=leaders,
|
|
142
|
+
unsupported_opcodes=unsupported_opcodes,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def normalized_functions_metadata(
|
|
147
|
+
functions: tuple[LuaFunctionListing, ...],
|
|
148
|
+
) -> list[dict]:
|
|
149
|
+
return [
|
|
150
|
+
normalized.to_metadata() for normalized in normalize_lua_functions(functions)
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _normalize_instructions(
|
|
155
|
+
instructions: tuple[LuaInstructionListing, ...],
|
|
156
|
+
) -> tuple[NormalizedLuaInstruction, ...]:
|
|
157
|
+
return tuple(
|
|
158
|
+
NormalizedLuaInstruction(
|
|
159
|
+
pc=instruction.pc,
|
|
160
|
+
line=instruction.line,
|
|
161
|
+
opcode=instruction.opcode,
|
|
162
|
+
operands=instruction.operands,
|
|
163
|
+
next_pc=_next_pc(instructions, index),
|
|
164
|
+
explicit_target_pc=_target_from_comment(instruction.comment),
|
|
165
|
+
)
|
|
166
|
+
for index, instruction in enumerate(instructions)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _normalize_branches(
|
|
171
|
+
instructions: tuple[LuaInstructionListing, ...],
|
|
172
|
+
) -> tuple[NormalizedLuaBranch, ...]:
|
|
173
|
+
branches: list[NormalizedLuaBranch] = []
|
|
174
|
+
|
|
175
|
+
for index, instruction in enumerate(instructions):
|
|
176
|
+
target_pc = _target_from_comment(instruction.comment)
|
|
177
|
+
if target_pc is None:
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
if instruction.opcode in UNCONDITIONAL_JUMP_OPCODES:
|
|
181
|
+
previous = instructions[index - 1] if index > 0 else None
|
|
182
|
+
kind: BranchKind = (
|
|
183
|
+
"conditional-pair"
|
|
184
|
+
if previous is not None and previous.opcode in BRANCH_OPCODES
|
|
185
|
+
else "unconditional"
|
|
186
|
+
)
|
|
187
|
+
branches.append(
|
|
188
|
+
NormalizedLuaBranch(
|
|
189
|
+
pc=instruction.pc,
|
|
190
|
+
opcode=instruction.opcode,
|
|
191
|
+
target_pc=target_pc,
|
|
192
|
+
kind=kind,
|
|
193
|
+
fallthrough_pc=_next_pc(instructions, index),
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
return tuple(branches)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _basic_block_leaders(
|
|
201
|
+
instructions: tuple[LuaInstructionListing, ...],
|
|
202
|
+
branches: tuple[NormalizedLuaBranch, ...],
|
|
203
|
+
) -> tuple[int, ...]:
|
|
204
|
+
if not instructions:
|
|
205
|
+
return ()
|
|
206
|
+
|
|
207
|
+
instruction_pcs = {instruction.pc for instruction in instructions}
|
|
208
|
+
leaders = {instructions[0].pc}
|
|
209
|
+
|
|
210
|
+
for branch in branches:
|
|
211
|
+
if branch.target_pc in instruction_pcs:
|
|
212
|
+
leaders.add(branch.target_pc)
|
|
213
|
+
if branch.fallthrough_pc in instruction_pcs:
|
|
214
|
+
leaders.add(branch.fallthrough_pc)
|
|
215
|
+
|
|
216
|
+
for index, instruction in enumerate(instructions):
|
|
217
|
+
if instruction.opcode in TERMINATOR_OPCODES:
|
|
218
|
+
next_pc = _next_pc(instructions, index)
|
|
219
|
+
if next_pc in instruction_pcs:
|
|
220
|
+
leaders.add(next_pc)
|
|
221
|
+
|
|
222
|
+
return tuple(sorted(leaders))
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _next_pc(
|
|
226
|
+
instructions: tuple[LuaInstructionListing, ...],
|
|
227
|
+
index: int,
|
|
228
|
+
) -> int | None:
|
|
229
|
+
next_index = index + 1
|
|
230
|
+
if next_index >= len(instructions):
|
|
231
|
+
return None
|
|
232
|
+
return instructions[next_index].pc
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _target_from_comment(comment: str | None) -> int | None:
|
|
236
|
+
if comment is None:
|
|
237
|
+
return None
|
|
238
|
+
match = TARGET_COMMENT_RE.search(comment)
|
|
239
|
+
if match is None:
|
|
240
|
+
return None
|
|
241
|
+
return int(match.group("target"))
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unidecompiler.core.ir import ModuleIR
|
|
4
|
+
from unidecompiler.plugins import FrontendModule
|
|
5
|
+
from unidecompiler_plugin_lua.lifter import lift_lua_chunk
|
|
6
|
+
from unidecompiler_plugin_lua.chunk54 import decode_lua54_chunk, Lua54ChunkError
|
|
7
|
+
from unidecompiler_plugin_lua.luac import (
|
|
8
|
+
looks_like_luac,
|
|
9
|
+
PreferredLuaChunkDecoder,
|
|
10
|
+
LuaChunkDecoder,
|
|
11
|
+
)
|
|
12
|
+
from unidecompiler_plugin_lua.normalize import normalized_functions_metadata
|
|
13
|
+
from unidecompiler_plugin_lua.simulation import LuaSimulationAdapter
|
|
14
|
+
from unidecompiler_plugin_lua.support import LUA_VERSION_SUPPORT
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LuaFrontendPlugin:
|
|
18
|
+
id = "lua"
|
|
19
|
+
display_name = "Lua bytecode"
|
|
20
|
+
supported_inputs = (".luac",)
|
|
21
|
+
version_support = LUA_VERSION_SUPPORT
|
|
22
|
+
simulation_adapter = LuaSimulationAdapter
|
|
23
|
+
|
|
24
|
+
def __init__(self, decoder: LuaChunkDecoder | None = None) -> None:
|
|
25
|
+
self.decoder = decoder or PreferredLuaChunkDecoder()
|
|
26
|
+
|
|
27
|
+
def can_load(self, data: bytes, filename: str | None = None) -> bool:
|
|
28
|
+
return looks_like_luac(data) and self.decoder.can_decode(data, filename)
|
|
29
|
+
|
|
30
|
+
def decode(self, data: bytes, filename: str | None = None) -> FrontendModule:
|
|
31
|
+
chunk = self.decoder.decode(data, filename)
|
|
32
|
+
normalized_functions = normalized_functions_metadata(chunk.functions)
|
|
33
|
+
diagnostics = _normalization_diagnostics(normalized_functions)
|
|
34
|
+
return FrontendModule(
|
|
35
|
+
frontend_id=self.id,
|
|
36
|
+
payload=chunk,
|
|
37
|
+
metadata={
|
|
38
|
+
"filename": filename,
|
|
39
|
+
"format": "luac",
|
|
40
|
+
"version": chunk.header.version_label,
|
|
41
|
+
"endianness": (
|
|
42
|
+
None
|
|
43
|
+
if chunk.header.little_endian is None
|
|
44
|
+
else "little"
|
|
45
|
+
if chunk.header.little_endian
|
|
46
|
+
else "big"
|
|
47
|
+
),
|
|
48
|
+
"debug_info_present": chunk.disassembly is not None,
|
|
49
|
+
"diagnostics": diagnostics,
|
|
50
|
+
"lua": {
|
|
51
|
+
"int_size": chunk.header.int_size,
|
|
52
|
+
"size_t_size": chunk.header.size_t_size,
|
|
53
|
+
"instruction_size": chunk.header.instruction_size,
|
|
54
|
+
"number_size": chunk.header.lua_number_size,
|
|
55
|
+
"integral_numbers": chunk.header.integral_numbers,
|
|
56
|
+
"decoder": chunk.decoder_id or self.decoder.id,
|
|
57
|
+
"decoder_policy": self.decoder.id,
|
|
58
|
+
"has_disassembly": chunk.disassembly is not None,
|
|
59
|
+
"normalized_functions": normalized_functions,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def lift(self, module: FrontendModule) -> ModuleIR:
|
|
65
|
+
if module.frontend_id != self.id:
|
|
66
|
+
raise TypeError(
|
|
67
|
+
f"Lua frontend cannot lift module from {module.frontend_id!r}"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
return lift_lua_chunk(module.payload, module.metadata)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class Lua54BinaryChunkDecoder:
|
|
74
|
+
id = "lua54-binary"
|
|
75
|
+
|
|
76
|
+
def can_decode(self, data: bytes, filename: str | None = None) -> bool:
|
|
77
|
+
return looks_like_luac(data) and len(data) >= 8 and data[4] == 0x54
|
|
78
|
+
|
|
79
|
+
def decode(self, data: bytes, filename: str | None = None):
|
|
80
|
+
try:
|
|
81
|
+
return decode_lua54_chunk(data, filename)
|
|
82
|
+
except Lua54ChunkError as exc:
|
|
83
|
+
from unidecompiler_plugin_lua.luac import LuacDecodeError
|
|
84
|
+
|
|
85
|
+
raise LuacDecodeError(str(exc)) from exc
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _normalization_diagnostics(
|
|
89
|
+
normalized_functions: list[dict],
|
|
90
|
+
) -> list[dict[str, object]]:
|
|
91
|
+
diagnostics: list[dict[str, object]] = []
|
|
92
|
+
for function in normalized_functions:
|
|
93
|
+
unsupported_opcodes = function["unsupported_opcodes"]
|
|
94
|
+
if not unsupported_opcodes:
|
|
95
|
+
continue
|
|
96
|
+
diagnostics.append(
|
|
97
|
+
{
|
|
98
|
+
"severity": "info",
|
|
99
|
+
"code": "lua.requires-cfg-structuring",
|
|
100
|
+
"function": function["name"],
|
|
101
|
+
"message": (
|
|
102
|
+
"Function contains Lua instructions that require CFG/"
|
|
103
|
+
"structuring before safe pseudocode lifting."
|
|
104
|
+
),
|
|
105
|
+
"opcodes": unsupported_opcodes,
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
return diagnostics
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Optional Lua runtime facts for the decoupled generic IR simulator."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LuaSimulationAdapter:
|
|
7
|
+
"""Resolve Lua names and values without executing Lua bytecode."""
|
|
8
|
+
|
|
9
|
+
frontend_id = "lua"
|
|
10
|
+
|
|
11
|
+
def resolve_function(self, query, decoded_module, lifted_module):
|
|
12
|
+
from unidecompiler_simulator import NotHandled, ResolvedFunction
|
|
13
|
+
|
|
14
|
+
if not isinstance(query, str):
|
|
15
|
+
return NotHandled
|
|
16
|
+
matches = [
|
|
17
|
+
function
|
|
18
|
+
for function in self._walk(lifted_module.functions)
|
|
19
|
+
if function.name == query
|
|
20
|
+
]
|
|
21
|
+
if len(matches) != 1:
|
|
22
|
+
return NotHandled
|
|
23
|
+
return ResolvedFunction(matches[0], identifier=query)
|
|
24
|
+
|
|
25
|
+
def resolve_global(self, name, context):
|
|
26
|
+
from unidecompiler_simulator import IntrinsicCall, NotHandled
|
|
27
|
+
|
|
28
|
+
if name == "vm_forloop_continues":
|
|
29
|
+
return IntrinsicCall("range_continues")
|
|
30
|
+
return NotHandled
|
|
31
|
+
|
|
32
|
+
def list_simulation_targets(self, decoded_module, lifted_module):
|
|
33
|
+
from unidecompiler_simulator import SimulationTargetCandidate
|
|
34
|
+
|
|
35
|
+
functions = tuple(self._walk(lifted_module.functions))
|
|
36
|
+
names = {function.name for function in functions}
|
|
37
|
+
return tuple(
|
|
38
|
+
SimulationTargetCandidate(function.name, function.name)
|
|
39
|
+
for function in functions
|
|
40
|
+
if sum(candidate.name == function.name for candidate in functions) == 1
|
|
41
|
+
and function.name in names
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def unary_op(self, op, value, context):
|
|
45
|
+
from unidecompiler_simulator import NotHandled, TableValue
|
|
46
|
+
|
|
47
|
+
if op != "#":
|
|
48
|
+
return NotHandled
|
|
49
|
+
if isinstance(value, TableValue):
|
|
50
|
+
return len(value.array_items)
|
|
51
|
+
if isinstance(value, (list, str, tuple)):
|
|
52
|
+
return len(value)
|
|
53
|
+
return NotHandled
|
|
54
|
+
|
|
55
|
+
def get_item(self, obj, key, context):
|
|
56
|
+
from unidecompiler_simulator import NotHandled
|
|
57
|
+
|
|
58
|
+
if not isinstance(key, int):
|
|
59
|
+
return NotHandled
|
|
60
|
+
if isinstance(obj, list):
|
|
61
|
+
return obj[key - 1]
|
|
62
|
+
return NotHandled
|
|
63
|
+
|
|
64
|
+
def set_item(self, obj, key, value, context):
|
|
65
|
+
from unidecompiler_simulator import NotHandled
|
|
66
|
+
|
|
67
|
+
if not isinstance(key, int):
|
|
68
|
+
return NotHandled
|
|
69
|
+
if isinstance(obj, list):
|
|
70
|
+
obj[key - 1] = value
|
|
71
|
+
return None
|
|
72
|
+
return NotHandled
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def _walk(functions):
|
|
76
|
+
for function in functions:
|
|
77
|
+
yield function
|
|
78
|
+
yield from LuaSimulationAdapter._walk(function.nested_functions)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unidecompiler.plugins import FrontendVersionSupport
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
LUA_VERSION_SUPPORT = FrontendVersionSupport(
|
|
7
|
+
family="Lua bytecode",
|
|
8
|
+
versions=("5.4", "5.1 header/resource fallback"),
|
|
9
|
+
parser="internal Lua 5.4 chunk parser; header-only fallback for older chunks",
|
|
10
|
+
status="Lua 5.4 instruction submission; older chunks are detected but not lifted",
|
|
11
|
+
notes=(
|
|
12
|
+
"Adding Lua 5.1 support should add a Lua 5.1 decoder/opcode table in this frontend.",
|
|
13
|
+
"The frontend must still submit thin VM steps only.",
|
|
14
|
+
),
|
|
15
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: unidecompiler-plugin-lua
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Lua bytecode frontend plugin for unidecompiler
|
|
5
|
+
Author-email: Wker <1670133844@qq.com>
|
|
6
|
+
License-Expression: AGPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/Wker666/unidecompiler
|
|
8
|
+
Project-URL: Repository, https://github.com/Wker666/unidecompiler
|
|
9
|
+
Project-URL: Issues, https://github.com/Wker666/unidecompiler/issues
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: unidecompiler<0.2.0,>=0.1.1
|
|
13
|
+
|
|
14
|
+
# unidecompiler-plugin-lua
|
|
15
|
+
|
|
16
|
+
Frontend plugin for Lua `.luac` bytecode chunks. It decodes Lua bytecode and
|
|
17
|
+
submits neutral thin IR to `unidecompiler` for recovery and rendering.
|
|
18
|
+
|
|
19
|
+
Install with:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
python -m pip install unidecompiler-plugin-lua
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The plugin is discovered automatically by compatible CLI and GUI hosts.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
unidecompiler_plugin_lua/__init__.py,sha256=ljweni-50I--HtwaMZFPbrGMQ5yW94FKaMUKCjnXT8I,30
|
|
2
|
+
unidecompiler_plugin_lua/chunk54.py,sha256=oWTI-UmkQrfaLqr_qvUM4JjLNSJc-by7jtbyk2RY5jo,19535
|
|
3
|
+
unidecompiler_plugin_lua/lifter.py,sha256=F--6eYjqEUFYNqjM68qDV4s23qR0MD6uOw6Eks-cTes,56041
|
|
4
|
+
unidecompiler_plugin_lua/luac.py,sha256=uvVv6OlIBtqqP-gHYge_paaXtkLj8vylUyptSM1kGCo,18201
|
|
5
|
+
unidecompiler_plugin_lua/normalize.py,sha256=1y0C9nFYXnAhAwbN61-8SW5nqVD0bdZ66OUZ8oLj_EE,6862
|
|
6
|
+
unidecompiler_plugin_lua/plugin.py,sha256=qeaapfHSEeRf9sPEENd_ZZVyOMHkdcq7mo53VfvqEUY,4186
|
|
7
|
+
unidecompiler_plugin_lua/simulation.py,sha256=K5rHem8blewEGFEY_MurKj7SncsisrS73cV65_70gFM,2575
|
|
8
|
+
unidecompiler_plugin_lua/support.py,sha256=I5hM8JubH3pCBGWpwoXVDXI_gO42eoIMfCyQ0qM9f_s,569
|
|
9
|
+
unidecompiler_plugin_lua-0.1.1.dist-info/METADATA,sha256=Mncv_aK4UrFp_Irl2CTDwOmRKglg6Qu-GOO8xJQt10Q,830
|
|
10
|
+
unidecompiler_plugin_lua-0.1.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
11
|
+
unidecompiler_plugin_lua-0.1.1.dist-info/entry_points.txt,sha256=kA7THgRbo2r_aAVM5z4MCM0DqPZ3xiTB4GQQMMEHwvg,82
|
|
12
|
+
unidecompiler_plugin_lua-0.1.1.dist-info/top_level.txt,sha256=D-0vATcYYrv-RckgazUaZyYJZcCAuth6N7H6hLtuSjw,25
|
|
13
|
+
unidecompiler_plugin_lua-0.1.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
unidecompiler_plugin_lua
|