grid-apps-cmms/docs/refs/MSLA_FORMAT_REFERENCE.md
2026-07-26 13:52:55 -04:00

26 KiB

mSLA File Format Reference

Generated: 2026-05-16 Total Formats: 32


Format Capability Matrix

Format Encode Decode Extensions Versions Profiles RLE Type Notes
Chitubox Family
ChituboxFile photon, cbddlp, ctb, gktwo.ctb 1-5 (def: 5) No RLE125 Most common
CTBEncryptedFile ctb, encrypted.ctb 4-5 (def: 5) No RLE125+AES Encrypted
PHZFile phz 2 No RLE125 Chitubox PHZ
ChituboxZipFile zip - No PNG G-code based
AnycubicPhotonSFile photons - No RLE128 Legacy Photon S
Anycubic Family - Legacy (v1, v515-516)
AnycubicFile (PWS) pws, pwx 1 Yes (22) RLE1 (125) PhotonS, PhotonX
AnycubicFile (PW0) pw0 1 Yes (22) Nibble RLE4 PhotonZero
Anycubic Family - Modern (v515-518)
AnycubicFile (v515) pwmx, pwmo, dl2p, pwmb, pmx2 515-517 Yes (22) Nibble RLE4 Mono series
AnycubicFile (v517) pm3n, pm5, px6s 517 Yes (22) Nibble RLE4 Mono 2, X6Ks, M5
AnycubicFile (v518) pm5s, m5sp 518 Yes (22) Nibble RLE4 Mono M5s/Pro
AnycubicFile (all) dlp, pwms, pwma, pmsq, pm3, pm3m, pm3r, pwc varies Yes (22) Nibble RLE4 Additional models
AnycubicZipFile pm4u, pm7, pm7m, pwsz, pp1, pp1m - No PNG ZIP with JSON
Creality Family
CrealityCXDLPv1File v1.cxdlp 1 No RLE Legacy
CrealityCXDLPFile cxdlp 2-3 (def: 3) Yes RLE HALOT series
CrealityCXDLPv4File cxdlpv4 4 Yes RLE Current
Other Binary Formats
AnetFile n4, n7 3 Yes (N4/N7) RLE Anet printers
FDGFile fdg 2 No RLE Voxelab
GooFile goo, prz - No RLE+delim Elegoo/Phrozen
LGSFile lgs, lgs30, lgs120, lgs4k - Yes Struct Longer Orange
MDLPFile mdlp 1 No Vector Makerbase MKS
GR1File gr1 - No Vector GR Workshop
OSFFile osf 4 (def) No Compressed Vlare
OSLAFile osla - No Varies Open SLA
Archive Formats
SL1File sl1, sl1s - Yes PNG Prusa (INI)
UVJFile uvj - No PNG Vendor-neutral
VDAFile zip - No Images Voxeldance Additive
VDTFile vdt 1 No PNG Voxeldance Tango
NanoDLPFile nanodlp, rgb.nanodlp 1-2 No Images NanoDLP
KlipperFile zip, rgb.zip - No PNG Klipper firmware
ZCodeFile zcode - No Images UnizMaker IBEE
ZCodexFile zcodex - Yes Images Zortrax
CWSFile cws, rgb.cws, xml.cws - No PNG NovaMaker/CW
JXSFile jxs - No Images Uniformation
GenericZIPFile zip - No Varies Fallback
Text/Vector Formats
FlashForgeSVGXFile svgx - No SVG FlashForge
QDTFile qdt - No Text Emake3D Galaxy
Import Only
ImageFile png, jpg, jpeg, jp2, tga, tif, tiff, bmp, pbm - No N/A Image import

Total Extensions: 60+ Total Printer Profiles: 7 formats with profiles (30+ printer models)


Table of Contents - Corrected Binary Formats

1. Chitubox Family

1.1 CBDDLP (Chitubox v1-2)

Extensions: .cbddlp, .photon Magic: 0x12FD0019 (LE) Versions: 1, 2 RLE: RLE125 (limit: 125 pixels)

Header Structure (CORRECTED - 32-bit floats):

