This is a fork of marian-m12l/studio, maintained independently and focused on robustness in real use: making the device detect reliably, making failures report honestly, and progressively hardening what STUdio writes to the card. The upstream project has seen little activity recently; this fork keeps the same purpose, licence and attribution, and adds fixes, tests and characterization work on top.
It is work in progress, not a finished product. Several parts of the write path are still open, and nothing here claims to survive a power cut. Read Current status and Limitations below before deciding whether it fits your use.
Grouped by area rather than listed change by change. The detail is in TESTING.md and the commit
history.
Device and transport
- The partition search waits for the OS to mount the device instead of giving up after ten seconds, and an unplug cancels a search in flight.
- A dropped monitoring channel is no longer reported as a failed transfer.
- The libusb context has a single owner, with an idempotent init and shutdown, and the detection workers survive transient failures instead of dying silently.
- File handles are released on every path out, not only the nominal one.
Pack index and filesystem
- A
.piwhose length is not a whole number of 16-byte records is rejected rather than turned into a fabricated pack UUID. - An index entry whose content is missing no longer makes the whole device unreadable; the other packs stay listable.
- The free-space precheck is
longend to end and counts what the transfer is known to add — cipher padding, the boot file, the index growth and its temporary copy — instead of summing source sizes and truncating to anint. - The temporary
.pi.newis created exclusively, removed again on a controlled failure, and a pre-existing one is refused rather than overwritten. - The temporary is synchronised, then installed with a single
Files.move(ATOMIC_MOVE), so the index is no longer written through in place. If the atomic move is unsupported the operation fails — there is deliberately no fallback to the previous non-atomic copy. - An upload creates its
.contentfolder exclusively. A folder already at that name is refused, never written into and never emptied: it may be the residue of an interrupted upload, of a delete that could not finish, or of another tool, and nothing can tell which. The refusal reads nothing, writes nothing and removes nothing.
Library and conversions
- A converted file is no longer sent to the device merely because it is the most recent one present. When the library holds both something the device can read and something that could be converted into it, STUdio asks which one you mean — unless it can prove they correspond.
- New conversions record what they were made from. Before reusing one, STUdio re-reads both the source and the converted file and compares them against what it recorded: only a match transfers without asking. A difference, or anything it cannot establish, asks.
- Conversions made before this existed carry no such record, so they are never assumed to match. They ask, once, and re-converting produces one that no longer will.
Testing
- A CI workflow running the Java suite on Linux and Windows, plus the JavaScript suite.
- Characterization and specification tests covering the metadata, index, detection and write paths.
- An opt-in FAT32 suite, exercised by hand on a disposable volume — see
TESTING.md. - Four documented sessions of real device operations, on two devices — see
FIELD-VALIDATION.md.
Actively worked on, and not finished. What is described above is implemented and tested in the
conditions documented in TESTING.md. What is still open, and known to be:
- an upload that fails part-way leaves an orphan
.contentfolder that nothing cleans up; - that orphan then makes every later upload of the same pack refuse, which is deliberate and explained in the error — but STUdio still offers no supported way to resolve the state it leaves;
deletePackremoves the index entry before the content, so a failed removal leaves the content behind, de-indexed;- the driver can list
.contentfolders the index does not reference, but nothing calls it: a device is not examined for partial states when it is plugged in, and the web UI never shows them. Nothing acts on them either — the listing is read-only by design.
Use the operational protocol in FIELD-VALIDATION.md if you are writing to a real device.
- No crash-safety is claimed or proven. The improvements above concern ordinary failures — errors returned by the filesystem — not power loss or physical removal mid-write.
force()asks the operating system to push a file to the card. It is not evidence that the bytes reached the flash: the card's controller may acknowledge earlier, and nothing here can observe it.ATOMIC_MOVEis atomic with respect to the filesystem. It is not a guarantee about power loss.- FAT32, which is what a device uses, has no journal.
- The FAT corruption incident recorded in
FIELD-VALIDATION.mdis a forensic hypothesis, not a demonstrated cause. Nothing in this repository establishes what caused it.
At the time of writing: 282 Java tests in the standard suite and 57 JavaScript tests, all
green, on Linux and on Windows. TESTING.md holds the current counts, the opt-in FAT32 figure and
its caveats, and is the authority — the numbers here will go stale before it does.
Two caveats worth knowing before reading anything into any of those numbers:
- CI never exercises FAT32. A hosted runner has no such volume, so the opt-in tests skip there and are validated by hand on a disposable VHD instead.
- Field results come from four sessions, one machine, two devices. They do not generalise to other firmware revisions, other cards or other Windows versions.
TESTING.md describes how to run everything, what is covered, and what is deliberately not.
The first public pre-release of this fork is available as
0.4.3-fork.1. It is a
pre-release: published to widen validation beyond the devices used during development, not
because the work is finished. Read Current status and Limitations above first.
The download link in the upstream instructions below still points at upstream's own build, which does not contain any of the changes described here.
Download the pre-release. Take studio-web-ui-0.4.3-fork.1-dist.zip from
that release, unzip it, and
run the launcher script for your platform. The prerequisites and the rest of the procedure are the
upstream ones, described under Usage below.
Or build from source. The upstream prerequisites apply unchanged — Java JDK 11+ to run it, Maven 3+ to build it — but clone this repository rather than the one named in For developers:
git clone https://github.com/antoinevalentinHA/studio.git
cd studio
mvn clean install
That produces the same distribution archive in web-ui/target/.
Based on STUdio by @marian-m12l, whose reverse-engineering work this exists on top of. Licence, attribution and disclaimers are unchanged and reproduced below. The fork can be rebased on upstream if it becomes active again; until then the changes above are maintained here.
The rest of this file is the upstream README, kept as it was except where it would say something untrue of this fork. What is annotated: the release badge and the download link, which refer to upstream builds; the clone URL, which is upstream's repository and not this one; the rule deciding which file is transferred, which this fork changed; and the asset format lists, whose asterisk markers were dropped in an upstream rewrite while the sentence explaining them stayed, so that the text described markers no longer present. Nothing else in it is edited.
This badge and the download links below refer to upstream builds, which do not include this
fork's changes. This fork's own pre-release is
0.4.3-fork.1 — see
Getting started.
Warning
Support for V3 devices has been added thanks to the community effort! 🥳
Create and transfer your own story packs to and from the Lunii* story teller device.
This software relies on my own reverse engineering research, which is limited to gathering the information necessary to ensure interoperability with the Lunii* story teller device, and does not distribute any protected content.
USE AT YOUR OWN RISK. Be advised that despite my best efforts to keep this software safe, it comes with NO WARRANTY and may brick your device.
* Lunii is a registered trademark of Lunii SAS. I am (and this work is) in no way affiliated with Lunii SAS.
- Java JDK 11+
- On Windows, this application requires the libusb driver to be installed. The easiest way to achieve this is to have the official Luniistore* software installed (but not running).
- Download the latest upstream release — this is upstream's build and does not contain this fork's changes; for this fork, take the archive from its own pre-release instead — (alternatively, you can build the application).
- Unzip the distribution archive
- Run the launcher script: either
studio-linux.sh,studio-macos.shorstudio-windows.batdepending on your platform. You may need to make them executable first. - If it does not open automatically, open a browser and type the url
http://localhost:8080to load the web UI.
Note: avoid running the script as superuser/administrator, as this may create permissions issues.
The web UI is made of two screens:
- The pack library, to manage your local library and transfer to / from your device
- The pack editor, to create or edit a story pack
The pack library screen always shows the story packs in your local library. These are the packs located on your computer
(in a per-user .studio folder). Three file formats may exist in your library:
Rawis the official format understood by the older devices (firmware v1.x -- these devices use a low-level USB protocol)FSis the official format understood by the newer devices (firmware v2.x -- these devices are seen as a removable storage)Archiveis an unofficial format, used by STUdio only in the story pack editor
Conversion of a story pack will happen automatically when a transfer is initiated, or may be triggered manually. Variations of a given story pack are grouped together in the UI for better readability. The most recent file (highlighted in the UI) gets transferred to the device.
This fork changed that rule. Recency no longer decides: when the library holds both a file the device can read and something that could be converted into one, this fork asks which you mean, unless it can prove the first was produced from the second. See What this fork changes.
When the device is plugged, another pane will appear on the left side, showing the device metadata and story packs. Dragging and dropping a pack from or to the device will initiate the transfer.
The pack editor screen shows the current story pack being edited. By default, it shows a sample story pack intended as a model of correct usage.
A pack is composed of a few metadata and the diagram describing the various steps in the story:
- Stage nodes are used to display an image and/or play a sound
- Action nodes are used to transition from one stage to the next, and to manage the available options
The editor supports several file formats for audio and image assets.
Image files may use the following formats:
- PNG
- JPEG
- BMP (24-bits)
Image dimensions must be 320x240. Images may use colors, even though some colors may not render accurately due to the screen being behind the plastic cover. Bear in mind that the color of the cover may change.
Audio files may use the following formats:
- MP3
- OGG/Vorbis
- WAVE (signed 16-bits, mono, 32000 Hz)
MP3 and OGG files are expected to be sampled at 44100Hz.
The formats above are what the editor accepts. What reaches the device is narrower, and depends on the pack format being transferred:
- a
Rawpack (firmware v1.x) carries 24-bit BMP images and WAVE audio (signed 16-bits, mono, 32000 Hz); - an
FSpack (firmware v2.x) carries 4-bit RLE-encoded BMP images and MP3 audio (mono, 44100 Hz).
Assets not already in the target form are converted while the transfer is prepared. This is not
limited to the compressed formats: transferring to an FS pack re-encodes a 24-bit BMP into a
4-bit RLE BMP, and re-encodes an MP3 that is not mono/44100 Hz (an MP3 kept as it is has its ID3
tags removed).
Conversely, on the Raw path a BMP or a WAVE file is written out as it stands, its bit depth and
its sample parameters unchecked. Meeting the constraints listed above is therefore yours to do, not
something the conversion will fix.
More information, including an illustrated usage guide courtesy of @appenzellois, available in the project wiki.
- Maven 3+
- Clone this repository:
git clone https://github.com/marian-m12l/studio.git— that is upstream's repository; to build this fork clonehttps://github.com/antoinevalentinHA/studio.gitinstead, see Getting started - Build the application:
mvn clean install
This will produce the distribution archive in web-ui/target/.
If you liked STUdio, you will also like:
- Moiki is an online tool to create interactive stories that can be exported for STUdio (courtesy of @kaelhem)
- mhios (Mes Histoires Interactives Open Stories) was an online open library of interactive stories (courtesy of @sebbelese)
This project is licensed under the terms of the Mozilla Public License 2.0. The terms of the license are in
the LICENSE file.
The vorbis-java library, as well as the VorbisEncoder class are licensed by the Xiph.org Foundation. The terms of
the license can be found in the LICENSE.vorbis-java file.
The com.jhlabs.image package is licensed by Jerry Huxtable under the terms of the Apache License 2.0. The terms of
the license can be found in the LICENSE.jhlabs file.