chill1Penguin / l64decode

A Python script to decode Farming Simulator 2019 .l64 files to LuaJIT bytecode files.
MIT License
13 stars 7 forks source link

Unable to decode FS25 files #11

Open TAEMBO opened 2 weeks ago

TAEMBO commented 2 weeks ago

This script works fine to decode Farming Simulator 22 files, however Farming Simulator 25 seems to be using a different format - an error 'File "{filePath}" contains an invalid .l64 header.' is returned for any and all l64 files retrieved from FS25.

Here's a download link to one of the .l64 files in question: https://cdn.taembo.net/main.l64

tn4799 commented 2 weeks ago

An importent information is that the l64 files differ from each other starting at the third byte, compared to the old files where the first 16 bytes always are the same.

tn4799 commented 2 weeks ago

@VidhosticeSDK Would your algorithm also work if the bytes now change from row to row? Since in fs22 the files all had the same first 16 bytes.

19BlueDragon83 commented 2 weeks ago

If someone has a solution, it would be nice if he would share it :). How did you open the .gar file?

scfmod commented 2 weeks ago

FS25 does not use LuaJIT anymore, they now use Luau. The first 2 bytes of the .l64 is the version (0x02 0xEF = luau bytecode version 3 when interpreted by the game .exe). So the old lookup tables won't work at all for these :-) Other than that I don't have much information on it.

scfmod commented 2 weeks ago
0x02, 0x13, 0x0a, 0x08, 0x01, 0x07, 0x02, 0x02
Rockstar94FS commented 2 weeks ago

Hi, I found a way to decode the .l64 files. https://github.com/Rockstar94FS/Farming-Simulator-L64-Decoder

Unfortunately, they are still unreadable. Does anyone know of any tool to decompile them?

TAEMBO commented 2 weeks ago

@Rockstar94FS What does this attempt decode them to? LuaJIT or Luau?

Rockstar94FS commented 2 weeks ago

@TAEMBO Luau bytecode

scfmod commented 2 weeks ago

That script will only decode FS19/FS22 .l64 files, not FS25

Rockstar94FS commented 2 weeks ago

It works with FS25, the algorithm is similar but different from that of FS19/FS22.

scfmod commented 2 weeks ago

Yeah my bad, it actually worked :-) I didn't notice the change of the formula, just that it used the same lookup table from previous versions. The lookup table I posted here works with the old formula

TAEMBO commented 1 week ago

@CoderKane https://github.com/xgladius/luauDec seems to be the most prevalent Luau decompiler out there based on my research that isn't pay-walled, however I'm unable to comprehend how to fully build it w/ make or with VS, have only gotten past the cmake part.

If anyone has any insight into that or knows a better solution, do let me know. I'm looking for something I can ship as a binary as opposed to a GUI or online web kind.

scfmod commented 1 week ago

luauDec only supports bytecode version 2 and unfortunately seems to fail randomly every time it's executed.

19BlueDragon83 commented 1 week ago

Maybe this?

https://github.com/cadenmarinozzi/Luau-Decompiler

scfmod commented 1 week ago

It seems that the bytecode format in FS25 is a bit modified from the "standard" format. I can deserialize v4 bytecode generated by luau compiler without any problems, but not these files.

Edit: Seemingly the bytecode version they use is 3

ThibBer commented 1 week ago

@scfmod How can you be sure FS25 uses luau format ? What tip did you use to find this out? Any magic numbers somewhere ?

Rockstar94FS commented 1 week ago

@scfmod How can you be sure FS25 uses luau format ? What tip did you use to find this out? Any magic numbers somewhere ?

You can check this in game using print(_G._VERSION) function

scfmod commented 1 week ago

@scfmod How can you be sure FS25 uses luau format ? What tip did you use to find this out? Any magic numbers somewhere ?

Found it when inspecting the executable. Luau files doesn't have a magic number like LuaJIT. It starts with version byte where as LuaJIT has a 3 byte magic number 0x1B 0x4C 0x4A.

Namrikanets commented 1 week ago

As I understand it, there is no working solution to get scripts?

VPR96 commented 1 week ago

