feat: mii rendering
This commit is contained in:
parent
3a2e4c9b63
commit
fb4d790b3d
3 changed files with 67 additions and 7 deletions
|
|
@ -11,6 +11,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/prisma-adapter": "2.7.2",
|
"@auth/prisma-adapter": "2.7.2",
|
||||||
|
"@pretendonetwork/mii-js": "github:PretendoNetwork/mii-js#publish-and-eslint",
|
||||||
"@prisma/client": "^6.5.0",
|
"@prisma/client": "^6.5.0",
|
||||||
"@trafficlunar/asmcrypto.js": "^1.0.2",
|
"@trafficlunar/asmcrypto.js": "^1.0.2",
|
||||||
"@yudiel/react-qr-scanner": "2.2.2-beta.2",
|
"@yudiel/react-qr-scanner": "2.2.2-beta.2",
|
||||||
|
|
@ -18,6 +19,7 @@
|
||||||
"jsqr": "^1.4.0",
|
"jsqr": "^1.4.0",
|
||||||
"next": "15.2.4",
|
"next": "15.2.4",
|
||||||
"next-auth": "5.0.0-beta.25",
|
"next-auth": "5.0.0-beta.25",
|
||||||
|
"qrcode-generator": "^1.4.4",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-dropzone": "^14.3.8",
|
"react-dropzone": "^14.3.8",
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ importers:
|
||||||
'@auth/prisma-adapter':
|
'@auth/prisma-adapter':
|
||||||
specifier: 2.7.2
|
specifier: 2.7.2
|
||||||
version: 2.7.2(@prisma/client@6.5.0(prisma@6.5.0(typescript@5.8.2))(typescript@5.8.2))
|
version: 2.7.2(@prisma/client@6.5.0(prisma@6.5.0(typescript@5.8.2))(typescript@5.8.2))
|
||||||
|
'@pretendonetwork/mii-js':
|
||||||
|
specifier: github:PretendoNetwork/mii-js#publish-and-eslint
|
||||||
|
version: https://codeload.github.com/PretendoNetwork/mii-js/tar.gz/8b857460b61dde9729eff848d99fd3ef9128f2a5
|
||||||
'@prisma/client':
|
'@prisma/client':
|
||||||
specifier: ^6.5.0
|
specifier: ^6.5.0
|
||||||
version: 6.5.0(prisma@6.5.0(typescript@5.8.2))(typescript@5.8.2)
|
version: 6.5.0(prisma@6.5.0(typescript@5.8.2))(typescript@5.8.2)
|
||||||
|
|
@ -32,6 +35,9 @@ importers:
|
||||||
next-auth:
|
next-auth:
|
||||||
specifier: 5.0.0-beta.25
|
specifier: 5.0.0-beta.25
|
||||||
version: 5.0.0-beta.25(next@15.2.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
|
version: 5.0.0-beta.25(next@15.2.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
|
||||||
|
qrcode-generator:
|
||||||
|
specifier: ^1.4.4
|
||||||
|
version: 1.4.4
|
||||||
react:
|
react:
|
||||||
specifier: ^19.0.0
|
specifier: ^19.0.0
|
||||||
version: 19.1.0
|
version: 19.1.0
|
||||||
|
|
@ -626,6 +632,10 @@ packages:
|
||||||
'@panva/hkdf@1.2.1':
|
'@panva/hkdf@1.2.1':
|
||||||
resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==}
|
resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==}
|
||||||
|
|
||||||
|
'@pretendonetwork/mii-js@https://codeload.github.com/PretendoNetwork/mii-js/tar.gz/8b857460b61dde9729eff848d99fd3ef9128f2a5':
|
||||||
|
resolution: {tarball: https://codeload.github.com/PretendoNetwork/mii-js/tar.gz/8b857460b61dde9729eff848d99fd3ef9128f2a5}
|
||||||
|
version: 1.0.4
|
||||||
|
|
||||||
'@prisma/client@6.5.0':
|
'@prisma/client@6.5.0':
|
||||||
resolution: {integrity: sha512-M6w1Ql/BeiGoZmhMdAZUXHu5sz5HubyVcKukbLs3l0ELcQb8hTUJxtGEChhv4SVJ0QJlwtLnwOLgIRQhpsm9dw==}
|
resolution: {integrity: sha512-M6w1Ql/BeiGoZmhMdAZUXHu5sz5HubyVcKukbLs3l0ELcQb8hTUJxtGEChhv4SVJ0QJlwtLnwOLgIRQhpsm9dw==}
|
||||||
engines: {node: '>=18.18'}
|
engines: {node: '>=18.18'}
|
||||||
|
|
@ -1001,6 +1011,9 @@ packages:
|
||||||
barcode-detector@3.0.1:
|
barcode-detector@3.0.1:
|
||||||
resolution: {integrity: sha512-3fCzG/Py4SVgZJhubD1mt7rVprtHEVWrxQN4FUOG0oulPE4193evbgyptxcOYsfTNEtMlWc+Ec9tdxhjlR4/Ww==}
|
resolution: {integrity: sha512-3fCzG/Py4SVgZJhubD1mt7rVprtHEVWrxQN4FUOG0oulPE4193evbgyptxcOYsfTNEtMlWc+Ec9tdxhjlR4/Ww==}
|
||||||
|
|
||||||
|
bit-buffer@0.2.5:
|
||||||
|
resolution: {integrity: sha512-x1yGnmXvFg6e3DiyRztElbcn1bsCTFSoM/ncAzY62uE0JdTl5xlKJd0ooqLYoPbhdsnpehSIQrdIvclcZJYwiA==}
|
||||||
|
|
||||||
brace-expansion@1.1.11:
|
brace-expansion@1.1.11:
|
||||||
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||||
|
|
||||||
|
|
@ -1934,6 +1947,9 @@ packages:
|
||||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
qrcode-generator@1.4.4:
|
||||||
|
resolution: {integrity: sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==}
|
||||||
|
|
||||||
queue-microtask@1.2.3:
|
queue-microtask@1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
|
|
||||||
|
|
@ -2722,6 +2738,10 @@ snapshots:
|
||||||
|
|
||||||
'@panva/hkdf@1.2.1': {}
|
'@panva/hkdf@1.2.1': {}
|
||||||
|
|
||||||
|
'@pretendonetwork/mii-js@https://codeload.github.com/PretendoNetwork/mii-js/tar.gz/8b857460b61dde9729eff848d99fd3ef9128f2a5':
|
||||||
|
dependencies:
|
||||||
|
bit-buffer: 0.2.5
|
||||||
|
|
||||||
'@prisma/client@6.5.0(prisma@6.5.0(typescript@5.8.2))(typescript@5.8.2)':
|
'@prisma/client@6.5.0(prisma@6.5.0(typescript@5.8.2))(typescript@5.8.2)':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
prisma: 6.5.0(typescript@5.8.2)
|
prisma: 6.5.0(typescript@5.8.2)
|
||||||
|
|
@ -3109,6 +3129,8 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/emscripten'
|
- '@types/emscripten'
|
||||||
|
|
||||||
|
bit-buffer@0.2.5: {}
|
||||||
|
|
||||||
brace-expansion@1.1.11:
|
brace-expansion@1.1.11:
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.2
|
balanced-match: 1.0.2
|
||||||
|
|
@ -4201,6 +4223,8 @@ snapshots:
|
||||||
|
|
||||||
punycode@2.3.1: {}
|
punycode@2.3.1: {}
|
||||||
|
|
||||||
|
qrcode-generator@1.4.4: {}
|
||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
|
|
||||||
react-dom@19.1.0(react@19.1.0):
|
react-dom@19.1.0(react@19.1.0):
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,14 @@ import { useEffect, useState } from "react";
|
||||||
import { useDropzone } from "react-dropzone";
|
import { useDropzone } from "react-dropzone";
|
||||||
import { Icon } from "@iconify/react";
|
import { Icon } from "@iconify/react";
|
||||||
|
|
||||||
|
import { AES_CCM } from "@trafficlunar/asmcrypto.js";
|
||||||
|
import Mii from "@pretendonetwork/mii-js";
|
||||||
|
import qrcode from "qrcode-generator";
|
||||||
|
|
||||||
import TagSelector from "./submit/tag-selector";
|
import TagSelector from "./submit/tag-selector";
|
||||||
import QrUpload from "./submit/qr-upload";
|
import QrUpload from "./submit/qr-upload";
|
||||||
import QrScanner from "./submit/qr-scanner";
|
import QrScanner from "./submit/qr-scanner";
|
||||||
|
|
||||||
import { AES_CCM } from "@trafficlunar/asmcrypto.js";
|
|
||||||
|
|
||||||
const key = new Uint8Array([0x59, 0xfc, 0x81, 0x7e, 0x64, 0x46, 0xea, 0x61, 0x90, 0x34, 0x7b, 0x20, 0xe9, 0xbd, 0xce, 0x52]);
|
const key = new Uint8Array([0x59, 0xfc, 0x81, 0x7e, 0x64, 0x46, 0xea, 0x61, 0x90, 0x34, 0x7b, 0x20, 0xe9, 0xbd, 0xce, 0x52]);
|
||||||
|
|
||||||
export default function SubmitForm() {
|
export default function SubmitForm() {
|
||||||
|
|
@ -22,10 +24,14 @@ export default function SubmitForm() {
|
||||||
const [isQrScannerOpen, setIsQrScannerOpen] = useState(false);
|
const [isQrScannerOpen, setIsQrScannerOpen] = useState(false);
|
||||||
const [qrBytes, setQrBytes] = useState<Uint8Array>(new Uint8Array());
|
const [qrBytes, setQrBytes] = useState<Uint8Array>(new Uint8Array());
|
||||||
|
|
||||||
|
const [studioUrl, setStudioUrl] = useState<string | undefined>();
|
||||||
|
const [generatedQrCodeUrl, setGeneratedQrCodeUrl] = useState<string | undefined>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (qrBytes.length == 0) return;
|
if (qrBytes.length == 0) return;
|
||||||
|
|
||||||
const decrypt = async () => {
|
const decode = async () => {
|
||||||
|
// Decrypt the QR code
|
||||||
const nonce = qrBytes.subarray(0, 8);
|
const nonce = qrBytes.subarray(0, 8);
|
||||||
const content = qrBytes.subarray(8, 0x70);
|
const content = qrBytes.subarray(8, 0x70);
|
||||||
|
|
||||||
|
|
@ -33,17 +39,45 @@ export default function SubmitForm() {
|
||||||
nonceWithZeros.set(nonce, 0);
|
nonceWithZeros.set(nonce, 0);
|
||||||
|
|
||||||
const decrypted = AES_CCM.decrypt(content, key, nonceWithZeros, undefined, 16);
|
const decrypted = AES_CCM.decrypt(content, key, nonceWithZeros, undefined, 16);
|
||||||
const result = new Uint8Array([...decrypted.subarray(0, 12), ...qrBytes.subarray(0, 8), ...decrypted.subarray(12, decrypted.length - 4)]);
|
const result = new Uint8Array(96);
|
||||||
|
result.set(decrypted.subarray(0, 12), 0);
|
||||||
|
result.set(nonce, 12);
|
||||||
|
result.set(decrypted.subarray(12), 20);
|
||||||
|
|
||||||
console.log(result);
|
// Convert to Mii class
|
||||||
|
const buffer = Buffer.from(result);
|
||||||
|
const mii = new Mii(buffer);
|
||||||
|
|
||||||
|
setStudioUrl(mii.studioUrl({ width: 128 }));
|
||||||
|
|
||||||
|
// Generate a new QR code for aesthetic reasons
|
||||||
|
const byteString = String.fromCharCode(...qrBytes);
|
||||||
|
const generatedCode = qrcode(0, "L");
|
||||||
|
generatedCode.addData(byteString, "Byte");
|
||||||
|
generatedCode.make();
|
||||||
|
|
||||||
|
setGeneratedQrCodeUrl(generatedCode.createDataURL());
|
||||||
};
|
};
|
||||||
|
|
||||||
decrypt();
|
decode();
|
||||||
}, [qrBytes]);
|
}, [qrBytes]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={(e) => e.preventDefault()} className="grid grid-cols-2">
|
<form onSubmit={(e) => e.preventDefault()} className="grid grid-cols-2">
|
||||||
<div className="p-4">
|
<div className="p-4 flex flex-col gap-2">
|
||||||
|
<div className="flex justify-center gap-2">
|
||||||
|
<img
|
||||||
|
src={studioUrl}
|
||||||
|
alt="Nintendo Studio URL"
|
||||||
|
className="aspect-square size-32 bg-orange-100 rounded-xl border-2 border-amber-500 text-[0px]"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
src={generatedQrCodeUrl}
|
||||||
|
alt="Generated QR Code"
|
||||||
|
className="aspect-square size-32 bg-orange-100 rounded-xl border-2 border-amber-500 text-[0px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="p-2 border-2 bg-orange-100 border-amber-500 rounded-2xl shadow-lg h-48">
|
<div className="p-2 border-2 bg-orange-100 border-amber-500 rounded-2xl shadow-lg h-48">
|
||||||
<div
|
<div
|
||||||
{...getRootProps({
|
{...getRootProps({
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue