Skip to content

GabHST/xlua-decompiler

Repository files navigation

xlua-decompiler

CI PyPI Python License: MIT

Convert xLua compiled bytecode back to readable Lua 5.4 source, using an unmodified unluac as the back-end decompiler.

Why

Tencent's xLua framework is widely used in Unity projects for hot-updating Lua scripts. These scripts ship as a custom bytecode format that standard Lua decompilers cannot read. Run unluac against a raw xLua file and you will hit:

The input file does not have the signature of a valid Lua file.

xlua-decompiler acts as a preprocessor. It reshapes xLua's file header and root proto into standard Lua 5.4 framing so that unluac can do the heavy lifting on the remaining bytecode.

What xLua changes

xLua's bytecode differs from standard Lua 5.4 in four places:

Difference xLua Standard Lua 5.4
File header ?@<source_path>.lua + \x80\x80\x00 \x1bLua\x54\x00 + integrity checks
Root proto start nupval + maxstacksize source, linedefined, lastlinedefined, numparams, is_vararg, maxstacksize
Constant types 0x03 = integer, 0x13 = float 0x03 = float, 0x13 = integer
Upvalues 3 bytes: instack, idx, kind 2 bytes: instack, idx

This tool handles the first two by rewriting the header and injecting the missing root proto fields. The other two are handled by unluac itself:

  • Constant types: --typemap xlua_typemap.txt (auto-generated on first run).
  • 3-byte upvalues: supported natively in unluac v1.2.3.569+.

Installation

From PyPI (recommended once published)

pip install xlua-decompiler

From source

git clone https://github.com/GabHST/xlua-decompiler.git
cd xlua-decompiler
pip install -e .

Get unluac

unluac is a Java JAR and is distributed separately. Download the latest unstable build (needed for 3-byte upvalues):

curl -L -o unluac.jar \
    "https://sourceforge.net/projects/unluac/files/Unstable/unluac_2025_10_20.jar/download"

You also need a working Java 11+ runtime on PATH.

Quickstart

1. Decompile a single file

xlua-decompiler decompile encrypted_script.lua \
    --unluac unluac.jar \
    -o readable.lua

2. Batch decompile a directory

xlua-decompiler batch ./xlua_scripts/ \
    --unluac unluac.jar \
    -o ./decompiled/ \
    --recursive \
    -j 16

A tqdm progress bar shows per-file progress through the convert and decompile stages.

3. Convert only (no Java dependency)

If you want to run unluac by hand or pipe the output into a different tool:

xlua-decompiler convert encrypted_script.lua -o standard.luac
java -jar unluac.jar --typemap xlua_typemap.txt standard.luac > readable.lua

Also works as a module

python -m xlua_decompiler --help

Command reference

xlua-decompiler [-v|-vv] {convert, decompile, batch} ...
Subcommand Purpose Needs Java?
convert Rewrite a single xLua file to Lua 5.4 binary No
decompile Convert and run unluac on one file Yes
batch Parallel convert + decompile of a whole folder Yes

Flags worth knowing

Flag Applies to Description
--overwrite all Overwrite output files instead of refusing.
--recursive batch Recurse into subdirectories.
--extensions batch Input extensions to scan (default: .lua .luac .bytes).
-j / --workers batch Parallel unluac workers (default: 8).
--timeout decompile / batch Per-file unluac timeout, seconds (default: 60).
--no-progress batch Disable the tqdm progress bar (useful in CI).
-v / -vv global Increase log verbosity (INFO / DEBUG).

How it works

The conversion is minimal and surgical:

  1. Locate the bytecode by finding the \x80\x80\x00 marker after the source path header.
  2. Extract nupval and maxstacksize from the xLua root proto.
  3. Build a standard Lua 5.4 31-byte header (signature, version, endianness check, float check).
  4. Inject the missing root proto fields (source = NULL, linedefined = 0, lastlinedefined = 0, numparams = 0, is_vararg = 1, maxstacksize).
  5. Copy the rest of the bytecode verbatim — constants, upvalues, nested protos, debug info.
xLua format:                          Standard Lua 5.4:
?@path/to/script.lua\x80\x80\x00      \x1bLua\x54\x00 ... (31 bytes)
[nupval][maxstacksize]                [nupval]
[sizecode][code ...]                  [source_str][linedefined][lastlinedefined]
[constants ...]                       [numparams][is_vararg][maxstacksize]
[upvalues ...]                        [sizecode][code ...]
[protos ...]                          [constants ...]
[debug ...]                           [upvalues ...]
                                      [protos ...]
                                      [debug ...]