385948103-d2dcbf7e-ecdb-4c7a-b030-1f436f0fac68 385948129-649ed265-4131-4c8d-9ff3-7a686d5a8ec4 I was able to get the l64 to readable hex using this, but the "lua jit deompiler" can't finish the job. I prepended the luajit header to try and fool it to work. 385951648-00e5b271-7f7a-44c3-b8e3-4eb47db8d518

Rockstar94FS commented 1 week ago

This is no longer LuaJIT, the old method will not work, valid files start with 0x01 0x03, and such files work normally in the game, so they are certainly valid.

At the moment there is a half-working tool to get readable scripts https://github.com/atrexus/unluau/tree/v2.0.0-beta unfortunately we will not get a ready solution from it, only instructions in this form:

function <Player.lua:63> (19 instructions, 96 bytes)
1 params, 8 slots, 0 upvalues, 16 constants
function registerXMLPaths(v1) -- line 63 through 72
   1    GETIMPORT           3 2
   2                        2147484672
   3    LOADK               4 3
   4    LOADK               5 4
   5    LOADNIL             
   6    LOADB               7 1 0
   7    NAMECALL            1 0 163
   8                        5
   9    CALL                1 7 1
   10   GETIMPORT           1 8
   11                       2153782272
   12   MOVE                2 0
   13   LOADK               3 9
   14   CALL                1 3 1
   15   GETIMPORT           1 11
   16                       2157976576
   17   MOVE                2 0
   18   CALL                1 2 1
   19   GETIMPORT           1 14
   20                       2160079872
   21   MOVE                2 0
   22   LOADK               3 15
   23   CALL                1 3 1
   24   RETURN              0 1

   constants (16)
      index  type     value
      0      string   "XMLValueType"
      1      string   "STRING"
      2      import   ["XMLValueType","STRING"]
      3      string   "player.filename"
      4      string   "The file path of the player\'s i3d file"
      5      string   "register"
      6      string   "HumanModel"
      7      string   "registerXMLPaths"
      8      import   ["HumanModel","registerXMLPaths"]
      9      string   "player"
      10     string   "PlayerStyle"
      11     import   ["PlayerStyle","registerXMLPaths"]
      12     string   "IKUtil"
      13     string   "registerIKChainXMLPaths"
      14     import   ["IKUtil","registerIKChainXMLPaths"]
      15     string   "player.ikChains.ikChain(?)"

   locals (1)
      index  name     startpc  endpc    type
      0      xmlSchema 0        24       
end

You can try to create something readable from it, but there is currently no tool to do it for over 1700 files

function Player.registerXMLPaths(xmlSchema)
      xmlSchema:register(XMLValueType.STRING, "player.filename", "The file path of the player's i3d file")
      HumanModel:registerXMLPaths(xmlSchema, "player")
      PlayerStyle:registerXMLPaths(xmlSchema)
      IKUtil:registerIKChainXMLPaths(xmlSchema, "player.ikChains.ikChain(?)")
end
Namrikanets commented 1 week ago

I already understood that this is not luajit, is it really possible to write code to return the original form of lua after l64decode.py, knowing the instructions?

ThibBer commented 1 week ago

@Rockstar94FS Do you use this tool after your l64Decoder.py ? Because i got this error : Unluau.DecompilerException: Bytecode version mismatch, expected version 3...6, got 1 when I use Unlau on the output files from your script. And if manually change the version (first byte), between 0x03 and 0x06, the decompilation can't finished due to various exceptions

Rockstar94FS commented 1 week ago

@Rockstar94FS Do you use this tool after your l64Decoder.py ? Because i got this error : Unluau.DecompilerException: Bytecode version mismatch, expected version 3...6, got 1 when I use Unlau on the output files from your script. And if manually change the version (first byte), between 0x03 and 0x06, the decompilation can't finished due to various exceptions

You need to remove the first byte 0x01 from the file, I haven't verified this but it is very possible that it is used by the game to check what form the file is in, encrypted (0x02), decrypted (0x01) or normal.

ThibBer commented 1 week ago

