Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"vite-tsconfig-paths": "^6.0.5"
},
"dependencies": {
"7z-wasm": "^1.2.0",
"@bjorn3/browser_wasi_shim": "^0.4.2",
"@bokuweb/zstd-wasm": "^0.0.27",
"@ffmpeg/core": "^0.12.10",
Expand Down
10 changes: 2 additions & 8 deletions src/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import threejsHandler from "./threejs.ts";
import sqlite3Handler from "./sqlite.ts";
import vtfHandler from "./vtf.ts";
import mcMapHandler from "./mcmap.ts";
import jszipHandler from "./jszip.ts";
import sevenZipHandler from "./sevenZip.ts";
import json5Handler from "./json5.ts";
import alsHandler from "./als.ts";
import qoaFuHandler from "./qoa-fu.ts";
Expand Down Expand Up @@ -53,15 +53,13 @@ import n64romHandler from "./n64rom.ts";
import vexflowHandler from "./vexflow.ts";
import toonHandler from "./toon.ts";
import rpgmvpHandler from "./rpgmvp.ts";
import tarHandler from "./tar.ts";
import otaHandler from "./ota.ts";
import comicsHandler from "./comics.ts";
import terrariaWldHandler from "./terrariawld.ts";
import opusMagnumHandler from "./opusMagnum.ts";
import aperturePictureHandler from "./aperturePicture.ts";
import xcfHandler from "./xcf.ts";
import pdfparseHandler from "./pdfparse.ts";
import { tarGzHandler, tarZstdHandler, tarXzHandler } from "./tarCompressed.ts";
import mclangHandler from "./minecraftLangfileHandler.ts";
import cybergrindHandler from "./cybergrindHandler.ts";
import textToSourceHandler from "./textToSource.ts";
Expand All @@ -88,7 +86,7 @@ try { handlers.push(new threejsHandler()) } catch (_) { };
try { handlers.push(new sqlite3Handler()) } catch (_) { };
try { handlers.push(new vtfHandler()) } catch (_) { };
try { handlers.push(new mcMapHandler()) } catch (_) { };
try { handlers.push(new jszipHandler()) } catch (_) { };
try { handlers.push(new sevenZipHandler()) } catch (_) { };
try { handlers.push(new json5Handler()) } catch (_) { };
try { handlers.push(new alsHandler()) } catch (_) { };
try { handlers.push(new qoaFuHandler()) } catch (_) { };
Expand Down Expand Up @@ -124,17 +122,13 @@ try { handlers.push(new n64romHandler()) } catch (_) { };
try { handlers.push(new vexflowHandler()) } catch (_) { };
try { handlers.push(new toonHandler()) } catch (_) { };
try { handlers.push(new rpgmvpHandler()) } catch (_) { };
try { handlers.push(new tarHandler()) } catch (_) { };
try { handlers.push(new otaHandler()) } catch (_) { };
try { handlers.push(new comicsHandler()) } catch (_) { };
try { handlers.push(new terrariaWldHandler()) } catch (_) { };
try { handlers.push(new opusMagnumHandler()) } catch (_) { };
try { handlers.push(new aperturePictureHandler()) } catch (_) { };
try { handlers.push(new xcfHandler()) } catch (_) { };
try { handlers.push(new pdfparseHandler()) } catch (_) { };
try { handlers.push(tarGzHandler) } catch (_) { };
try { handlers.push(tarZstdHandler) } catch (_) { };
try { handlers.push(tarXzHandler) } catch (_) { };
try { handlers.push(new mclangHandler()) } catch (_) { };
try { handlers.push(new cybergrindHandler()) } catch (_) { };
try { handlers.push(new textToSourceHandler()) } catch (_) { };
Expand Down
41 changes: 0 additions & 41 deletions src/handlers/jszip.ts

This file was deleted.

174 changes: 174 additions & 0 deletions src/handlers/sevenZip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// file: 7z.ts

import type { FileData, FileFormat, FormatHandler } from "../FormatHandler.ts";
import CommonFormats, { Category } from "src/CommonFormats.ts";
import SevenZip from "7z-wasm";
import mime from "mime";
import normalizeMimeType from "src/normalizeMimeType.ts";

const defaultSevenZipOptions = {
locateFile: () => "/convert/wasm/7zz.wasm"
}

class sevenZipHandler implements FormatHandler {

public name: string = "sevenZip";
public supportedFormats: FileFormat[] = [];
public ready: boolean = false;

public supportAnyInput: boolean = true;

#tarCompressedFormats: string[] = [];

async init () {
this.supportedFormats = [];
this.#tarCompressedFormats = [];

const stdout: number[] = [];
const sevenZip = await SevenZip({
...defaultSevenZipOptions,
stdout: (c) => {
stdout.push(c);
},
});

sevenZip.callMain(["i"]);

const text = new TextDecoder().decode(new Uint8Array(stdout));

// no codecs for now
const formatsText = text.match(/\n\n\nFormats:\n(.*?)\n\n/s);
if (!formatsText) throw new Error("7zz output did not have any formats");
const formatLines = formatsText[1].split("\n");

// this will totally break in future 7z versions but its the only way
for (const formatLine of formatLines) {
// 7zz i gives more than 1 extension, but i dont think we will
// need those as they are mostly aliases and thats renamehandler's job.
// also we cant faithfully parse more than 1 extension because there is no
// way to know where extensions stop and weird signature stuff begins
const [flags, name, extension, ...extra] = formatLine.trim().split(/ +/);

if (name === "Hash") continue;
const mimeType = normalizeMimeType(mime.getType(extension) || `application/${extension}`);
let displayName = `${name} archive`;
let format = extension;

if (extra.includes("(.tar)")) {
// compressed formats will be only tar for now
this.#tarCompressedFormats.push(name);
displayName = `${name} compressed tar archive`;
format = `tar.${extension}`;
}

this.supportedFormats.push({
name: displayName,
format,
extension,
mime: mimeType,
from: true,
to: flags.includes("C"),
internal: name,
category: Category.ARCHIVE,
lossless: false, // archive metadata is too complicated
});
}

// push zip and tar up the list
const priority = ["tar", "zip"];
const prioritized = [];
for (const format of priority) {
prioritized.push(this.supportedFormats.find(f => f.internal === format)!);
}
this.supportedFormats = [
...prioritized,
...this.supportedFormats.filter(f => !priority.includes(f.internal))
];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just for some better routing


this.ready = true;
}

async doConvert (
inputFiles: FileData[],
inputFormat: FileFormat,
outputFormat: FileFormat
): Promise<FileData[]> {
const outputFiles: FileData[] = [];

if (!this.supportedFormats.some(format => format.to && format.internal === outputFormat.internal)) {
throw new Error(`sevenZipHandler cannot convert to ${outputFormat.mime}`);
}

// handle compressed tars
if (this.#tarCompressedFormats.includes(inputFormat.internal)
|| this.#tarCompressedFormats.includes(outputFormat.internal)) {

if (outputFormat.internal === "tar") {
for (const inputFile of inputFiles) {
const sevenZip = await SevenZip(defaultSevenZipOptions);

sevenZip.FS.writeFile(inputFile.name, inputFile.bytes);
sevenZip.callMain(["x", inputFile.name]);

const name = inputFile.name.replace(/\.[^.]+$/, "");
const bytes = sevenZip.FS.readFile(name);
outputFiles.push({ bytes, name });
}
} else if (inputFormat.internal === "tar") {
for (const inputFile of inputFiles) {
const sevenZip = await SevenZip(defaultSevenZipOptions);
sevenZip.FS.writeFile(inputFile.name, inputFile.bytes);

const name = inputFile.name + `.${outputFormat.extension}`;
sevenZip.callMain(["a", name, inputFile.name]);

const bytes = sevenZip.FS.readFile(name);
outputFiles.push({ bytes, name });
}
} else {
throw new Error(`sevenZipHandler cannot convert from ${inputFormat.mime} to ${outputFormat.mime}`);
}

return outputFiles;
}

if (this.supportedFormats.some(format => format.internal === inputFormat.internal)) {
for (const inputFile of inputFiles) {
const sevenZip = await SevenZip(defaultSevenZipOptions);

sevenZip.FS.writeFile(inputFile.name, inputFile.bytes);
sevenZip.callMain(["x", inputFile.name, `-odata`]);

const name = inputFile.name.replace(/\.[^.]+$/, "") + `.${outputFormat.extension}`;
sevenZip.FS.chdir("data"); // we need to preserve the structure of the input archive
sevenZip.callMain(["a", "../" + name]);
sevenZip.FS.chdir("..");

const bytes = sevenZip.FS.readFile(name);
outputFiles.push({ bytes, name });
}
} else {
const sevenZip = await SevenZip(defaultSevenZipOptions);

sevenZip.FS.mkdir("data");
sevenZip.FS.chdir("data");
for (const inputFile of inputFiles) {
sevenZip.FS.writeFile(inputFile.name, inputFile.bytes);
}

const name = inputFiles.length === 1 ?
inputFiles[0].name + `.${outputFormat.extension}`
: `archive.${outputFormat.extension}`;
sevenZip.callMain(["a", "../" + name]);
sevenZip.FS.chdir("..");

const bytes = sevenZip.FS.readFile(name);
outputFiles.push({ bytes, name });
}

return outputFiles;
}

}

export default sevenZipHandler;
Loading
Loading