summaryrefslogtreecommitdiffstats
path: root/extra/7z2lzma/7z2lzma.bash
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xextra/7z2lzma/7z2lzma.bash115
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 $?