Comparison with other tools

Tool xLua support Lua 5.4 Typemap support Root proto fix Upvalue fix Batch Output
xlua-decompiler Yes Yes Yes Yes Yes (unluac) Yes Readable Lua source
unluac No Yes No No Partial No Needs standard-format input
xLuaDumper Partial No No No No No Bytecode dump only
luadec No No (5.x) No No No No Lua 5.1 to 5.3
LuaDecompy No No No No No No Lua 5.1 only, experimental
luajit-decompiler-v2 No No N/A N/A N/A Yes LuaJIT only
RustyLuaDec No Yes No No No No Standard Lua 5.4 only

Key idea: xlua-decompiler is the only open-source pipeline that handles xLua's custom framing end-to-end. Other tools either don't support the format or only perform partial steps.

Why not just use unluac directly?

unluac is an excellent Lua decompiler, but it expects standard Lua bytecode. xLua's custom file header and missing root proto fields cause unluac to reject the file outright. xlua-decompiler acts as a preprocessor that transforms xLua bytecode into the standard format that unluac expects.

Why not xLuaDumper?

xLuaDumper can extract bytecode from xLua, but it focuses on opcode diffing between xLua and standard Lua. It does not handle the constant type swap, the root proto format differences, or provide a decompilation pipeline. It also requires the target DLLs to be loaded.

Typemap

The xlua_typemap.txt file tells unluac how to interpret xLua's constant types. It is auto-created next to the CLI on first run:

.type 0 nil
.type 1 false
.type 17 true
.type 3 integer
.type 19 float
.type 4 short_string
.type 20 long_string

In standard Lua 5.4 types 0x03 and 0x13 represent float and integer respectively. xLua swaps these. The typemap corrects this at decompile time without modifying the bytecode.

Performance

Batch decompilation with parallel workers on a typical laptop:

Files Workers Time
100 8 ~15 s
1,000 16 ~90 s
2,000 16 ~3 min

Each file spawns a JVM process for unluac. Increase -j on machines with more cores.

Compatibility

Tested against:

  • xLua bytecode produced by Tencent xLua with Lua 5.4 as the target.
  • unluac v1.2.3.569 (October 2025).
  • Python 3.10, 3.11, 3.12, 3.13.
  • Windows 10/11, Linux.

Not compatible with:

  • LuaJIT bytecode — use luajit-decompiler-v2.
  • Lua 5.1, 5.2, 5.3 bytecode — different file format.
  • xLua with additional custom encryption — decrypt first, then pipe through this tool.

Extracting xLua bytecode from Unity builds

xLua scripts are typically stored inside Unity AssetBundles. To extract them:

  1. Extract AssetBundles using AssetStudio or UnityPy.
  2. Locate TextAsset objects that contain xLua bytecode. They begin with ?@.
  3. Decrypt if needed. Many titles XOR the blob with a fixed key; the key is usually visible as a string literal in the native runtime library.
  4. Run this tool on the decrypted files.

FAQ

Q: I get "The input file does not have the signature of a valid Lua file". A: You ran unluac against a raw xLua file. Use xlua-decompiler convert (or decompile) first.

Q: I get "Unknown type name" with --typemap. A: Make sure you are pointing at the xlua_typemap.txt that this tool generates. Type names are case-sensitive and must match unluac's expectations.

Q: I get "The size of an integer is outside the range". A: You need unluac v1.2.3.569 or newer. Older versions do not support 3-byte upvalues.

Q: Some files are silently skipped. A: Files whose contents begin with local data are plain-text Lua data tables, not bytecode. They are already readable and the tool ignores them.

Q: Can this decompile any Unity + Lua project? A: Only builds that use Tencent xLua with Lua 5.4 bytecode. Plain Lua, LuaJIT, and other bindings use incompatible formats.

Contributing

See CONTRIBUTING.md for development setup, test, and lint instructions.

License

MIT — see LICENSE.

Acknowledgments

  • unluac by tehtmi, which does all the hard work of decompiling the standardized bytecode.
  • Tencent/xLua for the framework.
  • The reverse-engineering community for the public documentation of the Lua 5.4 bytecode format.

About

Convert xLua compiled bytecode to standard Lua 5.4 format for decompilation with unluac

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages