0.0.5 • Published 7 months ago

ev3rbf-decompiler v0.0.5

Weekly downloads
-
License
MIT
Repository
github
Last release
7 months ago

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:

  1. "MULF" with value of "360" at the first position.
  2. followed by a "PORT_CNV_OUTPUT"
  3. 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".

0.0.5

7 months ago

0.0.3

7 months ago

0.0.2

7 months ago

0.0.1

7 months ago