This story box came out of a trash bin. It is a Lunii « Ma Fabrique à Histoires », a small, chunky, wonderfully simple gadget for kids: a wheel, a few buttons, and a little screen that shows flat silhouette images while a narrator reads interactive stories. You pick a hero, a place, a companion, and the box stitches a tale together out of your choices. Lovely hardware. Lovely idea. Completely closed format.
Naturally, I wanted to put my own stories on it.
The official path is the Lunii store: you buy story packs, they sync to the box, and that is the entire deal. There is no import button, no SDK, no documented format. The packs sitting on the microSD card are encrypted and tied to the device, and the company, quite understandably, has no interest in explaining any of it. Which means that if you want a custom interactive story on this thing, you are going to learn about cryptography whether you like it or not.
Spoiler: it worked. One custom interactive story now lives on the box, generated entirely at home, from the branching narrative down to the encrypted bytes on the SD card. Not bad for a bin find. But first, the bin.

Rescued from the trash
Someone had apparently given up on it over a broken selection wheel, the big yellow knob that is half of the whole user interface. Everything else worked. For a while.
Later, after all that careful writing to its SD card, the box greeted me with a NO SD CARD screen, which is exactly the message you want to see at that point. So this story box has had two lives on my bench, and the second one needed a soldering iron.


A quick measurement showed the 3.3 V rail reading about 2.5 V, floating and sad. The culprit turned out to be the voltage regulator, an AP7362-33 part, whose ground pin was simply not soldered to the ground plane. The regulator was doing its best, but without a ground reference its best was 2.5 V of disappointment, and the SD card power rail was along for the ride.


Swapping a surface-mount regulator means hot air, and hot air does not stop at the part you aim it at. So before the rework, Kapton tape went over everything that should not get hot: the microcontroller, the flex cable, the SD socket.

The fix was almost embarrassingly simple: a fresh AP7362-33 in place of the old one, and a jumper strap from its ground pin to the USB-C connector ground, so it could never float again. One new chip and one short wire later, the rail sat at a steady 3.3 V, the SD card powered properly, and the box booted like nothing had happened.

With the case open anyway, the shell got a proper clean too. The previous owner’s stickers came off with a little hot air and a scalpel, and both halves were washed down with 99.9 percent isopropanol. A box pulled out of a bin deserves a bath.

A fair warning before anyone reaches for their soldering iron: this was one specific failure with one specific cause. A NO SD CARD screen can have plenty of other reasons, and I got lucky that mine had a name, a datasheet, and a pin I could actually reach.
The format, and why V2 is friendly
First, some luck. Lunii boxes exist in different hardware generations, and the pack format changed with them. The V3 devices encrypt content with AES-128. The V2 devices, like this one, use XXTEA instead, a tiny block cipher that is, let us say, considerably more approachable for a hobbyist with a Python interpreter and an evening to kill.
You can tell which generation you have before touching any cipher. The SD card holds a small metadata file, the .md file, and its size gives the version away. This box carries a 512 byte .md, the V2 kind (a V3 carries 112 or 128 bytes), which settled the question immediately.
One nuance matters a lot: the XXTEA key for a story pack is derived from that device-specific .md file. In plain terms, every box has its own key. A pack built for one Lunii will not play on another. This is not a format for sharing content, and it never will be. It is a format for making exactly one specific box play exactly your story. For a personal project, that is fine. Honestly, it is almost a feature.
Now, honesty about credit. I did not reverse engineer this format from scratch, and I want to be very clear about that. Several public community projects had already mapped large parts of it: the SD card layout, the role of the .md file, the XXTEA scheme, the overall pack structure. My contribution was the unglamorous rest. Reading, testing, crashing the firmware in new and creative ways, and building a pipeline that turns a story idea into a working pack. I wrote it all down as a survival guide for my future self, the kind of magic recipe you leave behind so you can resume the project years later without reverse engineering your own notes. Standing on the shoulders of the community, then adding a small ladder of my own.
Tagless MP3s and other fine print
Once the encryption was understood, the remaining work was discovering just how picky the firmware is about media. The answer is: extremely.
Audio must be MP3, mono, 44.1 kHz. Simple enough, except for two traps. First, no ID3 tags whatsoever. After decryption, the file must begin directly with the MP3 sync bytes, and even a stray encoder tag quietly left behind by your audio tools is enough to make the box reject the file. Second, the audio needs roughly 300 ms of silence padding at the start. The hardware DAC takes a moment to wake up, and without the padding it eats the beginning of every sentence, which is a bold narrative choice but not the one I wanted.
Images are their own little museum exhibit: 320×240 pixels, 16 shades of grey, stored as BMP files with RLE4 compression. If you have never hand-rolled run-length encoded 4-bit bitmaps before, I cannot in good conscience recommend the hobby, but there is something satisfying about watching your own grey pixels appear on that little screen. The style that works best is pure silhouette, which conveniently matches the device’s official aesthetic.
Everything is then tied together by a binary pack structure with internal sections, files named with plain hex identifiers, and a .pi index file that lists the packs on the card and controls their order on the selection wheel. Simple in principle. Merciless about mistakes.
The home pipeline
With the format understood, I built the fun part: a small pipeline that goes from idea to deployed story.
Step one is the narrative. A generator builds an interactive story tree: a matrix of places crossed with companions, with branching sentence choices at each node, the same choose-your-path structure the official stories use. What goes into that matrix is the part I like best. I walked around our village taking photos of the places a small listener actually knows, then asked an AI to spin stories around them: real streets, real routines, romanticized just enough for bedtime. The result is deeply personal, which is exactly why you will only ever see it described here, never heard.
Step two is audio, and the narrator is me. Or rather a copy of me: my voice, cloned by a model running locally on my own machine. Any node without a cloned recording falls back to a stock neural TTS voice (edge-tts, fr-FR-DeniseNeural), so the story is always complete. I will not pretend the clone is indistinguishable from the original; it is not. It sounds like me reading after a long day, charming in its own slightly uncanny way, and it makes the story feel homemade in the good sense.
Step three is illustration. The images are generated in a silhouette style that matches the device’s aesthetic, then converted down to those 16 shades of grey and squeezed into RLE4 bitmaps.
Step four is compilation. The pipeline assembles the binary pack structure, encrypts what needs encrypting with XXTEA, derives the device key from the .md file, and produces a tidy folder of hex-named files ready for the card.

Step five is deployment, the part where everything can still go wrong. The card gets mounted, the files copied, the writes flushed, the filesystem checked, and the .pi index updated so the new story appears on the wheel. And because I know myself, there is also an emergency rollback script that restores the previous official content state, so a bad deploy means one command and a sigh of relief rather than a broken bedtime routine.
The first time the wheel landed on a story I had generated from scratch, and the box just played it, I may have said something undignified out loud. No witnesses, fortunately.

Honest limits
A few things worth saying plainly. None of the cryptographic material will ever be published: no device keys, no .md contents, nothing that would compromise the device or the format. Packs are locked to a single box by design, so nothing here is portable or shareable. The cloned narrator voice is a cloned voice, not a magic replica. And the story itself stays private, because a device made for one child deserves to stay a personal project, not content for the internet.
What I can share is the recipe’s shape: community research, a friendly cipher, picky media formats, a home pipeline, and one small jumper wire. Sometimes the closed box just needs a patient owner.