
A disassembler & experimental decompiler for TLOU2 DC Scripts.

dconstruct is a reverse engineering tool for the DC-Script files used in The Last of Us Part II. It features a disassembler and a decompiler.
It outputs .asm files containing the disassembled structures and bytecode, aswell as .dcpl (DC Pseudo Language) files containing C-like pseudo code.
You can also make edits to files via the command line, including replacing entire structures with little effort. This makes creating mods that simply change a couple values inside the .bin files extremely easy.
-e flag, creating new files that you can use for modsFirst, it's recommended that you move the unzipped dconstruct directory into some safe location, such as C:\Program Files.
In order to make dconstruct as easy to use as possible, it's recommended that you add the .\bin directory inside the dconstruct folder to your PATH. You can find out more here, or follow these quick steps:

Make sure your path ends in "\bin" and NOT "\dconstruct".
Click 'OK' on all open dialogs.
To verify that it worked, open a new command prompt and type dconstruct --about. You should see some output from the program and no error message.
Run a command like this in the command line to generate your first disassembled file:
dconstruct my_bin_file.bin
This will then output a file called my_bin_file.bin.asm in the same directory as your input file. You can then open that file using a text/code editor. I would recommend using something like VSCode which offers advanced searching features and is good at handling large files. Standard Windows notepad is not recommended.
To decompile a file, add the --decompile flag when running the command.
-i - input file or folder. Can be omitted if passing in the input path as the first argument.
-o - output path. If your input path is a folder, this cannot be a file. If no output is specified, the .txt file will be put next to the input file. If the input is a folder and no output is specified, the program will create a "output" directoy in the current working directory and put all the files in there.
The sidbase is loaded from sidbase.bin located next to the executable.
--no_decompile - don'T emit decompiled pseudo code into a .dcpl file. The file will be placed next to the .asm file. This is false by default.
--no_optimize - don't optimize and cleanup the dcpl code. involves inlining function calls, removing unused variables, transforming compatible for loops into foreach loops, and turning some if-else chains into match expressions.
--pascal_case - convert the games function names into pascal case in the dcpl output, e.g. get-boolean -> GetBoolean.
--graphs - emit .svg files containing control flow graphs for all decompiled functions. Each .bin file gets its own folder containing all of its graphs. This significantly slows down decompilation speed, so it is not recommended when decompiling a large number of files at the same time.
--emit_once - prohibits the same structure from being emitted twice in the disassembly. If a structure shows up multiple times, only the first instance will be fully emitted, and all other occasions will be replaced by a ALREADY_EMITTED tag. This can significantly reduce file size.
-e - make an edit. More info in the section below.
--edit_file - provide an edit file. an edit file contains one edit per line. it uses the same syntax as the -e flag.
A disassembler is a tool that reads binary instructions (a.k.a. bytecode or machine code) and translates each into a human readable version called a mnemonic. Disassemblers generally don't try to interpret much about the meaning of these instructions and just transform them 1-1 into their readable versions. For example, the instructions:
15 00 00 00
4A 01 01 00
43 31 01 00
1C 00 00 01
are disassembled into the following human readable versions:
LookupPointer r0, 0
LoadStaticU64Imm r1, 1
Move r49, r1
CallFf r0, r0, 1
All numbers in the bytecode are written in hexadecimal. The first column in each row represents the opcode, or the type of instruction to be executed. The next column is the destination register, where the result of the operation will be stored. The last two columns are operands 1 and 2, which are either registers or literal numbers on which the operation will be performed. Not all instructions use all 4 bytes, for example, the first LookupPointer instruction only needs one operand.
The dconstruct disassembler also adds some additional information meant to make reading the instructions a bit easier. It also inserts labels (e.g. L_0) to make branches in the code easier to trace.
15 00 00 00 LookupPointer r0, 0 r0 = ST[0] -> <is-player-abby?>
4A 01 01 00 LoadStaticU64Imm r1, 1 r1 = ST[1] -> <player>
43 31 01 00 Move r49, r1 r49 = player
1C 00 00 01 CallFf r0, r0, 1 r0 = is-player-abby?(player)
2F 0D 00 00 BranchIfNot r0, 0xD IF NOT r0 => L_0
This is useful when you want to look at the raw contents of the file without the program making too many guesses. But it can be difficult to read for large blocks of code, as there is no structure whatsoever. This is where a decompiler comes in.