Skip to content

Latest commit

 

History

History
299 lines (238 loc) · 14.1 KB

File metadata and controls

299 lines (238 loc) · 14.1 KB

Makera Studio .tlslibrary File Format

Reverse-engineered specification of the tool library export format used by Makera Studio (CAM software for the Makera Carvera CNC). Verified against files produced by Makera Studio in September 2026 (library version = 1). A reference parser/writer in Python (tlslib.py) round-trips real exports byte-for-byte.

Status: everything below has been confirmed by probe exports (changing a value in the UI and diffing the file) except where marked unverified.


1. Container

The file is not text. It is a Qt QDataStream serialization written in Qt's default (big-endian) byte order. There is no magic number, no header signature, and no length prefix for the whole file. Opening it as UTF‑16 in a text editor shows the strings because QString is stored as UTF‑16BE, but the bytes between strings are binary integers, doubles and booleans, not text.

Primitive encodings

Type Encoding
int32 4 bytes, big-endian, signed
double 8 bytes, big-endian IEEE‑754 binary64
bool 1 byte, 0x00 or 0x01
QString uint32 byte length N, then N bytes of UTF‑16BE (no BOM, no terminator). A null string is the single value 0xFFFFFFFF with no payload. An empty string is 0x00000000.

All strings observed are ASCII, so N is always 2 × character count.

Records are packed with no alignment padding. Because a bool is one byte, offsets after the first per-material record are odd, which is why hex dumps look "shifted" halfway through the file.


2. Top-level layout

Library
  int32     version            = 1
  QString   id                 (null in all observed files)
  QString   name               e.g. "Custom Tools"
  QString   description        (null in all observed files)
  int32     tool_count
  Tool[tool_count]
  int32     material_count     = 15 in stock installs
  Material[material_count]
<EOF>

The file ends immediately after the last Material; a correct parser consumes exactly the file length.


3. Tool record

Tool
  QString   uuid               "{xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx}"   (QUuid::toString, braces, random v4)
  QString   name               user-visible tool name
  int32     tool_number        "Tool Number" field in the UI
  int32     tool_type          enum, see §3.1
  double[13] geometry          see §3.2
  int32     metal              Non-Metal / Metal radio: 0 = Non-Metal, 1 = Metal
  int32     material_count     number of ToolMaterial records that follow (0..N)
  ToolMaterial[material_count]

metal was verified by importing a tool with metal = 0, which opened with Non-Metal selected.

material_count may be less than the library's material count, including zero. The app's own exports always write one record per library material, but a file with two records (or none) imports correctly and the Tool Properties table lists only the materials present.

3.1 tool_type enum

value UI label default name in stock library
0 Ball Nose ballnose
1 Flat End flatend
2 V‑Bit vbit
3 Engraving engraving
4 Bull Nose bullnose
5 Drill drill
6 Thread thread
7 Tapered Ball Nose taperedballnose

The name string is free text; only tool_type determines the shape.

3.2 geometry — 13 doubles, all in millimetres / degrees

Every tool stores all 13 values regardless of type. The UI simply hides the ones that don't apply and leaves them at their defaults, so a writer should always emit the defaults for unused slots.

idx field UI label default used by
0 DS shank (handle) diameter Handle Diameter (DS) 3.175 all
1 unknown — (never shown) 12.0 —
2 LS shoulder length Shoulder Length (LS) 12.0 all
3 LC flute length Flute Length (LC) 5.0 all
4 DC cutting diameter Diameter (DC) 3.175 all
5 D1 tip diameter Tip Diameter (D1) see note engraving
6 CR corner radius Corner Radius (CR) 1.0 bullnose, taperedballnose
7 TI included angle Angle (TI) 60.0 vbit
8 AN half angle Half Angle (AN) 30.0 engraving, drill
9 thread specification Thread Specification 0.0 thread (stock default 2.5)
10 PI thread pitch Pitch (PI) 0.0 thread (stock default 0.45)
11 TA thread angle Thread Angle (TA) 0.0 thread
12 thread drill diameter Drill Diameter 0.0 thread (stock default 2.05)

D1 note: for flatend and ballnose the app writes D1 = DC (it follows DC when DC is edited). For every other type the stock default is 0.2.

Index 1: always 12.0 in every observed file, unchanged by editing any visible field. Probably a total/overall length that the current UI does not expose. Leave it at 12.0.

Verification of the mapping: setting DS=6.35, LS=20, LC=8, DC=3.0 on a flat-end tool changed exactly indices 0, 2, 3, 4 (and 5, which followed DC).


4. ToolMaterial record — per-material machining parameters

One record per material in the library's material list (§5), in the same order the app keeps them (which is not the order of §5). A material the user has not enabled for the tool is still present, with all numeric fields zero.

ToolMaterial
  QString   uuid               "{...}"  random v4, braces
  QString   material_id        matches Material.id (§5)
  QString   material_name      matches Material.name
  int32     spindle_rpm        Spindle Speed (r.p.m)
  int32     feed_rate          Feed Rate (mm/min)
  int32     plunge_rate        Plunge Rate (mm/min)
  double    step_over_pct      Step Over (%)
  double    step_over_mm       Step Over in mm  — the greyed-out derived field
  double    step_down_mm       Step Down (mm)
  int32     coolant            Coolant Category: 0 = none, 1 = Air Cooling (any other value shows a blank dropdown)
  bool      enabled            the material's Enable checkbox (see note)
  QString   note               null in all observed records

Total fixed-size payload between material_name and note is 41 bytes (3×4 + 3×8 + 4 + 1).

