/* * Garderobe: gespeicherte Skins und Umhänge zum schnellen Wechseln. * * Mojang merkt sich keine früheren Skins – wer einen neuen hochlädt, verliert * den alten. Die Garderobe hält Skins (samt Armform) und eigene AeroMC-Umhänge * als PNG-Dateien im Launcher-Datenordner fest; vor jedem Skinwechsel im * Launcher wird der bisherige Skin automatisch gesichert. Gleiche Dateien * (gleiche Prüfsumme) werden nicht doppelt abgelegt. */ const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const MAX_AUTOMATISCH = 20; // ältere automatische Sicherungen fliegen raus let ordner = null; function init(userDataDir) { ordner = path.join(userDataDir, 'garderobe'); } const indexDatei = () => path.join(ordner, 'garderobe.json'); function lies() { try { return JSON.parse(fs.readFileSync(indexDatei(), 'utf8')); } catch { return []; } } function schreibe(liste) { fs.mkdirSync(ordner, { recursive: true }); fs.writeFileSync(indexDatei(), JSON.stringify(liste, null, 1)); } /** Alle Einträge, neueste zuerst, jeweils mit Bild als data:-Adresse. */ function liste() { return lies() .filter((e) => fs.existsSync(path.join(ordner, e.datei))) .sort((a, b) => String(b.erstellt).localeCompare(String(a.erstellt))) .map((e) => Object.assign({}, e, { dataUri: 'data:image/png;base64,' + fs.readFileSync(path.join(ordner, e.datei)).toString('base64'), })); } /** * Legt einen Skin/Umhang ab. art: 'skin' | 'umhang', daten: PNG-Buffer. * Gibt es dieselbe Datei schon, wird nur ggf. aus "automatisch" ein fester Eintrag. */ function hinzufuegen({ art, daten, name, variante, automatisch }) { const hash = crypto.createHash('sha1').update(daten).digest('hex'); const eintraege = lies(); const vorhanden = eintraege.find((e) => e.art === art && e.hash === hash); if (vorhanden) { if (!automatisch && vorhanden.automatisch) { vorhanden.automatisch = false; if (name) vorhanden.name = name; schreibe(eintraege); } return { eintrag: vorhanden, neu: false }; } fs.mkdirSync(ordner, { recursive: true }); const id = Date.now().toString(36) + crypto.randomBytes(3).toString('hex'); const datei = `${art}-${id}.png`; fs.writeFileSync(path.join(ordner, datei), daten); const eintrag = { id, art, hash, datei, name: String(name || '').trim().slice(0, 40) || (art === 'skin' ? 'Skin' : 'Umhang'), variante: art === 'skin' ? (variante === 'slim' ? 'slim' : 'classic') : undefined, automatisch: !!automatisch, erstellt: new Date().toISOString(), }; eintraege.push(eintrag); // automatische Sicherungen begrenzen (feste Einträge bleiben immer) const auto = eintraege.filter((e) => e.automatisch).sort((a, b) => String(b.erstellt).localeCompare(String(a.erstellt))); for (const alt of auto.slice(MAX_AUTOMATISCH)) loescheIntern(eintraege, alt.id); schreibe(eintraege); return { eintrag, neu: true }; } function loescheIntern(eintraege, id) { const i = eintraege.findIndex((e) => e.id === id); if (i < 0) return false; try { fs.unlinkSync(path.join(ordner, eintraege[i].datei)); } catch { /* schon weg */ } eintraege.splice(i, 1); return true; } function loeschen(id) { const eintraege = lies(); const ok = loescheIntern(eintraege, id); if (ok) schreibe(eintraege); return ok; } function umbenennen(id, name) { const eintraege = lies(); const e = eintraege.find((x) => x.id === id); if (!e) return false; e.name = String(name || '').trim().slice(0, 40) || e.name; e.automatisch = false; // umbenannt = bewusst behalten schreibe(eintraege); return true; } /** Eintrag samt Dateipfad (zum Anziehen). */ function holen(id) { const e = lies().find((x) => x.id === id); return e ? Object.assign({}, e, { pfad: path.join(ordner, e.datei) }) : null; } module.exports = { init, liste, hinzufuegen, loeschen, umbenennen, holen };