Splash Studio — source and operation
===================================
Open /splash/ on roms.danielspringer.at. Linked on the OTA/archive homepage in a
dedicated tool card since 1 November 2026, after operator testing and approval.
The editor remains marked experimental and noindex.

All editing is local to the browser. The file picker reads a local File; it does
not upload anything. No image-processing backend, queue, service or new Nginx
configuration is required. Existing OTA/cache/download counters are untouched.
Anonymous usage events go to index.php?action=track_splash_studio (same-origin
JSON POST, no cookies/session): page, opened, changed, exported, grown, failed.
For failures, a fixed allowlisted reason code is additionally sent. Never send
files, names, contents, dimensions, error messages or stack traces. Browser and
worker errors use stable codes for format/data/size/processing/verification,
missing confirmation, unapplied changes, cancellation, timeout or browser failure.
The server rejects arbitrary reason strings and stores the category atomically
with the existing failure counter. Older clients without a reason remain supported.
Since 3 November 2026, new failures distinguish a too-small splash, splash input,
replacement and output byte limits, edited-memory and decoded-data budgets, and
individual BMP size. Format reasons distinguish missing SPLASH LOGO! magic, AVB
footer, nonzero gap data and nonzero trailing data. Earlier size_limit and
unsupported_splash counts remain unchanged as legacy categories; they cannot be
split retrospectively. No file metadata is added to telemetry.
The hosting site's independent aggregate observer records event totals/day buckets.
Exports count browser-generated download handoffs, not flashed/installed images;
failures include blocked actions/validation errors. Historical
uncategorized failures appear as Unknown / older entries by subtraction at display
time; the old aggregate/history is never rewritten. Cancellation errors are only
counted once even when several waiting promises are rejected. Blocking telemetry
does not block editing. No retries/history backfill/unique-visitor tracking are used.

Source layout
  index.html, studio.css, studio-layout.css: standalone responsive English interface
  app.js, theme.js: browser UI and shared theme preference
  worker.js: image preparation and packing off the main thread
  core.js: validated splash table, BMP, gzip and original-header-preserving packer
  vendor/pako.esm.js: unminified pako 2.1.0 (only extension renamed for server MIME)
  LICENSE.txt, NOTICE.txt, vendor/PAKO-LICENSE: attribution and licenses
  tests/: source-level regressions (in source.zip; not linked as UI)

There is no compilation step. Serve the folder with a normal static HTTP server
(ES modules and workers cannot reliably run from file://). The live theme CSS is
/css/base.css; theme-base.css in this source bundle is a corresponding copy.
For standalone use, change its href in index.html to ./theme-base.css. Navigation
and favicon links may also be adjusted. To regenerate source.zip, ZIP these plain
sources, the theme copy and tests; exclude source.zip itself and all firmware
fixtures. There are no secrets or uploaded images in the bundle.

Editing
  Open an original supported splash.img, choose an existing screen, choose a PNG,
  JPG or uncompressed BMP. Default target is that screen's original dimensions,
  not a guessed phone resolution. Fill auto-crops; Fit adds the chosen background;
  Stretch changes proportions. Adjust zoom/position, rotate and optionally add text.
  Apply each edited screen, then export. Save original and per-screen Restore
  original remain available. Preview PNG exports the current composition.
  Replacement PNG/JPG/BMP files may be up to 64 MiB (previously 32 MiB); the
  existing pixel/dimension checks still apply before full decoding. Splash input
  and exported files remain limited to 64 MiB. These are browser resource limits,
  not a guarantee that the output fits a device partition.
  Phone/tablet widths through 960 px use Screens / Edit / Export sections, a
  desktop-recommendation entry notice and a Continue on this device button.
  File selection is revealed after continuing; desktop is unaffected. The choice
  lasts only for this page, without a new cookie/storage value, and does not
  interrupt an already opened project when the window is made smaller. The
  bootloader/brick warning and export confirmation remain unchanged. There is a
  compact sticky preview while adjusting controls and a full-width screen grid.
  Horizontal and vertical crop controls each get their own full-width row.
  Ranges have explicit high-contrast WebKit/Firefox tracks and large thumbs.
  Add your own text has its own entry button and starts collapsed; text focus
  shrinks the mobile preview to leave room for the software keyboard.

Safety boundaries
  SPLASH LOGO! at 0x4000, information at 0x410c, metadata at 0x4120, data 0x8000.
  Up to 64 images, 64 MiB file, 64 MiB per decompressed BMP, 512 MiB cumulative
  declared BMP data, 16 megapixels and 8192 per side; edited BMPs total <=256 MiB.
  Bounded gzip inflation with CRC/size verification. Supports uncompressed BI_RGB
  1/4/8-bit paletted and 24/32-bit BMPs. Palette size, offsets and pixel indices
  are checked; top-down rows, packed pixels and four-byte row padding are handled.
  Original palette BMPs are retained byte-for-byte unless edited; edited screens
  are encoded as standard uncompressed 24-bit BMPs, as for the other screen types.
  Duplicate/overlapping ranges, unknown nonzero data outside image blocks, AVB
  footers and unsupported layouts fail closed; no claim that all signatures or
  device-specific constraints can be detected. Pixel previews are opaque.
  Existing header bytes and raw names remain intact. Unedited compressed images
  are copied byte-for-byte. Metadata offsets and sizes update only during packing.
  No edits + original capacity => a byte-identical copy of the original file.
  Like FolkSplash, output size is max(original file length, required image data).
  A larger file is generated automatically; original/new size and growth are shown.
  Optional user-supplied known partition capacity is enforced as a ceiling, never
  used as a padding target. The header has no reliably identified maximum-partition
  field; unknown header values must not be interpreted as such. The 64 MiB processing
  ceiling is an editor resource limit, not a known device partition size. No partition
  size is inferred from a device name or trimmed firmware file length.
  Edited output is fully reread and decompressed
  again, with byte comparison against all intended original/edited BMPs.
  Browser operations can be cancelled; cancelling terminates the worker and
  discards this in-memory project. Nothing is stored server-side or auto-saved.

This is an experimental image editor, not a flash validation service. No device
was flashed for testing. Unknown firmware checksums/signatures/hardware behavior
can prevent a valid-looking image from working. Keep stock and use device-specific
instructions. It is not for MediaTek/vendor formats without this exact layout.
The prominent page warning and changed-image export confirmation explicitly say
not to relock the bootloader with a modified splash.img, due to brick risk. Restore
the correct complete stock firmware, including the original splash, before any
device-specific relocking procedure; the editor cannot certify it is safe.

Removal: remove the tool-card include/template, tracking action/endpoint and
dashboard card, then the standalone splash/ folder. Existing aggregate history
does not need deletion; remove only the six allowlisted splash event names if
retiring tracking. No services, caches, OTA manifests or file uploads need cleanup.
