Been using these on termux when I’m too lazy to leave my bed. Repacking has not been extensively tested. Haven’t flashed any yet.
If someone made a TUI, that’d be cool.
install-dependencies.sh
#!/bin/bash
# Android ROM Tools - Termux Dependency Installer
# Installs all required packages for unpacking/repacking Android images
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
print_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_header() {
echo -e "${CYAN}================================${NC}"
echo -e "${CYAN}$1${NC}"
echo -e "${CYAN}================================${NC}"
}
# Check if running in Termux
if [ ! -d "/data/data/com.termux" ]; then
print_error "This script is designed for Termux only"
exit 1
fi
print_header "Android ROM Tools Installer"
echo ""
print_info "This script will install all dependencies for:"
echo " - super.img unpacking/repacking"
echo " - ext4 partition unpacking/repacking"
echo ""
# Update package lists
print_info "Updating package lists..."
pkg update -y
# Install required packages
print_header "Installing Required Packages"
PACKAGES=(
# Core utilities
"coreutils"
# Sparse image tools
"android-tools" # Contains simg2img, img2simg, lpunpack, lpmake, etc.
# ext4 filesystem tools
"e2fsprogs" # Contains debugfs, mke2fs, etc.
# Additional utilities
"util-linux" # Contains various utilities
)
for pkg in "${PACKAGES[@]}"; do
print_info "Installing $pkg..."
pkg install -y "$pkg" 2>/dev/null || print_warn "$pkg may already be installed or unavailable"
done
echo ""
print_header "Verifying Installation"
# Verify tools are installed
TOOLS=(
"simg2img:Sparse to raw image converter"
"img2simg:Raw to sparse image converter"
"lpunpack:Super partition unpacker"
"lpmake:Super partition creator"
"debugfs:ext4 filesystem debugger/extractor"
"mke2fs.android:Android ext4 filesystem creator"
"e2fsdroid:ext4 image populator"
"ext2simg:ext4 to sparse converter"
)
ALL_OK=1
for tool_info in "${TOOLS[@]}"; do
tool="${tool_info%%:*}"
desc="${tool_info#*:}"
if command -v "$tool" &> /dev/null; then
echo -e " ${GREEN}✓${NC} $tool - $desc"
else
echo -e " ${RED}✗${NC} $tool - $desc (NOT FOUND)"
ALL_OK=0
fi
done
echo ""
if [ "$ALL_OK" -eq 1 ]; then
print_header "Installation Complete!"
echo ""
print_info "All tools installed successfully."
echo ""
echo "Available scripts:"
echo " ./unpack_super.sh - Unpack super.img to partition images"
echo " ./repack_super.sh - Repack partition images to super.img"
echo " ./unpack_ext4.sh - Extract files from ext4 images"
echo " ./repack_ext4.sh - Create ext4 images from directories"
echo ""
echo "Example workflow:"
echo " 1. ./unpack_super.sh super.img"
echo " 2. ./unpack_ext4.sh super_unpacked/system_a.img"
echo " 3. (make modifications)"
echo " 4. ./repack_ext4.sh system_a_extracted -o system_a.img"
echo " 5. ./repack_super.sh super_unpacked -o super_new.img -s"
else
print_error "Some tools could not be installed."
print_error "Try running 'pkg update && pkg upgrade' first."
fi
unpack-super.sh
#!/bin/bash
# Android super.img unpacker script
# Handles both sparse and raw super images
set -e
RAW_IMG=""
# Cleanup function to ensure super_raw.img is always deleted
cleanup() {
if [ -n "$RAW_IMG" ] && [ -f "$RAW_IMG" ]; then
rm -f "$RAW_IMG"
fi
}
# Trap to run cleanup on exit, error, or interrupt
trap cleanup EXIT ERR INT TERM
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
print_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
usage() {
echo "Usage: $0 <super.img> [output_directory]"
echo ""
echo "Arguments:"
echo " super.img Path to the super.img file to unpack"
echo " output_directory Optional output directory (default: <super_name>_unpacked)"
exit 1
}
# Check arguments
if [ $# -lt 1 ]; then
usage
fi
INPUT_IMG="$1"
INPUT_NAME=$(basename "$INPUT_IMG" .img)
# Set output directory
if [ -n "$2" ]; then
OUTPUT_DIR="$2"
else
OUTPUT_DIR="$(dirname "$INPUT_IMG")/${INPUT_NAME}_unpacked"
fi
# Check if input file exists
if [ ! -f "$INPUT_IMG" ]; then
print_error "File not found: $INPUT_IMG"
exit 1
fi
# Check for required tools
for tool in lpunpack simg2img; do
if ! command -v $tool &> /dev/null; then
print_error "$tool is required but not installed."
exit 1
fi
done
# Create output directory
print_info "Creating output directory: $OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR"
# Check if image is sparse by reading magic bytes
# Sparse magic: 0xed26ff3a (little-endian: 3a ff 26 ed)
MAGIC=$(od -A n -t x1 -N 4 "$INPUT_IMG" | tr -d ' ')
if [ "$MAGIC" = "3aff26ed" ]; then
print_info "Detected sparse image format"
print_info "Converting sparse image to raw..."
RAW_IMG="$OUTPUT_DIR/super_raw.img"
simg2img "$INPUT_IMG" "$RAW_IMG"
print_info "Conversion complete"
UNPACK_IMG="$RAW_IMG"
CLEANUP_RAW=1
else
print_info "Detected raw image format"
UNPACK_IMG="$INPUT_IMG"
CLEANUP_RAW=0
fi
# Unpack partitions
print_info "Unpacking partitions with lpunpack..."
lpunpack "$UNPACK_IMG" "$OUTPUT_DIR"
# Delete super_raw.img after unpacking
if [ -n "$RAW_IMG" ] && [ -f "$RAW_IMG" ]; then
print_info "Deleting intermediate file: super_raw.img"
rm -f "$RAW_IMG"
fi
# List extracted partitions
echo ""
print_info "Extraction complete! Partitions extracted to: $OUTPUT_DIR"
echo ""
echo "Extracted partitions:"
ls -lh "$OUTPUT_DIR"/*.img 2>/dev/null || print_warn "No partition images found"
unpack_ext4.sh
#!/bin/bash
# Android ext4 image unpacker script
# Extracts contents from ext4 partition images without root
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
print_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
usage() {
echo "Usage: $0 <image.img> [output_directory]"
echo ""
echo "Arguments:"
echo " image.img Path to ext4 image file (system, vendor, product, etc.)"
echo " output_directory Optional output directory (default: <image_name>_extracted)"
echo ""
echo "Examples:"
echo " $0 system_a.img"
echo " $0 vendor_a.img vendor_extracted"
exit 1
}
# Check arguments
if [ $# -lt 1 ]; then
usage
fi
INPUT_IMG="$1"
INPUT_NAME=$(basename "$INPUT_IMG" .img)
# Set output directory
if [ -n "$2" ]; then
OUTPUT_DIR="$2"
else
OUTPUT_DIR="$(dirname "$INPUT_IMG")/${INPUT_NAME}_extracted"
fi
# Check if input file exists
if [ ! -f "$INPUT_IMG" ]; then
print_error "File not found: $INPUT_IMG"
exit 1
fi
# Check for required tools
if ! command -v debugfs &> /dev/null; then
print_error "debugfs is required but not installed."
print_error "Install with: pkg install e2fsprogs"
exit 1
fi
# Verify it's an ext4 image by checking magic at offset 0x438
MAGIC=$(od -A n -t x1 -N 2 -j 1080 "$INPUT_IMG" | tr -d ' ')
if [ "$MAGIC" != "53ef" ]; then
print_error "Not a valid ext4 image (magic: $MAGIC, expected: 53ef)"
print_error "This might be erofs or another filesystem type"
exit 1
fi
print_info "Valid ext4 image detected"
# Create output directory
print_info "Creating output directory: $OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR"
# Get absolute path for output
OUTPUT_DIR=$(cd "$OUTPUT_DIR" && pwd)
# Extract using debugfs
print_info "Extracting files from $INPUT_IMG..."
print_info "This may take a while for large images..."
# Use debugfs to recursively dump all contents
debugfs -R "rdump / \"$OUTPUT_DIR\"" "$INPUT_IMG" 2>&1 | grep -v "^debugfs:" || true
# Count extracted files
FILE_COUNT=$(find "$OUTPUT_DIR" -type f 2>/dev/null | wc -l)
DIR_COUNT=$(find "$OUTPUT_DIR" -type d 2>/dev/null | wc -l)
LINK_COUNT=$(find "$OUTPUT_DIR" -type l 2>/dev/null | wc -l)
echo ""
print_info "Extraction complete!"
print_info "Output directory: $OUTPUT_DIR"
print_info "Files: $FILE_COUNT | Directories: $DIR_COUNT | Symlinks: $LINK_COUNT"
# Show top-level contents
echo ""
echo "Top-level contents:"
ls -la "$OUTPUT_DIR" 2>/dev/null | head -20
repack_partition.sh
#!/bin/bash
# Android partition repacker script
# Repacks extracted directories back to ext4 images (sparse or raw)
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
print_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
usage() {
echo "Usage: $0 <input_directory> [options]"
echo ""
echo "Arguments:"
echo " input_directory Directory containing extracted filesystem"
echo ""
echo "Options:"
echo " -o, --output FILE Output image file (default: <dir_name>.img)"
echo " -s, --size SIZE Exact image size in bytes (default: auto-calculated)"
echo " -S, --sparse Output sparse image instead of raw"
echo " -h, --help Show this help"
echo ""
echo "Examples:"
echo " $0 system_a_extracted"
echo " $0 vendor_a_extracted -o vendor_new.img -S"
echo " $0 product_a_extracted -s 734003200 -S"
exit 1
}
# Default values
OUTPUT_FILE=""
IMG_SIZE=""
SPARSE=0
# Parse arguments
INPUT_DIR=""
while [[ $# -gt 0 ]]; do
case $1 in
-o|--output)
OUTPUT_FILE="$2"
shift 2
;;
-s|--size)
IMG_SIZE="$2"
shift 2
;;
-S|--sparse)
SPARSE=1
shift
;;
-h|--help)
usage
;;
-*)
print_error "Unknown option: $1"
usage
;;
*)
if [ -z "$INPUT_DIR" ]; then
INPUT_DIR="$1"
else
print_error "Unexpected argument: $1"
usage
fi
shift
;;
esac
done
# Check input directory
if [ -z "$INPUT_DIR" ]; then
print_error "Input directory is required"
usage
fi
# Remove trailing slash
INPUT_DIR="${INPUT_DIR%/}"
if [ ! -d "$INPUT_DIR" ]; then
print_error "Directory not found: $INPUT_DIR"
exit 1
fi
# Set default output file
if [ -z "$OUTPUT_FILE" ]; then
DIR_NAME=$(basename "$INPUT_DIR" | sed 's/_extracted$//')
OUTPUT_FILE="${DIR_NAME}.img"
fi
# Check for required tools
for tool in mke2fs e2fsdroid; do
if ! command -v $tool &> /dev/null; then
print_error "$tool is required but not installed."
exit 1
fi
done
# Calculate directory size if not specified
if [ -z "$IMG_SIZE" ]; then
print_info "Calculating directory size..."
# Get actual content size
CONTENT_SIZE=$(du -sb "$INPUT_DIR" | cut -f1)
# Calculate with overhead for ext4 metadata (~5-10%)
# Also ensure minimum size and 4K alignment
OVERHEAD=$((CONTENT_SIZE / 10))
[ "$OVERHEAD" -lt 10485760 ] && OVERHEAD=10485760 # Min 10MB overhead
IMG_SIZE=$((CONTENT_SIZE + OVERHEAD))
# Align to 4K blocks
IMG_SIZE=$(( (IMG_SIZE + 4095) / 4096 * 4096 ))
print_info "Content size: $(numfmt --to=iec $CONTENT_SIZE 2>/dev/null || echo $CONTENT_SIZE)"
print_info "Image size (with overhead): $(numfmt --to=iec $IMG_SIZE 2>/dev/null || echo $IMG_SIZE)"
fi
# Calculate block count (4K blocks)
BLOCK_COUNT=$((IMG_SIZE / 4096))
print_info "Creating ext4 image: $OUTPUT_FILE"
print_info "Size: $IMG_SIZE bytes ($BLOCK_COUNT blocks)"
# Remove existing output file
rm -f "$OUTPUT_FILE"
# Create ext4 filesystem (no journal, like Android images)
print_info "Creating ext4 filesystem..."
mke2fs -t ext4 -b 4096 -m 0 -O ^has_journal,^metadata_csum -L "/" "$OUTPUT_FILE" "$BLOCK_COUNT" 2>&1 | grep -v "^$" || true
# Populate with e2fsdroid
print_info "Populating image with files..."
e2fsdroid -e -a / -f "$INPUT_DIR" "$OUTPUT_FILE"
# Convert to sparse if requested
if [ "$SPARSE" -eq 1 ]; then
if command -v img2simg &> /dev/null; then
print_info "Converting to sparse image..."
TEMP_FILE="${OUTPUT_FILE}.raw"
mv "$OUTPUT_FILE" "$TEMP_FILE"
img2simg "$TEMP_FILE" "$OUTPUT_FILE"
rm -f "$TEMP_FILE"
print_info "Output format: sparse"
else
print_warn "img2simg not found, keeping raw image"
fi
else
print_info "Output format: raw"
fi
echo ""
print_info "Image created successfully: $OUTPUT_FILE"
ls -lh "$OUTPUT_FILE"
repack-super.sh
#!/bin/bash
# Android super.img repacker script
# Repacks partition images into a super.img (sparse or raw)
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
print_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
usage() {
echo "Usage: $0 <input_directory> [options]"
echo ""
echo "Arguments:"
echo " input_directory Directory containing partition .img files"
echo ""
echo "Options:"
echo " -o, --output FILE Output file (default: super_repacked.img)"
echo " -s, --sparse Output sparse image (default: raw)"
echo " -S, --size SIZE Super partition size in bytes (default: auto)"
echo " -g, --group NAME Partition group name (default: main)"
echo " -G, --group-size SZ Group size in bytes (default: auto)"
echo " -m, --metadata SIZE Metadata size (default: 65536)"
echo " -l, --slots COUNT Metadata slots (default: 2)"
echo " -h, --help Show this help"
exit 1
}
# Default values
OUTPUT_FILE="super_repacked.img"
SPARSE=0
SUPER_SIZE="auto"
GROUP_NAME="main"
GROUP_SIZE=""
METADATA_SIZE=65536
METADATA_SLOTS=2
# Parse arguments
INPUT_DIR=""
while [[ $# -gt 0 ]]; do
case $1 in
-o|--output)
OUTPUT_FILE="$2"
shift 2
;;
-s|--sparse)
SPARSE=1
shift
;;
-S|--size)
SUPER_SIZE="$2"
shift 2
;;
-g|--group)
GROUP_NAME="$2"
shift 2
;;
-G|--group-size)
GROUP_SIZE="$2"
shift 2
;;
-m|--metadata)
METADATA_SIZE="$2"
shift 2
;;
-l|--slots)
METADATA_SLOTS="$2"
shift 2
;;
-h|--help)
usage
;;
-*)
print_error "Unknown option: $1"
usage
;;
*)
if [ -z "$INPUT_DIR" ]; then
INPUT_DIR="$1"
else
print_error "Unexpected argument: $1"
usage
fi
shift
;;
esac
done
# Check input directory
if [ -z "$INPUT_DIR" ]; then
print_error "Input directory is required"
usage
fi
if [ ! -d "$INPUT_DIR" ]; then
print_error "Directory not found: $INPUT_DIR"
exit 1
fi
# Check for required tools
if ! command -v lpmake &> /dev/null; then
print_error "lpmake is required but not installed."
exit 1
fi
# Find partition images
print_info "Scanning for partition images in: $INPUT_DIR"
PARTITIONS=()
PARTITION_ARGS=""
IMAGE_ARGS=""
TOTAL_SIZE=0
for img in "$INPUT_DIR"/*.img; do
[ -f "$img" ] || continue
# Skip super_raw.img if present
basename_img=$(basename "$img")
if [ "$basename_img" = "super_raw.img" ]; then
continue
fi
# Get partition name (remove .img extension)
part_name="${basename_img%.img}"
# Get file size
file_size=$(stat -c%s "$img" 2>/dev/null || stat -f%z "$img" 2>/dev/null)
PARTITIONS+=("$part_name")
TOTAL_SIZE=$((TOTAL_SIZE + file_size))
print_info "Found partition: $part_name ($(numfmt --to=iec $file_size 2>/dev/null || echo $file_size bytes))"
# Build lpmake arguments
PARTITION_ARGS="$PARTITION_ARGS -p ${part_name}:readonly:${file_size}:${GROUP_NAME}"
IMAGE_ARGS="$IMAGE_ARGS -i ${part_name}=${img}"
done
if [ ${#PARTITIONS[@]} -eq 0 ]; then
print_error "No partition images found in $INPUT_DIR"
exit 1
fi
print_info "Total partitions: ${#PARTITIONS[@]}"
print_info "Total size: $(numfmt --to=iec $TOTAL_SIZE 2>/dev/null || echo $TOTAL_SIZE bytes)"
# Calculate group size if not specified
if [ -z "$GROUP_SIZE" ]; then
# Add 10% overhead for alignment
GROUP_SIZE=$((TOTAL_SIZE + TOTAL_SIZE / 10))
fi
# Build lpmake command
LPMAKE_CMD="lpmake"
LPMAKE_CMD="$LPMAKE_CMD -d $SUPER_SIZE"
LPMAKE_CMD="$LPMAKE_CMD -m $METADATA_SIZE"
LPMAKE_CMD="$LPMAKE_CMD -s $METADATA_SLOTS"
LPMAKE_CMD="$LPMAKE_CMD -g ${GROUP_NAME}:${GROUP_SIZE}"
LPMAKE_CMD="$LPMAKE_CMD $PARTITION_ARGS"
LPMAKE_CMD="$LPMAKE_CMD $IMAGE_ARGS"
LPMAKE_CMD="$LPMAKE_CMD -o $OUTPUT_FILE"
if [ "$SPARSE" -eq 1 ]; then
LPMAKE_CMD="$LPMAKE_CMD -S"
print_info "Output format: sparse"
else
LPMAKE_CMD="$LPMAKE_CMD -F"
print_info "Output format: raw"
fi
# Execute lpmake
print_info "Creating super.img..."
echo ""
eval $LPMAKE_CMD
echo ""
print_info "Super image created: $OUTPUT_FILE"
ls -lh "$OUTPUT_FILE"
With all of the above in a zip:
termux-rom-tools.zip (7.2 KB)