Application web progressive pour jouer à vos ROMs Nintendo DS depuis n'importe quel appareil, avec sauvegarde cloud automatique.
| Couche | Techno |
|---|---|
| Frontend | React 18 + Vite |
| Style | Tailwind CSS |
| Backend / DB | Supabase (PostgreSQL) |
| Stockage fichiers | Supabase Storage |
| Émulation | EmulatorJS · core melonDS |
| PWA | vite-plugin-pwa + Workbox |
- 🔒 PIN gate — accès protégé par code à 4 chiffres (session)
- 📚 Bibliothèque — grille de ROMs avec aperçu du dernier save state
- ⬆️ Upload chunké — fichiers découpés en blocs de 5 Mo et uploadés 4 en parallèle (contourne la limite 50 Mo du plan gratuit Supabase)
▶️ Émulation DS — melonDS via EmulatorJS, DirectBoot activé (pas de BIOS requis pour la majorité des jeux)- ☁️ Save states cloud — synchronisation automatique à chaque sauvegarde, chargement cross-device au démarrage
- 📱 PWA installable — fonctionne comme une app native, EmulatorJS mis en cache pour les sessions suivantes
- Node.js ≥ 18
- Un compte Supabase (plan gratuit suffisant)
git clone <repo>
cd emuste
npm install
cp .env.example .env # puis remplir les variablesVITE_SUPABASE_URL=https://xxxxxxxxxxxxxxxxxxxx.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Disponibles dans Supabase Dashboard → Project Settings → API.
Dans SQL Editor → New Query, coller et exécuter l'intégralité de supabase-setup.sql.
Ce script crée :
- Les tables
romsetsave_states - Les buckets Storage
roms(1 Go max/fichier) etsaves(20 Mo max) - Toutes les policies d'accès public
- Désactive le RLS (app sans authentification)
Si la base existait déjà, exécuter en plus :
alter table roms add column if not exists chunk_count integer not null default 1; alter table roms disable row level security; alter table save_states disable row level security;
npm run dev # développement → http://localhost:5173
npm run build # production (dossier dist/)
npm run preview # prévisualiser le buildnpm run build
# déployer le dossier dist/Ou connecter le repo GitHub à Netlify avec :
- Build command :
npm run build - Publish directory :
dist
Penser à ajouter les variables d'environnement dans Netlify → Site settings → Environment variables.
src/
├── main.jsx # Entrée — enveloppe PinGate + App
├── App.jsx # Routage simple Library ↔ Emulator
├── lib/supabase.js # Client Supabase + helpers URL publiques
└── components/
├── PinGate.jsx # Écran de protection PIN (sessionStorage)
├── Library.jsx # Grille de ROMs, fetch Supabase
├── RomCard.jsx # Carte jeu : aperçu, taille, date save
├── UploadModal.jsx # Upload chunké + parallèle avec progression
└── Emulator.jsx # iframe + réception save state via postMessage
public/
└── emulator.html # Page isolée chargée dans l'iframe
# Télécharge les chunks → assemble en Blob URL
# Gère save/load state ↔ parent via postMessage
Fichier .nds → découpe en blocs de 5 Mo
→ upload 4 blocs en parallèle → Supabase Storage (bucket roms)
→ INSERT roms { id, name, chunk_count, … }
Clic "Jouer" → iframe /emulator.html?romId=…&chunks=N&supabase=…
→ télécharge N blocs en parallèle (6 connexions simultanées)
→ assemble ArrayBuffers → Blob → URL.createObjectURL()
→ si save cloud existant → EJS_saveStateURL (chargement auto)
→ EmulatorJS (melonDS) démarre
Bouton Save (dans EmulatorJS)
→ EJS_onSaveState(e) intercepté dans l'iframe
→ postMessage { state: Uint8Array, screenshot } → parent
→ upload binaire → saves/{romId}/auto.state (upsert)
→ upsert save_states en base
| Limite | Détail |
|---|---|
| BIOS DS | Certains jeux NDSi-enhanced nécessitent les fichiers BIOS officiels (non distribués légalement) |
| Load state cross-device | Nécessite de relancer le jeu pour charger le save depuis le cloud |
| Battery save (.sav) | Stocké localement par EmulatorJS (IndexedDB), non synchronisé cloud |
| Supabase Free | 500 Mo de storage total · 2 Go de bande passante/mois |
Pour le changer : constante PIN dans src/components/PinGate.jsx.
Le PIN est vérifié côté client et mémorisé en sessionStorage (reset à la fermeture de l'onglet).