This document describes the file layout and record format for the phz
filetype used by the Phrozen Sonic Mini 3D printer.
The phz
format is closely related to the ctb
format, which I have
previously described. Please study that format first. This document will focus
on the deviations.
phz
contains an evolved version of the file header from ctb
/cbddlp
. Some
of the oddities and historical details have been cleaned up, and the information
has been collapsed into a single record.
The fields play exactly the same roles as in ctb
, just rearranged. As such,
I’m not going to discuss them in detail beyond their layout.
The newly unified file header record is 0xD8 / 216 bytes in length, with several areas zeroed, either encoding yet-unobserved features, or reserved for expansion.
x0 |
x4 |
x8 |
xC |
||
0x |
|
|
|
|
|
1x |
|
|
|
|
|
2x |
|
|
|
|
|
3x |
|
|
|
|
|
4x |
zero |
|
|
||
5x |
|
|
|
|
|
6x |
|
|
zero |
|
|
7x |
|
|
|
|
|
8x |
|
|
|
zero |
|
9x |
|
|
zero |
||
Ax |
zero |
||||
Bx |
|
|
|
|
|
Cx |
zero |
||||
Dx |
zero |
(start of next record) |
Note
|
encryption_mode seems to be reliably set to 0x1c for the Phrozen Sonic
Mini. Changing it makes files unreadable.
|
The encoding of the preview images and layer table are identical to those in
ctb
.
Layers are encoded as 7bpp images using yet another RLE scheme. This scheme,
which is called RLE7a
in the Catibo sources because it’s our second 7-bit
scheme, works as follows:
-
A byte with MSB set represents a pixel in bits 6:0.
-
A byte with MSB clear represents a repeat count for the last generated pixel.
Thus, 0x80
encodes a single zero pixel, and 0x80 0x7f
encodes 128 zero
pixels.
Quirks observed in the wild:
-
The repeat count generated by proprietary software never exceeds
0x7d
, so you can recognize this encoding by looking for long runs of0x7d
. It’s not clear whether0x7e
/0x7f
are somehow reserved, or if this is an off-by-one error in the encoder, but Catibo currently mimics this behavior. -
Unlike the other RLE schemes, the encoder for "7a" takes care to terminate runs at each half scanline — so for a 1080-pixel wide scanline, it will generate two runs of 540, neither of which overlaps the ends of the lines. It’s not clear whether this behavior is necessary, but Catibo currently mimics it. (If someone with access to an actual Phrozen Sonic Mini could try it and report back, I’d appreciate it. It inflates file sizes by 10+%.)
Like ctb
, the phz
format encrypts layer data for some goddamn reason. Which
is why I took a look at the format, despite not having a compatible printer.
Fortunately for owners of the Sonic Mini, the encryption is straightforward.
It’s another 32-bit block XOR cipher. See the ctb
doc for details and ranting.
I’m referring to this cipher as the "9f Cipher," named after the initial byte of files that use it.
Here’s the concise version, showing just which parameters vary. The keystream
X[n]
is produced by:
c = (key % 0x4324) * 0x34a3_2231 X[0] = (iv ^ 0x3fad_2212) * (key % 0x4324) * 0x4910_913d X[n + 1] = X[n] + c
(Where +
and *
are addition and multiplication mod 232, respectively, and
%
is unsigned remainder.)
Each block of data is simply XOR’d with the keystream, as in ctb
.
A key of 0 causes the desktop software to accept unencrypted files. This may or
may not have been deliberate: there is a class of weak keys for this cipher that
includes any number that is 0 mod 0x4324
; all these keys result in a zero
keystream, disabling encryption.
(However, note that ctb
seems to have deliberately skipped encryption or keys
of 0, so it might be deliberate here too.)
Tip
|
The proprietary desktop software does not appear to check for these weak keys when selecting its output keys, so it produces unencrypted output with a probability of 5.818e-5. |
I kept commentary to a minimum in the cbddlp
doc, but this is the third
variation on the file format I’ve analyzed, and I have thoughts.
On the new unified header format: This is a great improvement. It really cleans things up.
On the new RLE scheme: In practice, layer images will tend to include long
runs of fully empty and fully solid pixels. In cases like that, this scheme
achieves strictly worse compression than RLE7
. Using catibo-convert
to
recompress a sample phz
file as ctb
produces an output file 0.1x the size. I
have to assume that the RLE scheme was dictated by whatever image processor
Phrozen used to drive their display — otherwise I can’t imagine why this would
be chosen over their previous RLE7
scheme.
On the new cipher: Don’t get me started.