From fb4d790b3d05ee5536ed1525b068e907105b0916 Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Fri, 4 Apr 2025 20:42:03 +0100 Subject: [PATCH] feat: mii rendering --- package.json | 2 ++ pnpm-lock.yaml | 24 +++++++++++++++ src/app/components/submit-form.tsx | 48 +++++++++++++++++++++++++----- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index a5f0676..8838018 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@auth/prisma-adapter": "2.7.2", + "@pretendonetwork/mii-js": "github:PretendoNetwork/mii-js#publish-and-eslint", "@prisma/client": "^6.5.0", "@trafficlunar/asmcrypto.js": "^1.0.2", "@yudiel/react-qr-scanner": "2.2.2-beta.2", @@ -18,6 +19,7 @@ "jsqr": "^1.4.0", "next": "15.2.4", "next-auth": "5.0.0-beta.25", + "qrcode-generator": "^1.4.4", "react": "^19.0.0", "react-dom": "^19.0.0", "react-dropzone": "^14.3.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8a2dbcc..bf72d60 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@auth/prisma-adapter': 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)) + '@pretendonetwork/mii-js': + specifier: github:PretendoNetwork/mii-js#publish-and-eslint + version: https://codeload.github.com/PretendoNetwork/mii-js/tar.gz/8b857460b61dde9729eff848d99fd3ef9128f2a5 '@prisma/client': specifier: ^6.5.0 version: 6.5.0(prisma@6.5.0(typescript@5.8.2))(typescript@5.8.2) @@ -32,6 +35,9 @@ importers: next-auth: 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) + qrcode-generator: + specifier: ^1.4.4 + version: 1.4.4 react: specifier: ^19.0.0 version: 19.1.0 @@ -626,6 +632,10 @@ packages: '@panva/hkdf@1.2.1': 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': resolution: {integrity: sha512-M6w1Ql/BeiGoZmhMdAZUXHu5sz5HubyVcKukbLs3l0ELcQb8hTUJxtGEChhv4SVJ0QJlwtLnwOLgIRQhpsm9dw==} engines: {node: '>=18.18'} @@ -1001,6 +1011,9 @@ packages: barcode-detector@3.0.1: resolution: {integrity: sha512-3fCzG/Py4SVgZJhubD1mt7rVprtHEVWrxQN4FUOG0oulPE4193evbgyptxcOYsfTNEtMlWc+Ec9tdxhjlR4/Ww==} + bit-buffer@0.2.5: + resolution: {integrity: sha512-x1yGnmXvFg6e3DiyRztElbcn1bsCTFSoM/ncAzY62uE0JdTl5xlKJd0ooqLYoPbhdsnpehSIQrdIvclcZJYwiA==} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -1934,6 +1947,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qrcode-generator@1.4.4: + resolution: {integrity: sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -2722,6 +2738,10 @@ snapshots: '@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)': optionalDependencies: prisma: 6.5.0(typescript@5.8.2) @@ -3109,6 +3129,8 @@ snapshots: transitivePeerDependencies: - '@types/emscripten' + bit-buffer@0.2.5: {} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -4201,6 +4223,8 @@ snapshots: punycode@2.3.1: {} + qrcode-generator@1.4.4: {} + queue-microtask@1.2.3: {} react-dom@19.1.0(react@19.1.0): diff --git a/src/app/components/submit-form.tsx b/src/app/components/submit-form.tsx index a8d64c1..5043fdb 100644 --- a/src/app/components/submit-form.tsx +++ b/src/app/components/submit-form.tsx @@ -4,12 +4,14 @@ import { useEffect, useState } from "react"; import { useDropzone } from "react-dropzone"; 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 QrUpload from "./submit/qr-upload"; 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]); export default function SubmitForm() { @@ -22,10 +24,14 @@ export default function SubmitForm() { const [isQrScannerOpen, setIsQrScannerOpen] = useState(false); const [qrBytes, setQrBytes] = useState(new Uint8Array()); + const [studioUrl, setStudioUrl] = useState(); + const [generatedQrCodeUrl, setGeneratedQrCodeUrl] = useState(); + useEffect(() => { if (qrBytes.length == 0) return; - const decrypt = async () => { + const decode = async () => { + // Decrypt the QR code const nonce = qrBytes.subarray(0, 8); const content = qrBytes.subarray(8, 0x70); @@ -33,17 +39,45 @@ export default function SubmitForm() { nonceWithZeros.set(nonce, 0); 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]); return (
e.preventDefault()} className="grid grid-cols-2"> -
+
+
+ Nintendo Studio URL + Generated QR Code +
+