Newer printers take a second CTB layout with the magic number 0x12FD0107
(07 01 FD 12), still with the extension .ctb. Its settings
are encrypted with AES, and the file ends with an encrypted signature that printers
check. The key and initialisation vector are not published by CBD-Tech; they were
recovered by reverse engineering and are built into UVtools
(CTBEncryptedFile.cs), which this page follows. They are not reproduced
here. All numbers are little-endian.
Order of blocks
- File header (48 bytes)
- Settings, 288 bytes, AES-256 in CBC mode without padding
- Large and small previews, as in the older layout
- Machine name, copyright notice (320 bytes), then resin parameters (version 5)
- Layer pointer table
- For each layer: an 88-byte layer record, then its image data
- In version 5, 8 bytes of unknown purpose
- Signature, 32 bytes
File header
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | uint32 | Magic | 0x12FD0107 |
| 4 | 4 | uint32 | SettingsSize | 288 |
| 8 | 4 | uint32 | SettingsOffset | 48 |
| 12 | 4 | uint32 | Unknown | 0 |
| 16 | 4 | uint32 | Version | 4 or 5 |
| 20 | 4 | uint32 | SignatureSize | 32 |
| 24 | 4 | uint32 | SignatureOffset | Offset of the signature at the end |
| 28 | 4 | uint32 | Unknown | 0 |
| 32 | 2 | uint16 | Unknown | 1 |
| 34 | 2 | uint16 | Unknown | 1 |
| 36 | 4 | uint32 | Unknown | 0 |
| 40 | 4 | uint32 | Unknown | 42 (0x2A) |
| 44 | 4 | uint32 | Unknown | 0 or 1 |
Settings (after decryption)
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 8 | uint64 | ChecksumValue | Value whose hash forms the signature (UVtools writes 0xCAFEBABE) |
| 8 | 4 | uint32 | LayerPointersOffset | Offset of the layer pointer table |
| 12 | 4 | float | DisplayWidth | Screen width, mm |
| 16 | 4 | float | DisplayHeight | Screen height, mm |
| 20 | 4 | float | MachineZ | Build height, mm |
| 24 | 8 | uint32 × 2 | Unknown | |
| 32 | 4 | float | TotalHeight | Model height, mm |
| 36 | 4 | float | LayerHeight | mm |
| 40 | 4 | float | ExposureTime | Normal layers, s |
| 44 | 4 | float | BottomExposureTime | Bottom layers, s |
| 48 | 4 | float | LightOffDelay | s |
| 52 | 4 | uint32 | BottomLayerCount | |
| 56 | 4 | uint32 | ResolutionX | pixels |
| 60 | 4 | uint32 | ResolutionY | pixels |
| 64 | 4 | uint32 | LayerCount | |
| 68 | 4 | uint32 | LargePreviewOffset | |
| 72 | 4 | uint32 | SmallPreviewOffset | |
| 76 | 4 | uint32 | PrintTime | s |
| 80 | 4 | uint32 | ProjectorType | 0 normal, 1 mirrored |
| 84 | 4 | float | BottomLiftHeight | mm |
| 88 | 4 | float | BottomLiftSpeed | mm/min |
| 92 | 4 | float | LiftHeight | mm |
| 96 | 4 | float | LiftSpeed | mm/min |
| 100 | 4 | float | RetractSpeed | mm/min |
| 104 | 4 | float | MaterialMilliliters | ml |
| 108 | 4 | float | MaterialGrams | g |
| 112 | 4 | float | MaterialCost | |
| 116 | 4 | float | BottomLightOffDelay | s |
| 120 | 4 | uint32 | Unknown | 1 |
| 124 | 2 | uint16 | LightPWM | 0 to 255 |
| 126 | 2 | uint16 | BottomLightPWM | 0 to 255 |
| 128 | 4 | uint32 | LayerXorKey | Seed for the layer XOR, as in the older layout; 0 for none |
| 132 | 4 | float | BottomLiftHeight2 | mm |
| 136 | 4 | float | BottomLiftSpeed2 | mm/min |
| 140 | 4 | float | LiftHeight2 | mm |
| 144 | 4 | float | LiftSpeed2 | mm/min |
| 148 | 4 | float | RetractHeight2 | mm |
| 152 | 4 | float | RetractSpeed2 | mm/min |
| 156 | 4 | float | RestTimeAfterLift | s |
| 160 | 4 | uint32 | MachineNameOffset | |
| 164 | 4 | uint32 | MachineNameSize | |
| 168 | 1 | uint8 | AntiAliasFlag | 7 without, 15 with antialiasing |
| 169 | 2 | uint16 | Padding | |
| 171 | 1 | uint8 | PerLayerSettings | 0 not allowed, 0x40 to 0x50 allowed |
| 172 | 4 | uint32 | ModifiedMinutes | Minutes since 1970 UTC |
| 176 | 4 | uint32 | AntiAliasLevel | 1 or 8 |
| 180 | 4 | float | RestTimeAfterRetract | s |
| 184 | 4 | float | RestTimeAfterLift2 | s |
| 188 | 4 | uint32 | TransitionLayerCount | |
| 192 | 4 | float | BottomRetractSpeed | mm/min |
| 196 | 4 | float | BottomRetractSpeed2 | mm/min |
| 200 | 16 | mixed | Unknown | Padding, 4.0, padding, 4.0 (as in version 4 parameters) |
| 216 | 4 | float | RestTimeAfterRetract2 | s |
| 220 | 4 | float | RestTimeAfterLift3 | s |
| 224 | 4 | float | RestTimeBeforeLift | s |
| 228 | 4 | float | BottomRetractHeight2 | mm |
| 232 | 12 | uint32 × 3 | Unknown | The last usually 4 |
| 244 | 4 | uint32 | LastLayerIndex | LayerCount − 1 |
| 248 | 16 | uint32 × 4 | Padding | |
| 264 | 4 | uint32 | DisclaimerOffset | |
| 268 | 4 | uint32 | DisclaimerSize | 320 |
| 272 | 4 | uint32 | Padding | |
| 276 | 4 | uint32 | ResinParametersOffset | Resin parameters, laid out as in the older layout |
| 280 | 8 | uint32 × 2 | Padding |
Layer pointers (16 bytes each)
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | uint32 | LayerOffset | Offset of the layer record within its 4 GiB page |
| 4 | 4 | uint32 | PageNumber | Page; real offset is PageNumber × 232 + LayerOffset |
| 8 | 4 | uint32 | LayerTableSize | 88 |
| 12 | 4 | uint32 | Padding | 0 |
Layer record (88 bytes)
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | uint32 | TableSize | 88 |
| 4 | 4 | float | PositionZ | mm |
| 8 | 4 | float | ExposureTime | s |
| 12 | 4 | float | LightOffDelay | s |
| 16 | 4 | uint32 | LayerDataOffset | Offset of the image data |
| 20 | 4 | uint32 | PageNumber | Its 4 GiB page |
| 24 | 4 | uint32 | DataLength | Length of the image data |
| 28 | 4 | uint32 | Unknown | |
| 32 | 4 | uint32 | EncryptedDataOffset | Start, within the image data, of a part that is also AES-encrypted |
| 36 | 4 | uint32 | EncryptedDataLength | Length of that part; 0 if none |
| 40 | 4 | float | LiftHeight | mm |
| 44 | 4 | float | LiftSpeed | mm/min |
| 48 | 4 | float | LiftHeight2 | mm |
| 52 | 4 | float | LiftSpeed2 | mm/min |
| 56 | 4 | float | RetractSpeed | mm/min |
| 60 | 4 | float | RetractHeight2 | mm |
| 64 | 4 | float | RetractSpeed2 | mm/min |
| 68 | 4 | float | RestTimeBeforeLift | s |
| 72 | 4 | float | RestTimeAfterLift | s |
| 76 | 4 | float | RestTimeAfterRetract | s |
| 80 | 4 | float | LightPWM | 0 to 255 |
| 84 | 4 | uint32 | Unknown |
The image data uses the CTB 7-bit run-length code
(Image encoding). To read it, first
AES-decrypt the part given by EncryptedDataOffset and
EncryptedDataLength, if any, then undo the XOR with
LayerXorKey and the layer number. Decoded black runs may run past the
end of the image as padding.
Signature
The last 32 bytes, at SignatureOffset: the SHA-256 hash of the
settings’ 8-byte ChecksumValue, encrypted with the same AES key and
initialisation vector. A reader can check it by recomputing the hash.