@Rockstar94FS Do you use this tool after your l64Decoder.py ? Because i got this error : Unluau.DecompilerException: Bytecode version mismatch, expected version 3...6, got 1 when I use Unlau on the output files from your script. And if manually change the version (first byte), between 0x03 and 0x06, the decompilation can't finished due to various exceptions

You need to remove the first byte 0x01 from the file, I haven't verified this but it is very possible that it is used by the game to check what form the file is in, encrypted (0x02), decrypted (0x01) or normal.

Thanks for the tips πŸ˜‰

You said on your previous message that we only get instructions. I tested with some files and i get readable lua code (eg: debug/MemoryLeaks.lua or A_staticAnalyzer.lua) but with main.lua, decompilation failed with error Unluau.DecompilerException: unhandled operation code (91), same with Player.lua with error : Unexpected error; please report here: https://github.com/atrexus/unluau/issues System.NullReferenceException: Object reference not set to an instance of an object.

But I have the impression that you were able to open it.

It seems strange that Giants use different format for compiling to bytecode

Rockstar94FS commented 1 week ago

@ThibBer This is a problem with the tool itself, it is in very early alpha version and cannot convert many files correctly. I tried it on my own files compiled with luau-compiler and got similar errors. Version 2.0 works much better, but does not generate ready script.

ThibBer commented 1 week ago

I have tried main & v2.0.0-beta branch but it's seems to be the same result with Player.lua, NullPointerException. It would be interesting to explore this idea further, as I'm sure it's not difficult to convert the instructions into readable code.

Namrikanets commented 1 week ago

This code from the example above was compiled by some lua according to the instructions, as I understand it, if you study all the instructions you can translate them en masse, but my brain is not enough for this, bad

def parse_instructions_to_lua(instructions, constants, locals_info): """ Converts raw bytecode instructions into a Lua-like code structure.

Parameters:
- instructions: List of raw bytecode instructions.
- constants: Dictionary of constants used in the bytecode.
- locals_info: Information about local variables (e.g., names, start/end scope).

Returns:
- A list of strings representing Lua-like code.
"""
lua_code = ["function registerXMLPaths(xmlSchema)"]  # Start of the Lua function

# Add comments for local variables if provided
for local in locals_info:
    lua_code.append(f"    local {local['name']} = nil -- local variable")

# Parse each instruction and convert it to Lua syntax
for instr in instructions:
    parts = instr.split()
    opcode = parts[0]

    # Map bytecode opcodes to Lua code
    if opcode == "GETIMPORT":
        _, reg, const_idx = parts
        const_value = constants[int(const_idx)]
        lua_code.append(f"    local var{reg} = {const_value}")

    elif opcode == "LOADK":
        _, reg, const_idx = parts
        const_value = constants[int(const_idx)]
        lua_code.append(f"    local var{reg} = '{const_value}'")

    elif opcode == "LOADNIL":
        _, reg = parts
        lua_code.append(f"    local var{reg} = nil")

    elif opcode == "MOVE":
        _, dest, src = parts
        lua_code.append(f"    var{dest} = var{src}")

    elif opcode == "CALL":
        _, func_reg, *args = parts
        args_list = ", ".join([f"var{arg}" for arg in args])
        lua_code.append(f"    var{func_reg}({args_list})")

    elif opcode == "RETURN":
        lua_code.append("    return")

lua_code.append("end")  # Close the function
return lua_code

def transform_to_readable_lua(): """ Converts parsed Lua code into a final readable script.

This function takes the logic of the bytecode instructions and maps it to
meaningful Lua constructs.

Returns:
- A string containing the complete Lua script.
"""
lua_code = [
    "function registerXMLPaths(xmlSchema)",
    "    -- Import values",
    "    local XMLValueType_STRING = XMLValueType.STRING",
    "    xmlSchema:register(XMLValueType_STRING, \"player.filename\", \"The file path of the player's i3d file\")",
    "",
    "    -- Register PlayerStyle",
    "    xmlSchema:register(PlayerStyle.registerXMLPaths, \"player\")",
    "",
    "    -- Register IKChains",
    "    xmlSchema:register(IKUtil.registerIKChainXMLPaths, \"player.ikChains.ikChain(?)\")",
    "end"
]
return "\n".join(lua_code)

