import { AppendableBuffer, isNodeServer, isTauri, saveAsset, type LocalWriter, type VirtualWriter } from "../storage/globalApi"; import * as fflate from "fflate"; import { sleep } from "../util"; import { alertStore } from "../alert"; import { Capacitor } from "@capacitor/core"; export async function processZip(dataArray: Uint8Array): Promise { const jszip = await import("jszip"); const blob = new Blob([dataArray], { type: "application/zip" }); const zip = new jszip.default(); const zipData = await zip.loadAsync(blob); const imageFile = Object.keys(zipData.files).find(fileName => /\.(jpg|jpeg|png)$/.test(fileName)); if (imageFile) { const imageData = await zipData.files[imageFile].async("base64"); return `data:image/png;base64,${imageData}`; } else { throw new Error("No image found in ZIP file"); } } export class CharXWriter{ zip:fflate.Zip writeEnd:boolean = false apb = new AppendableBuffer() constructor(private writer:LocalWriter|WritableStreamDefaultWriter|VirtualWriter){ const handlerAsync = async (err:Error, dat:Uint8Array, final:boolean) => { if(dat){ this.apb.append(dat) } if(final){ this.writeEnd = true } } this.zip = new fflate.Zip() this.zip.ondata = handlerAsync } async init(){ //do nothing, just to make compatible with other writer } async write(key:string,data:Uint8Array|string){ console.log('write',key) let dat:Uint8Array if(typeof data === 'string'){ dat = new TextEncoder().encode(data) } else{ dat = data } this.writeEnd = false const file = new fflate.ZipDeflate(key, { level: 0 }); await this.zip.add(file) await file.push(dat, true) await this.writer.write(this.apb.buffer) this.apb.buffer = new Uint8Array(0) if(this.writeEnd){ await this.writer.close() } } async end(){ await this.zip.end() await this.writer.write(this.apb.buffer) this.apb.buffer = new Uint8Array(0) if(this.writeEnd){ await this.writer.close() } } } export class CharXReader{ unzip:fflate.Unzip assets:{[key:string]:string} = {} assetBuffers:{[key:string]:AppendableBuffer} = {} assetPromises:Promise[] = [] excludedFiles:string[] = [] cardData:string|undefined moduleData:Uint8Array|undefined constructor(){ this.unzip = new fflate.Unzip() this.unzip.register(fflate.UnzipInflate) this.unzip.onfile = (file) => { const assetIndex = file.name this.assetBuffers[assetIndex] = new AppendableBuffer() file.ondata = (err, dat, final) => { this.assetBuffers[assetIndex].append(dat) if(final){ const assetData = this.assetBuffers[assetIndex].buffer if(assetData.byteLength > 50 * 1024 * 1024){ this.excludedFiles.push(assetIndex) } else if(file.name === 'card.json'){ this.cardData = new TextDecoder().decode(assetData) } else if(file.name === 'module.risum'){ this.moduleData = assetData } else{ this.assetPromises.push((async () => { const assetId = await saveAsset(assetData) this.assets[assetIndex] = assetId })()) } } } if(file.originalSize ?? 0 < 50 * 1024 * 1024){ file.start() } } } async push(data:Uint8Array, final:boolean = false){ if(data.byteLength > 1024 * 1024){ let pointer = 0 while(true){ const chunk = data.slice(pointer, pointer + 1024 * 1024) this.unzip.push(chunk, false) await Promise.all(this.assetPromises) if(pointer + 1024 * 1024 >= data.byteLength){ if(final){ this.unzip.push(new Uint8Array(0), final) } break } pointer += 1024 * 1024 } return } this.unzip.push(data, final) await Promise.all(this.assetPromises) } async read(data:Uint8Array|File|ReadableStream, arg:{ alertInfo?:boolean } = {}){ if(data instanceof ReadableStream){ const reader = data.getReader() while(true){ const {done, value} = await reader.read() if(value){ await this.push(value, false) } if(done){ await this.push(new Uint8Array(0), true) break } } await this.push(new Uint8Array(0), true) return } const getSlice = async (start:number, end:number) => { if(data instanceof Uint8Array){ return data.slice(start, end) } if(data instanceof File){ return new Uint8Array(await data.slice(start, end).arrayBuffer()) } } const getLength = () => { if(data instanceof Uint8Array){ return data.byteLength } if(data instanceof File){ return data.size } } const alertInfo = () => { if(arg.alertInfo){ alertStore.set({ type: 'wait', msg: `Loading... (Getting Data: ${Math.floor(pointer / getLength() * 100)}%)` }) } } let pointer = 0 while(true){ const chunk = await getSlice(pointer, pointer + 1024 * 1024) await this.push(chunk, false) alertInfo() if(pointer + 1024 * 1024 >= getLength()){ await this.push(new Uint8Array(0), true) break } pointer += 1024 * 1024 if(!isTauri && !Capacitor.isNativePlatform() &&!isNodeServer){ const promiseLength = this.assetPromises.length this.assetPromises = [] await sleep(promiseLength * 100) } } await sleep(100) } }