diff --git a/hlna.py b/hlna.py index 09b82a4..4fa9eb3 100755 --- a/hlna.py +++ b/hlna.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 import os import re +import sys import zlib import struct import shutil +import logging import datetime import subprocess @@ -399,33 +401,22 @@ def modextract(id_mod, id_game_workshop): # if not os.path.isfile(dstfile) or os.path.getmtime(srcfile) > os.path.getmtime(dstfile): # print_line(dstfile) # shutil.copy2(srcfile, dstfile) - for root, dirs, files in os.walk(dir_steam_workshop): - for file in files: - if file.endswith('.z'): - filepath = os.path.join(root, file) - extract_path = os.path.join(dir_extract, os.path.splitext(file)[0]) - - if not os.path.isfile(extract_path) or os.path.getmtime(filepath) > os.path.getmtime(extract_path): - print(f"{os.path.getsize(filepath):<10} {file:<20} ", end="") - with open(filepath, "rb") as zfile: - data = zfile.read() - if data[0:8] != b"\xC1\x83\x2A\x9E\x00\x00\x00\x00": - raise ValueError("Bad file magic") - chunk_size, compressed_size, uncompressed_size = struct.unpack("(.+)', modname) @@ -474,6 +465,87 @@ def modextract(id_mod, id_game_workshop): os.makedirs(dir_ark_mods) +logging.basicConfig(stream=sys.stderr, level=logging.CRITICAL) + +class UnpackException(Exception): + pass + +class SignatureUnpackException(UnpackException): + pass + +class CorruptUnpackException(UnpackException): + pass + + +def arkit(src, dst): + with open(src, 'rb') as f: + sigver = struct.unpack('q', f.read(8))[0] + unpacked_chunk = f.read(8) + packed = f.read(8) + unpacked = f.read(8) + size_unpacked_chunk = struct.unpack('q', unpacked_chunk)[0] + size_packed = struct.unpack('q', packed)[0] + size_unpacked = struct.unpack('q', unpacked)[0] + + #Verify the integrity of the Archive Header + if sigver == 2653586369: + if isinstance(size_unpacked_chunk, int) and isinstance(size_packed , int) and isinstance(size_unpacked , int): + logging.info("Archive is valid.") + logging.debug(f"Archive header size information. Unpacked Chunk: {size_unpacked_chunk}({unpacked_chunk}) Full Packed: {size_packed}({packed}) Full Unpacked: {size_unpacked}({unpacked})") + + #Obtain the Archive Compression Index + compression_index = [] + size_indexed = 0 + while size_indexed < size_unpacked: + raw_compressed = f.read(8) + raw_uncompressed = f.read(8) + compressed = struct.unpack('q', raw_compressed)[0] + uncompressed = struct.unpack('q', raw_uncompressed)[0] + compression_index.append((compressed, uncompressed)) + size_indexed += uncompressed + logging.debug(f"{len(compression_index)}: {size_indexed}/{size_unpacked} ({compressed}/{uncompressed}) - {raw_compressed} - {raw_uncompressed}") + + if size_unpacked != size_indexed: + msg = f"Header-Index mismatch. Header indicates it should only have {size_unpacked} bytes when uncompressed but the index indicates {size_indexed} bytes." + logging.critical(msg) + raise CorruptUnpackException(msg) + + #Read the actual archive data + data = b'' + read_data = 0 + for compressed, uncompressed in compression_index: + compressed_data = f.read(compressed) + uncompressed_data = zlib.decompress(compressed_data) + + #Verify the size of the data is consistent with the archives index + if len(uncompressed_data) == uncompressed: + data += uncompressed_data + read_data += 1 + + #Verify there is only one partial chunk + if len(uncompressed_data) != size_unpacked_chunk and read_data != len(compression_index): + msg = f"Index contains more than one partial chunk: was {len(uncompressed_data)} when the full chunk size is {size_unpacked_chunk}, chunk {read_data}/{len(compression_index)}" + logging.critical(msg) + raise CorruptUnpackException(msg) + else: + msg = f"Uncompressed chunk size is not the same as in the index: was {len(uncompressed_data)} but should be {uncompressed}." + logging.critical(msg) + raise CorruptUnpackException(msg) + else: + msg = f"Data types in the headers should be int's. Size Types: unpacked_chunk({type(size_unpacked_chunk)}), packed({type(size_packed)}), unpacked({type(size_unpacked)})" + logging.critical(msg) + raise CorruptUnpackException(msg) + else: + msg = "The signature and format version is incorrect. Signature was {} should be 2653586369.".format(sigver) + logging.critical(msg) + raise SignatureUnpackException(msg) + + #Write the extracted data to disk + with open(dst, 'wb') as f: + f.write(data) + logging.info("Archive has been extracted.") + + @ hlna.command() @ click.option("-m", required=True, help="Название Сервера") @ click.option("-e/-d", default=True, help="-e активировать карты, -d деактивировать")