Offset | Size | Type   | Field                    | Notes
-------|------|--------|--------------------------|------------------
0x00   | 4    | uint32 | Magic                    | 0x12FD0019 (LE)
0x04   | 4    | uint32 | Version                  | 1 or 2
0x08   | 4    | float  | BedSizeX                 | mm (32-bit)
0x0C   | 4    | float  | BedSizeY                 | mm (32-bit)
0x10   | 4    | float  | BedSizeZ                 | mm (32-bit)
0x14   | 4    | uint32 | Unknown1                 |
0x18   | 4    | uint32 | Unknown2                 |
0x1C   | 4    | float  | TotalHeightMillimeter    | mm (32-bit)
0x20   | 4    | float  | LayerHeightMillimeter    | mm (32-bit)
0x24   | 4    | float  | LayerExposureSeconds     | seconds (32-bit)
0x28   | 4    | float  | BottomExposureSeconds    | seconds (32-bit)
0x2C   | 4    | float  | LightOffDelay            | seconds (32-bit)
0x30   | 4    | uint32 | BottomLayersCount        |
0x34   | 4    | uint32 | ResolutionX              | pixels
0x38   | 4    | uint32 | ResolutionY              | pixels
0x3C   | 4    | uint32 | PreviewLargeOffsetAddress |
0x40   | 4    | uint32 | LayersDefinitionOffsetAddress |
0x44   | 4    | uint32 | LayerCount               |
0x48   | 4    | uint32 | PreviewSmallOffsetAddress |
0x4C   | 4    | uint32 | PrintTime                | seconds
0x50   | 4    | uint32 | ProjectorType            | 0=Normal
0x54   | 4    | uint32 | PrintParametersOffsetAddress | (v2+)
0x58   | 4    | uint32 | PrintParametersSize      | bytes (v2+)
0x5C   | 4    | uint32 | AntiAliasLevel           | 1 (v2+)
0x60   | 2    | uint16 | LightPWM                 | 0-255 (v2+)
0x62   | 2    | uint16 | BottomLightPWM           | 0-255 (v2+)

Header Size: 0x64 (100 bytes) for v2

1.2 CTB (Chitubox v3)

Extensions: .ctb Magic: 0x12FD0086 (LE) Version: 3 RLE: RLE125 (limit: 125 pixels)

Header Structure (CORRECTED - matches Chitubox 1.8 fixtures):

Offset | Size | Type   | Field                    | Notes
-------|------|--------|--------------------------|------------------
0x00   | 4    | uint32 | Magic                    | 0x12FD0086 (LE)
0x04   | 4    | uint32 | Version                  | 3
0x08   | 4    | float  | BedSizeX                 | mm (32-bit float)
0x0C   | 4    | float  | BedSizeY                 | mm (32-bit float)
0x10   | 4    | float  | BedSizeZ                 | mm (32-bit float)
0x14   | 4    | uint32 | Unknown1                 |
0x18   | 4    | uint32 | Unknown2                 |
0x1C   | 4    | float  | TotalHeightMillimeter    | mm (32-bit float)
0x20   | 4    | float  | LayerHeightMillimeter    | mm (32-bit float)
0x24   | 4    | float  | LayerExposureSeconds     | seconds (32-bit float)
0x28   | 4    | float  | BottomExposureSeconds    | seconds (32-bit float)
0x2C   | 4    | float  | LightOffDelay            | seconds (32-bit float)
0x30   | 4    | uint32 | BottomLayersCount        |
0x34   | 4    | uint32 | ResolutionX              | pixels
0x38   | 4    | uint32 | ResolutionY              | pixels
0x3C   | 4    | uint32 | PreviewLargeOffsetAddress |
0x40   | 4    | uint32 | LayersDefinitionOffsetAddress |
0x44   | 4    | uint32 | LayerCount               |
0x48   | 4    | uint32 | PreviewSmallOffsetAddress |
0x4C   | 4    | uint32 | PrintTime                | seconds
0x50   | 4    | uint32 | ProjectorType            | 0=Normal
0x54   | 4    | uint32 | PrintParametersOffsetAddress |
0x58   | 4    | uint32 | PrintParametersSize      | bytes
0x5C   | 4    | uint32 | AntiAliasLevel           | 1, 2, 4, 8
0x60   | 2    | uint16 | LightPWM                 | 0-255
0x62   | 2    | uint16 | BottomLightPWM           | 0-255
0x64   | 4    | uint32 | Padding                  |
0x68   | 4    | uint32 | SlicerOffsetAddress      |
0x6C   | 4    | uint32 | SlicerDataSize           | bytes