def main(): """ Main function that handles parsing and transformation of bytecode instructions. It demonstrates the entire process, from raw instructions to a readable Lua script. """

Example bytecode instructions from the input

instructions = [
    "GETIMPORT 3 2",
    "LOADK 4 3",
    "LOADK 5 4",
    "LOADNIL 6",
    "LOADB 7 1",
    "NAMECALL 1 0 163",
    "CALL 1 7 1",
    "MOVE 2 0",
    "LOADK 3 9",
    "CALL 1 3 1",
    "RETURN 0 1"
]

# Constants that map indices to strings or values in the bytecode
constants = {
    0: "XMLValueType",
    1: "STRING",
    2: ["XMLValueType", "STRING"],
    3: "player.filename",
    4: "The file path of the player's i3d file",
    5: "register",
    6: "HumanModel",
    7: "registerXMLPaths",
    8: ["HumanModel", "registerXMLPaths"],
    9: "player",
    10: "PlayerStyle",
    11: ["PlayerStyle", "registerXMLPaths"],
    12: "IKUtil",
    13: "registerIKChainXMLPaths",
    14: ["IKUtil", "registerIKChainXMLPaths"],
    15: "player.ikChains.ikChain(?)",
}

# Local variable definitions (if available)
locals_info = [
    {"name": "xmlSchema", "startpc": 0, "endpc": 24, "type": "local"}
]

# Generate Lua-like code from bytecode
lua_code = parse_instructions_to_lua(instructions, constants, locals_info)

# Transform into the final readable Lua script
readable_lua_code = transform_to_readable_lua()

# Save the result to a file
output_file = "decompiled_script.lua"
with open(output_file, "w", encoding="utf-8") as f:
    f.write(readable_lua_code)

print(f"Lua script successfully generated in file: {output_file}")

if name == "main": main()

scfmod commented 1 week ago

I have tried main & v2.0.0-beta branch but it's seems to be the same result with Player.lua, NullPointerException. It would be interesting to explore this idea further, as I'm sure it's not difficult to convert the instructions into readable code.

If you decode the file correctly, the 2.0.0-beta branch will disassemble the Player.l64 file without any issues. Shift all the bytes in the file, remove the first byte and then save it. Voila.

scfmod commented 1 week ago

@Rockstar94FS Do you use this tool after your l64Decoder.py ? Because i got this error : Unluau.DecompilerException: Bytecode version mismatch, expected version 3...6, got 1 when I use Unlau on the output files from your script. And if manually change the version (first byte), between 0x03 and 0x06, the decompilation can't finished due to various exceptions

You need to remove the first byte 0x01 from the file, I haven't verified this but it is very possible that it is used by the game to check what form the file is in, encrypted (0x02), decrypted (0x01) or normal.

You remove the first byte because the interpreter in the game .exe reads both bytes to determine the bytecode version. That's basically it :-) Where as the "standard" Luau only uses the first byte.

Rockstar94FS commented 1 week ago

I'm not entirely sure. If we change the second byte from 0xEF to eg. 0xFF we get a message about incompatible bytecode '256'. The second byte is swapped by the game from 0xEF to 0x03 if it detects that the first byte is 0x02. So if it is swapped, the bytocede version matches, meaning it is 0x03. Now the most important thing is how to present bytecode instructions in a readable form, what each byte is responsible for is less important.

scfmod commented 1 week ago

Yeah creating a Lua(u) writer from instructions is doable, but a lot of work. I'd suggest looking at https://github.com/marsinator358/luajit-decompiler-v2 to get an overview and maybe use as a basis :-)

Or contribute to https://github.com/atrexus/unluau ofc.

kbrandwijk commented 1 week ago

AI is a great tool to process LUA instructions into readable code, especially if you train it on the FS22 lua files for coding style. It happily generates functions based on the example above. When you use the v2 branch of unluau and a bit of OpenAI, it will happily process all the files for you... image

Namrikanets commented 1 week ago

