Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: react version of smdh creator #3

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 22 additions & 55 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@ import JSZip from 'jszip';
import React, { DragEvent, useEffect, useState } from "react";
import { Image, Layer, Rect, Stage, Text } from "react-konva";

import { SplashImage } from './SplashImage'
import { MetaSection } from './components/MetaSection'
import { SplashImage } from './components/SplashImage'

// import original3DsImage from "./assets/original-3ds.png";
import logoImage from "./assets/splash-ds-logo.png";
import new3DsImage from "./assets/new-3ds.png";

import './App.css';
import './styles/App.css';
import './styles/smdh_creator.css';


import { convertImageToBin } from './utils/convertImageToBin';
import { downloadFile } from './utils/downloadFile';
import { loadImage } from './utils/loadImage';
import { rotateImage } from './utils/rotateImage';
import { SMDH } from './utils/SMDH'

// @ts-expect-error
window.Konva.pixelRatio = 1;
Expand Down Expand Up @@ -41,16 +50,6 @@ const screenConfig: IIndexable = {
// height: 48,
// }

const loadImage = (file: any) => {
return new Promise((resolve, reject) => {
const img = new window.Image();
img.onload = function () {
resolve(img);
};
img.src = file;
});
};

// const scaleImage = (image: CanvasImageSource) => {
// const scaledCanvas = document.createElement('canvas');
// scaledCanvas.width = image.width as number * 0.5;
Expand All @@ -63,24 +62,6 @@ const loadImage = (file: any) => {
// return scaledCanvas;
// }

const rotateImage = (image: CanvasImageSource) => {
const rotationCanvas = document.createElement('canvas');
rotationCanvas.width = image.height as number;
rotationCanvas.height = image.width as number;

const rotationContext = rotationCanvas.getContext("2d");

if(rotationContext) {
rotationContext.save();
rotationContext.translate(rotationCanvas.width/2, rotationCanvas.height/2);
rotationContext.rotate(90 * Math.PI/180);
rotationContext.drawImage(image, -image.width/2, -image.height/2);
rotationContext.restore();
}

return rotationCanvas;
}

// const previewImage = (canvas: HTMLCanvasElement) => {
// const imageUrl = canvas.toDataURL("image/png");
// const image = document.createElement('img');
Expand All @@ -90,44 +71,23 @@ const rotateImage = (image: CanvasImageSource) => {
// document.getElementById('preview')?.appendChild(image);
// }

const convertImageToBin = (canvas: HTMLCanvasElement) => {
if(canvas) {
const canvasData = canvas?.getContext('2d')?.getImageData(0, 0, canvas.width, canvas.height).data;
let fileData = '';

if(canvasData) {
for(var i = 0; i < canvasData.length; i += 4) {
fileData += String.fromCharCode(canvasData[i+2], canvasData[i+1], canvasData[i]);
}
}

return fileData;
}
}

const downloadFile = (url: string, name: string) => {
const link = document.createElement('a');
link.href = url;
link.download = name;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}

const App = () => {
const [splashImage, setSplashImage] = useState(undefined);
const [splashDataTop, setSplashDataTop] = useState('');
const [splashDataBottom, setSplashDataBottom] = useState('');
const [dsImage, setDsImage] = useState(undefined);
const [previewImage, setPreviewImage] = useState('');
// const [iconImage, setIconImage] = useState('');
const [smdhFile, setSmdhFile] = useState<Blob | undefined>(undefined);

const [dsOpacity, setDsOpacity] = useState(0)
const [keepRatio, setKeepRatio] = useState(true)
const [backgroundColor, setBackgroundColor] = useState('#228ae2');

const [downloadRequested, setDownloadRequested] = useState(false);

const smdh = new SMDH();

const reset = () => {
setSplashImage(undefined);
setSplashDataTop('');
Expand Down Expand Up @@ -252,6 +212,10 @@ const App = () => {
}

const splishSplash = () => {
const smdhBlob = smdh.save();
console.log('splishSplash smdh', smdhBlob);
setSmdhFile(smdhBlob);

setDownloadRequested(true);
}

Expand Down Expand Up @@ -299,7 +263,9 @@ const App = () => {
zip.file('splash.bin', splashDataTop, { binary: true, createFolders: false });
zip.file('splashbottom.bin', splashDataBottom, { binary: true, createFolders: false });
zip.file('preview.png', previewImage.slice(22), { base64: true, createFolders: false });
// zip.file('icon.png', iconImage.slice(22), { base64: true, createFolders: false});
if(smdhFile) {
zip.file('info.smdh', smdhFile, { binary: true, createFolders: false});
}

zip.generateAsync({ type: "blob" }).then((blob) => {
const reader = new FileReader();
Expand Down Expand Up @@ -365,6 +331,7 @@ const App = () => {
{dsImage && <Image image={dsImage} listening={false} opacity={1 - dsOpacity} />}
</Layer>
</Stage>
<MetaSection smdh={smdh} />
{/* <div id="icon">
</div> */}
<button onClick={splishSplash} disabled={!splashImage}>download splash screen</button>
Expand Down
Binary file added src/assets/flags.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions src/assets/smdh_creator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
SMDH Creator v1.1 by Marc Robledo 2015
based on SMDH Creator by GEMISIS https://github.com/gemisis/SMDH-Creator
more info about SMDH format: http://3dbrew.org/wiki/SMDH
*/
function el(e) {
return document.getElementById(e);
}

function addEvent(e, ev, f) {
if (e.addEventListener) {
e.addEventListener(ev, f, false);
return true;
} else if (e.attachEvent) e.attachEvent("on" + ev, f);
}

var smdh, tempImage, tempImageReader, tempFile, tempFileLoadFunction;

// function importBigCanvas() {
// tempImageReader.readAsDataURL(el("file-load").files[0]);
// }
// function importSmallCanvas() {
// tempImageReader.readAsDataURL(el("file-load").files[0]);
// }

// function clickImportBigCanvas() {
// tempFileLoadFunction = importBigCanvas;
// el("file-load").click();
// }
// function clickImportSmallCanvas() {
// tempFileLoadFunction = importSmallCanvas;
// el("file-load").click();
// }
// function clickLoadSMDH() {
// tempFileLoadFunction = loadSMDH;
// el("file-load").click();
// }
// function clickSaveSMDH() {
// smdh.save();
// }

/* Initialize SMDH Creator */
addEvent(window, "load", function () {
// smdh = new SMDH();

tempImage = document.createElement("img");
addEvent(tempImage, "load", function () {
if (el("both-icons").checked) {
el("big-icon").getContext("2d").drawImage(tempImage, 0, 0, 48, 48);
el("small-icon").getContext("2d").drawImage(tempImage, 0, 0, 24, 24);
smdh.convertIcon(true, false);
smdh.convertIcon(false, false);
} else if (tempFileLoadFunction == importBigCanvas) {
el("big-icon").getContext("2d").drawImage(tempImage, 0, 0, 48, 48);
smdh.convertIcon(true, false);
} else if (tempFileLoadFunction == importSmallCanvas) {
el("small-icon").getContext("2d").drawImage(tempImage, 0, 0, 24, 24);
smdh.convertIcon(false, false);
}
});

tempImageReader = new FileReader();
addEvent(tempImageReader, "load", function () {
//if(validFileType)
tempImage.src = tempImageReader.result;
});
});
84 changes: 84 additions & 0 deletions src/components/MetaSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, { useEffect, useRef, useState } from "react";
import { SMDH } from '../utils/SMDH'
import { loadImage } from '../utils/loadImage';

type MetaSectionProps = {
smdh: SMDH
}

const MetaSection = ({ smdh }: MetaSectionProps) => {

const [iconFile, setIconFile] = useState<File | undefined>();

const bigIconRef = useRef<HTMLCanvasElement>(null);
// const smallIconRef = useRef<HTMLCanvasElement>(null);
const iconFileRef = useRef<HTMLInputElement>(null);

const clickImportBigCanvas = () => {
console.log('clickImportBigCanvas');
iconFileRef.current?.click();
}

useEffect(() => {
console.log('MetaSection useEffect');

if(iconFile) {
const reader = new window.FileReader();
reader.onload = (() => {
return function (event: any) {
const dataUrl = event.target.result;
loadImage(dataUrl).then((img: any) => {
// setSplashImage(img);


// console.log(img);

bigIconRef?.current?.getContext("2d")?.drawImage(img, 0, 0, 48, 48);
// smallIconRef?.current?.getContext("2d")?.drawImage(img, 0, 0, 24, 24);

smdh.convertIcon(true, false);
// smdh.convertIcon(false, false);
});
};
})();

reader.readAsDataURL(iconFile);
}
});

return (
<div className="home-block">
<form id="file-form">
<input id="file-load" type="file" ref={iconFileRef} onChange={event => {
setIconFile(event?.target?.files?.[0]);
}} />
</form>
<div className="row">
<label htmlFor="big-icon" className="label">Icon:</label>
<canvas id="big-icon" ref={bigIconRef} width={48} height={48} className="button" onClick={clickImportBigCanvas}></canvas>
{/* <canvas id="small-icon" ref={smallIconRef} width={24} height={24} className="button" onClick={clickImportSmallCanvas}></canvas><br/> */}
{/* <input type="checkbox" defaultChecked id="both-icons" /> */}
{/* <label htmlFor="both-icons"> Update both when importing</label> */}
</div>

<div id="langs">
<div className="row" id="row-short-description">
<label htmlFor="short-description" className="label">Title:</label>
<input id="short-description" type="text" maxLength={64} className="input" />
</div>

<div className="row" id="row-long-description">
<label htmlFor="long-description" className="label">Description:</label>
<input id="long-description" type="text" maxLength={128} className="input" />
</div>

<div className="row" id="row-publisher">
<label htmlFor="publisher" className="label">Author:</label>
<input id="publisher" type="text" maxLength={64} className="input" defaultValue="splash-ds" />
</div>
</div>
</div>
)
}

export { MetaSection }
4 changes: 2 additions & 2 deletions src/SplashImage.tsx → src/components/SplashImage.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React, { useRef } from "react";
import { Image, Transformer } from "react-konva";

type Props = {
type SplashImageProps = {
image: any
keepRatio: boolean
showTransformer: boolean
}

const SplashImage = (props: Props) => {
const SplashImage = (props: SplashImageProps) => {

const { image, keepRatio, showTransformer } = props

Expand Down
File renamed without changes.
Loading