unluac 1.0.0

Multi-dialect Lua decompiler written in Rust.
Documentation
# unluac-rs

简体中文 | [English]./README_en.md

> 当前仓库仍处于测试阶段,行为、接口和输出细节后续都可能继续调整。非常欢迎提交反编译失败、输出不理想或存在兼容性问题的测试样例,也欢迎通过 issue / discussion 提出使用反馈、改进建议和发布体验上的意见。

## 简介

一个基于 Rust 的、支持多种 dialect 的 Lua 反编译器,目前支持以下版本 / dialect:

- [Lua 5.1]https://www.lua.org/versions.html#5.1
- [Lua 5.2]https://www.lua.org/versions.html#5.2
- [Lua 5.3]https://www.lua.org/versions.html#5.3
- [Lua 5.4]https://www.lua.org/versions.html#5.4
- [Lua 5.5]https://www.lua.org/versions.html#5.5
- [LuaJIT 2.1]https://luajit.org/
- [Luau]https://luau.org/

基于控制流分析、支配树分析等手段,去除了大多数中间变量。对于当前仓库中的 case 来说,基本可以恢复源码形状。

当前仓库的代码组织大致如下:

- 根包 `unluac`:核心反编译库
- `packages/unluac-cli`:命令行入口
- `packages/unluac-wasm`:wasm 绑定层
- `packages/unluac-js`:npm 包装层
- `xtask`:测试与 Lua 工具链编排

## 使用方式

目前仓库已经围绕以下几种使用方式组织代码,分别面向不同的集成和分发场景:

1. **命令行工具**:当前仓库内可以直接通过 `cargo unluac` 调试使用,后续 GitHub Release 二进制也会从同一套 CLI 产出。
2. **Rust库**:可以将核心库 `unluac` 集成到 Rust 项目中,直接调用反编译 pipeline。
3. **npm包**:仓库内提供了 `packages/unluac-js` 作为 npm 包装层,面向 Node.js / 浏览器等 JavaScript 环境。
4. **WebAssembly**:仓库内提供了 `packages/unluac-wasm` 作为 wasm 绑定层,可供其他语言或运行时继续封装。

### 命令行工具

当前仓库里最直接的命令行入口是:

```bash
cargo unluac -- --input /absolute/path/to/chunk.out --dialect=lua5.1
cargo unluac -- --source tests/lua_cases/lua5.1/01_setfenv.lua --dialect=lua5.1
```

等价命令:

```bash
cargo run -p unluac-cli -- --input /absolute/path/to/chunk.out --dialect=lua5.1
```

说明:

- CLI 当前要求你显式传入 `--input``--source`
- 如果传入 `--source`,CLI 会先调用外部编译器生成 chunk,再执行反编译
- GitHub Release 提供的裸 `unluac-cli` 二进制不会自带 Lua 编译器;`--source` 只有在你显式传入 `--luac`,或运行环境中存在 `lua/build/<dialect>/` / PATH 上的兼容编译器时才可用
- CLI 默认直接输出纯源码,不打印 debug dump
- `generate` 阶段默认会在输出源码中附带 chunk / proto 注释元信息;可通过 `--comment false` 关闭
- CLI 的 dialect / parse / readability / naming / generate 默认值仍与 [examples/debug.rs]./examples/debug.rs 共用同一份 repo 调试 preset
- 如果你想看 repo debug preset 风格的调试输出,可以显式附加 `--debug`

| 参数 | 说明 | 默认值 |
| - | - | - |
| `--input` | 已编译 chunk 路径 ||
| `--source` | Lua 源码路径,CLI 会先调用外部编译器,再执行反编译 ||
| `--luac` | 显式指定 `--source` 使用的外部编译器路径 | 先尝试仓库内 `lua/build/<dialect>/`,否则回退到 PATH 上的兼容编译器 |
| `--dialect` | 反编译 / 编译时使用的 dialect | `lua5.1` |
| `--stop-after` | 反编译 pipeline 截止阶段 | `generate` |
| `--debug` | 启用 debug dump | `false` |
| `--detail` | 调试输出粒度 | `verbose`(启用 debug 时) |
| `--color` | 调试输出颜色模式 | `always`(启用 debug 时) |
| `--timing` | 输出耗时报告 | `false` |
| `--parse-mode` | parser 宽松 / 严格模式 | `permissive` |
| `--encoding` | 字符串解码编码 | `utf-8` |
| `--decode-mode` | 字符串解码失败策略 | `strict` |
| `--naming-mode` | 命名策略 | `debug-like` |
| `--comment` | 是否输出 generate 注释元信息 | `true` |

