2022-01-03 22:29:28 +00:00
|
|
|
import { Buffer } from "buffer";
|
2022-01-07 09:38:12 +00:00
|
|
|
import type { EmbeddedFile, ImageProcessor } from "./main";
|
2022-01-04 15:36:43 +00:00
|
|
|
import { BufferWriteStream } from "./png";
|
2022-01-13 13:13:19 +00:00
|
|
|
import { decodeCoom3Payload, uploadFiles } from "./utils";
|
2022-01-03 22:29:28 +00:00
|
|
|
|
2022-01-04 15:36:43 +00:00
|
|
|
const netscape = Buffer.from("!\xFF\x0BNETSCAPE2.0", 'ascii');
|
2022-01-13 08:45:37 +00:00
|
|
|
const magic = Buffer.from("!\xFF\x0B" + "DOOMTECH1.1", 'ascii');
|
2022-01-03 22:29:28 +00:00
|
|
|
|
2022-01-07 09:38:12 +00:00
|
|
|
const read_section = (gif: Buffer, pos: number) => {
|
|
|
|
const begin = pos;
|
|
|
|
pos += 3 + gif[pos + 2];
|
|
|
|
let buf = Buffer.alloc(0);
|
|
|
|
while (pos < gif.byteLength) {
|
|
|
|
const v = gif[pos++];
|
|
|
|
buf = Buffer.concat([buf, gif.slice(pos, pos + v)]);
|
|
|
|
if (v == 0)
|
|
|
|
break;
|
|
|
|
pos += v;
|
|
|
|
}
|
|
|
|
const appname = gif.slice(begin + 3, begin + 11).toString('ascii');
|
|
|
|
return {
|
|
|
|
appname,
|
|
|
|
data: buf,
|
|
|
|
end: pos
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2022-01-03 22:29:28 +00:00
|
|
|
const extractBuff = (gif: Buffer) => {
|
2022-01-04 15:36:43 +00:00
|
|
|
const field = gif.readUInt8(10);
|
|
|
|
const gcte = !!(field & (1 << 7));
|
2022-01-03 22:29:28 +00:00
|
|
|
let end = 13;
|
|
|
|
if (gcte) {
|
|
|
|
end += 3 * (1 << ((field & 7) + 1));
|
|
|
|
}
|
|
|
|
// skip beeg blocks
|
2022-01-07 09:38:12 +00:00
|
|
|
while (gif[end] == '!'.charCodeAt(0)) {
|
|
|
|
let sec = read_section(gif, end); // this section contains the size to more easily preallocate a buffer size, but you don't need to care care
|
2022-01-13 13:13:19 +00:00
|
|
|
if (sec.appname == "DOOMTECH") {
|
2022-01-07 09:38:12 +00:00
|
|
|
const ret = Buffer.alloc(sec.data.readInt32LE(0));
|
|
|
|
let ptr = 0;
|
|
|
|
do {
|
|
|
|
sec = read_section(gif, sec.end);
|
|
|
|
sec.data.copy(ret, ptr);
|
|
|
|
ptr += sec.data.byteLength;
|
|
|
|
end = sec.end;
|
2022-01-13 13:13:19 +00:00
|
|
|
} while (sec.appname == "DOOMTECH" && gif[end] == '!'.charCodeAt(0));
|
|
|
|
return decodeCoom3Payload(ret);
|
2022-01-03 22:29:28 +00:00
|
|
|
}
|
2022-01-07 09:38:12 +00:00
|
|
|
end = sec.end;
|
2022-01-03 22:29:28 +00:00
|
|
|
}
|
2022-04-11 11:19:43 +00:00
|
|
|
throw new Error("Shouldn't happen");
|
2022-01-03 22:29:28 +00:00
|
|
|
// metadata ended, nothing...
|
|
|
|
};
|
|
|
|
|
2022-01-05 01:14:23 +00:00
|
|
|
const extract = extractBuff;
|
2022-01-03 22:29:28 +00:00
|
|
|
|
2022-01-07 09:38:12 +00:00
|
|
|
const write_data = async (writer: WritableStreamDefaultWriter<Buffer>, inj: Buffer) => {
|
2022-01-03 22:29:28 +00:00
|
|
|
await writer.write(magic);
|
|
|
|
const byte = Buffer.from([0]);
|
|
|
|
let size = inj.byteLength;
|
|
|
|
let ws;
|
|
|
|
let offset = 0;
|
|
|
|
while (size != 0) {
|
|
|
|
ws = size >= 255 ? 255 : size;
|
|
|
|
byte.writeUInt8(ws, 0);
|
|
|
|
await writer.write(byte);
|
|
|
|
await writer.write(inj.slice(offset, offset + ws));
|
|
|
|
size -= ws;
|
|
|
|
offset += ws;
|
|
|
|
}
|
|
|
|
byte.writeUInt8(0, 0);
|
|
|
|
await writer.write(byte);
|
|
|
|
};
|
|
|
|
|
2022-01-07 09:38:12 +00:00
|
|
|
const write_embedding = async (writer: WritableStreamDefaultWriter<Buffer>, inj: Buffer) => {
|
|
|
|
const b = Buffer.alloc(4);
|
|
|
|
b.writeInt32LE(inj.byteLength, 0);
|
|
|
|
await write_data(writer, b);
|
|
|
|
let size = inj.byteLength;
|
|
|
|
let offset = 0;
|
|
|
|
while (size != 0) {
|
|
|
|
const ws = size >= (3 << 13) ? (3 << 13) : size;
|
|
|
|
await write_data(writer, inj.slice(offset, offset + ws));
|
|
|
|
offset += ws;
|
|
|
|
size -= ws;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-04-14 19:52:21 +00:00
|
|
|
const inject = async (container: File, links: string[]) => {
|
2022-01-03 22:29:28 +00:00
|
|
|
const [writestream, extract] = BufferWriteStream();
|
|
|
|
const writer = writestream.getWriter();
|
|
|
|
|
2022-01-13 08:45:37 +00:00
|
|
|
const inj = Buffer.from(links.join(' '));
|
|
|
|
|
2022-01-04 15:36:43 +00:00
|
|
|
const contbuff = Buffer.from(await container.arrayBuffer());
|
2022-01-03 22:29:28 +00:00
|
|
|
|
2022-01-04 15:36:43 +00:00
|
|
|
const field = contbuff.readUInt8(10);
|
|
|
|
const gcte = !!(field & (1 << 0x7));
|
2022-01-03 22:29:28 +00:00
|
|
|
let endo = 13;
|
|
|
|
if (gcte)
|
|
|
|
endo += 3 * (1 << ((field & 7) + 1));
|
|
|
|
|
|
|
|
if (netscape.compare(contbuff, endo, endo + netscape.byteLength) == 0)
|
2022-01-06 00:10:12 +00:00
|
|
|
endo += 19;
|
2022-01-03 22:29:28 +00:00
|
|
|
await writer.write(contbuff.slice(0, endo));
|
2022-01-13 08:45:37 +00:00
|
|
|
await write_embedding(writer, Buffer.from(inj));
|
2022-01-03 22:29:28 +00:00
|
|
|
await writer.write(contbuff.slice(endo));
|
|
|
|
return extract();
|
2022-01-04 15:36:43 +00:00
|
|
|
};
|
|
|
|
|
2022-01-05 01:14:23 +00:00
|
|
|
const has_embed = (gif: Buffer) => {
|
2022-01-04 15:36:43 +00:00
|
|
|
const field = gif.readUInt8(10);
|
|
|
|
const gcte = !!(field & (1 << 7));
|
|
|
|
let end = 13;
|
|
|
|
if (gcte) {
|
|
|
|
end += 3 * (1 << ((field & 7) + 1));
|
|
|
|
}
|
|
|
|
// skip beeg blocks
|
|
|
|
while (end < gif.byteLength && gif.readUInt8(end) == '!'.charCodeAt(0)) {
|
|
|
|
if (magic.compare(gif, end, end + magic.byteLength) != 0) {
|
|
|
|
end += 3 + gif.readUInt8(end + 2);
|
|
|
|
// eslint-disable-next-line no-constant-condition
|
|
|
|
while (true) { // skip sub blocks
|
|
|
|
const v = gif.readUInt8(end++);
|
|
|
|
if (!v)
|
|
|
|
break;
|
|
|
|
end += v;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (end >= gif.byteLength)
|
|
|
|
return; // Don't know yet, need more to decide.
|
|
|
|
return false; // no more extension blocks, so definite no
|
2022-01-05 01:14:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export default {
|
|
|
|
extract,
|
|
|
|
has_embed,
|
2022-01-13 08:45:37 +00:00
|
|
|
inject,
|
2022-01-05 01:14:23 +00:00
|
|
|
match: fn => !!fn.match(/\.gif$/)
|
|
|
|
} as ImageProcessor;
|