diff options
Diffstat (limited to 'extra/7z2lzma')
-rwxr-xr-x | extra/7z2lzma/7z2lzma.bash | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/extra/7z2lzma/7z2lzma.bash b/extra/7z2lzma/7z2lzma.bash new file mode 100755 index 0000000..1777c78 --- /dev/null +++ b/extra/7z2lzma/7z2lzma.bash @@ -0,0 +1,115 @@ +#!/bin/bash +# +############################################################################# +# +# 7z2lzma.bash is very primitive .7z to .lzma converter. The input file must +# have exactly one LZMA compressed stream, which has been created with the +# default lc, lp, and pb values. The CRC32 in the .7z archive is not checked, +# and the script may seem to succeed while it actually created a corrupt .lzma +# file. You should always try uncompressing both the original .7z and the +# created .lzma and compare that the output is identical. +# +# This script requires basic GNU tools and 7z or 7za tool from p7zip. +# +# Last modified: 2009-01-15 14:25+0200 +# +############################################################################# +# +# Author: Lasse Collin <lasse.collin@tukaani.org> +# +# This file has been put into the public domain. +# You can do whatever you want with this file. +# +############################################################################# + +# You can use 7z or 7za, both will work. +SEVENZIP=7za + +if [ $# != 2 -o -z "$1" -o -z "$2" ]; then + echo "Usage: $0 input.7z output.lzma" + exit 1 +fi + +# Converts an integer variable to little endian binary integer. +int2bin() +{ + local LEN=$1 + local NUM=$2 + local HEX=(0 1 2 3 4 5 6 7 8 9 A B C D E F) + local I + for ((I=0; I < "$LEN"; ++I)); do + printf "\\x${HEX[(NUM >> 4) & 0x0F]}${HEX[NUM & 0x0F]}" + NUM=$((NUM >> 8)) + done +} + +# Make sure we get possible errors from pipes. +set -o pipefail + +# Get information about the input file. At least older 7z and 7za versions +# may return with zero exit status even when an error occurred, so check +# if the output has any lines beginning with "Error". +INFO=$("$SEVENZIP" l -slt "$1") +if [ $? != 0 ] || printf '%s\n' "$INFO" | grep -q ^Error; then + printf '%s\n' "$INFO" + exit 1 +fi + +# Check if the input file has more than one compressed block. +if printf '%s\n' "$INFO" | grep -q '^Block = 1'; then + echo "Cannot convert, because the input file has more than" + echo "one compressed block." + exit 1 +fi + +# Get compressed, uncompressed, and dictionary size. +CSIZE=$(printf '%s\n' "$INFO" | sed -rn 's|^Packed Size = ([0-9]+$)|\1|p') +USIZE=$(printf '%s\n' "$INFO" | sed -rn 's|^Size = ([0-9]+$)|\1|p') +DICT=$(printf '%s\n' "$INFO" | sed -rn 's|^Method = LZMA:([0-9]+[bkm]?)$|\1|p') + +if [ -z "$CSIZE" -o -z "$USIZE" -o -z "$DICT" ]; then + echo "Parsing output of $SEVENZIP failed. Maybe the file uses some" + echo "other compression method than plain LZMA." + exit 1 +fi + +# The following assumes that the default lc, lp, and pb settings were used. +# Otherwise the output will be corrupt. +printf '\x5D' > "$2" + +# Dictionary size can be either was power of two, bytes, kibibytes, or +# mebibytes. We need to convert it to bytes. +case $DICT in + *b) + DICT=${DICT%b} + ;; + *k) + DICT=${DICT%k} + DICT=$((DICT << 10)) + ;; + *m) + DICT=${DICT%m} + DICT=$((DICT << 20)) + ;; + *) + DICT=$((1 << DICT)) + ;; +esac +int2bin 4 "$DICT" >> "$2" + +# Uncompressed size +int2bin 8 "$USIZE" >> "$2" + +# Copy the actual compressed data. Using multiple dd commands to avoid +# copying large amount of data with one-byte block size, which would be +# annoyingly slow. +BS=8192 +BIGSIZE=$((CSIZE / BS)) +CSIZE=$((CSIZE % BS)) +{ + dd of=/dev/null bs=32 count=1 \ + && dd bs="$BS" count="$BIGSIZE" \ + && dd bs=1 count="$CSIZE" +} < "$1" >> "$2" + +exit $? |