Files
bitburner-src/src/utils/SaveDataUtils.ts
catloversg 8553bcb8fc MISC: Support compression of save data (#1162)
* Use Compression Streams API instead of jszip or other libraries.
* Remove usage of base64 in the new binary format.
* Do not convert binary data to string and back. The type of save data is SaveData, it's either string (old base64 format) or Uint8Array (new binary format).
* Proper support for interacting with electron-related code. Electron-related code assumes that save data is in the base64 format.
* Proper support for other tools (DevMenu, pretty-save.js). Full support for DevMenu will be added in a follow-up PR. Check the comments in src\DevMenu\ui\SaveFileDev.tsx for details.
2024-03-27 21:08:09 -07:00

60 lines
2.1 KiB
TypeScript

import { SaveData } from "../types";
export abstract class SaveDataError extends Error {
constructor(message: string) {
super(message);
this.name = this.constructor.name;
}
}
export class UnsupportedSaveData extends SaveDataError {}
export class InvalidSaveData extends SaveDataError {}
export function canUseBinaryFormat(): boolean {
return "CompressionStream" in globalThis;
}
async function compress(dataString: string): Promise<Uint8Array> {
const compressedReadableStream = new Blob([dataString]).stream().pipeThrough(new CompressionStream("gzip"));
return new Uint8Array(await new Response(compressedReadableStream).arrayBuffer());
}
async function decompress(binaryData: Uint8Array): Promise<string> {
const decompressedReadableStream = new Blob([binaryData]).stream().pipeThrough(new DecompressionStream("gzip"));
const reader = decompressedReadableStream.pipeThrough(new TextDecoderStream("utf-8", { fatal: true })).getReader();
let result = "";
try {
for (let { value, done } = await reader.read(); !done; { value, done } = await reader.read()) {
result += value;
}
} catch (error) {
throw new InvalidSaveData(String(error));
}
return result;
}
export async function encodeJsonSaveString(jsonSaveString: string): Promise<SaveData> {
// Fallback to the base64 format if player's browser does not support Compression Streams API.
if (canUseBinaryFormat()) {
return await compress(jsonSaveString);
} else {
// The unescape(encodeURIComponent()) pair encodes jsonSaveString into a "binary string" suitable for btoa, despite
// seeming like a no-op at first glance.
// Ref: https://stackoverflow.com/a/57713220
return btoa(unescape(encodeURIComponent(jsonSaveString)));
}
}
/** Return json save string */
export async function decodeSaveData(saveData: SaveData): Promise<string> {
if (saveData instanceof Uint8Array) {
if (!canUseBinaryFormat()) {
throw new UnsupportedSaveData("Your browser does not support Compression Streams API");
}
return await decompress(saveData);
} else {
return decodeURIComponent(escape(atob(saveData)));
}
}