AI is a great tool to process LUA instructions into readable code, especially if you train it on the FS22 lua files for coding style. It happily generates functions based on the example above. When you use the v2 branch of unluau and a bit of OpenAI, it will happily process all the files for you... image

or maybe it can automatically process 1k files and give us ready-made scripts?)

Rockstar94FS commented 1 week ago

I'm not very optimistic about this solution, how can I be sure that the code generated by the AI ​​is 1:1 the same as in the game? I can already see that it doesn't look completely correct.

DasVolka commented 4 days ago

Do we know what the correct Lookup table is yet or is that still under investigation?

scfmod commented 4 days ago

Do we know what the correct Lookup table is yet or is that still under investigation?

https://github.com/chill1Penguin/l64decode/issues/11#issuecomment-2468969751

DasVolka commented 4 days ago

Do we know what the correct Lookup table is yet or is that still under investigation?

#11 (comment)

I checked this against a few l64s and that didn't seem to match up to the hex. However, one thing I came across messing with a few luau decompilers is that they all say the bytecode is version 2, which unluau is v3-v4 (v2.0.0 beta is v3-v5) and Luau-Decompiler supports v5. I haven't been able to find a v2 support just yet

scfmod commented 4 days ago

For FS25 Luau it does match up, you just have to shift all bytes and then remove the first.

VPR96 commented 3 days ago

Still no way to decompile the files tho?

4c65736975 commented 3 days ago

For FS25 Luau it does match up, you just have to shift all bytes and then remove the first.

Please link us to the tool being used and let us know how many bytes to shift by.

I personally would appreciate it greatly.

Run this script on a directory with .l64 files and your bytes should be fine

import os
import argparse

def read_and_modify_files(directory):
    for root, _, files in os.walk(directory):
        for file in files:
            file_path = os.path.join(root, file)
            try:
                with open(file_path, 'rb') as f:
                    first_byte = f.read(1)
                    rest_of_file = f.read()

                    if first_byte:
                        first_byte_value = int.from_bytes(first_byte, 'big')

                        if first_byte_value == 1:
                            with open(file_path, 'wb') as f_mod:
                                f_mod.write(rest_of_file)

                                print(f"Removed first byte with value {first_byte_value} from {file_path}")
                    else:
                        print(f"{file_path}: Empty file")
            except Exception as e:
                print(f"Could not process {file_path}: {e}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Read and modify files in a directory.")
    parser.add_argument("-dir", "--directory", required=True, help="Path to the directory to process")

    args = parser.parse_args()
    directory_path = args.directory

    read_and_modify_files(directory_path)
scfmod commented 3 days ago
import { readFileSync, writeFileSync } from "node:fs"

const BYTECODE_SHIFT_TABLE_LUAU_V3 = [0x02, 0x13, 0x0a, 0x08, 0x01, 0x07, 0x02, 0x02]

function shiftBytesUsingTable(buffer: Buffer, table: Array<number>, offset = 0): void {
    for (let i = offset; i < buffer.length; i++) {
        const value = buffer[i]

        // Mask i with 0x07 to get the bytecode for this index
        const bytecode = table[i & 0x07]

        // Apply the shifting formula and ensure the result stays in the 0-255 range
        buffer[i] = (value + bytecode + i) & 0xFF
    }
}

const inputBuffer = readFileSync("C:/path/file.l64")

shiftBytesUsingTable(inputBuffer, BYTECODE_SHIFT_TABLE_LUAU_V3)

// Omit the first byte
const outputBuffer = inputBuffer.subarray(1)

writeFileSync("C:/path/file_decoded.l64", outputBuffer)
Bizyak13 commented 3 days ago

@4c65736975 Not sure if this script works, since the .l64 files report first bite as \x02, and the decoded ones (using @scfmod script for example) report first bite as \x03 (but you expect a 1).

But this still only gives bytecode, and not decompiled code right? I've tried all the decompilers here (LuauDec, Unluau, ...) with not much success apart from some instructions. I even tried the Lua 5.1 decompiler since luau supposedly has support for lua5.1. Anyone managed to get somewhat decent code out yet or is there a Discord or something for passing ideas around?

4c65736975 commented 3 days ago

