import React, { useState, useCallback, useMemo, useRef, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { motion, AnimatePresence } from "framer-motion"; import { useConfig, useAudio, useUI } from "../../context/LauncherContext"; import { SwfImage, SwfService, SwfTag } from "../../services/SwfService"; export default function SwfView() { const { t } = useTranslation(); const { animationsEnabled } = useConfig(); const { playBackSound, playPressSound } = useAudio(); const { setActiveView } = useUI(); const [swfData, setSwfData] = useState<{ version: number, compressed: boolean, frameHeader: Uint8Array, tags: SwfTag[] } | null>(null); const [images, setImages] = useState([]); const [selectedImageId, setSelectedImageId] = useState(null); const [searchTerm, setSearchTerm] = useState(""); const [fileName, setFileName] = useState(null); const [imageUrls, setImageUrls] = useState>({}); const [notification, setNotification] = useState<{ message: string, type: "success" | "error" } | null>(null); const fileInputRef = useRef(null); const replaceInputRef = useRef(null); const showNotification = (message: string, type: "success" | "error" = "success") => { setNotification({ message, type }); setTimeout(() => setNotification(null), 3000); }; const filteredImages = useMemo(() => { return images.filter(img => img.id.toString().includes(searchTerm) || (img.name && img.name.toLowerCase().includes(searchTerm.toLowerCase())) ); }, [images, searchTerm]); const selectedImage = useMemo(() => { return images.find(img => img.id === selectedImageId) || null; }, [images, selectedImageId]); const handleBack = useCallback(() => { playBackSound(); setActiveView("devtools"); }, [playBackSound, setActiveView]); const loadUrl = async (img: SwfImage) => { if (imageUrls[img.id]) return imageUrls[img.id]; let url = ""; if (img.type === "jpeg") { const blob = new Blob([img.data], { type: "image/jpeg" }); url = URL.createObjectURL(blob); } else if (img.type === "lossless") { const rgba = await SwfService.decodeLosslessToRGBA(img); const canvas = document.createElement("canvas"); canvas.width = img.width!; canvas.height = img.height!; const ctx = canvas.getContext("2d"); if (ctx) { const imageData = new ImageData(new Uint8ClampedArray(rgba), img.width!, img.height!); ctx.putImageData(imageData, 0, 0); url = canvas.toDataURL(); } } if (url) { setImageUrls(prev => ({ ...prev, [img.id]: url })); } return url; }; useEffect(() => { if (selectedImage) { loadUrl(selectedImage); } }, [selectedImage]); const processFile = async (file: File) => { setFileName(file.name); setImageUrls({}); try { const buffer = await file.arrayBuffer(); const bytes = new Uint8Array(buffer); const swf = SwfService.parse(bytes); setSwfData(swf); const extracted = SwfService.extractImages(swf.tags); setImages(extracted); if (extracted.length > 0) { setSelectedImageId(extracted[0].id); } showNotification(t("swfEditor.loaded", { name: file.name })); } catch (e: unknown) { console.error(e); showNotification(t("swfEditor.failedToProcess"), "error"); setImages([]); setSwfData(null); } }; const handleFileChange = (e: React.ChangeEvent) => { if (e.target.files && e.target.files[0]) { processFile(e.target.files[0]); } }; const handleDownload = async (img: SwfImage) => { playPressSound(); let blob: Blob; let ext = "png"; if (img.type === "jpeg") { blob = new Blob([img.data], { type: "image/jpeg" }); ext = "jpg"; } else if (img.type === "lossless") { const rgba = await SwfService.decodeLosslessToRGBA(img); const canvas = document.createElement("canvas"); canvas.width = img.width!; canvas.height = img.height!; const ctx = canvas.getContext("2d"); if (ctx) { const imageData = new ImageData(new Uint8ClampedArray(rgba), img.width!, img.height!); ctx.putImageData(imageData, 0, 0); const dataUrl = canvas.toDataURL("image/png"); const res = await fetch(dataUrl); blob = await res.blob(); } else { blob = new Blob([rgba], { type: "application/octet-stream" }); ext = "bin"; } } else { blob = new Blob([img.data], { type: "application/octet-stream" }); ext = "bin"; } const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `${img.name || `image_${img.id}`}.${ext}`; a.click(); URL.revokeObjectURL(url); showNotification(t("swfEditor.exported", { name: img.name || img.id })); }; const handleReplace = async (e: React.ChangeEvent) => { if (!swfData || !selectedImageId || !e.target.files?.[0]) return; const file = e.target.files[0]; playPressSound(); const buffer = await file.arrayBuffer(); const newData = new Uint8Array(buffer); const newTags = SwfService.updateImageTag( swfData.tags, selectedImageId, newData, selectedImage?.type || "unknown" ); const newSwfData = { ...swfData, tags: newTags }; setSwfData(newSwfData); const extracted = SwfService.extractImages(newTags); setImages(extracted); setImageUrls(prev => { const next = { ...prev }; delete next[selectedImageId]; return next; }); showNotification(t("swfEditor.imageReplaced"), "success"); e.target.value = ""; }; const handleSaveSwf = () => { if (!swfData) return; playPressSound(); const result = SwfService.serialize(swfData.version, swfData.compressed, swfData.frameHeader, swfData.tags); const blob = new Blob([result], { type: "application/x-shockwave-flash" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = fileName || "output.swf"; a.click(); URL.revokeObjectURL(url); showNotification(t("swfEditor.savedSuccessfully")); }; return (

{t("swfEditor.title")}

{!swfData ? (

{t("swfEditor.openToBegin")}

) : (
setSearchTerm(e.target.value)} className="w-full bg-black/40 border-2 border-[#373737] text-white px-4 py-2 outline-none focus:border-[#FFFF55] transition-colors" />
{filteredImages.map(img => (
{ playPressSound(); setSelectedImageId(img.id); }} className={`flex items-center gap-3 p-3 cursor-pointer border-l-4 ${selectedImageId === img.id ? "bg-[#FFFF55]/10 border-[#FFFF55] text-[#FFFF55]" : "border-transparent text-white/60" }`} >
ID: {img.id} {img.name ? `- ${img.name}` : ""} {img.type} {img.width ? `(${img.width}x${img.height})` : ""}
))}
{!selectedImage ? (
{t("swfEditor.selectImageViewDetails")}
) : (

{selectedImage.name || `Bitmap ${selectedImage.id}`}

{t("swfEditor.characterId")} {selectedImage.id} {t("swfEditor.type")} {selectedImage.type} {selectedImage.width && {t("swfEditor.size")} {selectedImage.width}x{selectedImage.height}}
{imageUrls[selectedImage.id] ? ( {`Bitmap ) : (
{t("swfEditor.loadingPreview")}
)}
)}
)} {notification && ( {notification.message} )}
); }