diff --git a/.gitignore b/.gitignore
index a76dad1..a251088 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,11 @@
.vs/*
packages/*
+
+[Bb]in/
+[Oo]bj/
+
+.idea/
+*.user
+
+packages/
+*.nupkg
diff --git a/LuaToolkit.sln b/LuaToolkit.sln
index 72e39a2..3b8b742 100644
--- a/LuaToolkit.sln
+++ b/LuaToolkit.sln
@@ -17,54 +17,170 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recompiler", "demo\Recompil
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "demo\LuaDecompiler-Web\Web.csproj", "{30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cli", "cli", "{342A349A-D343-8551-4064-2E2800C39E13}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuaToolkit.Cli", "cli\LuaToolkit.Cli.csproj", "{BE269D79-DA30-4DBB-8C84-7D5978E70C0C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
TestDebug|Any CPU = TestDebug|Any CPU
+ TestDebug|x64 = TestDebug|x64
+ TestDebug|x86 = TestDebug|x86
WebRelease|Any CPU = WebRelease|Any CPU
+ WebRelease|x64 = WebRelease|x64
+ WebRelease|x86 = WebRelease|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3C38C968-E485-4154-B6BD-E8446651307F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C38C968-E485-4154-B6BD-E8446651307F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.Debug|x64.Build.0 = Debug|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.Debug|x86.Build.0 = Debug|Any CPU
{3C38C968-E485-4154-B6BD-E8446651307F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C38C968-E485-4154-B6BD-E8446651307F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.Release|x64.ActiveCfg = Release|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.Release|x64.Build.0 = Release|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.Release|x86.ActiveCfg = Release|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.Release|x86.Build.0 = Release|Any CPU
{3C38C968-E485-4154-B6BD-E8446651307F}.TestDebug|Any CPU.ActiveCfg = TestDebug|Any CPU
{3C38C968-E485-4154-B6BD-E8446651307F}.TestDebug|Any CPU.Build.0 = TestDebug|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.TestDebug|x64.ActiveCfg = TestDebug|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.TestDebug|x64.Build.0 = TestDebug|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.TestDebug|x86.ActiveCfg = TestDebug|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.TestDebug|x86.Build.0 = TestDebug|Any CPU
{3C38C968-E485-4154-B6BD-E8446651307F}.WebRelease|Any CPU.ActiveCfg = WebRelease|Any CPU
{3C38C968-E485-4154-B6BD-E8446651307F}.WebRelease|Any CPU.Build.0 = WebRelease|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.WebRelease|x64.ActiveCfg = WebRelease|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.WebRelease|x64.Build.0 = WebRelease|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.WebRelease|x86.ActiveCfg = WebRelease|Any CPU
+ {3C38C968-E485-4154-B6BD-E8446651307F}.WebRelease|x86.Build.0 = WebRelease|Any CPU
{9A45374E-5548-4C3A-AF06-C31A962295DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A45374E-5548-4C3A-AF06-C31A962295DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.Debug|x64.Build.0 = Debug|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.Debug|x86.Build.0 = Debug|Any CPU
{9A45374E-5548-4C3A-AF06-C31A962295DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A45374E-5548-4C3A-AF06-C31A962295DD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.Release|x64.ActiveCfg = Release|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.Release|x64.Build.0 = Release|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.Release|x86.ActiveCfg = Release|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.Release|x86.Build.0 = Release|Any CPU
{9A45374E-5548-4C3A-AF06-C31A962295DD}.TestDebug|Any CPU.ActiveCfg = TestDebug|Any CPU
{9A45374E-5548-4C3A-AF06-C31A962295DD}.TestDebug|Any CPU.Build.0 = TestDebug|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.TestDebug|x64.ActiveCfg = TestDebug|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.TestDebug|x64.Build.0 = TestDebug|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.TestDebug|x86.ActiveCfg = TestDebug|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.TestDebug|x86.Build.0 = TestDebug|Any CPU
{9A45374E-5548-4C3A-AF06-C31A962295DD}.WebRelease|Any CPU.ActiveCfg = WebRelease|Any CPU
{9A45374E-5548-4C3A-AF06-C31A962295DD}.WebRelease|Any CPU.Build.0 = WebRelease|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.WebRelease|x64.ActiveCfg = WebRelease|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.WebRelease|x64.Build.0 = WebRelease|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.WebRelease|x86.ActiveCfg = WebRelease|Any CPU
+ {9A45374E-5548-4C3A-AF06-C31A962295DD}.WebRelease|x86.Build.0 = WebRelease|Any CPU
{C10726C0-8D2C-4F92-9961-5008D1B6940E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C10726C0-8D2C-4F92-9961-5008D1B6940E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.Debug|x64.Build.0 = Debug|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.Debug|x86.Build.0 = Debug|Any CPU
{C10726C0-8D2C-4F92-9961-5008D1B6940E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C10726C0-8D2C-4F92-9961-5008D1B6940E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.Release|x64.ActiveCfg = Release|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.Release|x64.Build.0 = Release|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.Release|x86.ActiveCfg = Release|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.Release|x86.Build.0 = Release|Any CPU
{C10726C0-8D2C-4F92-9961-5008D1B6940E}.TestDebug|Any CPU.ActiveCfg = TestDebug|Any CPU
{C10726C0-8D2C-4F92-9961-5008D1B6940E}.TestDebug|Any CPU.Build.0 = TestDebug|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.TestDebug|x64.ActiveCfg = TestDebug|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.TestDebug|x64.Build.0 = TestDebug|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.TestDebug|x86.ActiveCfg = TestDebug|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.TestDebug|x86.Build.0 = TestDebug|Any CPU
{C10726C0-8D2C-4F92-9961-5008D1B6940E}.WebRelease|Any CPU.ActiveCfg = WebRelease|Any CPU
{C10726C0-8D2C-4F92-9961-5008D1B6940E}.WebRelease|Any CPU.Build.0 = WebRelease|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.WebRelease|x64.ActiveCfg = WebRelease|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.WebRelease|x64.Build.0 = WebRelease|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.WebRelease|x86.ActiveCfg = WebRelease|Any CPU
+ {C10726C0-8D2C-4F92-9961-5008D1B6940E}.WebRelease|x86.Build.0 = WebRelease|Any CPU
{CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.Debug|x64.Build.0 = Debug|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.Debug|x86.Build.0 = Debug|Any CPU
{CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.Release|x64.ActiveCfg = Release|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.Release|x64.Build.0 = Release|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.Release|x86.ActiveCfg = Release|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.Release|x86.Build.0 = Release|Any CPU
{CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.TestDebug|Any CPU.ActiveCfg = TestDebug|Any CPU
{CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.TestDebug|Any CPU.Build.0 = TestDebug|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.TestDebug|x64.ActiveCfg = TestDebug|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.TestDebug|x64.Build.0 = TestDebug|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.TestDebug|x86.ActiveCfg = TestDebug|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.TestDebug|x86.Build.0 = TestDebug|Any CPU
{CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.WebRelease|Any CPU.ActiveCfg = WebRelease|Any CPU
{CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.WebRelease|Any CPU.Build.0 = WebRelease|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.WebRelease|x64.ActiveCfg = WebRelease|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.WebRelease|x64.Build.0 = WebRelease|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.WebRelease|x86.ActiveCfg = WebRelease|Any CPU
+ {CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB}.WebRelease|x86.Build.0 = WebRelease|Any CPU
{30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.Debug|x64.Build.0 = Debug|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.Debug|x86.Build.0 = Debug|Any CPU
{30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.Release|Any CPU.Build.0 = Release|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.Release|x64.ActiveCfg = Release|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.Release|x64.Build.0 = Release|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.Release|x86.ActiveCfg = Release|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.Release|x86.Build.0 = Release|Any CPU
{30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.TestDebug|Any CPU.ActiveCfg = Debug|Any CPU
{30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.TestDebug|Any CPU.Build.0 = Debug|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.TestDebug|x64.ActiveCfg = TestDebug|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.TestDebug|x64.Build.0 = TestDebug|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.TestDebug|x86.ActiveCfg = TestDebug|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.TestDebug|x86.Build.0 = TestDebug|Any CPU
{30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.WebRelease|Any CPU.ActiveCfg = Release|Any CPU
{30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.WebRelease|Any CPU.Build.0 = Release|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.WebRelease|x64.ActiveCfg = WebRelease|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.WebRelease|x64.Build.0 = WebRelease|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.WebRelease|x86.ActiveCfg = WebRelease|Any CPU
+ {30E34A9B-E065-4E33-BB4F-4F3B23C9DB77}.WebRelease|x86.Build.0 = WebRelease|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.Debug|x64.Build.0 = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.Debug|x86.Build.0 = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.Release|x64.ActiveCfg = Release|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.Release|x64.Build.0 = Release|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.Release|x86.ActiveCfg = Release|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.Release|x86.Build.0 = Release|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.TestDebug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.TestDebug|Any CPU.Build.0 = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.TestDebug|x64.ActiveCfg = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.TestDebug|x64.Build.0 = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.TestDebug|x86.ActiveCfg = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.TestDebug|x86.Build.0 = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.WebRelease|Any CPU.ActiveCfg = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.WebRelease|Any CPU.Build.0 = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.WebRelease|x64.ActiveCfg = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.WebRelease|x64.Build.0 = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.WebRelease|x86.ActiveCfg = Debug|Any CPU
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C}.WebRelease|x86.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -75,6 +191,7 @@ Global
{C10726C0-8D2C-4F92-9961-5008D1B6940E} = {6645C52B-19A3-4114-81F8-DEBA70779569}
{CE7FDAB8-2F8A-4E39-AFC9-900D3458B9CB} = {6645C52B-19A3-4114-81F8-DEBA70779569}
{30E34A9B-E065-4E33-BB4F-4F3B23C9DB77} = {6645C52B-19A3-4114-81F8-DEBA70779569}
+ {BE269D79-DA30-4DBB-8C84-7D5978E70C0C} = {342A349A-D343-8551-4064-2E2800C39E13}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CA86C95D-BB88-475B-B999-4332121C087F}
diff --git a/cli/.gitignore b/cli/.gitignore
new file mode 100644
index 0000000..d45db4d
--- /dev/null
+++ b/cli/.gitignore
@@ -0,0 +1,2 @@
+[Bb]in/
+[Oo]bj/
\ No newline at end of file
diff --git a/cli/LuaToolkit.Cli.csproj b/cli/LuaToolkit.Cli.csproj
new file mode 100644
index 0000000..68714e4
--- /dev/null
+++ b/cli/LuaToolkit.Cli.csproj
@@ -0,0 +1,12 @@
+
+
+ Exe
+ net8.0
+ LatestMajor
+ disable
+ luadec
+
+
+
+
+
\ No newline at end of file
diff --git a/cli/Program.cs b/cli/Program.cs
new file mode 100644
index 0000000..b9aed48
--- /dev/null
+++ b/cli/Program.cs
@@ -0,0 +1,36 @@
+using System;
+using System.IO;
+using LuaToolkit.Core;
+using LuaToolkit.Disassembler;
+using LuaToolkit.Decompiler;
+
+namespace LuaToolkit.Cli
+{
+ class Program
+ {
+ static int Main(string[] args)
+ {
+ if (args.Length < 1)
+ {
+ Console.Error.WriteLine("usuage: luadec [more.luac ...]");
+ Console.Error.WriteLine(" decompiles compiled Lua 5.1 (.luac) back to source code.");
+ return 1;
+ }
+
+ foreach(var path in args)
+ {
+ if (!File.Exists(path))
+ {
+ Console.Error.WriteLine($"[!] File not found: {path}");
+ continue;
+ }
+
+ var luac = new LuaCFile(File.ReadAllBytes(path));
+ var decoder = new LuaDecoder(luac);
+ var decompiler = new LuaDecompiler(decoder);
+ Console.WriteLine(decompiler.LuaScript);
+ }
+ return 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Decompiler/LuaDecompiler.cs b/src/Decompiler/LuaDecompiler.cs
index 16338a2..80f281c 100644
--- a/src/Decompiler/LuaDecompiler.cs
+++ b/src/Decompiler/LuaDecompiler.cs
@@ -28,12 +28,28 @@ public LuaDecompiler(LuaDecoder decoder)
WriteFile();
}
+ private void ResClosureNames()
+ {
+ // walk every function we built and point each CLOSURE line at its child's name
+ // GetFunctionRef() got hooked up by the child in HandleUpvalues(), so by now its set
+ foreach (var f in this.LuaFunctions)
+ foreach (var line in f.GetLines())
+ if (line.Instr != null && line.Instr.OpCode == LuaOpcode.CLOSURE && line.GetFunctionRef() != null)
+ line.Op3 = line.GetFunctionRef().ScriptFunction.Name;
+ }
+
private void WriteFile()
{
// create Script Functions
this.Decoder.File.Function.Name = "CRoot"; // or main?
WriteF(this.Decoder.File.Function);
+ // NOTE: closures point at child functions that didnt exist yet while
+ // building the parent lines, so UpdateClosures ran too early and left them as
+ // 'IDK_SHIT_WENT_MISSING_BRO' lol, now that every function is created we loop
+ // back over the closure lines and finally fill in their names
+ ResClosureNames();
+
// NOTE: this is done on GetText
//// allign/format/whatever each function
//foreach (var f in this.LuaFunctions)
@@ -45,14 +61,23 @@ private void WriteF(LuaFunction func)
{
CreateScripFunction(func); // root first and then inside ?
// TODO: write functions on CLOSURE and not each list?
+ // for (int i = 0; i < func.Functions.Count; i++)
+ // {
+ // func.Functions[i].Name = func.Name + "_" + i; // set default name
+ // CreateScripFunction(func.Functions[i], func.ScriptFunction.Depth+1);
+ // foreach (var f in func.Functions[i].Functions)
+ // {
+ // WriteF(f); // children NOTE: write children in body of parent?
+ // }
+ // }
+
for (int i = 0; i < func.Functions.Count; i++)
{
func.Functions[i].Name = func.Name + "_" + i; // set default name
- CreateScripFunction(func.Functions[i], func.ScriptFunction.Depth+1);
- foreach (var f in func.Functions[i].Functions)
- {
- WriteF(f); // children NOTE: write children in body of parent?
- }
+ // NOTE: recurse into the child itself so it (and any of ITS children) get
+ // created and named too. before this only the direct children were getting a name
+ // but grand children came out as "IDK_SHIT_WENT_MISSING_BRO"
+ WriteF(func.Functions[i]);
}
}
diff --git a/src/Decompiler/LuaScriptLine.cs b/src/Decompiler/LuaScriptLine.cs
index 05404e9..db733bf 100644
--- a/src/Decompiler/LuaScriptLine.cs
+++ b/src/Decompiler/LuaScriptLine.cs
@@ -92,14 +92,27 @@ public void SetMain(LuaInstruction Instr = null)
}
break;
case LuaOpcode.GETUPVAL:
- // NOTE: child get local from parent
+ // NOTE: R(A) = Upvalue[B], luac keeps the upvalue names in debug info,
+ // so grab that for real names instead of "unk"
this.Op1 = WriteIndex(Instr.A);
this.Op2 = " = ";
- this.Op3 = WriteIndex(Instr.B);
+
+ if (this.Func.DebugUpvalues.Count > Instr.B)
+ this.Op3 = this.Func.DebugUpvalues[Instr.B].TrimEnd('\0');
+ else if (this.Func.Upvalues.Count > Instr.B)
+ this.Op3 = this.Func.Upvalues[Instr.B].ToString(); // if no debug info
+ else
+ this.Op3 = WriteIndex(Instr.B);
+
+
+ // NOTE: child get local from parent
+ // this.Op1 = WriteIndex(Instr.A);
+ // this.Op2 = " = ";
+ // this.Op3 = WriteIndex(Instr.B);
// TODO: figure if an upvalue is a function or not?
- if(this.Func.Upvalues.Count > Instr.B)
- this.Op3 = this.Func.Upvalues[Instr.B].ToString();
+ // if(this.Func.Upvalues.Count > Instr.B)
+ // this.Op3 = this.Func.Upvalues[Instr.B].ToString();
//this.Op3 = this.Func.Upvalues[Instr.B].ToString().Substring(1, this.Func.Upvalues[Instr.B].ToString().Length - 2); // this is legit for prototypes etc
break;
case LuaOpcode.GETGLOBAL:
@@ -118,10 +131,20 @@ public void SetMain(LuaInstruction Instr = null)
this.Op3 = $"var{Instr.A}";
break;
case LuaOpcode.SETUPVAL:
- // NOTE: child writes to parent locals
- this.Op1 = $"{WriteIndex(Instr.B)}"; // no?
+ // NOTE: Upvalue[B] = R(A), the name lives in debug info, and
+ // the value is just whatever reg is in A (a read, so no local)
+ if (this.Func.DebugUpvalues.Count > Instr.B)
+ this.Op1 = this.Func.DebugUpvalues[Instr.B].TrimEnd('\0');
+ else
+ this.Op1 = $"var{Instr.B}";
+
this.Op2 = " = ";
- this.Op3 = $"{GetConstant(Instr.A)}";
+ this.Op3 = WriteIndex(Instr.A, false);
+
+ // NOTE: child writes to parent locals
+ // this.Op1 = $"{WriteIndex(Instr.B)}"; // no?
+ // this.Op2 = " = ";
+ // this.Op3 = $"{GetConstant(Instr.A)}";
break;
case LuaOpcode.SETTABLE:
this.Op1 = $"{WriteIndex(Instr.A)}[{WriteIndex(Instr.B)}]";
@@ -268,31 +291,11 @@ public void SetMain(LuaInstruction Instr = null)
this.Op2 = $"var{Instr.A}"; // func name only (used lateron)
// Function Args
- if(Instr.B == 0)
- {
- // func parms range from A+1 to B (B = top of stack)
- this.Op3 = "(";
- for (int i = Instr.A; i < Instr.B; i++)
- //for (int i = Instr.A; i < Instr.A + Instr.B - 1; ++i)
- {
- this.Op3 += $"var{i + 1}";
- if (i < Instr.A + Instr.B - 2)
- this.Op3 += ", ";
- }
- this.Op3 += ")";
- }
- else
- {
- this.Op3 = "(";
- for (int i = Instr.A; i < Instr.A + Instr.B - 1; i++)
- //for (int i = Instr.A; i < Instr.A + Instr.B - 1; ++i)
- {
- this.Op3 += $"var{i + 1}";
- if (i < Instr.A + Instr.B - 2)
- this.Op3 += ", ";
- }
- this.Op3 += ")";
- }
+
+ // NOTE: B tells us how many args there are; B >= 1 means B-1 fixed args,
+ // but B==0 means "everything from A+1 up to the top of the stack" which
+ // some earlier VARARG/CALL pushed there, so we dig that top up.
+ this.Op3 = WriteArgs(Instr.A, Instr.B);
break;
case LuaOpcode.TAILCALL:
// TODO: there is something off here?
@@ -300,32 +303,9 @@ public void SetMain(LuaInstruction Instr = null)
// Function Name
this.Op1 = $"return var{Instr.A}"; // func name only (used lateron)
//this.Op2 = $"({Instr.A+1}";
- // Function Args
- if (Instr.B == 0)
- {
- // func parms range from A+1 to B (B = top of stack)
- this.Op3 = $"(";
- for (int i = Instr.A; i < Instr.B; i++)
- //for (int i = Instr.A; i < Instr.A + Instr.B - 1; ++i)
- {
- this.Op3 += $"var{i + 1}";
- if (i < Instr.A + Instr.B - 2)
- this.Op3 += ", ";
- }
- this.Op3 += ")";
- }
- else
- {
- this.Op3 = $"(";
- for (int i = Instr.A; i < Instr.A + Instr.B - 1; i++)
- //for (int i = Instr.A; i < Instr.A + Instr.B - 1; ++i)
- {
- this.Op3 += $"var{i + 1}";
- if (i < Instr.A + Instr.B - 2)
- this.Op3 += ", ";
- }
- this.Op3 += ")";
- }
+
+ // Function Args (same B logic as CALL above)
+ this.Op3 = WriteArgs(Instr.A, Instr.B);
break;
case LuaOpcode.RETURN:
// this gets overwritten by an 'end' afterwards in case its the last RETURN value of a func
@@ -399,12 +379,19 @@ public void SetMain(LuaInstruction Instr = null)
case LuaOpcode.VARARG:
this.Func.ScriptFunction.HasVarargs = true;
this.Op1 = "local ";
- for (int i = Instr.A; i < Instr.A+Instr.B-1; i++)
+ int varargCount = Instr.B == 0 ? 1 : Instr.B - 1;
+ for (int i = 0; i < varargCount; i++)
{
- this.Op2 += $"var{i}";
- if (i < Instr.B - 2)
+ this.Op2 += $"var{Instr.A + i}";
+ if (i < varargCount - 1)
this.Op2 += ", ";
}
+ // for (int i = Instr.A; i < Instr.A+Instr.B-1; i++)
+ // {
+ // this.Op2 += $"var{i}";
+ // if (i < Instr.B - 2) // <-- bug #1: should be A+b-2, not B-2
+ // this.Op2 += ", ";
+ // }
this.Op2 += " = ...";
break;
default:
@@ -451,7 +438,7 @@ private string WriteConstant(int index, LuaFunction targetFunc = null)
if (index > 255 && targetFunc.Constants[index - 256] != null)
return targetFunc.Constants[index - 256].ToString();
else
- return WriteIndex(index);
+ return WriteIndex(index, false);
}
public void SetFunctionRef(LuaFunction func)
@@ -503,6 +490,46 @@ public string WriteIndex(int value, bool useLocalKeyword = true)
}
}
+ private string WriteArgs(int funcReg, int b)
+ {
+ int argCount;
+
+ if (b == 0)
+ argCount = FindStackTop(funcReg) - funcReg; // A+1 .. top
+ else
+ argCount = b - 1; // exactly B-1 fixed args
+
+ string result = "(";
+ for (int i = 0; i < argCount; i++)
+ {
+ result += $"var{funcReg + 1 + i}";
+ if (i < argCount - 1)
+ result += ", ";
+ }
+ result += ")";
+ return result;
+ }
+
+
+ // NOTE: when B/C == 0 the args reach up to the 'top' of the stack, which a
+ // preceding VARARG (B==0) or CALL (C==0) pushed there. Walk back to find it
+ // and return the register that top sits on
+ private int FindStackTop(int funcReg)
+ {
+ int idx = this.Func.Instructions.IndexOf(this.Instr);
+ for (int i = idx - 1; i >= 0; i--)
+ {
+ LuaInstruction prev = this.Func.Instructions[i];
+ if (prev.A <= funcReg)
+ continue; // not part of this call's args
+ if (prev.OpCode == LuaOpcode.VARARG && prev.B == 0)
+ return prev.A; // varargs spill from here to top
+ if (prev.OpCode == LuaOpcode.CALL && prev.C == 0)
+ return prev.A; // call results spill from here to top
+ }
+ return funcReg; // nothing found, no args
+ }
+
public int ToIndex(int value, out bool isConstant)
{
// this is the logic from lua's source code (lopcodes.h)