LHA LHarc archive
AI-powered detection and analysis of LHarc archive files.
Instant LHA File Detection
Use our advanced AI-powered tool to instantly detect and analyze LHarc archive files with precision and speed.
File Information
LHarc archive
Archive
.lha, .lzh
application/x-lha
LHA (LHarc Archive)
What is an LHA file?
An LHA file is a compressed archive format created by the LHarc archiving utility, developed by Haruyasu Yoshizaki (Yoshi) in Japan. LHA files use the extension .lha or .lzh and employ the LZSS (Lempel-Ziv-Storer-Szymanski) compression algorithm combined with Huffman coding. This format was particularly popular in the late 1980s and 1990s, especially in Japan and among bulletin board systems (BBS) users.
History and Development
LHA was developed as an improvement over earlier compression formats, providing better compression ratios and faster decompression times. It became one of the most widely used archive formats in Japan and gained popularity worldwide through BBS networks.
Key milestones:
- 1988: LHarc 1.0 released by Haruyasu Yoshizaki
- 1991: LHA 2.x series with improved algorithms
- 1992: LHA becomes popular on Amiga and PC platforms
- 1995: Peak popularity during BBS and early internet era
- 2000s: Gradual decline as ZIP and RAR formats gained dominance
- Present: Still supported by various archiving tools, mainly for legacy compatibility
Technical Specifications
Attribute | Details |
---|---|
File Extensions | .lha, .lzh |
MIME Type | application/x-lha, application/x-lzh-compressed |
Compression | LZSS + Huffman coding |
Maximum File Size | 4 GB per file |
Maximum Archive Size | Virtually unlimited |
Encryption | Not supported |
Unicode Support | Limited (depends on implementation) |
File Structure and Format
LHA Header Format
LHA File Structure:
├── Header Size (1 byte)
├── Header Checksum (1 byte)
├── Method ID (5 bytes)
├── Compressed Size (4 bytes)
├── Uncompressed Size (4 bytes)
├── File Time (4 bytes)
├── File Attributes (1 byte)
├── Filename Length (1 byte)
├── Filename (variable)
├── CRC-16 (2 bytes)
└── Compressed Data (variable)
Compression Methods
Method ID Description
-h0- No compression (store only)
-lh1- LZSS with 4KB dictionary + Huffman
-lh4- LZSS with 4KB dictionary + improved Huffman
-lh5- LZSS with 8KB dictionary + Huffman
-lh6- LZSS with 32KB dictionary + Huffman
-lh7- LZSS with 64KB dictionary + Huffman
-lhd- Directory entry
Creating and Extracting LHA Archives
Command Line Tools
LHA/LHZ Utilities
# Create LHA archive
lha a archive.lha file1.txt file2.txt folder/
# Create with specific compression method
lha a -lh6 archive.lha files/
# Extract all files
lha x archive.lha
# Extract to specific directory
lha x archive.lha -w target_directory/
# List archive contents
lha l archive.lha
# Test archive integrity
lha t archive.lha
# Add files to existing archive
lha a archive.lha newfile.txt
# Update archive (replace newer files)
lha u archive.lha files/
# Freshen archive (update existing files only)
lha f archive.lha files/
Using 7-Zip
# Extract LHA archive
7z x archive.lha
# List contents
7z l archive.lha
# Test archive
7z t archive.lha
# Extract with full paths
7z x archive.lha -o"output_folder"
Programming Examples
Python with py7zr
import py7zr
import os
from pathlib import Path
def extract_lha_archive(archive_path, extract_to=None):
"""Extract LHA archive using py7zr."""
if extract_to is None:
extract_to = Path(archive_path).stem
try:
with py7zr.SevenZipFile(archive_path, mode='r') as archive:
archive.extractall(path=extract_to)
print(f"Successfully extracted {archive_path} to {extract_to}")
return True
except Exception as e:
print(f"Error extracting {archive_path}: {e}")
return False
def list_lha_contents(archive_path):
"""List contents of LHA archive."""
try:
with py7zr.SevenZipFile(archive_path, mode='r') as archive:
file_list = archive.getnames()
return file_list
except Exception as e:
print(f"Error reading {archive_path}: {e}")
return []
def get_lha_info(archive_path):
"""Get detailed information about LHA archive."""
try:
with py7zr.SevenZipFile(archive_path, mode='r') as archive:
info = {
'filename': Path(archive_path).name,
'files': [],
'total_files': 0,
'total_compressed_size': 0,
'total_uncompressed_size': 0
}
for file_info in archive.list():
file_data = {
'filename': file_info.filename,
'compressed_size': file_info.compressed if hasattr(file_info, 'compressed') else 0,
'uncompressed_size': file_info.uncompressed if hasattr(file_info, 'uncompressed') else 0,
'is_directory': file_info.is_directory if hasattr(file_info, 'is_directory') else False,
'crc': file_info.crc32 if hasattr(file_info, 'crc32') else None
}
info['files'].append(file_data)
info['total_compressed_size'] += file_data['compressed_size']
info['total_uncompressed_size'] += file_data['uncompressed_size']
info['total_files'] = len(info['files'])
info['compression_ratio'] = (
(info['total_uncompressed_size'] - info['total_compressed_size'])
/ info['total_uncompressed_size'] * 100
if info['total_uncompressed_size'] > 0 else 0
)
return info
except Exception as e:
print(f"Error analyzing {archive_path}: {e}")
return None
# Usage examples
archive_info = get_lha_info('example.lha')
if archive_info:
print(f"Archive: {archive_info['filename']}")
print(f"Files: {archive_info['total_files']}")
print(f"Compressed size: {archive_info['total_compressed_size']:,} bytes")
print(f"Uncompressed size: {archive_info['total_uncompressed_size']:,} bytes")
print(f"Compression ratio: {archive_info['compression_ratio']:.1f}%")
files = list_lha_contents('example.lha')
for file in files:
print(f" {file}")
extract_lha_archive('example.lha', 'extracted_files')
C++ with libarchive
#include <archive.h>
#include <archive_entry.h>
#include <iostream>
#include <fstream>
class LHAExtractor {
private:
struct archive* a;
struct archive* ext;
public:
LHAExtractor() : a(nullptr), ext(nullptr) {
a = archive_read_new();
archive_read_support_format_lha(a);
archive_read_support_filter_all(a);
ext = archive_write_disk_new();
archive_write_disk_set_options(ext, ARCHIVE_EXTRACT_TIME);
archive_write_disk_set_standard_lookup(ext);
}
~LHAExtractor() {
if (a) archive_read_free(a);
if (ext) archive_write_free(ext);
}
bool extractArchive(const std::string& archivePath,
const std::string& outputDir = ".") {
int r = archive_read_open_filename(a, archivePath.c_str(), 10240);
if (r != ARCHIVE_OK) {
std::cerr << "Error opening archive: " << archive_error_string(a) << std::endl;
return false;
}
struct archive_entry* entry;
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
// Modify output path
std::string currentFile = archive_entry_pathname(entry);
std::string outputPath = outputDir + "/" + currentFile;
archive_entry_set_pathname(entry, outputPath.c_str());
r = archive_write_header(ext, entry);
if (r != ARCHIVE_OK) {
std::cerr << "Error writing header: " << archive_error_string(ext) << std::endl;
} else {
copyData(a, ext);
}
archive_write_finish_entry(ext);
}
archive_read_close(a);
return true;
}
void listContents(const std::string& archivePath) {
int r = archive_read_open_filename(a, archivePath.c_str(), 10240);
if (r != ARCHIVE_OK) {
std::cerr << "Error opening archive: " << archive_error_string(a) << std::endl;
return;
}
struct archive_entry* entry;
std::cout << "Contents of " << archivePath << ":" << std::endl;
std::cout << "Size\t\tName" << std::endl;
std::cout << "----\t\t----" << std::endl;
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
int64_t size = archive_entry_size(entry);
const char* name = archive_entry_pathname(entry);
std::cout << size << "\t\t" << name << std::endl;
archive_read_data_skip(a);
}
archive_read_close(a);
}
private:
int copyData(struct archive* ar, struct archive* aw) {
int r;
const void* buff;
size_t size;
la_int64_t offset;
for (;;) {
r = archive_read_data_block(ar, &buff, &size, &offset);
if (r == ARCHIVE_EOF) return ARCHIVE_OK;
if (r < ARCHIVE_OK) return r;
r = archive_write_data_block(aw, buff, size, offset);
if (r < ARCHIVE_OK) {
std::cerr << "Error writing data: " << archive_error_string(aw) << std::endl;
return r;
}
}
}
};
int main() {
LHAExtractor extractor;
// List archive contents
extractor.listContents("example.lha");
// Extract archive
if (extractor.extractArchive("example.lha", "extracted")) {
std::cout << "Archive extracted successfully" << std::endl;
} else {
std::cout << "Failed to extract archive" << std::endl;
}
return 0;
}
Java with Apache Commons Compress
import org.apache.commons.compress.archivers.lha.LhaArchiveEntry;
import org.apache.commons.compress.archivers.lha.LhaArchiveInputStream;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class LHAHandler {
public static void extractLHA(String archivePath, String outputDir)
throws IOException {
Path outputPath = Paths.get(outputDir);
Files.createDirectories(outputPath);
try (FileInputStream fis = new FileInputStream(archivePath);
LhaArchiveInputStream lhaIn = new LhaArchiveInputStream(fis)) {
LhaArchiveEntry entry;
while ((entry = lhaIn.getNextLhaEntry()) != null) {
if (!entry.isDirectory()) {
Path filePath = outputPath.resolve(entry.getName());
Files.createDirectories(filePath.getParent());
try (FileOutputStream fos = new FileOutputStream(filePath.toFile())) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = lhaIn.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
System.out.println("Extracted: " + entry.getName());
} else {
Files.createDirectories(outputPath.resolve(entry.getName()));
}
}
}
}
public static void listLHAContents(String archivePath) throws IOException {
try (FileInputStream fis = new FileInputStream(archivePath);
LhaArchiveInputStream lhaIn = new LhaArchiveInputStream(fis)) {
System.out.println("Archive: " + archivePath);
System.out.println("Method\tSize\tComp Size\tName");
System.out.println("------\t----\t---------\t----");
LhaArchiveEntry entry;
while ((entry = lhaIn.getNextLhaEntry()) != null) {
System.out.printf("%s\t%d\t%d\t\t%s%s%n",
entry.getMethod(),
entry.getSize(),
entry.getCompressedSize(),
entry.getName(),
entry.isDirectory() ? "/" : "");
}
}
}
public static void analyzeLHA(String archivePath) throws IOException {
long totalUncompressed = 0;
long totalCompressed = 0;
int fileCount = 0;
int dirCount = 0;
try (FileInputStream fis = new FileInputStream(archivePath);
LhaArchiveInputStream lhaIn = new LhaArchiveInputStream(fis)) {
LhaArchiveEntry entry;
while ((entry = lhaIn.getNextLhaEntry()) != null) {
if (entry.isDirectory()) {
dirCount++;
} else {
fileCount++;
totalUncompressed += entry.getSize();
totalCompressed += entry.getCompressedSize();
}
}
}
double compressionRatio = totalUncompressed > 0 ?
(double)(totalUncompressed - totalCompressed) / totalUncompressed * 100 : 0;
System.out.println("LHA Archive Analysis:");
System.out.println("Files: " + fileCount);
System.out.println("Directories: " + dirCount);
System.out.println("Uncompressed size: " + formatSize(totalUncompressed));
System.out.println("Compressed size: " + formatSize(totalCompressed));
System.out.println("Compression ratio: " + String.format("%.1f%%", compressionRatio));
}
private static String formatSize(long bytes) {
if (bytes < 1024) return bytes + " B";
if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
if (bytes < 1024 * 1024 * 1024) return String.format("%.1f MB", bytes / (1024.0 * 1024));
return String.format("%.1f GB", bytes / (1024.0 * 1024 * 1024));
}
public static void main(String[] args) {
try {
String archivePath = "example.lha";
System.out.println("=== Listing Archive Contents ===");
listLHAContents(archivePath);
System.out.println("\n=== Archive Analysis ===");
analyzeLHA(archivePath);
System.out.println("\n=== Extracting Archive ===");
extractLHA(archivePath, "extracted");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Converting Between Archive Formats
Batch Conversion Scripts
PowerShell Script
# Convert LHA archives to ZIP format
param(
[Parameter(Mandatory=$true)]
[string]$SourceDir,
[Parameter(Mandatory=$true)]
[string]$DestDir,
[switch]$DeleteOriginal
)
# Ensure 7-Zip is available
$sevenZip = "C:\Program Files\7-Zip\7z.exe"
if (-not (Test-Path $sevenZip)) {
Write-Error "7-Zip not found at $sevenZip"
exit 1
}
# Create destination directory
if (-not (Test-Path $DestDir)) {
New-Item -ItemType Directory -Path $DestDir | Out-Null
}
# Find all LHA files
$lhaFiles = Get-ChildItem -Path $SourceDir -Filter "*.lha" -Recurse
$lzhFiles = Get-ChildItem -Path $SourceDir -Filter "*.lzh" -Recurse
$allFiles = $lhaFiles + $lzhFiles
Write-Host "Found $($allFiles.Count) LHA/LZH files to convert"
foreach ($file in $allFiles) {
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($file.Name)
$zipPath = Join-Path $DestDir "$baseName.zip"
$tempDir = Join-Path $env:TEMP "lha_temp_$baseName"
try {
Write-Host "Converting: $($file.Name)"
# Extract LHA to temporary directory
& $sevenZip x "$($file.FullName)" -o"$tempDir" -y | Out-Null
if ($LASTEXITCODE -eq 0) {
# Create ZIP from extracted files
& $sevenZip a "$zipPath" "$tempDir\*" -tzip | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Host " -> Created: $zipPath"
if ($DeleteOriginal) {
Remove-Item $file.FullName
Write-Host " -> Deleted original: $($file.Name)"
}
} else {
Write-Warning "Failed to create ZIP: $zipPath"
}
} else {
Write-Warning "Failed to extract: $($file.Name)"
}
}
finally {
# Clean up temporary directory
if (Test-Path $tempDir) {
Remove-Item $tempDir -Recurse -Force
}
}
}
Write-Host "Conversion complete!"
Bash Script
#!/bin/bash
# Convert LHA/LZH archives to modern formats
convert_lha_archives() {
local source_dir="$1"
local dest_dir="$2"
local target_format="${3:-zip}" # zip, 7z, tar.gz
local delete_original="${4:-false}"
# Check if 7z is available
if ! command -v 7z &> /dev/null; then
echo "Error: 7z command not found"
exit 1
fi
# Create destination directory
mkdir -p "$dest_dir"
# Find and convert LHA/LZH files
find "$source_dir" -type f \( -name "*.lha" -o -name "*.lzh" \) | while read -r lha_file; do
filename=$(basename "$lha_file")
basename="${filename%.*}"
case $target_format in
zip)
output_file="$dest_dir/$basename.zip"
format_option="-tzip"
;;
7z)
output_file="$dest_dir/$basename.7z"
format_option="-t7z"
;;
tar.gz)
output_file="$dest_dir/$basename.tar.gz"
format_option="-ttar -so | gzip"
;;
*)
echo "Unsupported format: $target_format"
continue
;;
esac
echo "Converting: $filename -> $(basename "$output_file")"
# Create temporary directory
temp_dir=$(mktemp -d)
# Extract LHA archive
if 7z x "$lha_file" -o"$temp_dir" -y > /dev/null 2>&1; then
# Create new archive
if [[ $target_format == "tar.gz" ]]; then
tar -czf "$output_file" -C "$temp_dir" .
else
7z a "$output_file" "$temp_dir"/* $format_option > /dev/null 2>&1
fi
if [[ $? -eq 0 ]]; then
echo " -> Success: $output_file"
if [[ $delete_original == "true" ]]; then
rm "$lha_file"
echo " -> Deleted original: $filename"
fi
else
echo " -> Failed to create: $output_file"
fi
else
echo " -> Failed to extract: $filename"
fi
# Clean up
rm -rf "$temp_dir"
done
}
# Usage examples
convert_lha_archives "/path/to/lha/files" "/path/to/output" "zip" "false"
convert_lha_archives "/old/archives" "/new/archives" "7z" "true"
Legacy Software and Historical Context
Historical Significance
LHA archives were crucial in the pre-internet era for:
- BBS file distribution: Efficient compression for slow modems
- Floppy disk storage: Maximizing limited storage space
- Software distribution: Particularly popular in Japan and Asia
- Amiga systems: One of the primary archive formats
- Game modifications: Distribution of patches and add-ons
Common Issues and Solutions
Character Encoding Problems
import chardet
def detect_filename_encoding(lha_path):
"""Detect character encoding in LHA archive filenames."""
# This is a simplified example - actual implementation would
# require parsing the LHA header structure
with open(lha_path, 'rb') as f:
# Read sample data from headers
sample_data = f.read(1024)
# Detect encoding
result = chardet.detect(sample_data)
return result['encoding'], result['confidence']
def handle_japanese_filenames(archive_path):
"""Handle Japanese characters in LHA archives."""
# Common encodings used in Japanese LHA archives
encodings = ['shift_jis', 'euc-jp', 'iso-2022-jp', 'utf-8']
for encoding in encodings:
try:
# Attempt to extract with specific encoding
extract_with_encoding(archive_path, encoding)
print(f"Successfully extracted with encoding: {encoding}")
break
except UnicodeDecodeError:
continue
else:
print("Failed to determine correct encoding")
def extract_with_encoding(archive_path, encoding):
"""Extract LHA archive with specific character encoding."""
# Implementation would depend on the specific library used
# This is a conceptual example
pass
Corruption Recovery
def verify_lha_integrity(archive_path):
"""Verify LHA archive integrity and attempt basic recovery."""
issues = []
try:
with open(archive_path, 'rb') as f:
# Check file size
f.seek(0, 2) # Seek to end
file_size = f.tell()
f.seek(0) # Reset to beginning
if file_size == 0:
issues.append("File is empty")
return issues
# Check LHA signature/headers
header = f.read(1)
if not header:
issues.append("Cannot read header")
return issues
header_size = ord(header)
if header_size == 0:
issues.append("Invalid header size")
# Additional integrity checks would go here
# This is a simplified example
except IOError as e:
issues.append(f"I/O error: {e}")
return issues
def attempt_partial_recovery(corrupted_archive_path, output_dir):
"""Attempt to recover files from partially corrupted LHA archive."""
recovered_files = []
# This would require implementing a custom LHA parser
# that can skip over corrupted sections and recover
# intact file entries
print(f"Attempting recovery of {corrupted_archive_path}")
print("This is a placeholder for recovery logic")
return recovered_files
Modern Usage and Compatibility
While LHA archives are rarely created today, they're still encountered in:
- Retro computing: Amiga, PC-98, and vintage game preservation
- Digital archaeology: Preserving old BBS and shareware collections
- Legacy system maintenance: Supporting older Japanese software
- Archive migration: Converting old archives to modern formats
Most modern archiving tools (7-Zip, WinRAR, PeaZip) can extract LHA files, ensuring backward compatibility for accessing historical data and preserving digital heritage.
AI-Powered LHA File Analysis
Instant Detection
Quickly identify LHarc archive files with high accuracy using Google's advanced Magika AI technology.
Security Analysis
Analyze file structure and metadata to ensure the file is legitimate and safe to use.
Detailed Information
Get comprehensive details about file type, MIME type, and other technical specifications.
Privacy First
All analysis happens in your browser - no files are uploaded to our servers.
Related File Types
Explore other file types in the Archive category and discover more formats:
Start Analyzing LHA Files Now
Use our free AI-powered tool to detect and analyze LHarc archive files instantly with Google's Magika technology.
⚡ Try File Detection Tool