138 lines
4.6 KiB
TypeScript
138 lines
4.6 KiB
TypeScript
import { Packr, Unpackr, decode } from "msgpackr";
|
|
import * as fflate from "fflate";
|
|
import { AppendableBuffer, isTauri } from "../globalApi.svelte";
|
|
|
|
const packr = new Packr({
|
|
useRecords:false
|
|
});
|
|
|
|
const unpackr = new Unpackr({
|
|
int64AsType: 'number',
|
|
useRecords:false
|
|
})
|
|
|
|
const magicHeader = new Uint8Array([0, 82, 73, 83, 85, 83, 65, 86, 69, 0, 7]);
|
|
const magicCompressedHeader = new Uint8Array([0, 82, 73, 83, 85, 83, 65, 86, 69, 0, 8]);
|
|
const magicStreamCompressedHeader = new Uint8Array([0, 82, 73, 83, 85, 83, 65, 86, 69, 0, 9]);
|
|
|
|
|
|
async function checkCompressionStreams(){
|
|
if(!CompressionStream){
|
|
const {makeCompressionStream} = await import('compression-streams-polyfill/ponyfill');
|
|
globalThis.CompressionStream = makeCompressionStream(TransformStream);
|
|
}
|
|
if(!DecompressionStream){
|
|
const {makeDecompressionStream} = await import('compression-streams-polyfill/ponyfill');
|
|
globalThis.DecompressionStream = makeDecompressionStream(TransformStream);
|
|
}
|
|
}
|
|
|
|
export function encodeRisuSaveLegacy(data:any, compression:'noCompression'|'compression' = 'noCompression'){
|
|
let encoded:Uint8Array = packr.encode(data)
|
|
if(compression === 'compression'){
|
|
encoded = fflate.compressSync(encoded)
|
|
const result = new Uint8Array(encoded.length + magicCompressedHeader.length);
|
|
result.set(magicCompressedHeader, 0)
|
|
result.set(encoded, magicCompressedHeader.length)
|
|
return result
|
|
}
|
|
else{
|
|
const result = new Uint8Array(encoded.length + magicHeader.length);
|
|
result.set(magicHeader, 0)
|
|
result.set(encoded, magicHeader.length)
|
|
return result
|
|
}
|
|
}
|
|
|
|
export async function encodeRisuSave(data:any) {
|
|
await checkCompressionStreams()
|
|
let encoded:Uint8Array = packr.encode(data)
|
|
const cs = new CompressionStream('gzip');
|
|
const writer = cs.writable.getWriter();
|
|
writer.write(encoded);
|
|
writer.close();
|
|
const buf = await new Response(cs.readable).arrayBuffer()
|
|
const result = new Uint8Array(new Uint8Array(buf).length + magicStreamCompressedHeader.length);
|
|
result.set(magicStreamCompressedHeader, 0)
|
|
result.set(new Uint8Array(buf), magicStreamCompressedHeader.length)
|
|
return result
|
|
}
|
|
|
|
export async function decodeRisuSave(data:Uint8Array){
|
|
try {
|
|
switch(checkHeader(data)){
|
|
case "compressed":
|
|
data = data.slice(magicCompressedHeader.length)
|
|
return decode(fflate.decompressSync(data))
|
|
case "raw":
|
|
data = data.slice(magicHeader.length)
|
|
return unpackr.decode(data)
|
|
case "stream":{
|
|
await checkCompressionStreams()
|
|
data = data.slice(magicStreamCompressedHeader.length)
|
|
const cs = new DecompressionStream('gzip');
|
|
const writer = cs.writable.getWriter();
|
|
writer.write(data);
|
|
writer.close();
|
|
const buf = await new Response(cs.readable).arrayBuffer()
|
|
return unpackr.decode(new Uint8Array(buf))
|
|
}
|
|
}
|
|
return unpackr.decode(data)
|
|
}
|
|
catch (error) {
|
|
try {
|
|
console.log('risudecode')
|
|
const risuSaveHeader = new Uint8Array(Buffer.from("\u0000\u0000RISU",'utf-8'))
|
|
const realData = data.subarray(risuSaveHeader.length)
|
|
const dec = unpackr.decode(realData)
|
|
return dec
|
|
} catch (error) {
|
|
const buf = Buffer.from(fflate.decompressSync(Buffer.from(data)))
|
|
try {
|
|
return JSON.parse(buf.toString('utf-8'))
|
|
} catch (error) {
|
|
return unpackr.decode(buf)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function checkHeader(data: Uint8Array) {
|
|
|
|
let header:'none'|'compressed'|'raw'|'stream' = 'raw'
|
|
|
|
if (data.length < magicHeader.length) {
|
|
return false;
|
|
}
|
|
|
|
for (let i = 0; i < magicHeader.length; i++) {
|
|
if (data[i] !== magicHeader[i]) {
|
|
header = 'none'
|
|
break
|
|
}
|
|
}
|
|
|
|
if(header === 'none'){
|
|
header = 'compressed'
|
|
for (let i = 0; i < magicCompressedHeader.length; i++) {
|
|
if (data[i] !== magicCompressedHeader[i]) {
|
|
header = 'none'
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if(header === 'none'){
|
|
header = 'stream'
|
|
for (let i = 0; i < magicStreamCompressedHeader.length; i++) {
|
|
if (data[i] !== magicStreamCompressedHeader[i]) {
|
|
header = 'none'
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// All bytes matched
|
|
return header;
|
|
} |