mirror of
https://github.com/tcsenpai/qrare.git
synced 2025-06-07 03:35:26 +00:00
Revamped
This commit is contained in:
parent
5bde7ab10c
commit
24db27aa21
169
binary_qr.py
Normal file
169
binary_qr.py
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Binary QR Converter - A utility to convert binary files to QR codes and back.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from converter import BinaryQRConverter
|
||||||
|
|
||||||
|
__version__ = '0.1.0'
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
"""Parse command line arguments."""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Convert binary files to QR codes and back",
|
||||||
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||||
|
)
|
||||||
|
|
||||||
|
subparsers = parser.add_subparsers(dest="command", help="Command to execute")
|
||||||
|
|
||||||
|
# Encode command
|
||||||
|
encode_parser = subparsers.add_parser("encode", help="Encode a binary file to QR codes")
|
||||||
|
encode_parser.add_argument("file", type=str, help="Path to the binary file to encode")
|
||||||
|
encode_parser.add_argument(
|
||||||
|
"-o", "--output-dir",
|
||||||
|
type=str,
|
||||||
|
default="./qrcodes",
|
||||||
|
help="Directory to save QR code images"
|
||||||
|
)
|
||||||
|
encode_parser.add_argument(
|
||||||
|
"-c", "--chunk-size",
|
||||||
|
type=int,
|
||||||
|
default=1024,
|
||||||
|
help="Size of binary chunks in bytes"
|
||||||
|
)
|
||||||
|
encode_parser.add_argument(
|
||||||
|
"-v", "--qr-version",
|
||||||
|
type=int,
|
||||||
|
default=40,
|
||||||
|
choices=range(1, 41),
|
||||||
|
help="QR code version (1-40, higher means more capacity)"
|
||||||
|
)
|
||||||
|
encode_parser.add_argument(
|
||||||
|
"-z", "--compression-level",
|
||||||
|
type=int,
|
||||||
|
default=9,
|
||||||
|
help="Zlib compression level (0-9)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Decode command
|
||||||
|
decode_parser = subparsers.add_parser("decode", help="Decode QR codes back to a binary file")
|
||||||
|
decode_parser.add_argument(
|
||||||
|
"images",
|
||||||
|
type=str,
|
||||||
|
nargs="+",
|
||||||
|
help="Paths to QR code images or directory containing QR code images"
|
||||||
|
)
|
||||||
|
decode_parser.add_argument(
|
||||||
|
"-o", "--output-dir",
|
||||||
|
type=str,
|
||||||
|
default="./output",
|
||||||
|
help="Directory to save the reconstructed file"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Version command
|
||||||
|
version_parser = subparsers.add_parser("version", help="Show version information")
|
||||||
|
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
def get_image_paths(image_args: List[str]) -> List[Path]:
|
||||||
|
"""
|
||||||
|
Get a list of image paths from command line arguments.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_args: List of image paths or directories from command line
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of image file paths
|
||||||
|
"""
|
||||||
|
image_paths = []
|
||||||
|
|
||||||
|
for path_str in image_args:
|
||||||
|
path = Path(path_str)
|
||||||
|
|
||||||
|
if path.is_dir():
|
||||||
|
# If path is a directory, find all PNG files in it
|
||||||
|
image_paths.extend(sorted(path.glob("*.png")))
|
||||||
|
elif path.is_file():
|
||||||
|
# If path is a file, add it directly
|
||||||
|
image_paths.append(path)
|
||||||
|
else:
|
||||||
|
# If path is a glob pattern, expand it
|
||||||
|
expanded_paths = list(Path().glob(path_str))
|
||||||
|
if expanded_paths:
|
||||||
|
image_paths.extend(expanded_paths)
|
||||||
|
|
||||||
|
return image_paths
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main entry point for the command-line interface."""
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
if args.command == "version":
|
||||||
|
print(f"Binary QR Converter version {__version__}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if args.command == "encode":
|
||||||
|
file_path = Path(args.file)
|
||||||
|
output_dir = Path(args.output_dir)
|
||||||
|
|
||||||
|
if not file_path.exists():
|
||||||
|
logger.error(f"File not found: {file_path}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
converter = BinaryQRConverter(
|
||||||
|
chunk_size=args.chunk_size,
|
||||||
|
qr_version=args.qr_version,
|
||||||
|
compression_level=args.compression_level
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
qr_image_paths = converter.encode_file(file_path, output_dir)
|
||||||
|
logger.info(f"Created {len(qr_image_paths)} QR code images in {output_dir}")
|
||||||
|
return 0
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Encoding failed: {str(e)}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
elif args.command == "decode":
|
||||||
|
image_paths = get_image_paths(args.images)
|
||||||
|
output_dir = Path(args.output_dir)
|
||||||
|
|
||||||
|
if not image_paths:
|
||||||
|
logger.error("No image files found")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
logger.info(f"Found {len(image_paths)} image files")
|
||||||
|
|
||||||
|
converter = BinaryQRConverter()
|
||||||
|
|
||||||
|
try:
|
||||||
|
output_path = converter.decode_qr_images(image_paths, output_dir)
|
||||||
|
if output_path:
|
||||||
|
logger.info(f"Successfully decoded to {output_path}")
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
logger.error("Decoding failed")
|
||||||
|
return 1
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Decoding failed: {str(e)}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
else:
|
||||||
|
logger.error("No command specified. Use --help for usage information.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
330
converter.py
Normal file
330
converter.py
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
"""
|
||||||
|
Core functionality for converting binary files to QR codes and back.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import base64
|
||||||
|
import zlib
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import hashlib
|
||||||
|
from typing import List, Dict, Optional, Tuple
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import qrcode
|
||||||
|
from qrcode.constants import ERROR_CORRECT_H
|
||||||
|
from PIL import Image
|
||||||
|
from pyzbar.pyzbar import decode, ZBarSymbol
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class BinaryQRConverter:
|
||||||
|
"""
|
||||||
|
A class to convert binary files to QR codes and back.
|
||||||
|
|
||||||
|
This class provides methods to:
|
||||||
|
- Encode binary files into a series of QR code images
|
||||||
|
- Decode QR code images back into the original binary file
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
chunk_size: int = 1024,
|
||||||
|
error_correction: int = ERROR_CORRECT_H,
|
||||||
|
qr_version: int = 40,
|
||||||
|
compression_level: int = 9
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize the BinaryQRConverter.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
chunk_size: Size in bytes of each binary chunk to encode
|
||||||
|
error_correction: QR code error correction level
|
||||||
|
qr_version: QR code version (1-40, higher means more capacity)
|
||||||
|
compression_level: Zlib compression level (0-9)
|
||||||
|
"""
|
||||||
|
self.chunk_size = chunk_size
|
||||||
|
self.error_correction = error_correction
|
||||||
|
self.qr_version = qr_version
|
||||||
|
self.compression_level = compression_level
|
||||||
|
|
||||||
|
def _calculate_file_hash(self, file_path: Path) -> str:
|
||||||
|
"""
|
||||||
|
Calculate SHA-256 hash of a file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path: Path to the file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Hexadecimal string representation of the hash
|
||||||
|
"""
|
||||||
|
sha256_hash = hashlib.sha256()
|
||||||
|
|
||||||
|
with open(file_path, "rb") as f:
|
||||||
|
# Read and update hash in chunks
|
||||||
|
for byte_block in iter(lambda: f.read(4096), b""):
|
||||||
|
sha256_hash.update(byte_block)
|
||||||
|
|
||||||
|
return sha256_hash.hexdigest()
|
||||||
|
|
||||||
|
def _compress_data(self, data: bytes) -> bytes:
|
||||||
|
"""
|
||||||
|
Compress binary data using zlib.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: Binary data to compress
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Compressed binary data
|
||||||
|
"""
|
||||||
|
return zlib.compress(data, level=self.compression_level)
|
||||||
|
|
||||||
|
def _decompress_data(self, compressed_data: bytes) -> bytes:
|
||||||
|
"""
|
||||||
|
Decompress binary data using zlib.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
compressed_data: Compressed binary data
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Decompressed binary data
|
||||||
|
"""
|
||||||
|
return zlib.decompress(compressed_data)
|
||||||
|
|
||||||
|
def _encode_chunk(
|
||||||
|
self,
|
||||||
|
chunk: bytes,
|
||||||
|
chunk_index: int,
|
||||||
|
total_chunks: int,
|
||||||
|
filename: str,
|
||||||
|
file_hash: str
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Encode a binary chunk with metadata as a JSON string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
chunk: Binary chunk data
|
||||||
|
chunk_index: Index of this chunk
|
||||||
|
total_chunks: Total number of chunks
|
||||||
|
filename: Original filename
|
||||||
|
file_hash: Hash of the original file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON string with chunk data and metadata
|
||||||
|
"""
|
||||||
|
# Base64 encode the binary chunk
|
||||||
|
b64_data = base64.b64encode(chunk).decode('utf-8')
|
||||||
|
|
||||||
|
# Create a dictionary with chunk data and metadata
|
||||||
|
chunk_dict = {
|
||||||
|
'data': b64_data,
|
||||||
|
'chunk_index': chunk_index,
|
||||||
|
'total_chunks': total_chunks,
|
||||||
|
'filename': filename,
|
||||||
|
'file_hash': file_hash
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convert to JSON
|
||||||
|
return json.dumps(chunk_dict)
|
||||||
|
|
||||||
|
def _create_qr_code(self, data: str) -> Image.Image:
|
||||||
|
"""
|
||||||
|
Create a QR code image from data.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: String data to encode in the QR code
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
PIL Image object containing the QR code
|
||||||
|
"""
|
||||||
|
qr = qrcode.QRCode(
|
||||||
|
version=self.qr_version,
|
||||||
|
error_correction=self.error_correction,
|
||||||
|
box_size=10,
|
||||||
|
border=4,
|
||||||
|
)
|
||||||
|
qr.add_data(data)
|
||||||
|
qr.make(fit=True)
|
||||||
|
|
||||||
|
return qr.make_image(fill_color="black", back_color="white")
|
||||||
|
|
||||||
|
def encode_file(self, file_path: Path, output_dir: Path) -> List[Path]:
|
||||||
|
"""
|
||||||
|
Encode a binary file into a series of QR code images.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path: Path to the binary file
|
||||||
|
output_dir: Directory to save QR code images
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of paths to the generated QR code images
|
||||||
|
"""
|
||||||
|
file_path = Path(file_path)
|
||||||
|
output_dir = Path(output_dir)
|
||||||
|
|
||||||
|
# Create output directory if it doesn't exist
|
||||||
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Calculate file hash for integrity verification
|
||||||
|
file_hash = self._calculate_file_hash(file_path)
|
||||||
|
logger.info(f"File hash: {file_hash}")
|
||||||
|
|
||||||
|
# Get file size and calculate number of chunks
|
||||||
|
file_size = file_path.stat().st_size
|
||||||
|
compressed_size = 0
|
||||||
|
original_filename = file_path.name
|
||||||
|
|
||||||
|
# Read and compress the entire file first to get accurate chunk count
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
file_data = f.read()
|
||||||
|
|
||||||
|
compressed_data = self._compress_data(file_data)
|
||||||
|
compressed_size = len(compressed_data)
|
||||||
|
logger.info(f"Original size: {file_size} bytes, Compressed size: {compressed_size} bytes")
|
||||||
|
|
||||||
|
total_chunks = (compressed_size + self.chunk_size - 1) // self.chunk_size
|
||||||
|
logger.info(f"Splitting into {total_chunks} chunks")
|
||||||
|
|
||||||
|
qr_image_paths = []
|
||||||
|
|
||||||
|
for i in range(total_chunks):
|
||||||
|
start_pos = i * self.chunk_size
|
||||||
|
end_pos = min(start_pos + self.chunk_size, compressed_size)
|
||||||
|
chunk = compressed_data[start_pos:end_pos]
|
||||||
|
|
||||||
|
# Encode chunk with metadata
|
||||||
|
chunk_data = self._encode_chunk(
|
||||||
|
chunk, i, total_chunks, original_filename, file_hash
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create QR code
|
||||||
|
qr_img = self._create_qr_code(chunk_data)
|
||||||
|
|
||||||
|
# Save QR code image
|
||||||
|
image_filename = f"{original_filename}_chunk_{i+1}_of_{total_chunks}.png"
|
||||||
|
image_path = output_dir / image_filename
|
||||||
|
qr_img.save(image_path)
|
||||||
|
qr_image_paths.append(image_path)
|
||||||
|
|
||||||
|
logger.info(f"Created QR code {i+1}/{total_chunks}: {image_path}")
|
||||||
|
|
||||||
|
return qr_image_paths
|
||||||
|
|
||||||
|
def decode_qr_image(self, image_path: Path) -> Optional[Dict]:
|
||||||
|
"""
|
||||||
|
Decode a QR code image to extract chunk data and metadata.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_path: Path to the QR code image
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary with chunk data and metadata, or None if decoding fails
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Open the image
|
||||||
|
img = Image.open(image_path)
|
||||||
|
|
||||||
|
# Decode QR code
|
||||||
|
decoded_objects = decode(img, symbols=[ZBarSymbol.QRCODE])
|
||||||
|
|
||||||
|
if not decoded_objects:
|
||||||
|
logger.error(f"No QR code found in {image_path}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Get the data from the first QR code found
|
||||||
|
qr_data = decoded_objects[0].data.decode('utf-8')
|
||||||
|
|
||||||
|
# Parse JSON data
|
||||||
|
chunk_data = json.loads(qr_data)
|
||||||
|
|
||||||
|
return chunk_data
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error decoding QR code {image_path}: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def decode_qr_images(self, image_paths: List[Path], output_dir: Path) -> Optional[Path]:
|
||||||
|
"""
|
||||||
|
Decode a series of QR code images back to the original binary file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_paths: List of paths to QR code images
|
||||||
|
output_dir: Directory to save the reconstructed file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Path to the reconstructed file, or None if decoding fails
|
||||||
|
"""
|
||||||
|
output_dir = Path(output_dir)
|
||||||
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Decode all QR codes
|
||||||
|
chunk_data_list = []
|
||||||
|
|
||||||
|
for image_path in image_paths:
|
||||||
|
chunk_data = self.decode_qr_image(image_path)
|
||||||
|
if chunk_data:
|
||||||
|
chunk_data_list.append(chunk_data)
|
||||||
|
else:
|
||||||
|
logger.error(f"Failed to decode {image_path}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Sort chunks by index
|
||||||
|
chunk_data_list.sort(key=lambda x: x['chunk_index'])
|
||||||
|
|
||||||
|
# Verify we have all chunks
|
||||||
|
if not chunk_data_list:
|
||||||
|
logger.error("No chunks were successfully decoded")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Get metadata from the first chunk
|
||||||
|
first_chunk = chunk_data_list[0]
|
||||||
|
total_chunks = first_chunk['total_chunks']
|
||||||
|
original_filename = first_chunk['filename']
|
||||||
|
file_hash = first_chunk['file_hash']
|
||||||
|
|
||||||
|
if len(chunk_data_list) != total_chunks:
|
||||||
|
logger.error(f"Missing chunks: got {len(chunk_data_list)}, expected {total_chunks}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Verify all chunks have the same metadata
|
||||||
|
for i, chunk in enumerate(chunk_data_list):
|
||||||
|
if chunk['total_chunks'] != total_chunks:
|
||||||
|
logger.error(f"Chunk {i} has inconsistent total_chunks")
|
||||||
|
return None
|
||||||
|
if chunk['filename'] != original_filename:
|
||||||
|
logger.error(f"Chunk {i} has inconsistent filename")
|
||||||
|
return None
|
||||||
|
if chunk['file_hash'] != file_hash:
|
||||||
|
logger.error(f"Chunk {i} has inconsistent file_hash")
|
||||||
|
return None
|
||||||
|
if chunk['chunk_index'] != i:
|
||||||
|
logger.error(f"Expected chunk index {i}, got {chunk['chunk_index']}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Reconstruct the compressed data
|
||||||
|
compressed_data = b''
|
||||||
|
for chunk in chunk_data_list:
|
||||||
|
chunk_bytes = base64.b64decode(chunk['data'])
|
||||||
|
compressed_data += chunk_bytes
|
||||||
|
|
||||||
|
# Decompress the data
|
||||||
|
try:
|
||||||
|
original_data = self._decompress_data(compressed_data)
|
||||||
|
except zlib.error as e:
|
||||||
|
logger.error(f"Decompression failed: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Write the reconstructed file
|
||||||
|
output_path = output_dir / original_filename
|
||||||
|
with open(output_path, 'wb') as f:
|
||||||
|
f.write(original_data)
|
||||||
|
|
||||||
|
# Verify file hash
|
||||||
|
reconstructed_hash = self._calculate_file_hash(output_path)
|
||||||
|
if reconstructed_hash != file_hash:
|
||||||
|
logger.error(f"Hash verification failed: expected {file_hash}, got {reconstructed_hash}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
logger.info(f"Successfully reconstructed {original_filename}")
|
||||||
|
return output_path
|
60
example.py
Normal file
60
example.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Example usage of the Binary QR Converter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from converter import BinaryQRConverter
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Run an example of encoding and decoding a binary file."""
|
||||||
|
# Create a temporary directory for our example
|
||||||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
temp_dir_path = Path(temp_dir)
|
||||||
|
|
||||||
|
# Create a test binary file
|
||||||
|
test_file_path = temp_dir_path / "test_file.bin"
|
||||||
|
with open(test_file_path, 'wb') as f:
|
||||||
|
f.write(os.urandom(1000000)) # 1MB of random data
|
||||||
|
|
||||||
|
print(f"Created test file: {test_file_path}")
|
||||||
|
|
||||||
|
# Create output directories
|
||||||
|
qr_output_dir = temp_dir_path / "qrcodes"
|
||||||
|
decode_output_dir = temp_dir_path / "output"
|
||||||
|
|
||||||
|
# Create converter
|
||||||
|
converter = BinaryQRConverter(chunk_size=500)
|
||||||
|
|
||||||
|
# Encode the test file to QR codes
|
||||||
|
print("Encoding file to QR codes...")
|
||||||
|
qr_image_paths = converter.encode_file(test_file_path, qr_output_dir)
|
||||||
|
|
||||||
|
print(f"Created {len(qr_image_paths)} QR code images:")
|
||||||
|
for path in qr_image_paths:
|
||||||
|
print(f" - {path}")
|
||||||
|
|
||||||
|
# Decode the QR codes back to a file
|
||||||
|
print("\nDecoding QR codes back to file...")
|
||||||
|
decoded_file_path = converter.decode_qr_images(qr_image_paths, decode_output_dir)
|
||||||
|
|
||||||
|
if decoded_file_path:
|
||||||
|
print(f"Successfully decoded to: {decoded_file_path}")
|
||||||
|
|
||||||
|
# Verify the decoded file matches the original
|
||||||
|
with open(test_file_path, 'rb') as f1, open(decoded_file_path, 'rb') as f2:
|
||||||
|
original_data = f1.read()
|
||||||
|
decoded_data = f2.read()
|
||||||
|
|
||||||
|
if original_data == decoded_data:
|
||||||
|
print("Verification successful: Decoded file matches the original!")
|
||||||
|
else:
|
||||||
|
print("Verification failed: Decoded file does not match the original.")
|
||||||
|
else:
|
||||||
|
print("Decoding failed.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
x
Reference in New Issue
Block a user