@Bizyak13 This script removes the first byte from .l64 files decompiled by L64Decoder, allowing decompilers like Unluau to recognize the bytecode version. It works for me, and using this method, I was able to successfully decompile 1,228 files out of 1,703 to plain Lua.

Bizyak13 commented 3 days ago

@4c65736975 This is still confusing, since using the L64Decoder, the files are already correct for Unluau v2.0.0 unless you are using some other decompiler?

If I run your script on a decoded file, I get zero output, since the first byte is not "1". But I do get the files properly decompiled if I use the Unluau, they just contain this:

; unluau disassembler version 2.0.0.0, elapsed: 0.1578657s
testing.lua: Luau bytecode executable, version 3, hash: 0x99892b7b730184275aedac347b9161c6

function <testing.lua:13> (31 instructions, 180 bytes)
0 params, 5 slots, 1 upvalues, 20 constants
function initTesting() -- line 13 through 31
   1    GETIMPORT           0 2
   2                        2147484672
   3    LOADK               1 3
   4    CALL                0 2 2
   5    JUMPXEQKNIL         0 38
   6                        0
   7    GETUPVAL            2 0
   8    GETTABLE            1 2 0
   9    JUMPXEQKNIL         1 34
   10                       0
   11   GETIMPORT           2 5
   12                       1077936128
   13   GETTABLEKS          3 1 138
   14                       6
   15   CALL                2 2 1
   16   GETIMPORT           2 9
   17                       2154831872
   18   GETTABLEKS          3 1 187
   19                       10
   20   CALL                2 2 2
   21   SETGLOBAL           2 0 43
   22                       11
   23   GETGLOBAL           2 0 43
   24                       11
   25   JUMPXEQKNIL         2 13
   26                       0
   27   GETGLOBAL           3 0 43
   28                       11
   29   GETTABLEKS          2 3 19
   30                       12
   31   CALL                2 1 1
   32   GETIMPORT           2 15
   33                       2161129472
   34   LOADK               3 16
   35   MOVE                4 0
   36   CALL                2 3 1
   37   LOADB               2 1 0
   38   RETURN              2 2
   39   GETIMPORT           2 18
   40                       2161132544
   41   LOADK               3 19
   42   MOVE                4 0
   43   CALL                2 3 1
   44   LOADB               1 0 0
   45   RETURN              1 2

   constants (20)
      index  type     value
      0      string   "StartParams"
      1      string   "getValue"
      2      import   ["StartParams","getValue"]
      3      string   "test"
      4      string   "source"
      5      import   ["source"]
      6      string   "filename"
      7      string   "ClassUtil"
      8      string   "getClassObject"
      9      import   ["ClassUtil","getClassObject"]
      10     string   "className"
      11     string   "g_currentTest"
      12     string   "init"
      13     string   "Logging"
      14     string   "info"
      15     import   ["Logging","info"]
      16     string   "Started test \'%s\'"
      17     string   "error"
      18     import   ["Logging","error"]
      19     string   "Test \'%s\' not defined"

   locals (2)
      index  name     startpc  endpc    type
      0      data     8        43       
      1      testName 4        45       

   upvalues (1)
      index  name     type
      0      tests    