Header Size: 0x70 (112 bytes)

CRITICAL NOTE: Previous documentation incorrectly showed 8-byte (double) floats. CTB v3 uses 32-bit floats throughout. This matches Chitubox 1.8 fixtures and actual UVtools implementation.

1.3 CTB v4-5 (Chitubox v4-5)

Extensions: .ctb, .gktwo.ctb Magic: 0x12FD0106 (LE, v4), 0xFF220810 (LE, GKtwo variant) Versions: 4, 5 (default: 5) RLE: RLE125

Same header structure as CTB v3, with additional support for:

  • Per-layer exposure times
  • Per-layer lift heights/speeds
  • PageNumber field in layer table
  • Transition layers

2. Anycubic Family

2.1 Legacy Anycubic Formats (v1)

2.1.1 PWS (Photon / Photon S)

Extensions: .pws, .pwx Version: 1 RLE: RLE1 (limit: 125) Machines: PhotonS, PhotonX

RLE1 Encoding (PWS):

For each run (2 bytes):
  byte[0] = color (0x00 = black, 0xFF = white)
  byte[1] = length (1-125)

Constant: RLE1EncodingLimit = 0x7D (125)
2.1.2 PW0 (Photon Zero) - CORRECTED

Extensions: .pw0 Version: 1 RLE: Nibble-coded RLE4 (limit: 4095) Machine: PhotonZero

Nibble-Coded RLE4 Encoding (CORRECTED from UVtools implementation):

The PW0 format uses a nibble-coded variable-length RLE, NOT a simple 3-byte RLE as previously documented.

Encoding Rules:

Each byte is split: [4-bit code] [4-bit repeat]

Code interpretation:
  0x0: Black (0x00) - Extended format (2 bytes total)
       Format: [0x0R] [RR]
       Repeat = (R << 8) | RR (12-bit value, max 4095)

  0xF: White (0xFF) - Extended format (2 bytes total)
       Format: [0xFR] [RR]
       Repeat = (R << 8) | RR (12-bit value, max 4095)

  0x1-0xE: Grayscale - Single byte format
       Color = (code << 4) | code
       Repeat = 4-bit repeat value (1-15)
       Examples:
         0x17 = color 0x11, repeat 7
         0x82 = color 0x88, repeat 2

Decode Algorithm (from UVtools):

for (int i = 0; i < encodedRle.Length; i++)
{
    byte b = encodedRle[i];
    int code = b >> 4;        // Upper nibble
    int repeat = b & 0xf;     // Lower nibble
    byte color;

    switch (code)
    {
        case 0x0:  // Black (extended)
            color = 0;
            i++;
            repeat = (repeat << 8) + encodedRle[i];
            break;

        case 0xf:  // White (extended)
            color = 255;
            i++;
            repeat = (repeat << 8) + encodedRle[i];
            break;

        default:   // Grayscale (single byte)
            color = (byte)((code << 4) | code);
            break;
    }

    // Fill repeat pixels with color
}

Encode Algorithm (from UVtools):

for each pixel:
    int color = pixel >> 4;  // Extract upper nibble

    if (color == 0 or color == 0xf):  // Black or white
        if (repeat > 4095):
            repeat = 4095

        // Encode as 2 bytes: [cR] [RR]
        ushort more = (ushort)(repeat | (color << 12));
        output.Add((byte)(more >> 8));
        output.Add((byte)more);
    else:  // Grayscale
        if (repeat > 15):
            repeat = 15

        // Encode as 1 byte: [cR]
        output.Add((byte)(repeat | color << 4));

Constants:

RLE4EncodingLimit = 0xFFF = 4095

Example Encodings:

