reverse engineering EOS laser scan file production
· research
I've been wanting to drive the laser on our lab's EOS M280 myself. we are kinda blocked by not having a magics subscription or anything, but all i want to do is scan the laser in 2d. the problem is that everything about how EOS gets vectors to the galvos is stuck in a proprietary file format. so i have to figure out how the machine gets its programmed data.
my test file to work from was a "quali job," one of the little test jobs EOS ships that uses the laser to engrave text onto a beam-dump plate. it engraves the machine name, material, operator, date, etc. two files come from it, a .eosjz and a .eosjob. naturally the first thing I did was open them as plain text, and of course I got a screen full of garbage. id be a little concerned if there was anything meaningful in there though.
the first break was pretty simple, the .eosjz isn't a crazy binary or anything like i thought. instead its basically just a relabeled ZIP archive. if you unzip it you get a pile of .sli files (one per text field, plus the build-plate outline and a laser fine-tuning pattern) and the single .eosjob. all the actual geometry is inside those SLIs, and they are completely unencrypted. i think thats basically everything inside those.
each SLI opens with the string "EOS 1993 SLI FILE" and a fixed-size header. looking at it per byte the main things that popped out were a scale factor, a bounding box in millimeters, and a group count. coordinates in the body were stored as unsigned 16-bit integers, and the real position in millimeters was just
\[ x_{mm} = v_{u16} \cdot s \]where s is that scale factor. Small labels use a fine scale, big ones use a coarse scale, so each field gets the best resolution its bounding box will allow. after the header comes a flat stream of polyline records, a type tag then a point count then that many (x, y) pairs, grouped into "parts" by a couple of marker bytes.
getting the parser to agree with the file took a few tries. my first attempt scanned for the marker bytes and kept desyncing, because the same byte values show up inside coordinate data by pure chance. funny enough, a sloppier version that just hunted for anything shaped like a valid polyline worked perfectly, because a real record is specific enough that random coordinate bytes basically never look like one. once it parsed, I threw the points at matplotlib, and it actually worked! It spelled out "Date:", "Operator:", "Powder Batch:", everything showed up as what and where it needed to be.
dropping everything back onto a 250 mm plate reconstructed the entire job. the fine-tuning beam-quality pattern in the center, ringed by the metadata fields, exactly as it would scan onto the plate.
that just left the .eosjob, and this is where I hit a bit of a wall. Its body is 5568 bytes, which is exactly 348 blocks of 16, which strongly suggests a block cipher. every byte value shows up with a flat, uniform distribution and no 16-byte block ever repeats. that has to be a block cipher, almost certainly AES, and in my opinion definitely not compression (I tested for compression of every kind really possible and it failed). without the EOS specific key it isnt really coming back. however, it may not have to. the descriptor only really holds job metadata and process parameters, so none of the geometry is in there. so he part I think i actually needed was never encrypted in the first place.
so basically, I can now read EOS scan paths, and I can write them. I've got a small reader and a writer that can go back and forth to one another pretty cleanly. unfortunately I dont think i can forge the encrypted descriptor, which means whether a custom job will actually run will probably come down to what the machine is willing to read. If it imports raw SLIs and builds the runnable job itself, it is fine. If it demands a valid .eosjob, the encryption wall is real and i am kinda screwed unless I decompile the PSW software and extract the encryption key which is a bit of a legal grey area. thats next to test though. we will see!!!