step_over_mm is stored, not recomputed on load: after DC was changed from 3.175 to 3.0 the file still held the old 0.064. The app writes round(DC × step_over_pct / 100, 3) (3.175 × 2 % → 0.064, i.e. it stores the 3‑decimal display value). A writer should compute and store this itself.

enabled: on import a material is treated as enabled if this byte is 1 or any of its numeric parameters is non-zero. Both were verified: a record with enabled = 1 and all-zero values imports as enabled, and a record with enabled = 0 and non-zero feeds also imports as enabled (with the Enable checkbox ticked). Makera Studio's own exports write 0 here even for enabled materials, relying on the non-zero rule. Writers should set enabled = 1 on every active material anyway; it costs nothing and does not depend on the derived rule.


5. Material record — library material list

Material
  QString   id                 UUID v7 string, no braces, e.g. "019bfae0-a419-75bf-9d86-d8d4ede8560d"
  QString   id_again           identical to id in all observed records (*purpose unknown*)
  QString   category_id        UUID v7 string, shared by materials in the same UI group
  QString   name
  QString   description        (null in all observed records)
  bool      flag               1 in all observed records (*meaning unknown*)

Stock Makera Studio materials and their UI groups (grouping inferred from shared category_id):

group materials
Wood Hardwood, Softwood
Plastic Acrylic, ABS, Polycarbonate, Delrin
Aluminum Alloys 6061 Aluminum, 7075 Aluminum
Copper Alloys Brass, Copper
PCB PCB
Composites Bakelite, Carbon Fiber, Synthetic Stone, Epoxy Tooling

The material and category IDs are fixed per Makera Studio install/version. When generating a library, copy the Material list (and the IDs used in ToolMaterial.material_id) from a real export rather than inventing them.


6. Import behaviour (observed)

Importing a .tlslibrary in Makera Studio creates a new nested group named after Library.name under the tool library. It does not merge into an existing group, so give the library a meaningful name.

UUIDs are not de-duplicated on import. The same file imported twice produces two groups, each with the full set of tools, even though every Tool.uuid is identical between them. Generated files should still use fresh random v4 UUIDs for Tool.uuid and ToolMaterial.uuid so that tools remain distinguishable if the app ever keys on them, but nothing observed requires it. Only ToolMaterial.material_id must match the app's existing material IDs.

One early import of a valid file produced an empty group; the same file later imported correctly and the failure could not be reproduced.

Zero-parameter tools import fine. A tool whose ToolMaterial records are all zero imports as a tool with no enabled materials. Default geometry (DS == DC, D1 == DC) is also accepted, as are tools with fewer than the full set of ToolMaterial records, or none.

Material.id / category_id are references to materials the app already knows, not definitions: a file with zero tools and a full Material list imports as an empty group, and the app itself exports an empty library as a 4-byte file with no material list at all. There is no evidence that new materials can be created through this file.

Confirmed working imports: a generated file with one fresh-UUID flatend tool and feeds on two of fifteen materials (parameters appeared on exactly those two); a stock export re-generated by the reference writer; and single-field variants of a real export (feeds zeroed; geometry reset).

7. Writing a library programmatically

Minimal recipe:

  1. Parse an existing non-empty export to obtain the 15 Material records and their IDs (an empty export contains no material list).
  2. For each tool to add, emit a Tool with a fresh v4 UUID, the 13 geometry doubles (defaults for unused slots, D1 = DC for flat/ball), metal = 1, and one ToolMaterial per material you want active, with enabled = 1 and computed step_over_mm. Emitting zero/disabled records for the other materials is optional (the app does it; the importer doesn't require it).
  3. Emit the unchanged Material list.
  4. Give Library.name a distinct value; it becomes the group name on import.
  5. Write big-endian, no padding, QString as length-prefixed UTF‑16BE.

A writer is correct if write(parse(f)) == f for a real export.


8. Worked example (first 0x2C bytes of a stock export)

00 00 00 01                      int32   version = 1
FF FF FF FF                      QString id = null
00 00 00 18  00 43 00 75 ...     QString name: 24 bytes → "Custom Tools"
FF FF FF FF                      QString description = null
00 00 00 08                      int32   tool_count = 8
00 00 00 4C  00 7B 00 30 ...     QString tool.uuid: 76 bytes → "{07168478-...}"

and one ToolMaterial payload with Hardwood set to spindle 3, feed 4, plunge 5, step-down 1.0, step-over 2 %, air cooling, on a 3.175 mm tool:

00 00 00 03                      spindle_rpm   = 3
00 00 00 04                      feed_rate     = 4
00 00 00 05                      plunge_rate   = 5
40 00 00 00 00 00 00 00          step_over_pct = 2.0
3F B0 62 4D D2 F1 A9 FC          step_over_mm  = 0.064
3F F0 00 00 00 00 00 00          step_down_mm  = 1.0
00 00 00 01                      coolant       = 1 (Air Cooling)
00                               enabled       = 0 (see §4 note)
FF FF FF FF                      note          = null

9. Open questions

  • geometry[1] (always 12.0): meaning. Setting it to 99 changed nothing visible in the tool editor or preview.
  • Material.flag, Material.id_again: meaning.
  • Whether ToolMaterial.enabled has any effect beyond the import-time rule in §4 (the app's exporter always writes 0).
  • Whether Makera Studio tolerates a Tool with fewer ToolMaterial records than the material list has (untested — safest to always emit all).
  • Whether a Material record with an unknown id creates a new material in the app, or is ignored.
  • Behaviour with non-ASCII tool names (should be fine given UTF‑16 storage, untested).

Contributions welcome — a probe export plus a one-line description of what was changed is enough to settle any of these.