import JSZip from "jszip" ; import { PlaylistStyle , SkinGenExColors , CursorImage } from "./types" ; import SKIN_SPRITES , { Sprite } from "./skinSprites" ; import { DEFAULT_SKIN } from "./constants" ; import * as Utils from "./utils" ; import * as FileUtils from "./fileUtils" ; export const getFileExtension = ( fileName ) : string | null => { const matches = /\.([a-z]{3,4})$/i . exec ( fileName ) ; return matches ? matches [ 1 ] . toLowerCase ( ) : null ; } ; function getFilenameRegex ( base , ext ) { return new RegExp ( `^(.*[/\\\\])?${ base }.(${ ext })$` , "i" ) ; } export async function getFileFromZip ( zip , fileName , ext , mode ) { const files = zip . file ( getFilenameRegex ( fileName , ext ) ) ; if ( ! files . length ) { return null ; } const lastFile = files [ files . length - 1 ] ; try { const contents = await lastFile . async ( mode ) ; return { contents , name : lastFile . name } ; } catch ( e ) { console . warn ( `Failed to extract "${ fileName }.${ ext }" from the skin archive.` ) ; return null ; } } function fallbackGetImgFromBlob ( blob ) { return Utils . imgFromUrl ( URL . createObjectURL ( blob ) ) ; } export async function getImgFromBlob ( blob ) { try { return await window . createImageBitmap ( blob ) ; } catch ( e ) { try { return await fallbackGetImgFromBlob ( blob ) ; } catch ( ee ) { return null ; } } } export function getSpriteUrisFromImg ( img , sprites ) { const canvas = document . createElement ( "canvas" ) ; const context = canvas . getContext ( "2d" ) ; if ( context == null ) { throw new Error ( "Failed to get canvas context" ) ; } const images = { } ; sprites . forEach ( ( sprite ) => { canvas . height = sprite . height ; canvas . width = sprite . width ; context . drawImage ( img , - sprite . x , - sprite . y ) ; const image = canvas . toDataURL ( ) ; images [ sprite . name ] = image ; } ) ; return images ; } export async function getImgFromFilename ( zip , fileName ) { const file = await getFileFromZip ( zip , fileName , "(png|bmp)" , "blob" ) ; if ( ! file ) { return null ; } const mimeType = `image/${ getFileExtension ( file . name ) || "*" }` ; const typedBlob = new Blob ( [ file . contents ] , { type : mimeType } ) ; return getImgFromBlob ( typedBlob ) ; } export async function getSpriteUrisFromFilename ( zip , fileName ) { const img = await getImgFromFilename ( zip , fileName ) ; if ( img == null ) { return { } ; } return getSpriteUrisFromImg ( img , SKIN_SPRITES [ fileName ] ) ; } const RIFF_MAGIC = "RIFF" . split ( "" ) . map ( ( c ) => c . charCodeAt ( 0 ) ) ; function arrayStartsWith ( arr , matcher ) { return matcher . every ( ( item , i ) => arr [ i ] === item ) ; } export async function getCursorFromFilename ( zip , fileName ) { const file = await getFileFromZip ( zip , fileName , "CUR" , "uint8array" ) ; if ( file == null ) { return null ; } const contents = file . contents as Uint8Array ; if ( arrayStartsWith ( contents , RIFF_MAGIC ) ) { return { type : "ani" , aniData : contents } ; } return { type : "cur" , url : FileUtils . curUrlFromByteArray ( contents ) } ; } export async function getPlaylistStyle ( zip ) { const files = zip . file ( getFilenameRegex ( "PLEDIT" , "txt" ) ) ; const file = files [ 0 ] ; if ( file == null ) { return DEFAULT_SKIN . playlistStyle ; } const ini = await file . async ( "text" ) ; if ( ini == null ) { return DEFAULT_SKIN . playlistStyle ; } const data = ini && Utils . parseIni ( ini ) . text ; if ( ! data ) { return DEFAULT_SKIN . playlistStyle ; } [ "normal" , "current" , "normalbg" , "selectedbg" , "mbFG" , "mbBG" ] . forEach ( ( colorKey ) => { let color = data [ colorKey ] ; if ( ! color ) { return ; } if ( color [ 0 ] !== "#" ) { color = `#${ color }` ; } data [ colorKey ] = color . slice ( 0 , 7 ) ; } ) ; return { ... DEFAULT_SKIN . playlistStyle , ... data } ; } export async function getGenExColors ( zip ) { const img = await getImgFromFilename ( zip , "GENEX" ) ; if ( img == null ) { return null ; } const canvas = document . createElement ( "canvas" ) ; const context = canvas . getContext ( "2d" ) ; if ( context == null ) { return null ; } canvas . width = img . width ; canvas . height = img . height ; context . drawImage ( img , 0 , 0 ) ; const getColorAt = ( x ) : string => `rgb(${ context . getImageData ( x , 0 , 1 , 1 ) . data . slice ( 0 , 3 ) . join ( "," ) })` ; return { itemBackground : getColorAt ( 48 ) , itemForeground : getColorAt ( 50 ) , windowBackground : getColorAt ( 52 ) , buttonText : getColorAt ( 54 ) , windowText : getColorAt ( 56 ) , divider : getColorAt ( 58 ) , playlistSelection : getColorAt ( 60 ) , listHeaderBackground : getColorAt ( 62 ) , listHeaderText : getColorAt ( 64 ) , listHeaderFrameTopAndLeft : getColorAt ( 66 ) , listHeaderFrameBottomAndRight : getColorAt ( 68 ) , listHeaderFramePressed : getColorAt ( 70 ) , listHeaderDeadArea : getColorAt ( 72 ) , scrollbarOne : getColorAt ( 74 ) , scrollbarTwo : getColorAt ( 76 ) , pressedScrollbarOne : getColorAt ( 78 ) , pressedScrollbarTwo : getColorAt ( 80 ) , scrollbarDeadArea : getColorAt ( 82 ) , listTextHighlighted : getColorAt ( 84 ) , listTextHighlightedBackground : getColorAt ( 86 ) , listTextSelected : getColorAt ( 88 ) , listTextSelectedBackground : getColorAt ( 90 ) , } ; }