end
main <testing.lua:1> (44 instructions, 260 bytes)
0+ params, 3 slots, 0 upvalues, 18 constants
function main(...) -- line 1 through 33
   1    PREPVARARGS         
   2    LOADNIL             
   3    SETGLOBAL           0 0 43
   4                        0
   5    NEWTABLE            0 0
   6                        0
   7    DUPTABLE            1 3
   8    LOADK               2 4
   9    SETTABLEKS          2 1 187
   10                       1
   11   LOADK               2 5
   12   SETTABLEKS          2 1 138
   13                       2
   14   SETTABLEKS          1 0 109
   15                       4
   16   DUPTABLE            1 3
   17   LOADK               2 6
   18   SETTABLEKS          2 1 187
   19                       1
   20   LOADK               2 7
   21   SETTABLEKS          2 1 138
   22                       2
   23   SETTABLEKS          1 0 37
   24                       6
   25   DUPTABLE            1 3
   26   LOADK               2 8
   27   SETTABLEKS          2 1 187
   28                       1
   29   LOADK               2 9
   30   SETTABLEKS          2 1 138
   31                       2
   32   SETTABLEKS          1 0 197
   33                       8
   34   DUPTABLE            1 3
   35   LOADK               2 10
   36   SETTABLEKS          2 1 187
   37                       1
   38   LOADK               2 11
   39   SETTABLEKS          2 1 138
   40                       2
   41   SETTABLEKS          1 0 142
   42                       10
   43   DUPTABLE            1 3
   44   LOADK               2 12
   45   SETTABLEKS          2 1 187
   46                       1
   47   LOADK               2 13
   48   SETTABLEKS          2 1 138
   49                       2
   50   SETTABLEKS          1 0 164
   51                       12
   52   DUPTABLE            1 3
   53   LOADK               2 14
   54   SETTABLEKS          2 1 187
   55                       1
   56   LOADK               2 15
   57   SETTABLEKS          2 1 138
   58                       2
   59   SETTABLEKS          1 0 220
   60                       14
   61   DUPCLOSURE          1 16
   62   CAPTURE             0 0 0
   63   SETGLOBAL           1 0 115
   64                       17
   65   RETURN              0 1

   constants (18)
      index  type     value
      0      string   "g_currentTest"
      1      string   "className"
      2      string   "filename"
      3      import   {"className", "filename"}
      4      string   "TestAnimalCluster"
      5      string   "dataS/scripts/animals/husbandry/cluster/TestAnimalCluster.lua"
      6      string   "TestI3DManager"
      7      string   "dataS/scripts/i3d/TestI3DManager.lua"
      8      string   "TestDebugElements"
      9      string   "dataS/scripts/debug/TestDebugElements.lua"
      10     string   "TestXML"
      11     string   "dataS/scripts/xml/TestXML.lua"
      12     string   "TestPolygon"
      13     string   "dataS/scripts/collections/TestPolygon.lua"
      14     string   "TestMathUtil"
      15     string   "dataS/scripts/utils/TestMathUtil.lua"
      16     closure  0
      17     string   "initTesting"

   locals (1)
      index  name     startpc  endpc    type
      0      tests    6        65       
end
DasVolka commented 3 days ago

So l64Decoder > script > luau decompiler Is the working method?

4c65736975 commented 3 days ago

So l64Decoder > script > luau decompiler

Is the working method?

Yes, but I had to adjust the decompiler a bit to decompile more scripts, because in the github version it throws errors for many files

4c65736975 commented 3 days ago

I recommend a simple batch script that will automatically run unluau on all files in a folder and delete the ones that failed to decompile (empty files)

CurtisFeatures commented 1 day ago

@Bizyak13 This script removes the first byte from .l64 files decompiled by L64Decoder, allowing decompilers like Unluau to recognize the bytecode version. It works for me, and using this method, I was able to successfully decompile 1,228 files out of 1,703 to plain Lua.

What version of Unluau are you using? I'm only getting 873 successfully converted files. any idea to why the other ones are failing?

Mmtrx commented 1 day ago

With Unluau.CLI v1.09.alpha, some bytecode instructions are not recognized (e.g. missionManager.l64):

[10:13:49 WRN] Encountered unhandled code JUMP, skipping
[10:13:49 WRN] Encountered unhandled code FASTCALL2, skipping
[10:13:49 WRN] Encountered unhandled code FASTCALL1, skipping
[10:13:49 WRN] Encountered unhandled code FASTCALL1, skipping
[10:13:49 WRN] Encountered unhandled code FASTCALL2K, skipping
[10:13:49 FTL] Unexpected error; please report here: https://github.com/atrexus/unluau/issues
System.NullReferenceException: Object reference not set to an instance of an object.
   at Unluau.Lifter.LiftBlock(Function function, Registers registers, Int32 pcStart, Int32 pcStop)

And, as has been said already, Unluau2.0.0.beta does not (yet?) generate lua source output. @4c65736975 , when you "had to adjust the decompiler a bit to decompile more scripts", did you work from v1.0.9alpha or v2.0.0beta?