White run of 1000 pixels:
  Binary: 1111 0011 1110 1000
  Hex: F3 E8
  Decode: code=0xF, repeat=3, next byte=0xE8
         repeat = (3 << 8) + 232 = 1000
         color = 255

Black run of 500 pixels:
  Binary: 0000 0001 1111 0100
  Hex: 01 F4
  Decode: code=0x0, repeat=1, next byte=0xF4
         repeat = (1 << 8) + 244 = 500
         color = 0

Gray (0x77) run of 10 pixels:
  Binary: 0111 1010
  Hex: 7A
  Decode: code=0x7, repeat=0xA (10)
         color = (7 << 4) | 7 = 0x77 = 119

Gray (0xAA) run of 5 pixels:
  Binary: 1010 0101
  Hex: A5
  Decode: code=0xA, repeat=0x5 (5)
         color = (10 << 4) | 10 = 0xAA = 170

Key Differences from Previous Documentation:

  • NOT a fixed 3-byte format
  • Variable length: 1 byte for grayscale (repeat ≤15), 2 bytes for black/white
  • Nibble-based encoding optimizes for common cases
  • Color is encoded in upper nibble, duplicated to both nibbles for final value
  • Maximum repeat: 4095 for black/white, 15 for grayscale

2.2 Modern Anycubic Formats (v515-518)

All modern Anycubic formats use Nibble-coded RLE4 encoding.

2.2.1 Version 515-516 Formats

Extensions: .pwmx, .pwmo, .dl2p, .pwmb, .pmx2, .dlp, .pwms, .pwma, .pmsq, .pm3, .pm3m, .pm3r

Machines:

  • PhotonMono, PhotonMonoX, PhotonMonoX2
  • PhotonMono4K, PhotonMonoSE, PhotonMonoSQ
  • PhotonMonoX6K, PhotonM3, PhotonM3Plus, PhotonM3Max, PhotonM3Premium
  • PhotonUltra, PhotonD2

Features (v515+):

  • Nibble-coded RLE4 encoding
  • Enhanced print parameters
  • Per-layer settings support

Features (v516+):

  • Dual lift/retract speeds
  • Advanced motion profiles
2.2.2 Version 517 Formats

Extensions: .pm3n, .pm5, .px6s

Machines:

  • PhotonMono2 (.pm3n)
  • PhotonMonoM5 (.pm5)
  • PhotonMonoX6Ks (.px6s)

New Features:

  • 7 preview images (vs 2 in v516)
  • Preview sizes: 224x168, 330x190
  • Layer table entry size: 92 bytes
2.2.3 Version 518 Formats

Extensions: .pm5s, .m5sp, .pwc

Machines:

  • PhotonMonoM5s (.pm5s)
  • PhotonMonoM5sPro (.m5sp)
  • AnycubicCustomMachine (.pwc)

New Features:

  • 9 preview images
  • 11 distinct printer profiles
  • Preview sizes: 224x168, 330x190
  • Layer table entry size: 96 bytes
  • 15 slicer metadata fields

Anycubic File Header (v515+):

Offset | Size | Type   | Field                    | Notes
-------|------|--------|--------------------------|------------------
0x00   | 2    | uint16 | HeaderSize               | bytes
0x02   | 2    | uint16 | Version                  | 515-518
0x04   | 4    | float  | BedSizeX                 | mm
0x08   | 4    | float  | BedSizeY                 | mm
0x0C   | 4    | float  | BedSizeZ                 | mm
...
(remainder similar to v1, with version-specific extensions)

Machine Detection (Extension → Machine mapping):

.pm3n  → PhotonMono2
.pm4n  → PhotonMono4
.pm5   → PhotonMonoM5
.pm5s  → PhotonMonoM5s
.m5sp  → PhotonMonoM5sPro
.px6s  → PhotonMonoX6Ks
.pwms  → PhotonMonoSE
.pwma  → PhotonMono4K
.pwmx  → PhotonMonoX
.pwmo  → PhotonMono
.pwmb  → PhotonMonoX6K / PhotonM3Plus

2.3 Anycubic ZIP Format (PWSZ)

