Skip to content

Instantly share code, notes, and snippets.

@zhuowei
Last active September 25, 2022 12:17
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zhuowei/56ffdda8913d603bf99b874d4be33984 to your computer and use it in GitHub Desktop.
Save zhuowei/56ffdda8913d603bf99b874d4be33984 to your computer and use it in GitHub Desktop.
Patches for unluac to disassemble (and partially decompile) Playdate's Lua 5.4.0 beta bytecode
# HG changeset patch
# User Zhuowei Zhang <zhuowei@worthdoingbadly.com>
# Date 1633490863 14400
# Tue Oct 05 23:27:43 2021 -0400
# Node ID 9a209bac7961ed4cc661f1a358d8ef2e23c74d7b
# Parent 0390d042f93125be8257ae3a0520aa6b1003145c
Add PlayDate's 0x23 integer type, fix boolean parsing for Lua 5.4 beta
diff --git a/src/unluac/parse/LConstantType.java b/src/unluac/parse/LConstantType.java
--- a/src/unluac/parse/LConstantType.java
+++ b/src/unluac/parse/LConstantType.java
@@ -167,13 +167,23 @@ class LConstantType54 extends LConstantT
case 0:
return LNil.NIL;
case 1:
+ return header.bool.parse(buffer, header);
+/*
+PlayDate: 5.4 beta doesn't have 0x11
return LBoolean.LFALSE;
case 0x11:
return LBoolean.LTRUE;
+*/
case 3:
return header.linteger.parse(buffer, header);
case 0x13:
return header.lfloat.parse(buffer, header);
+ // PlayDate: unsigned integer, maybe?
+ case 0x23:
+ if (header.debug) {
+ System.out.println("A 0x23 integer!");
+ }
+ return header.linteger.parse(buffer, header);
case 4:
return header.string.parse(buffer, header);
case 0x14: {
# HG changeset patch
# User Zhuowei Zhang <zhuowei@worthdoingbadly.com>
# Date 1633486956 14400
# Tue Oct 05 22:22:36 2021 -0400
# Node ID 0390d042f93125be8257ae3a0520aa6b1003145c
# Parent ed6eba8fa5792efc3b1c7c30532a145d64957362
Support Lua 5.4.0 beta header
diff --git a/src/unluac/parse/BHeader.java b/src/unluac/parse/BHeader.java
--- a/src/unluac/parse/BHeader.java
+++ b/src/unluac/parse/BHeader.java
@@ -84,6 +84,15 @@ public class BHeader {
int versionNumber = 0xFF & buffer.get();
int major = versionNumber >> 4;
int minor = versionNumber & 0x0F;
+
+ if (versionNumber == 0x03) {
+ // HACK: Lua 5.4 beta, PlayDate
+ // https://twitter.com/davehayden/status/1425959793504702466
+ int tempNumber = (versionNumber << 7) | (0x7F & buffer.get());
+ major = tempNumber / 100;
+ minor = tempNumber % 100;
+ versionNumber = major << 4 | minor;
+ }
version = Version.getVersion(major, minor);
if(version == null) {
# HG changeset patch
# User Zhuowei Zhang <zhuowei@worthdoingbadly.com>
# Date 1633494188 14400
# Wed Oct 06 00:23:08 2021 -0400
# Node ID ad22b05f3653ef1de6bf3455f22e791d1f33e4f9
# Parent 9a209bac7961ed4cc661f1a358d8ef2e23c74d7b
OpcodeMap: partial fix for Playdate's Lua 5.4.0 beta; still crashes during decompile
diff --git a/src/unluac/decompile/OpcodeMap.java b/src/unluac/decompile/OpcodeMap.java
--- a/src/unluac/decompile/OpcodeMap.java
+++ b/src/unluac/decompile/OpcodeMap.java
@@ -198,90 +198,96 @@ public class OpcodeMap {
map[46] = Op.EXTRAARG;
break;
case LUA54:
- map = new Op[83];
+ // Hack: PlayDate 5.4.0 beta doesn't have LFALSESKIP
+ // https://github.com/lua/lua/commit/9b7987a9d1471ba94764286b28e0998f73deb46a#diff-239329feffa8b7a9b93ec0d6e11d14279def61a1c351b52694f905b367368d10
+ //map = new Op[83];
+ map = new Op[81];
map[0] = Op.MOVE;
map[1] = Op.LOADI;
map[2] = Op.LOADF;
map[3] = Op.LOADK;
map[4] = Op.LOADKX;
- map[5] = Op.LOADFALSE;
- map[6] = Op.LFALSESKIP;
- map[7] = Op.LOADTRUE;
- map[8] = Op.LOADNIL52;
- map[9] = Op.GETUPVAL;
- map[10] = Op.SETUPVAL;
- map[11] = Op.GETTABUP54;
- map[12] = Op.GETTABLE54;
- map[13] = Op.GETI;
- map[14] = Op.GETFIELD;
- map[15] = Op.SETTABUP54;
- map[16] = Op.SETTABLE54;
- map[17] = Op.SETI;
- map[18] = Op.SETFIELD;
- map[19] = Op.NEWTABLE54;
- map[20] = Op.SELF54;
- map[21] = Op.ADDI;
- map[22] = Op.ADDK;
- map[23] = Op.SUBK;
- map[24] = Op.MULK;
- map[25] = Op.MODK;
- map[26] = Op.POWK;
- map[27] = Op.DIVK;
- map[28] = Op.IDIVK;
- map[29] = Op.BANDK;
- map[30] = Op.BORK;
- map[31] = Op.BXORK;
- map[32] = Op.SHRI;
- map[33] = Op.SHLI;
- map[34] = Op.ADD54;
- map[35] = Op.SUB54;
- map[36] = Op.MUL54;
- map[37] = Op.MOD54;
- map[38] = Op.POW54;
- map[39] = Op.DIV54;
- map[40] = Op.IDIV54;
- map[41] = Op.BAND54;
- map[42] = Op.BOR54;
- map[43] = Op.BXOR54;
- map[44] = Op.SHL54;
- map[45] = Op.SHR54;
- map[46] = Op.MMBIN;
- map[47] = Op.MMBINI;
- map[48] = Op.MMBINK;
- map[49] = Op.UNM;
- map[50] = Op.BNOT;
- map[51] = Op.NOT;
- map[52] = Op.LEN;
- map[53] = Op.CONCAT54;
- map[54] = Op.CLOSE;
- map[55] = Op.TBC;
- map[56] = Op.JMP54;
- map[57] = Op.EQ54;
- map[58] = Op.LT54;
- map[59] = Op.LE54;
- map[60] = Op.EQK;
- map[61] = Op.EQI;
- map[62] = Op.LTI;
- map[63] = Op.LEI;
- map[64] = Op.GTI;
- map[65] = Op.GEI;
- map[66] = Op.TEST54;
- map[67] = Op.TESTSET54;
- map[68] = Op.CALL;
- map[69] = Op.TAILCALL54;
- map[70] = Op.RETURN54;
- map[71] = Op.RETURN0;
- map[72] = Op.RETURN1;
- map[73] = Op.FORLOOP54;
- map[74] = Op.FORPREP54;
- map[75] = Op.TFORPREP54;
- map[76] = Op.TFORCALL54;
- map[77] = Op.TFORLOOP54;
- map[78] = Op.SETLIST54;
- map[79] = Op.CLOSURE;
- map[80] = Op.VARARG54;
- map[81] = Op.VARARGPREP;
- map[82] = Op.EXTRAARG;
+ //map[5] = Op.LOADFALSE;
+ //map[6] = Op.LFALSESKIP;
+ //map[7] = Op.LOADTRUE;
+ int opc = 5;
+ // HACK: probably not load false (load unsigned int?)
+ map[opc++] = Op.LOADFALSE;
+ map[opc++] = Op.LOADNIL52;
+ map[opc++] = Op.GETUPVAL;
+ map[opc++] = Op.SETUPVAL;
+ map[opc++] = Op.GETTABUP54;
+ map[opc++] = Op.GETTABLE54;
+ map[opc++] = Op.GETI;
+ map[opc++] = Op.GETFIELD;
+ map[opc++] = Op.SETTABUP54;
+ map[opc++] = Op.SETTABLE54;
+ map[opc++] = Op.SETI;
+ map[opc++] = Op.SETFIELD;
+ map[opc++] = Op.NEWTABLE54;
+ map[opc++] = Op.SELF54;
+ map[opc++] = Op.ADDI;
+ map[opc++] = Op.ADDK;
+ map[opc++] = Op.SUBK;
+ map[opc++] = Op.MULK;
+ map[opc++] = Op.MODK;
+ map[opc++] = Op.POWK;
+ map[opc++] = Op.DIVK;
+ map[opc++] = Op.IDIVK;
+ map[opc++] = Op.BANDK;
+ map[opc++] = Op.BORK;
+ map[opc++] = Op.BXORK;
+ map[opc++] = Op.SHRI;
+ map[opc++] = Op.SHLI;
+ map[opc++] = Op.ADD54;
+ map[opc++] = Op.SUB54;
+ map[opc++] = Op.MUL54;
+ map[opc++] = Op.MOD54;
+ map[opc++] = Op.POW54;
+ map[opc++] = Op.DIV54;
+ map[opc++] = Op.IDIV54;
+ map[opc++] = Op.BAND54;
+ map[opc++] = Op.BOR54;
+ map[opc++] = Op.BXOR54;
+ map[opc++] = Op.SHL54;
+ map[opc++] = Op.SHR54;
+ map[opc++] = Op.MMBIN;
+ map[opc++] = Op.MMBINI;
+ map[opc++] = Op.MMBINK;
+ map[opc++] = Op.UNM;
+ map[opc++] = Op.BNOT;
+ map[opc++] = Op.NOT;
+ map[opc++] = Op.LEN;
+ map[opc++] = Op.CONCAT54;
+ map[opc++] = Op.CLOSE;
+ map[opc++] = Op.TBC;
+ map[opc++] = Op.JMP54;
+ map[opc++] = Op.EQ54;
+ map[opc++] = Op.LT54;
+ map[opc++] = Op.LE54;
+ map[opc++] = Op.EQK;
+ map[opc++] = Op.EQI;
+ map[opc++] = Op.LTI;
+ map[opc++] = Op.LEI;
+ map[opc++] = Op.GTI;
+ map[opc++] = Op.GEI;
+ map[opc++] = Op.TEST54;
+ map[opc++] = Op.TESTSET54;
+ map[opc++] = Op.CALL;
+ map[opc++] = Op.TAILCALL54;
+ map[opc++] = Op.RETURN54;
+ map[opc++] = Op.RETURN0;
+ map[opc++] = Op.RETURN1;
+ map[opc++] = Op.FORLOOP54;
+ map[opc++] = Op.FORPREP54;
+ map[opc++] = Op.TFORPREP54;
+ map[opc++] = Op.TFORCALL54;
+ map[opc++] = Op.TFORLOOP54;
+ map[opc++] = Op.SETLIST54;
+ map[opc++] = Op.CLOSURE;
+ map[opc++] = Op.VARARG54;
+ map[opc++] = Op.VARARGPREP;
+ map[opc++] = Op.EXTRAARG;
break;
default:
throw new IllegalStateException();
# HG changeset patch
# User Zhuowei Zhang <zhuowei@worthdoingbadly.com>
# Date 1633495233 14400
# Wed Oct 06 00:40:33 2021 -0400
# Node ID 8f52eb1ea0af2abe900aca803fd0e8c3253e957b
# Parent ad22b05f3653ef1de6bf3455f22e791d1f33e4f9
Hack: work around too few registers error by just taking the max of register and declarations
diff --git a/src/unluac/decompile/Registers.java b/src/unluac/decompile/Registers.java
--- a/src/unluac/decompile/Registers.java
+++ b/src/unluac/decompile/Registers.java
@@ -22,6 +22,7 @@ public class Registers {
private final int[][] updated;
public Registers(int registers, int length, Declaration[] declList, Function f, boolean isNoDebug) {
+ if (registers < declList.length) registers = declList.length;
this.registers = registers;
this.length = length;
decls = new Declaration[registers][length + 1];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment