mirror of
https://github.com/LCE-Hub/LCE-Emerald-Launcher.git
synced 2026-08-20 04:27:29 +00:00
Some checks failed
Nix / build (push) Has been cancelled
Co-authored-by: str1k3r <115313679+S1l3ntStr1ke87@users.noreply.github.com>
206 lines
6.5 KiB
TypeScript
206 lines
6.5 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import { useLocalStorage } from "./useLocalStorage";
|
|
import { PckService } from "../services/PckService";
|
|
import { TauriService } from "../services/TauriService";
|
|
import { PCKAsset, PCKAssetType, PCKProperty } from "../types/pck";
|
|
interface Edition {
|
|
id: string;
|
|
supportsSlimSkins?: boolean;
|
|
}
|
|
|
|
interface UseSkinSyncProps {
|
|
username: string;
|
|
profile: string;
|
|
editions: Edition[];
|
|
}
|
|
|
|
export function useSkinSync({ username, profile, editions }: UseSkinSyncProps) {
|
|
const [skinUrl, setSkinUrl] = useLocalStorage(
|
|
"lce-skin",
|
|
"/images/Default.png",
|
|
);
|
|
const [skinIsSlim, setSkinIsSlim] = useLocalStorage("lce-skin-slim", false);
|
|
const [skinBase64, setSkinBase64] = useState<string | null>(null);
|
|
const [capeUrl, setCapeUrl] = useLocalStorage<string | null>(
|
|
"lce-cape",
|
|
null,
|
|
);
|
|
const [capeBase64, setCapeBase64] = useState<string | null>(null);
|
|
useEffect(() => {
|
|
let cancelled: Boolean = false;
|
|
let editionNotSupportSlimSkins: Boolean = !editions.find(
|
|
(edition: Edition) => edition.id === profile,
|
|
)?.supportsSlimSkins; //neo: the bang (!) for "not" here is not a typo. its a fix because neoLegacy supports 64x64 now, no need to remodel the arms
|
|
if (!skinUrl) return;
|
|
const img = new Image();
|
|
img.crossOrigin = "anonymous";
|
|
img.onload = async () => {
|
|
if (cancelled) return;
|
|
const cvs = document.createElement("canvas");
|
|
cvs.width = 64;
|
|
cvs.height = editionNotSupportSlimSkins ? 32 : 64;
|
|
const ctx = cvs.getContext("2d");
|
|
if (ctx) {
|
|
ctx.drawImage(img, 0, 0);
|
|
const b64 = cvs.toDataURL("image/png");
|
|
setSkinBase64(b64);
|
|
try {
|
|
const res = await fetch(b64);
|
|
const skinBuf = await res.arrayBuffer();
|
|
const isModernHeight = img.height === 64;
|
|
const animValue = skinIsSlim
|
|
? editionNotSupportSlimSkins
|
|
? "0x00041800"
|
|
: "0x00080000"
|
|
: isModernHeight
|
|
? "0x00040000"
|
|
: "0x00000000";
|
|
let boxes: PCKProperty[] = [];
|
|
if (skinIsSlim && editionNotSupportSlimSkins) {
|
|
boxes.push({
|
|
key: "BOX",
|
|
value: "ARM0 -2 -2 -2 3 12 4 40 16 0 0 0",
|
|
});
|
|
boxes.push({
|
|
key: "BOX",
|
|
value: "ARM1 -1 -2 -2 3 12 4 40 16 0 1 0",
|
|
});
|
|
}
|
|
|
|
let capeBuf: ArrayBuffer | null = null;
|
|
if (capeUrl) {
|
|
const capeImg = new Image();
|
|
capeImg.crossOrigin = "anonymous";
|
|
await new Promise<void>((resolve) => {
|
|
capeImg.onload = () => resolve();
|
|
capeImg.onerror = () => resolve();
|
|
capeImg.src = capeUrl;
|
|
});
|
|
if (!cancelled && capeImg.complete && capeImg.naturalWidth > 0) {
|
|
const capeCanvas = document.createElement("canvas");
|
|
capeCanvas.width = capeImg.naturalWidth;
|
|
capeCanvas.height = capeImg.naturalHeight;
|
|
const capeCtx = capeCanvas.getContext("2d");
|
|
if (capeCtx) {
|
|
capeCtx.drawImage(capeImg, 0, 0);
|
|
const capeDataUrl = capeCanvas.toDataURL("image/png");
|
|
const capeRes = await fetch(capeDataUrl);
|
|
capeBuf = await capeRes.arrayBuffer();
|
|
setCapeBase64(capeDataUrl);
|
|
}
|
|
}
|
|
} else {
|
|
setCapeBase64(null);
|
|
}
|
|
|
|
const getSeededId = (name: string) => {
|
|
let hash = 0;
|
|
for (let i = 0; i < name.length; i++) {
|
|
hash = (hash << 5) - hash + name.charCodeAt(i);
|
|
hash |= 0;
|
|
}
|
|
return Math.abs(hash).toString().padStart(8, "0").slice(-8);
|
|
};
|
|
|
|
const seededId = getSeededId(username);
|
|
//neo: (comment reason stated below) const packId = seededId.slice(-4);
|
|
const files: PCKAsset[] = [
|
|
/*neo: commented because it causes neoLegacy to think its a Texture Pack, while it does nothing when removed. preserved for future reference.
|
|
{
|
|
id: "0",
|
|
path: "0",
|
|
type: PCKAssetType.INFO,
|
|
size: 0,
|
|
data: new Uint8Array(0),
|
|
properties: [
|
|
{
|
|
key: "PACKID",
|
|
value: packId,
|
|
},
|
|
],
|
|
},*/
|
|
{
|
|
id: `dlcskin${seededId}`,
|
|
path: `dlcskin${seededId}.png`,
|
|
type: PCKAssetType.SKIN,
|
|
size: skinBuf.byteLength,
|
|
data: new Uint8Array(skinBuf),
|
|
properties: [
|
|
{
|
|
key: "DISPLAYNAME",
|
|
value: username,
|
|
},
|
|
{
|
|
key: "GAME_FLAGS",
|
|
value: "0x18",
|
|
},
|
|
{
|
|
key: "FREE",
|
|
value: "1",
|
|
},
|
|
{
|
|
key: "ANIM",
|
|
value: animValue,
|
|
},
|
|
...boxes,
|
|
{
|
|
key: "THEMENAME",
|
|
value: "Emerald Launcher",
|
|
},
|
|
{
|
|
key: "CAPEPATH",
|
|
value: `dlccape${seededId}.png`,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
if (capeBuf) {
|
|
files.push({
|
|
id: `dlccape${seededId}`,
|
|
path: `dlccape${seededId}.png`,
|
|
type: PCKAssetType.CAPE,
|
|
size: capeBuf.byteLength,
|
|
data: new Uint8Array(capeBuf),
|
|
properties: [],
|
|
});
|
|
}
|
|
|
|
const pckBuf = PckService.serializePCK({
|
|
version: 3,
|
|
endianness: "little",
|
|
xmlSupport: false,
|
|
properties: [
|
|
"ANIM",
|
|
"DISPLAYNAME",
|
|
"THEMENAME",
|
|
"GAME_FLAGS",
|
|
"FREE",
|
|
"BOX",
|
|
],
|
|
files,
|
|
});
|
|
await TauriService.saveGlobalSkinPck(new Uint8Array(pckBuf));
|
|
} catch (e) {
|
|
console.error("Failed to generate and save Skin PCK", e);
|
|
}
|
|
}
|
|
};
|
|
img.src = skinUrl;
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [skinUrl, capeUrl, username, profile, editions, skinIsSlim]);
|
|
|
|
return {
|
|
skinUrl,
|
|
setSkinUrl,
|
|
skinIsSlim,
|
|
setSkinIsSlim,
|
|
skinBase64,
|
|
capeUrl,
|
|
setCapeUrl,
|
|
capeBase64,
|
|
};
|
|
}
|