Extensions: .pm4u, .pm7, .pm7m, .pwsz, .pp1, .pp1m Type: ZIP archive with JSON manifest Layer Format: PNG images in staged directories

Structure:

  • manifest - JSON with version, machine_type, machine_extern
  • bott_0/*.png, bott_1/*.png - Bottom stage layers
  • normal_0/*.png, normal_1/*.png - Normal stage layers

RLE Encoding Comparison

Format Name Bytes/Run Max Repeat Color Bits Notes
RLE1 (PWS) Run-Length 1 2 (fixed) 125 8 Color, Length
RLE4 (PW0) Nibble-coded 1-2 (variable) 4095 (B/W), 15 (gray) 8 Optimized variable-length
RLE125 (CTB) Run-Length 125 2 (fixed) 125 8 Color, Length
RLE128 (PhotonS) Run-Length 128 2 (fixed) 128 8 Length, Color (BE)
RLE+Delim (GOO) Delimited 5 (+ delim) 65535 8 0x55 + Color + Length16 + 0x0D0A

Performance Comparison:

  • RLE1/RLE125/RLE128: Simple, predictable, easy to implement
  • Nibble RLE4: Best compression for grayscale AA, complex encoding
  • RLE+Delim: Highest max repeat, overhead from delimiters

Additional Binary Format Details

Photon S (Anycubic Legacy)

Extension: .photons Magic: TAG1=2, TAG2=49 (both big-endian) RLE: RLE128 (limit: 128, big-endian)

Header (Big-Endian):

Offset | Size | Type   | Field                    | Endianness
-------|------|--------|--------------------------|------------
0x00   | 4    | uint32 | TAG1                     | 2 (BE)
0x04   | 4    | uint32 | TAG2                     | 49 (BE)
0x08   | 8    | double | XYPixelSize              | mm (BE)
0x10   | 4    | float  | LayerHeight              | mm (BE)
0x14   | 4    | float  | ExposureTime             | seconds (BE)
0x18   | 4    | float  | ExposureTimeBottom       | seconds (BE)
...

RLE128 Encoding (Big-Endian):

For each run (2 bytes):
  byte[0] = length (1-128, BE)
  byte[1] = color (0x00 or 0xFF)

GOO (Elegoo)

Extensions: .goo, .prz Magic: "V3.0" + 0x07000000 (BE) + "DLP\0" RLE: Delimiter-based

RLE Format:

Each run (5 bytes):
  0x55                    Magic byte
  [color]                 0x00 or 0xFF
  [length_high]           Big-endian uint16
  [length_low]
  0x0D 0x0A               CRLF delimiter

Maximum repeat: 65535 pixels

CXDLP (Creality)

Extension: .cxdlp Magic: "CXSW3DV2" (9 bytes, BE) Versions: 2, 3, 4 RLE: Standard RLE

Header (v3):

Offset | Size | Type   | Field                    | Endianness
-------|------|--------|--------------------------|------------
0x00   | 4    | uint32 | MagicSize                | 9 (BE)
0x04   | 9    | char[] | MagicName                | "CXSW3DV2" (BE)
0x0D   | 4    | uint32 | Version                  | 3 (BE)
0x11   | 4    | uint32 | PrinterModel             | offset (BE)
0x15   | 4    | uint32 | ResolutionX              | pixels (BE)
0x19   | 4    | uint32 | ResolutionY              | pixels (BE)
0x1D   | 256  | uint32[]| LayerOffsets[64]         | offsets (BE)
...

Archive Format Structures

SL1 (Prusa)

Extensions: .sl1, .sl1s Type: ZIP with INI config Layer Format: PNG images

Required Files:

  • config.ini - Print parameters
  • prusaslicer.ini - Slicer settings (optional)
  • 00000.png, 00001.png, ... - Layers (5-digit zero-padded)

config.ini Sample:

[general]
expTime = 8.0
expTimeFirst = 35.0
layerHeight = 0.05
numFade = 10
numSlow = 5
printTime = 3600

[printer]
PrinterModel = SL1

UVJ (Vendor-Neutral)

Extension: .uvj Type: ZIP with JSON config Layer Format: PNG images

Structure:

  • config.json - Print configuration
  • slice/0.png, slice/1.png, ... - Layer images
  • preview/huge.png - Large preview
  • preview/tiny.png - Small preview

config.json Schema:

{
  "size": {
    "x": 2560,
    "y": 1620,
    "layerHeight": 0.05,
    "millimeter": {"x": 192.0, "y": 120.0}
  },
  "exposure": {
    "lightOnTime": 8.0,
    "lightOffTime": 1.0,
    "lightPWM": 255,
    "liftHeight": 5.0,
    "liftSpeed": 100.0,
    "retractSpeed": 150.0
  },
  "bottom": {
    "count": 5,
    "lightOnTime": 35.0,
    "lightPWM": 255,
    ...
  },
  "layers": [
    {"z": 0.05},
    {"z": 0.10},
    ...
  ]
}

Implementation Guidance for Sandboxed Environments

Tier 1 - Simplest (Start Here):

  1. ImageFile - Single image, no container
  2. UVJ - JSON + PNG (if JSON/ZIP available)
  3. SL1 - INI + PNG (if INI/ZIP available)

Tier 2 - Binary Formats:

  1. CBDDLP v1 - Simplest binary RLE
  2. CTB v3 - Modern standard, widely used
  3. PWS - Simple RLE1 encoding

Tier 3 - Advanced Binary:

  1. PW0 - Nibble-coded RLE4 (complex but efficient)
  2. PhotonS - Big-endian RLE128
  3. GOO - Delimiter-based RLE

Tier 4 - Specialized:

  1. CTBEncrypted - Requires AES-256-CBC
  2. AnycubicFile v517+ - Modern features, complex header
  3. CXDLP - Big-endian offsets

Essential Algorithms (Pseudocode)

RLE125 Decoder (CBDDLP, CTB)

def decode_rle125(data: bytes, width: int, height: int) -> bytes:
    pixels = bytearray(width * height)
    pixel_index = 0
    data_index = 0

    while data_index < len(data):
        color = data[data_index]
        length = data[data_index + 1]
        data_index += 2

        for _ in range(length):
            pixels[pixel_index] = color
            pixel_index += 1

    return pixels

RLE125 Encoder

def encode_rle125(pixels: bytes) -> bytes:
    result = bytearray()
    i = 0

    while i < len(pixels):
        color = pixels[i]
        length = 1

        while (i + length < len(pixels) and
               pixels[i + length] == color and
               length < 125):
            length += 1

        result.append(color)
        result.append(length)
        i += length

    return result

Nibble-Coded RLE4 Decoder (PW0)

def decode_rle4_nibble(data: bytes, width: int, height: int) -> bytes:
    pixels = bytearray(width * height)
    pixel_pos = 0
    i = 0

    while i < len(data):
        b = data[i]
        code = b >> 4        # Upper nibble
        repeat = b & 0x0F    # Lower nibble

        if code == 0x0:      # Black (extended)
            color = 0
            i += 1
            repeat = (repeat << 8) + data[i]
        elif code == 0xF:    # White (extended)
            color = 255
            i += 1
            repeat = (repeat << 8) + data[i]
        else:                # Grayscale (single byte)
            color = (code << 4) | code

        # Fill pixels
        for _ in range(repeat):
            pixels[pixel_pos] = color
            pixel_pos += 1

        i += 1

    return pixels

Nibble-Coded RLE4 Encoder (PW0)

def encode_rle4_nibble(pixels: bytes) -> bytes:
    result = bytearray()
    i = 0

    while i < len(pixels):
        color_nibble = pixels[i] >> 4
        length = 1

        # Count consecutive pixels
        while (i + length < len(pixels) and
               (pixels[i + length] >> 4) == color_nibble):
            length += 1

        # Encode run
        if color_nibble in (0x0, 0xF):  # Black or white
            while length > 0:
                run = min(length, 4095)
                more = (run | (color_nibble << 12))
                result.append(more >> 8)
                result.append(more & 0xFF)
                length -= run
        else:  # Grayscale
            while length > 0:
                run = min(length, 15)
                result.append((run | (color_nibble << 4)))
                length -= run

        i += length

    return result

Dependencies by Format

Minimal (No external libs):

  • Binary I/O, basic data structures
  • Formats: CBDDLP, CTB, PWS, PW0, PhotonS, GOO, CXDLP, FDG

Recommended:

  • ZIP library: SL1, UVJ, VDT, NanoDLP, ZCode, ZCodex, CWS, JXS
  • JSON parser: UVJ, VDT, AnycubicZip, NanoDLP
  • INI parser: SL1, JXS
  • PNG encoder/decoder: All archive formats

Advanced:

  • AES-256-CBC: CTBEncrypted
  • XML parser: CWS, VDA, ZCode, SVGX
  • SVG parser: SVGX

Common Pitfalls

  1. Endianness:

    • Chitubox family: Little-endian (LE)
    • PhotonS, GOO, CXDLP, MDLP: Big-endian (BE)
    • Always verify in format header!
  2. Float Size:

    • CTB v3 uses 32-bit floats, NOT 64-bit doubles
    • Previous docs had this wrong
    • Verified from Chitubox 1.8 fixtures
  3. Nibble RLE Complexity:

    • PW0 nibble-coded RLE is variable-length
    • NOT a fixed 3-byte format
    • Must handle both 1-byte and 2-byte runs
  4. File Offsets:

    • Offsets are absolute from file start (position 0)
    • Always seek before reading data sections
  5. RLE Limits:

    • RLE1/RLE125: max 125 pixels
    • RLE128: max 128 pixels
    • RLE4 nibble: max 4095 (B/W), 15 (grayscale)
    • Split longer runs
  6. String Encoding:

    • Most: ASCII or UTF-8
    • Anet: UTF-16 Big-Endian
    • Always null-terminate and pad

Format Comparison Quick Reference

By Use Case

Use Case Format Reason
Maximum compatibility CTB v3 Widest printer support
Smallest file size CBDDLP Simple binary RLE
Best compression (AA) PW0 (nibble RLE4) Optimized variable-length
Archival/preservation UVJ or SL1 Open, PNG-based
Editing/manipulation SL1 or UVJ Human-readable config
Fastest decode SL1, UVJ Standard PNG decoders
Embedded systems CBDDLP Minimal complexity
Modern Anycubic .pm5s (v518) Latest features

By Complexity

Level Formats
Low ImageFile, UVJ, SL1, CBDDLP, CTB v3, PWS
Medium PW0, PhotonS, GOO, PHZ, FDG, CXDLP
High AnycubicFile v517+, VDT, NanoDLP, ZCode
Very High CTBEncrypted, SVGX, OSF

Corrections Summary

v2 Changes from v1:

  1. CTB v3 Header: Fixed to 32-bit floats (was incorrectly documented as 64-bit doubles)
  2. Anycubic Split: Separated legacy (PWS/PW0) from modern (.pm3n, .pm4n, .pm5s) formats
  3. RLE4 Encoding: Fully documented nibble-coded variable-length algorithm (was simplified incorrectly)
  4. Version Support: Added v517 (.pm3n, .pm5, .px6s) and v518 (.pm5s, .m5sp) details
  5. Capability Matrix: Added comprehensive format capabilities table
  6. Extension Mapping: Added machine detection by extension
  7. Preview Counts: Documented v517 (7 previews) and v518 (9 previews) differences
  8. Implementation Order: Revised based on actual complexity

Validation Notes

Verified Against:

  • UVtools.Core/FileFormats/ source code
  • Chitubox 1.8 .ctb fixtures
  • AnycubicFile.cs implementation (lines 2270-2409 for RLE4)
  • ChituboxFile.cs Header class (lines 61-159)
  • Format version constants and extension mappings

Status:

  • ✓ CTB v3 header corrected (32-bit floats verified)
  • ✓ Nibble-coded RLE4 algorithm extracted from working implementation
  • ✓ Anycubic v517/v518 extensions documented
  • ✓ All 32 formats have encode/decode capability
  • ✓ 60+ file extensions cataloged

License & Attribution

Derived from UVtools (https://github.com/sn4k3/UVtools) License: AGPL-3.0

Document Version: 2.0 Generated: 2026-05-16