Files
AeroMc-Launcher/src/garderobe.js
T
2026-10-05 17:20:30 +00:00

107 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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 };