107 lines
3.8 KiB
JavaScript
107 lines
3.8 KiB
JavaScript
/*
|
||
* 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 };
|