You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
3.1 KiB
103 lines
3.1 KiB
import { Buffer } from "buffer";
|
|
import * as ebml from "ts-ebml";
|
|
import { concatAB } from "./png";
|
|
|
|
// unused, but will in case 4chan does file sig checks
|
|
//const password = Buffer.from("NOA");
|
|
|
|
const xor = (a: Buffer, p: Buffer) => {
|
|
let n = 0;
|
|
for (let i = 0; i < a.byteLength; ++i) {
|
|
a[i] ^= p[n];
|
|
n++;
|
|
n %= p.byteLength;
|
|
}
|
|
};
|
|
|
|
// just some debugging
|
|
const printChunks = (chunks: ebml.EBMLElementDetail[], ptr = 0, depth = 0): void => {
|
|
if (ptr >= chunks.length)
|
|
return;
|
|
const k = chunks[ptr];
|
|
const closing = ('isEnd' in k && k.isEnd ? 1 : 0);
|
|
console.log('\t'.repeat(depth - closing) + (closing ? '/' : '') + k.name);
|
|
switch (k.type) {
|
|
case "m":
|
|
if (k.isEnd) {
|
|
return printChunks(chunks, ptr + 1, depth - 1);
|
|
} else {
|
|
return printChunks(chunks, ptr + 1, depth + 1);
|
|
}
|
|
default:
|
|
return printChunks(chunks, ptr + 1, depth);
|
|
}
|
|
};
|
|
|
|
const embed = (webm: Buffer, data: Buffer) => {
|
|
const dec = new ebml.Decoder();
|
|
const chunks = dec.decode(webm);
|
|
const enc = new ebml.Encoder();
|
|
const embed = chunks.findIndex(e => e.name == "Targets" && e.type == "m" && e.isEnd);
|
|
if (embed == -1)
|
|
throw "Cannot embed, no tags section...";
|
|
// That's basically budget binary XML
|
|
chunks.splice(embed + 1, 0, ...[
|
|
{
|
|
type: "m",
|
|
isEnd: false,
|
|
name: 'SimpleTag',
|
|
data: Buffer.from('')
|
|
},
|
|
{
|
|
type: "8",
|
|
isEnd: false,
|
|
name: 'TagName',
|
|
data: Buffer.from('COOM')
|
|
},
|
|
{
|
|
type: "8",
|
|
isEnd: false,
|
|
name: 'TagBinary',
|
|
data
|
|
},
|
|
{
|
|
type: "m",
|
|
isEnd: true,
|
|
name: 'SimpleTag',
|
|
data: Buffer.from('')
|
|
}
|
|
] as any);
|
|
return Buffer.from(enc.encode(chunks.filter(e => e.name != "unknown")));
|
|
};
|
|
|
|
const extractBuff = (webm: Buffer) => {
|
|
const dec = new ebml.Decoder();
|
|
const chunks = dec.decode(webm);
|
|
|
|
const embed = chunks.findIndex(e => e.name == "TagName" && e.type == '8' && e.value == "COOM");
|
|
const cl = chunks.find(e => e.name == "Cluster");
|
|
if (cl && embed == -1)
|
|
return;
|
|
if (embed == -1)
|
|
return;
|
|
const chk = chunks[embed + 1];
|
|
if (chk.type == "b" && chk.name == "TagBinary")
|
|
return chk.data;
|
|
};
|
|
|
|
export const extract = async (reader: ReadableStreamDefaultReader<Uint8Array>): Promise<{ filename: string; data: Buffer } | undefined> => {
|
|
let total = Buffer.from('');
|
|
let chunk: ReadableStreamDefaultReadResult<Uint8Array>;
|
|
do {
|
|
chunk = await reader.read();
|
|
if (chunk.value)
|
|
total = concatAB(total, Buffer.from(chunk.value));
|
|
} while (!chunk.done);
|
|
const data = extractBuff(total);
|
|
if (!data)
|
|
return;
|
|
return { filename: 'embedded', data };
|
|
};
|
|
|
|
export const inject = async (container: File, inj: File): Promise<Buffer> =>
|
|
embed(Buffer.from(await container.arrayBuffer()), Buffer.from(await inj.arrayBuffer()));
|