OxiArc

ZIP, TAR — and LZH, the archive format of 1990s Japan. List what is inside, build new archives, and race six codecs against each other, all in your browser. Nothing leaves the page.

Choosing files, building archives, and exporting make no network requests.

Inspect an archive

Drop an archive and the page detects its format by magic bytes and lists what is inside. The listing below is the bundled sample — an LZH archive written by oxiarc itself, with entry dates fixed to 1995. Its filenames are recorded in Shift_JIS, the LZH convention: a tool that only knows UTF-8 shows them as mojibake, and this page decodes them correctly.

What this demo refuses

  • 7z / CAB / ISO 9660 — detected and named, not listed or extracted.
  • Password-protected ZIP — lists fine; extraction is refused.
  • XZ — decode only; the write half is not linked.
  • Snappy — detected only; not in the codec race.
  • Writing LZH — the create station builds ZIP / TAR.GZ only. The bundled sample was written with oxiarc's LZH writer on the host, where a clock exists.
oxiarcv— (Pure Rust, Apache-2.0)
targetwasm32-unknown-unknown
wasm size779 KB (gzip 411 KB)
sample entries
external requests
server round-trips0
C / C++ / Fortran0 bytes

External requests since the page finished loading:

Counting starts once the bundled sample archive has loaded. Choosing files, building archives, and exporting do not move this number. You can confirm it in your dev tools' Network tab.

Implementation code

// crates/oxiarc-wasm/src/create.rs:106-148 — verbatim, the code running above
pub fn finish(&mut self, level: u8) -> Result<Vec<u8>, OxiArcWasmError> {
    let level = level.clamp(1, 9);
    let mut out = Vec::new();
    let mut writer = oxiarc_archive::zip::write_zip(&mut out);
    for file in &self.files {
        // Same store-if-smaller policy as ZipWriter::add_file_with_options,
        // reimplemented because only add_file_raw takes an explicit mtime.
        let compressed = oxiarc_deflate::deflate(&file.data, level).map_err(|e| {
            OxiArcWasmError::CodecFailed {
                codec: "deflate".to_owned(),
                detail: e.to_string(),
            }
        })?;
        let (payload, method) = if compressed.len() < file.data.len() {
            (compressed, ZipMethod::Deflate)
        } else {
            (file.data.clone(), ZipMethod::Stored)
        };
        let crc = Crc32::compute(&file.data);
        writer
            .add_file_raw(
                &file.name,
                method,
                crc,
                file.data.len() as u64,
                Some(file.mtime),
                &payload,
            )
            .map_err(|e| OxiArcWasmError::ArchiveInvalid {
                format: "zip".to_owned(),
                detail: e.to_string(),
            })?;
    }
    writer
        .finish()
        .map_err(|e| OxiArcWasmError::ArchiveInvalid {
            format: "zip".to_owned(),
            detail: e.to_string(),
        })?;
    drop(writer);
    self.files.clear();
    Ok(out)
}

This is the code running above, right now.