mirror of
https://github.com/exoticorn/upkr.git
synced 2026-01-21 03:56:42 +01:00
Compare commits
3 Commits
fuzz
...
8c9e4311b9
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c9e4311b9 | |||
| 31c31bdcfb | |||
| 8f33ae0b1e |
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -62,6 +62,12 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lexopt"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.108"
|
||||
@@ -89,12 +95,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
|
||||
|
||||
[[package]]
|
||||
name = "sacabase"
|
||||
version = "2.0.0"
|
||||
@@ -121,8 +121,8 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cdivsufsort",
|
||||
"lexopt",
|
||||
"pbr",
|
||||
"pico-args",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "upkr"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cdivsufsort = "2"
|
||||
pico-args = "0.4"
|
||||
lexopt = "0.2.1"
|
||||
anyhow = "1"
|
||||
pbr = "1"
|
||||
@@ -1,4 +1,4 @@
|
||||
build/unpack_riscv64: ../c_unpacker/main.c ../c_unpacker/unpack.c
|
||||
build/unpack_riscv64: ../c_unpacker/main.c unpack_riscv.S
|
||||
mkdir -p build
|
||||
riscv64-linux-gnu-gcc -g -static -o $@ $^
|
||||
|
||||
@@ -6,6 +6,11 @@ test_riscv64: build/unpack_riscv64
|
||||
qemu-riscv64 $< test_data.upk /tmp/out.bin
|
||||
cmp test_data.bin /tmp/out.bin
|
||||
|
||||
build/unpack_riscv64.bin: unpack_riscv.S
|
||||
mkdir -p build
|
||||
riscv64-linux-gnu-gcc -c -o build/unpack_riscv64.o $?
|
||||
riscv64-linux-gnu-objcopy -O binary --only-section=.text build/unpack_riscv64.o $@
|
||||
|
||||
build/unpack_armv6m: ../c_unpacker/main.c unpack_armv6m.S
|
||||
mkdir -p build
|
||||
arm-linux-gnueabihf-gcc -g -static -o $@ $^
|
||||
@@ -15,6 +20,7 @@ test_armv6m: build/unpack_armv6m
|
||||
cmp test_data.bin /tmp/out.bin
|
||||
|
||||
build/unpack_armv6m.bin: unpack_armv6m.S
|
||||
mkdir -p build
|
||||
arm-none-eabi-gcc -march=armv6-m -c -o build/unpack_armv6m.o $?
|
||||
arm-none-eabi-objcopy -O binary --only-section=.text build/unpack_armv6m.o $@
|
||||
|
||||
@@ -26,5 +32,5 @@ test_c: build/unpack_c
|
||||
$< test_data.upk /tmp/out.bin
|
||||
cmp test_data.bin /tmp/out.bin
|
||||
|
||||
sizes: build/unpack_armv6m.bin
|
||||
sizes: build/unpack_armv6m.bin build/unpack_riscv64.bin
|
||||
ls -l build/*.bin
|
||||
144
asm_unpackers/unpack_riscv.S
Normal file
144
asm_unpackers/unpack_riscv.S
Normal file
@@ -0,0 +1,144 @@
|
||||
.section .text
|
||||
|
||||
#define FRAME_SIZE (256+64*4+4)
|
||||
|
||||
// x8 prob array ptr
|
||||
// x9 prev was literal
|
||||
// x10 out ptr
|
||||
// x11 in ptr
|
||||
// x12 offset
|
||||
// x13 state
|
||||
|
||||
.global upkr_unpack
|
||||
.type upkr_unpack, %function
|
||||
upkr_unpack:
|
||||
mv t4, ra
|
||||
mv x17, x8
|
||||
mv t6, x9
|
||||
li x13, FRAME_SIZE
|
||||
li x9, 128
|
||||
1:
|
||||
addi sp, sp, -1
|
||||
sb x9, 0(sp)
|
||||
addi x13, x13, -1
|
||||
bnez x13, 1b
|
||||
|
||||
.Lmainloop:
|
||||
li x14, 0
|
||||
jal upkr_decode_bit
|
||||
beqz x15, .Lliteral
|
||||
|
||||
li x14, 256
|
||||
beqz x9, .Lread_offset
|
||||
jal upkr_decode_bit
|
||||
beqz x15, .Lskip_offset
|
||||
.Lread_offset:
|
||||
jal t3, upkr_decode_number
|
||||
addi x12, x9, -1
|
||||
beqz x12, .Ldone
|
||||
|
||||
.Lskip_offset:
|
||||
li x14, 256+64
|
||||
jal t3, upkr_decode_number
|
||||
1:
|
||||
sub x15, x10, x12
|
||||
lbu x15, (x15)
|
||||
sb x15, (x10)
|
||||
addi x10, x10, 1
|
||||
addi x9, x9, -1
|
||||
bnez x9, 1b
|
||||
j .Lmainloop
|
||||
|
||||
.Lliteral:
|
||||
li x14, 1
|
||||
1:
|
||||
jal upkr_decode_bit
|
||||
slli x14, x14, 1
|
||||
add x14, x14, x15
|
||||
srli x9, x14, 8
|
||||
beqz x9, 1b
|
||||
sb x14, 0(x10)
|
||||
addi x10, x10, 1
|
||||
j .Lmainloop
|
||||
|
||||
.Ldone:
|
||||
addi sp, sp, FRAME_SIZE
|
||||
mv x8, x17
|
||||
mv x9, t6
|
||||
jr t4
|
||||
|
||||
// x14 context index
|
||||
// return: x9 decoded number
|
||||
upkr_decode_number:
|
||||
mv t5, x14
|
||||
li x9, 0
|
||||
li x8, 1
|
||||
1:
|
||||
addi x14, x14, 1
|
||||
jal upkr_decode_bit
|
||||
beqz x15, 1f
|
||||
addi x14, x14, 1
|
||||
jal upkr_decode_bit
|
||||
beqz x15, 2f
|
||||
add x9, x9, x8
|
||||
2:
|
||||
slli x8, x8, 1
|
||||
j 1b
|
||||
1:
|
||||
add x9, x9, x8
|
||||
|
||||
mv x14, t5
|
||||
jr t3
|
||||
|
||||
upkr_load_byte:
|
||||
lbu x15, 0(x11)
|
||||
addi x11, x11, 1
|
||||
slli x13, x13, 8
|
||||
add x13, x13, x15
|
||||
// x8 prob array ptr
|
||||
// x11 in ptr
|
||||
// x13 state
|
||||
// x14 context index
|
||||
// return: x15 decoded bit
|
||||
upkr_decode_bit:
|
||||
srli x15, x13, 12
|
||||
beqz x15, upkr_load_byte
|
||||
|
||||
mv t0, x9
|
||||
mv t1, x14
|
||||
mv t2, x10
|
||||
|
||||
add x14, x14, sp
|
||||
lbu x9, 0(x14)
|
||||
|
||||
andi x10, x13, 255
|
||||
sltu x15, x10, x9
|
||||
srli x13, x13, 8
|
||||
beqz x15, .Lelse
|
||||
|
||||
mul x13, x13, x9
|
||||
add x13, x13, x10
|
||||
li x10, 256 + 8
|
||||
sub x10, x10, x9
|
||||
srli x10, x10, 4
|
||||
add x9, x9, x10
|
||||
j .Lendif
|
||||
|
||||
.Lelse:
|
||||
li x16, 256
|
||||
sub x16, x16, x9
|
||||
mul x13, x13, x16
|
||||
add x13, x13, x10
|
||||
sub x13, x13, x9
|
||||
addi x10, x9, 8
|
||||
srli x10, x10, 4
|
||||
sub x9, x9, x10
|
||||
|
||||
.Lendif:
|
||||
|
||||
sb x9, 0(x14)
|
||||
|
||||
mv x9, t0
|
||||
mv x14, t1
|
||||
mv x10, t2
|
||||
ret
|
||||
28
src/lib.rs
28
src/lib.rs
@@ -9,21 +9,39 @@ pub use lz::unpack;
|
||||
|
||||
pub type ProgressCallback<'a> = &'a mut dyn FnMut(usize);
|
||||
|
||||
pub struct Config {
|
||||
pub use_bitstream: bool,
|
||||
pub parity_contexts: usize,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
use_bitstream: false,
|
||||
parity_contexts: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pack(
|
||||
data: &[u8],
|
||||
level: u8,
|
||||
use_bitstream: bool,
|
||||
parity_contexts: usize,
|
||||
config: Config,
|
||||
progress_callback: Option<ProgressCallback>,
|
||||
) -> Vec<u8> {
|
||||
if level == 0 {
|
||||
greedy_packer::pack(data, use_bitstream, parity_contexts, progress_callback)
|
||||
greedy_packer::pack(
|
||||
data,
|
||||
config.use_bitstream,
|
||||
config.parity_contexts,
|
||||
progress_callback,
|
||||
)
|
||||
} else {
|
||||
parsing_packer::pack(
|
||||
data,
|
||||
level,
|
||||
use_bitstream,
|
||||
parity_contexts,
|
||||
config.use_bitstream,
|
||||
config.parity_contexts,
|
||||
progress_callback,
|
||||
)
|
||||
}
|
||||
|
||||
23
src/lz.rs
23
src/lz.rs
@@ -106,9 +106,9 @@ impl CoderState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unpack(packed_data: &[u8], use_bitstream: bool, parity_contexts: usize) -> Vec<u8> {
|
||||
let mut decoder = RansDecoder::new(packed_data, use_bitstream);
|
||||
let mut contexts = ContextState::new((1 + 255) * parity_contexts + 1 + 64 + 64);
|
||||
pub fn unpack(packed_data: &[u8], config: crate::Config) -> Vec<u8> {
|
||||
let mut decoder = RansDecoder::new(packed_data, config.use_bitstream);
|
||||
let mut contexts = ContextState::new((1 + 255) * config.parity_contexts + 1 + 64 + 64);
|
||||
let mut result = vec![];
|
||||
let mut offset = 0;
|
||||
let mut prev_was_match = false;
|
||||
@@ -131,17 +131,26 @@ pub fn unpack(packed_data: &[u8], use_bitstream: bool, parity_contexts: usize) -
|
||||
}
|
||||
|
||||
loop {
|
||||
let literal_base = result.len() % parity_contexts * 256;
|
||||
let literal_base = result.len() % config.parity_contexts * 256;
|
||||
if decoder.decode_with_context(&mut contexts.context_mut(literal_base)) {
|
||||
if prev_was_match
|
||||
|| decoder.decode_with_context(&mut contexts.context_mut(256 * parity_contexts))
|
||||
|| decoder
|
||||
.decode_with_context(&mut contexts.context_mut(256 * config.parity_contexts))
|
||||
{
|
||||
offset = decode_length(&mut decoder, &mut contexts, 256 * parity_contexts + 1) - 1;
|
||||
offset = decode_length(
|
||||
&mut decoder,
|
||||
&mut contexts,
|
||||
256 * config.parity_contexts + 1,
|
||||
) - 1;
|
||||
if offset == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let length = decode_length(&mut decoder, &mut contexts, 256 * parity_contexts + 65);
|
||||
let length = decode_length(
|
||||
&mut decoder,
|
||||
&mut contexts,
|
||||
256 * config.parity_contexts + 65,
|
||||
);
|
||||
for _ in 0..length {
|
||||
result.push(result[result.len() - offset]);
|
||||
}
|
||||
|
||||
113
src/main.rs
113
src/main.rs
@@ -1,44 +1,81 @@
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::Result;
|
||||
use std::ffi::OsStr;
|
||||
use std::io::prelude::*;
|
||||
use std::process;
|
||||
use std::{fs::File, path::PathBuf};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut args = pico_args::Arguments::from_env();
|
||||
let mut config = upkr::Config::default();
|
||||
let mut reverse = false;
|
||||
let mut unpack = false;
|
||||
let mut level = 2;
|
||||
let mut infile: Option<PathBuf> = None;
|
||||
let mut outfile: Option<PathBuf> = None;
|
||||
|
||||
match args.subcommand()?.as_ref().map(|s| s.as_str()) {
|
||||
None => print_help(),
|
||||
Some("pack") => {
|
||||
let level = args.opt_value_from_str(["-l", "--level"])?.unwrap_or(2u8);
|
||||
let use_bitstream = args.contains(["-b", "--bitstream"]);
|
||||
let parity_contexts = args
|
||||
.opt_value_from_str(["-p", "--parity"])?
|
||||
.unwrap_or(1usize);
|
||||
let mut parser = lexopt::Parser::from_env();
|
||||
while let Some(arg) = parser.next()? {
|
||||
use lexopt::prelude::*;
|
||||
match arg {
|
||||
Short('b') | Long("bitstream") => config.use_bitstream = true,
|
||||
Short('p') | Long("parity") => config.parity_contexts = parser.value()?.parse()?,
|
||||
Short('r') | Long("reverse") => reverse = true,
|
||||
Short('u') | Long("unpack") => unpack = true,
|
||||
Short('l') | Long("level") => level = parser.value()?.parse()?,
|
||||
Short('h') | Long("help") => print_help(0),
|
||||
Value(val) if infile.is_none() => infile = Some(val.try_into()?),
|
||||
Value(val) if outfile.is_none() => outfile = Some(val.try_into()?),
|
||||
_ => return Err(arg.unexpected().into()),
|
||||
}
|
||||
}
|
||||
|
||||
if parity_contexts != 1 && parity_contexts != 2 && parity_contexts != 4 {
|
||||
eprintln!("--parity has to be 1, 2 or 4");
|
||||
let infile = infile.unwrap_or_else(|| print_help(1));
|
||||
let outfile = outfile.unwrap_or_else(|| {
|
||||
let mut name = infile.clone();
|
||||
if unpack {
|
||||
if name.extension().filter(|&e| e == "upk").is_some() {
|
||||
name.set_extension("");
|
||||
} else {
|
||||
name.set_extension("bin");
|
||||
}
|
||||
} else {
|
||||
let mut filename = name
|
||||
.file_name()
|
||||
.unwrap_or_else(|| OsStr::new(""))
|
||||
.to_os_string();
|
||||
filename.push(".upk");
|
||||
name.set_file_name(filename);
|
||||
}
|
||||
name
|
||||
});
|
||||
|
||||
if config.parity_contexts != 1 && config.parity_contexts != 2 && config.parity_contexts != 4 {
|
||||
eprintln!("--parity has to be 1, 2, or 4");
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let infile = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||
let outfile = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||
|
||||
if !unpack {
|
||||
let mut data = vec![];
|
||||
File::open(infile)?.read_to_end(&mut data)?;
|
||||
if reverse {
|
||||
data.reverse();
|
||||
}
|
||||
|
||||
let mut pb = pbr::ProgressBar::new(data.len() as u64);
|
||||
pb.set_units(pbr::Units::Bytes);
|
||||
let packed_data = upkr::pack(
|
||||
let mut packed_data = upkr::pack(
|
||||
&data,
|
||||
level,
|
||||
use_bitstream,
|
||||
parity_contexts,
|
||||
config,
|
||||
Some(&mut |pos| {
|
||||
pb.set(pos as u64);
|
||||
}),
|
||||
);
|
||||
pb.finish();
|
||||
|
||||
if reverse {
|
||||
packed_data.reverse();
|
||||
}
|
||||
|
||||
println!(
|
||||
"Compressed {} bytes to {} bytes ({}%)",
|
||||
data.len(),
|
||||
@@ -46,41 +83,33 @@ fn main() -> Result<()> {
|
||||
packed_data.len() as f32 * 100. / data.len() as f32
|
||||
);
|
||||
File::create(outfile)?.write_all(&packed_data)?;
|
||||
}
|
||||
Some("unpack") => {
|
||||
let use_bitstream = args.contains(["-b", "--bitstream"]);
|
||||
let parity_contexts = args
|
||||
.opt_value_from_str(["-p", "--parity"])?
|
||||
.unwrap_or(1usize);
|
||||
|
||||
if parity_contexts != 1 && parity_contexts != 2 && parity_contexts != 4 {
|
||||
eprintln!("--parity has to be 1, 2 or 4");
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let infile = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||
let outfile = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||
|
||||
} else {
|
||||
let mut data = vec![];
|
||||
File::open(infile)?.read_to_end(&mut data)?;
|
||||
let packed_data = upkr::unpack(&data, use_bitstream, parity_contexts);
|
||||
File::create(outfile)?.write_all(&packed_data)?;
|
||||
if reverse {
|
||||
data.reverse();
|
||||
}
|
||||
Some(other) => {
|
||||
bail!("Unknown subcommand '{}'", other);
|
||||
let mut unpacked_data = upkr::unpack(&data, config);
|
||||
if reverse {
|
||||
unpacked_data.reverse();
|
||||
}
|
||||
File::create(outfile)?.write_all(&unpacked_data)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
fn print_help(exit_code: i32) -> ! {
|
||||
eprintln!("Usage:");
|
||||
eprintln!(" upkr pack [-b] [-l level(0-9)] [-p N] <infile> <outfile>");
|
||||
eprintln!(" upkr unpack [-b] [-p N] <infile> <outfile>");
|
||||
eprintln!(" upkr [-l level(0-9)] [config options] <infile> [<outfile>]");
|
||||
eprintln!(" upkr -u [config options] <infile> [<outfile>]");
|
||||
eprintln!();
|
||||
eprintln!(" -b, --bitstream bitstream mode");
|
||||
eprintln!(" -l, --level N compression level 0-9");
|
||||
eprintln!(" -u, --unpack unpack infile");
|
||||
eprintln!();
|
||||
eprintln!("Config options (need to match when packing/unpacking):");
|
||||
eprintln!(" -b, --bitstream bitstream mode");
|
||||
eprintln!(" -p, --parity N use N (2/4) parity contexts");
|
||||
process::exit(1);
|
||||
eprintln!(" -r, --reverse reverse input & output");
|
||||
process::exit(exit_code);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user