Beginning Reverse-Engineering .tb File Format #41
Replies: 2 comments 1 reply
-
|
Additionally, I noticed that the presets downloaded from TourBox's website are using a different file extension {
"version": 3,
"uuid": "1224688408014753792",
"presetType": "fresco",
"settings": {
"3": {
"primary": {
"id": "fresco-22"
},
"feature": {
"mode": 1
},
"reverse": false
},
"27": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-16"
},
"reverse": false
},
"13": {
"reverse": false,
"feature": {
"vibra": 1,
"speed": 1
}
},
"17": {
"primary": {
"id": "fresco-19"
},
"reverse": false,
"feature": {
"mode": 0
}
},
"11": {
"feature": {
"vibra": 1,
"speed": 1
},
"reverse": false
},
"55": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-23"
},
"reverse": false
},
"30": {
"reverse": false,
"primary": {
"id": "fresco-11"
},
"feature": {
"mode": 0
}
},
"43": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-25"
},
"reverse": false
},
"23": {
"feature": {
"mode": 0
},
"reverse": false
},
"8": {
"reverse": false,
"feature": {
"vibra": 1,
"speed": 1
}
},
"44": {
"primary": {
"id": "fresco-24"
},
"feature": {
"mode": 0
},
"reverse": false
},
"16": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-20"
},
"reverse": false
},
"26": {
"primary": {
"id": "fresco-14"
},
"reverse": false,
"feature": {
"mode": 0
}
},
"12": {
"reverse": false,
"feature": {
"speed": 1,
"vibra": 1
}
},
"58": {
"reverse": false,
"feature": {
"mode": 0
}
},
"19": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-27"
},
"reverse": false
},
"28": {
"reverse": false,
"primary": {
"id": "fresco-1"
},
"feature": {
"mode": 0
}
},
"14": {
"reverse": false,
"feature": {
"speed": 1,
"vibra": 1
}
},
"56": {
"reverse": false,
"feature": {
"mode": 0
}
},
"29": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-13"
},
"reverse": false
},
"42": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-0"
},
"reverse": false
},
"18": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-18"
},
"reverse": false
},
"2": {
"primary": {
"id": "fresco-26"
},
"reverse": false,
"feature": {
"mode": 1
}
},
"24": {
"reverse": false,
"feature": {
"mode": 0
}
},
"37": {
"feature": {
"mode": 0
},
"reverse": false
},
"22": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-4"
},
"reverse": false
},
"21": {
"primary": {
"id": "fresco-6"
},
"feature": {
"mode": 0
},
"reverse": false
},
"33": {
"reverse": false,
"primary": {
"id": "fresco-3"
},
"feature": {
"mode": 0
}
},
"20": {
"reverse": false,
"primary": {
"id": "fresco-5"
},
"feature": {
"mode": 0
}
},
"35": {
"primary": {
"id": "fresco-8"
},
"reverse": false,
"feature": {
"mode": 0
}
},
"10": {
"reverse": false,
"feature": {
"mode": 0
}
},
"34": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-7"
},
"reverse": false
},
"6": {
"reverse": false,
"feature": {
"speed": 1,
"vibra": 1
}
},
"9": {
"feature": {
"vibra": 1,
"speed": 1
},
"reverse": false
},
"1": {
"feature": {
"mode": 1
},
"primary": {
"id": "fresco-21"
},
"reverse": false
},
"32": {
"primary": {
"id": "fresco-17"
},
"reverse": false,
"feature": {
"mode": 0
}
},
"7": {
"feature": {
"speed": 1,
"vibra": 1
},
"reverse": false
},
"36": {
"feature": {
"mode": 0
},
"reverse": false
},
"45": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-10"
},
"reverse": false
},
"46": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-9"
},
"reverse": false
},
"15": {
"reverse": false,
"feature": {
"speed": 1,
"vibra": 1
}
},
"0": {
"primary": {
"id": "fresco-20"
},
"reverse": false,
"feature": {
"mode": 1
}
},
"25": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-12"
},
"reverse": false
},
"57": {
"reverse": false,
"feature": {
"mode": 0
}
},
"31": {
"feature": {
"mode": 0
},
"primary": {
"id": "fresco-2"
},
"reverse": false
},
"5": {
"reverse": false,
"feature": {
"speed": 1,
"vibra": 1
}
},
"4": {
"reverse": false,
"primary": {
"id": "fresco-28"
},
"feature": {
"vibra": 2,
"speed": 1
}
}
},
"tags": {
"83-6": "Publish & export",
"71-0": "Fill",
"87-0": "Magic wand",
"66-0": "Switch to last brush",
"83-8": "Save",
"91-0": "Brush size decrease",
"69-0": "Eraser",
"72-10": "Sync & return to Home",
"78-8": "New layer",
"68-8": "Deselect",
"73-10": "Invert selection",
"67-8": "Copy",
"83-10": "Quick export",
"70-2": "Enter/exit Full Screen mode",
"72-0": "Live brush",
"88-8": "Cut",
"86-8": "Paste",
"44-8": "Hide/show layer",
"86-0": "Vector brush",
"73-0": "Eyedropper",
"90-8": "Paste",
"76-0": "Lasso",
"84-8": "Transform",
"-42-10": "Clear layer contents",
"93-0": "Brush size increase",
"90-10": "Redo",
"76-8": "Lock/unlock layer",
"86-2": "Flip canvas vertically",
"80-0": "Pixel brush",
"72-2": "Flip canvas horizontally"
},
"complexKeys": [],
"keyboard": 1,
"presetName": "Adobe Fresco"
}Clearly this is using a different format, again I haven't analyzed enough to identify which one might be easier to support. |
Beta Was this translation helpful? Give feedback.
-
|
This is a great start, thanks for digging in! After looking at both formats and checking TourBox's own documentation, I think the picture is actually more favorable than it looks at first. Per TourBox's tutorials, the Console app's user-export format is .tbx, not .tb. On desktop the flow is hamburger menu next to a preset → Export, and on iPad it's the three-dot menu → Share Preset → Save to Files. Both produce .tbx files. That's the same format the website's preset library distributes — meaning the JSON format you decoded in your second comment isn't just for downloadable presets, it's also what users get when they export their own profiles from Console. If that holds up, it largely sidesteps the .tb reverse-engineering problem. .tbx import alone would cover both "import an official preset" and "import my own Windows/Mac profile" — which is the real user-facing need. And the .tbx schema looks easy to read today: gzipped JSON, button IDs in the 1–58 range that almost certainly line up with TuxBox's existing internal codes, clean per-button entries with primary.id (action), feature.mode/vibra/speed (haptics — maps onto haptic.py), and reverse (dial direction). Probably a few days of work to wire up to see if it works. That leaves a question about your .tb finding. A few possibilities worth checking: So the practical plan: prioritize .tbx import — that's viable now and covers the realistic use case. Treat .tb as a legacy-compatibility nice-to-have, contingent on confirming whether current Console versions still produce it. I don't have access to a windows box at the moment but if anyone here has the latest Console and can confirm what their export menu produces, that'd shape the priority a lot. Thanks again for looking into this (I am surprised I did not think of the fact that it might be compressed) — really useful research. |
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
-
I'm starting this initially because I saw this message in a different discussion. I've done a bit of digging (still a ton to do) but it's at least an initial lead.
Using the official TourBox windows app, I created a profile that had no bindings in it, and two different profiles with one binding each (of two different buttons). I exported each of those profiles to their
.tbfile format. Starting with anxxdof the files, I don't see any human readable data. Running a diff on them shows a ton of different between them, so I assumed there was some sort of either obfuscation or compression happening.If I run
file, I see that it is a gzip archive.Naturally, I extract this into
No-bindings.decodedand rancat No-bindings.decoded. The output (see below) seems pretty obviously an XML file, and it appears human-readable at that!I did the same process for one of the presets that had a single binding (the side button to key A) and ran a diff between the two.
These indicate the byte locations and the difference in values between those bytes. The first two (byte 113 and 114) are in the config bytes. That shows me that the configuration bytes between these two configurations are extremely similar.
I don't have the time tonight to research any further, but maybe this can serve as a starting point for someone else.
Beta Was this translation helpful? Give feedback.
All reactions