ev3rbf-decompiler
v0.0.5
Published
EV3 RBF decompiler: disassemble and decompile compiled EV3G programs of RBF files
Downloads
95
Maintainers
Readme
EV3-RBF-decompiler
NOTE: Deprecated package.
Full functionality transferred to blocklypy library.
EV3 RBF decompiler: disassemble and decompile compiled EV3G programs of RBF files
This library will convert the LEGO standard EV3G compiled files to get disassembled and decompiled.
EV3 programs created with the Mindstorms program are compiled once they are copied to the brick. In the past several teams and individuals suffered from losing their because the original .ev3 file was lost due to PC issues.
While I encourage all teams to do proper backup and versioning - this tool is able to decode and show almost all EV3G blocks and structures compiled by the LEGO Mindstorms. This enables easier understanding of the real code and emergency backup.
Examples usage
See console.ts as an example.
Background
Although EV3G compile is a one way transformation back in 2018, after experiments I found that disassembly and decompile might be possible.
Here we must mention David Lechner's lmsdisasm that is able to create a meaningful disasembly.
Diasssembly
Diasssembly looks like this:
// Disassembly of demo_CityShaper_CraneMission.rbf
//
// Byte code version: 0.57
vmthread OBJECT1
{
1_0: OUTPUT_RESET(0, 15)
1_3: INPUT_DEVICE(CLR_ALL, -1)
1_6: OR32(0, 1, LOCAL28)
1_10: MOVE32_32(0, LOCAL36)
1_14: ADD32(GLOBAL40, 1, GLOBAL40)
1_20: MOVE8_8(GLOBAL100, LOCAL51)
1_25: ADD32(GLOBAL36, 1, GLOBAL36)
1_31: JR_FALSE(LOCAL51, OFFSET1_43)
1_35: ADD32(LOCAL36, 1, LOCAL36)
1_41: JR(OFFSET1_14)
1_43: ADD32(GLOBAL32, 1, GLOBAL32)
1_49: CALL(OBJECT2, 123, 0, 50, 2.25, 0, 1)
1_74: ADD32(GLOBAL8, 1, GLOBAL8)
1_78: MOVE32_32(GLOBAL92, LOCAL32)
1_83: AND32(LOCAL32, 1, LOCAL40)
1_89: CP_GT32(LOCAL40, 0, LOCAL50)
1_95: JR_EQ8(LOCAL50, 0, OFFSET1_105)
1_100: JR_EQ8(LOCAL50, 1, OFFSET1_107)
1_105: JR(OFFSET1_125)
1_107: XOR32(1, LOCAL32, LOCAL44)
1_113: MOVE32_32(LOCAL44, GLOBAL92)
1_118: TIMER_WAIT(25, LOCAL12)
1_121: TIMER_READY(LOCAL12)
1_123: JR(OFFSET1_125)
1_125: MOVE32_32(0, LOCAL16)
1_128: MOVE32_32(LOCAL16, LOCAL20)
1_131: MOVE32_F(LOCAL20, LOCAL4)
1_134: ADD32(GLOBAL20, 1, GLOBAL20)
1_138: CALL(OBJECT5, LOCAL8, 101)
1_147: ADD32(GLOBAL12, 1, GLOBAL12)
1_151: ADD32(GLOBAL68, 1, GLOBAL68)
1_157: CALL(OBJECT6, LOCAL0, LOCAL8, 40)
1_167: ADD32(GLOBAL80, 1, GLOBAL80)
1_173: ADD32(GLOBAL48, 1, GLOBAL48)
1_179: CALL(OBJECT7, 123, LOCAL0, 25, LOCAL28)
1_194: ADD32(GLOBAL64, 1, GLOBAL64)
1_200: ADD32(GLOBAL56, 1, GLOBAL56)
1_206: CALL(OBJECT8, 1.5, LOCAL28, LOCAL49)
1_217: ADD32(GLOBAL60, 1, GLOBAL60)
1_223: JR_TRUE(LOCAL49, OFFSET1_234)
1_227: ADD32(LOCAL16, 1, LOCAL16)
1_231: JR(OFFSET1_128)
1_234: ADD32(GLOBAL44, 1, GLOBAL44)
1_240: CALL(OBJECT9, 123, 0, 1)
1_250: ADD32(GLOBAL52, 1, GLOBAL52)
1_256: ADD32(GLOBAL84, 1, GLOBAL84)
1_262: CALL(OBJECT3, 123, 0, 15, 0.25, 0, 1)
1_287: ADD32(GLOBAL88, 1, GLOBAL88)
1_293: ADD32(GLOBAL72, 1, GLOBAL72)
1_299: CALL(OBJECT10, 101, 25, 60, 0, 1)
1_319: ADD32(GLOBAL76, 1, GLOBAL76)
1_325: MOVE32_32(0, LOCAL24)
1_328: ADD32(GLOBAL16, 1, GLOBAL16)
1_332: CALL(OBJECT11, 2, 0, LOCAL48)
1_343: ADD32(GLOBAL0, 1, GLOBAL0)
1_347: JR_TRUE(LOCAL48, OFFSET1_357)
1_351: ADD32(LOCAL24, 1, LOCAL24)
1_355: JR(OFFSET1_328)
1_357: ADD32(GLOBAL4, 1, GLOBAL4)
1_361: CALL(OBJECT12, 101, -25, 1, 0, 1)
1_381: ADD32(GLOBAL24, 1, GLOBAL24)
1_385: ADD32(GLOBAL28, 1, GLOBAL28)
1_389: CALL(OBJECT4, 123, 0, -100, 4, 0, 1)
1_414: ADD32(GLOBAL96, 1, GLOBAL96)
1_420: OBJECT_END()
}
subcall OBJECT2
{
IN_F LOCAL0
IN_F LOCAL4
IN_F LOCAL8
IN_F LOCAL12
IN_32 LOCAL16
IN_8 LOCAL20
2_7: CP_EQF(LOCAL12, 0, LOCAL73)
2_12: MOVE32_32(GLOBAL92, LOCAL60)
2_17: AND32(LOCAL16, LOCAL60, LOCAL52)
2_23: CP_GT32(LOCAL52, 0, LOCAL69)
2_29: OR8(LOCAL73, LOCAL69, LOCAL74)
2_36: JR_EQ8(LOCAL74, 1, OFFSET2_46)
2_41: JR_EQ8(LOCAL74, 0, OFFSET2_49)
2_46: JR(OFFSET2_141)
2_49: MULF(LOCAL12, 360F, LOCAL24)
2_57: CALL(OBJECT13, LOCAL0, LOCAL8, LOCAL24, LOCAL4, LOCAL67, LOCAL68, LOCAL20)
2_69: MOVE32_32(LOCAL16, LOCAL48)
2_73: MOVE8_8(LOCAL68, LOCAL66)
2_78: MOVE8_8(LOCAL67, LOCAL65)
2_83: MOVE32_32(0, LOCAL44)
2_87: MOVE32_32(GLOBAL92, LOCAL40)
2_92: AND32(LOCAL48, LOCAL40, LOCAL56)
2_99: CP_GT32(LOCAL56, 0, LOCAL72)
2_105: OUTPUT_TEST(LOCAL65, LOCAL66, LOCAL64)
2_112: CP_EQ8(LOCAL64, 0, LOCAL70)
2_118: OR8(LOCAL70, LOCAL72, LOCAL71)
2_125: SLEEP()
2_126: JR_TRUE(LOCAL71, OFFSET2_139)
2_130: ADD32(LOCAL44, 1, LOCAL44)
2_136: JR(OFFSET2_87)
2_139: JR(OFFSET2_141)
2_141: RETURN()
2_142: OBJECT_END()
}
...
Decompile
After disassembly we can pattern match the calls and recognize the -unoptimized- code to match the built-in blocks one-by-one. In the disasmebly call parameters are obvious and positional.
1_43: ADD32(GLOBAL32, 1, GLOBAL32)
1_49: CALL(OBJECT2, 123, 0, 50, 2.25, 0, 1)
1_74: ADD32(GLOBAL8, 1, GLOBAL8)
// Checking OBJECT2 // CALL(OBJECT2, 123, 0, 50, 2.25, 0, 1)
// Found Move.Rotations() in OBJECT2
This code calls OBJECT2.
The code of OBJECT2 is matching the following pattern:
- "MULF" with value of "360" at the first position.
- followed by a "PORT_CNV_OUTPUT"
- followed by a "OUTPUT_STEP_SYNC"
... that is matching the pattern of Move.Rotations()
.
The list of parameters: 123, 0, 50, 2.25, 0, 1
reprsenting the block parameters of "Ports, Steering, Speed, Rotations, <skip>, Brake_At_End".