UNPKG

@bitblit/asyncglk

Version:
132 lines (128 loc) 4.08 kB
/* Caching Dialog wrapper ====================== Copyright (c) 2022 Dannii Willis MIT licenced https://github.com/curiousdannii/asyncglk */ import { debounce } from 'lodash-es'; import { Array_to_BEBuffer, is_unicode_array, utf8encoder } from '../../common/misc.js'; import { filemode_Read, filemode_Write, seekmode_End, seekmode_Start } from '../../glkapi/constants.js'; /** A file buffer which stores its data in its preferred stream format, only converting back to a Uint8Array when required */ export class FileBuffer { binary; buf; fref; uni; constructor(buf, binary, fref) { this.binary = binary; this.buf = buf; this.fref = fref; this.uni = is_unicode_array(buf); } convert() { if (this.uni) { if (this.binary) { return Array_to_BEBuffer(this.buf); } else { const text = String.fromCodePoint(...this.buf); return utf8encoder.encode(text); } } else { return this.buf.slice(); } } } /** A Dialog class that wraps another Dialog instance, caching files so that they can be written asyncronously */ export class CachingDialogWrapper { 'async' = false; cache = {}; classname = 'CachingDialogWrapper'; Dialog; streaming = false; constructor(Dialog) { this.Dialog = Dialog; } init(_options) { } file_ref_exists(fref) { return !!this.cache[fref.filename] || this.Dialog.file_ref_exists(fref); } file_read(fref) { if (this.cache[fref.filename]) { // Convert the cached file buffer back into canonical form return this.cache[fref.filename].convert(); } if (this.Dialog.streaming) { const fstream = this.Dialog.file_fopen(filemode_Read, fref); if (!fstream) { return null; } fstream.fseek(0, seekmode_End); const length = fstream.ftell(); const buf = new Uint8Array(length); fstream.fseek(0, seekmode_Start); fstream.fread(buf); fstream.fclose(); return buf; } else { return this.Dialog.file_read(fref); } } file_remove_ref(fref) { if (this.cache[fref.filename]) { delete this.cache[fref.filename]; } this.Dialog.file_remove_ref(fref); } file_write(_fref, _content) { throw new Error('CachingDialogWrapper.write_file should be called instead'); } inited() { return true; } // Bundle all writes from one JS tick write = debounce(() => { for (const filename in this.cache) { const filebuf = this.cache[filename]; if (this.Dialog.streaming) { const fstream = this.Dialog.file_fopen(filemode_Write, filebuf.fref); fstream.fwrite(filebuf.convert()); fstream.fclose(); } else { this.Dialog.file_write(filebuf.fref, filebuf.convert()); } } this.cache = {}; }, 0); write_file(fref, content) { this.cache[fref.filename] = content; this.write(); return true; } // And the rest just call the wrapped instance autosave_read(signature) { return this.Dialog.autosave_read(signature); } autosave_write(signature, snapshot) { this.Dialog.autosave_write(signature, snapshot); } file_clean_fixed_name(filename, usage) { return this.Dialog.file_clean_fixed_name(filename, usage); } file_construct_ref(filename, usage, gameid) { return this.Dialog.file_construct_ref(filename, usage, gameid); } file_construct_temp_ref(usage) { return this.Dialog.file_construct_temp_ref(usage); } getlibrary(name) { return this.Dialog.getlibrary(name); } open(save, usage, gameid, callback) { this.Dialog.open(save, usage, gameid, callback); } }