更多调试相关命令和参数可参考 [docs/debug.md](./docs/debug.md)。

### Rust库

当前核心库包名为 `unluac`。

如果你希望在本地仓库或其他 Rust 项目里直接集成,当前最稳妥的方式是使用 `path` 或 `git` 依赖:

```toml
[dependencies]
unluac = { git = "https://github.com/X3ZvaWQ/unluac-rs" }
```

最小调用示例:

```rust
use std::fs;

use unluac::decompile::{decompile, DecompileDialect, DecompileOptions, DecompileStage};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let bytes = fs::read("sample.out")?;

    let result = decompile(
        &bytes,
        DecompileOptions {
            dialect: DecompileDialect::Lua51,
            target_stage: DecompileStage::Generate,
            ..DecompileOptions::default()
        },
    )?;

    if let Some(generated) = result.state.generated.as_ref() {
        println!("{}", generated.source);
    }

    Ok(())
}
```

这里需要注意:

- 库入口直接接受“已编译 chunk 的字节”,不会替你先编译 Lua 源码
- 如果你手上只有 Lua 源码,当前更方便的方式通常是直接用 CLI
- 主反编译入口在 [src/decompile/mod.rs]./src/decompile/mod.rs 下统一导出

### Npm包

仓库内的 npm 包装层位于 [packages/unluac-js](./packages/unluac-js)。

它当前是一个很薄的 TypeScript 壳层,会调用 `packages/unluac-wasm` 产出的 wasm 绑定,并将最终发布内容收敛到 `dist/`。

当前发布到 npm 的 wasm 构建会裁掉 `debug` / `timing` 相关能力,以控制包体积;CLI 与仓库内库接口仍保留完整调试能力。
同时 npm 包的 `decompile()` 会直接返回最终源码字符串,不再暴露中间 pipeline 元信息。

本地构建方式:

```bash
cd packages/unluac-js
npm install
npm run build
```

构建完成后,发布目录位于:

```text
packages/unluac-js/dist
```

对外暴露的主要 API 是:

- `init(input?)`
- `decompile(bytes, options?)`
- `supportedOptionValues()`

Node.js 环境中的最小示例:

```js
import { decompile } from "unluac-js";
import { readFile } from "node:fs/promises";

const chunkBytes = await readFile("./sample.luac");
const source = await decompile(chunkBytes, {
  dialect: "lua5.1",
  generate: {
    comment: false,
  },
});

console.log(source);
```

常用的 `generate` 选项包括:

- `indentWidth`: 缩进宽度
- `maxLineLength`: 软换行参考宽度
- `quoteStyle`: 字符串引号风格
- `tableStyle`: 表构造器布局风格
- `conservativeOutput`: 是否偏向保守输出
- `comment`: 是否输出文件头 / proto 注释,默认 `true`

浏览器与更完整的说明可参考 [packages/unluac-js/README.md](./packages/unluac-js/README.md)。

### WebAssembly

wasm 绑定层位于 [packages/unluac-wasm](./packages/unluac-wasm)。

它当前使用 `wasm-bindgen` 和 `serde-wasm-bindgen` 暴露 JS 友好的对象协议,而不是直接把 Rust 内部类型布局暴露到边界外。

如果你只是想在 JavaScript / TypeScript 环境使用,优先建议直接使用上面的 npm 包装层。  
如果你需要把 wasm 接到其他语言或运行时,也可以:

- 从 npm 包的发布目录中获取已经构建好的 `unluac_wasm.js``unluac_wasm_bg.wasm`
- 或者直接基于本仓库的 `packages/unluac-wasm` 自行构建并准备对应语言的绑定

如果你打算把 wasm 支持扩展到某个特定语言或运行时,欢迎提交 PR。

## 贡献与反馈

欢迎任何形式的贡献,无论是代码、文档、测试用例,还是其他方面的改进。
如果你在使用过程中遇到任何问题,或者有任何建议和想法,欢迎随时提交 issue;如果项目在某些 case 上表现不佳,也可以将对应的二进制文件作为附件上传,方便定位问题。

## License MIT 

本项目采用 MIT License 发布,具体内容见 [LICENSE.txt](./LICENSE.txt)。

## 鸣谢

- [metaworms's lua decompiler]https://luadec.metaworm.site - 该项目的设计与实现受到了这个项目的启发,也受到了作者教程的帮助。当前该网站已经无法访问。
- Codex + GPT-5.4 - 该项目的大部分代码由 Codex 和 GPT-5.4 生成,感谢 OpenAI 提供的强大工具。