Byte Wrapper—a lightweight, user-friendly utility designed to bundle your scripts, files, and installers into seamless, self-contained executable packages (.exe). Whether you're deploying batch files, PowerShell scripts, or custom tools, Byte Wrapper helps you automate execution, hide command windows, and secure your files effortlessly. Key Features at a Glance Unattended & Silent Execution: Run your scripts or installation files smoothly in the background without cluttering the desktop. Smart Source Handling: Simply select or drag and drop your files and source folders right into the interface. Flexible Output Control: Define custom extraction directories (such as %Temp% or %ProgramFiles%) and configure automated cleanup. Advanced Options: Hidden Run: Execute .bat, .cmd, or .ps1 files invisibly via an embedded VBS runner. Crypto & Password Protection: Encrypt archive headers and protect your packages with custom passwords. Custom Icons: Easily apply or extract high-resolution icons to give your packages a professional touch. Others are as explained by its title. Also Drag & Drop existing sfx/exe packages to top left main icon to review its code. Config Export & Import: Save your build settings into JSON configuration scripts for quick reuse and team sharing. Getting Started Set your Source: Drag and drop your target file or folder into the Source Folder field. Configure Paths: Choose where your package extracts and which file should run automatically upon launch. Select Options: Toggle admin privileges, hidden execution, or compression based on your deployment needs. Build: Click Start Process to generate your standalone, ready-to-use .exe package! How To Install and Run You will need to have python and 7zip installed in your system and install necessary python components for this script to run. First install components via cmd, second save script via notepad and name it as "Byte Wrapper.py" and run. Code: py -m pip install PyQt6 Pillow pillow-jxl-plugin pyinstaller Spoiler: Byte Wrapper Code: import os import re import sys import json import uuid import struct import base64 import hashlib import shutil import ctypes from ctypes import wintypes import subprocess import tempfile import threading import io from datetime import datetime from xml.sax.saxutils import escape as xml_escape # Support # # Update Python # py -m pip install --upgrade pip # Install Components # py -m pip install PyQt6 Pillow pillow-jxl-plugin pyinstaller # Compile # py -m PyInstaller --noconfirm --clean --onefile --windowed --name "Byte Wrapper" --icon "Byte.ICO" --collect-all pillow_jxl "Byte Wrapper.py" # ============================================================================== # 1. GLOBAL COMMANDS, CONSTANTS & CORE UTILITIES # ============================================================================== try: hwnd = ctypes.windll.kernel32.GetConsoleWindow() if hwnd: ctypes.windll.user32.ShowWindow(hwnd, 0) ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("ByteWrapper.SFXConstructor") except Exception: pass from PyQt6.QtCore import ( Qt, QPoint, QThread, pyqtSignal, QSize, QTimer, QRegularExpression ) from PyQt6.QtGui import ( QIcon, QImage, QPixmap, QDragEnterEvent, QDropEvent, QRegularExpressionValidator ) from PyQt6.QtWidgets import ( QApplication, QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QLabel, QLineEdit, QPushButton, QCheckBox, QProgressBar, QFileDialog, QMenu, QMessageBox ) from PIL import Image, ImageDraw try: import pillow_jxl # Support for .jxl files except ImportError: pass _BW_SOURCE_START_MARKER = b";!@ByteWrapperSource@UTF-8!" _BW_SOURCE_END_MARKER = b";!@ByteWrapperSourceEnd@!" _BW_HEADER_SCAN_LIMIT = 12 * 1024 * 1024 _IO_BUFFER_SIZE = 16 * 1024 * 1024 _RUNNABLE_EXTS = (".exe", ".msi", ".bat", ".cmd", ".ps1", ".reg", ".vbs", ".js", ".wsf", ".msc", ".com", ".scr") _DIRECT_ICON_EXTS = (".ico", ".png", ".jpg", ".jpeg", ".jxl") _EXECUTABLE_EXT_RE = re.compile( r"\.(?:bat|cmd|ps1|exe|msi|reg|vbs|js|wsf|msc|com|scr)(?=\s|$)", re.IGNORECASE, ) _SELF_ELEVATE_RE = re.compile( r"\bstart-process\b[^\r\n]*\s-verb\s+['\"]?runas\b" r"|\bshell\.application\b[\s\S]{0,240}?\bshellexecute\b[\s\S]{0,240}?['\"]runas['\"]" r"|^.*\b(?:fltmc|net\s+session)\b[^\r\n]*\|\|[^\r\n]*\bstart\b" r"|\bmshta\b[^\r\n]*\brunas\b", re.IGNORECASE | re.MULTILINE, ) _ADMIN_PROBE_RE = re.compile(r"\b(?:fltmc|net\s+session|whoami\s+/groups)\b", re.IGNORECASE) _RUNAS_RE = re.compile(r"runas", re.IGNORECASE) _COMMENT_LINE_RE = re.compile(r"^\s*(?:rem\s|::|#)", re.IGNORECASE) _INVALID_FILENAME_CHARS_RE = re.compile(r'[\\/:*?"<>|\x00-\x1f]') _RESERVED_WIN_NAMES_RE = re.compile( r"^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\..*)?$", re.IGNORECASE, ) _INVALID_PATH_CHARS = frozenset('*?"<>|') _PCT_RE = re.compile(rb"(?:^|[\r\n\x08])\s*(\d{1,3})%(?=\s|$)") _CFG_STR_KEYS = ( "source", "extract_to", "run_file", "output", "name", "description", "version", "icon_source", "password", ) _CFG_BOOL_KEYS = ( "run_as_admin", "hidden_run", "dont_replace", "extract_only", "self_remove", "progress_bar", "compress", "crypto", "instances", ) SECRET_EXPORT_KEY = b"ByteWrapper_Config_Cipher_Key" _CACHED_TOOL_PATHS: dict[str, str] = {} _SINGLE_INSTANCE_MUTEX = None _KERNEL32 = ctypes.WinDLL("kernel32", use_last_error=True) _ENUMRESNAMEPROCW = ctypes.WINFUNCTYPE( ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_ssize_t ) _KERNEL32.CreateMutexW.argtypes = [ctypes.c_void_p, wintypes.BOOL, ctypes.c_wchar_p] _KERNEL32.CreateMutexW.restype = wintypes.HANDLE _KERNEL32.LoadLibraryExW.argtypes = [ctypes.c_wchar_p, ctypes.c_void_p, ctypes.c_uint32] _KERNEL32.LoadLibraryExW.restype = ctypes.c_void_p _KERNEL32.FreeLibrary.argtypes = [ctypes.c_void_p] _KERNEL32.FreeLibrary.restype = wintypes.BOOL _KERNEL32.EnumResourceNamesW.argtypes = [ ctypes.c_void_p, ctypes.c_void_p, _ENUMRESNAMEPROCW, ctypes.c_ssize_t ] _KERNEL32.EnumResourceNamesW.restype = wintypes.BOOL _KERNEL32.FindResourceW.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] _KERNEL32.FindResourceW.restype = ctypes.c_void_p _KERNEL32.LoadResource.argtypes = [ctypes.c_void_p, ctypes.c_void_p] _KERNEL32.LoadResource.restype = ctypes.c_void_p _KERNEL32.LockResource.argtypes = [ctypes.c_void_p] _KERNEL32.LockResource.restype = ctypes.c_void_p _KERNEL32.SizeofResource.argtypes = [ctypes.c_void_p, ctypes.c_void_p] _KERNEL32.SizeofResource.restype = ctypes.c_uint32 _SHELL32 = ctypes.WinDLL("shell32", use_last_error=True) _SHELL32.PickIconDlg.argtypes = [ wintypes.HWND, ctypes.c_wchar_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_int) ] _SHELL32.PickIconDlg.restype = ctypes.c_int def _acquire_single_instance_lock() -> bool: """Ensure only a single instance of Byte Wrapper runs at a time.""" global _SINGLE_INSTANCE_MUTEX _SINGLE_INSTANCE_MUTEX = _KERNEL32.CreateMutexW( None, False, "Local\\ByteWrapper_SFXConstructor_SingleInstance" ) if not _SINGLE_INSTANCE_MUTEX or ctypes.get_last_error() == 183: # ERROR_ALREADY_EXISTS try: existing_hwnd = ctypes.windll.user32.FindWindowW(None, "Byte Wrapper") if existing_hwnd: if ctypes.windll.user32.IsIconic(existing_hwnd): ctypes.windll.user32.ShowWindow(existing_hwnd, 9) # SW_RESTORE ctypes.windll.user32.SetForegroundWindow(existing_hwnd) except Exception: pass return False return True def expand_path(raw_path: str, resolve_abs: bool = False) -> str: cleaned = os.path.expandvars((raw_path or "").strip().strip('"').strip("'")) if not cleaned: return "" if len(cleaned) == 2 and cleaned[1] == ":" and cleaned[0].isalpha(): cleaned += "\\" return os.path.abspath(cleaned) if resolve_abs else os.path.normpath(cleaned) def _has_invalid_path_chars(path_str: str) -> bool: if not path_str: return True _, tail = os.path.splitdrive(path_str) return ":" in tail or any(c in _INVALID_PATH_CHARS or ord(c) < 32 for c in path_str) def _validate_package_name(raw_name: str) -> str | None: """Validate package name and return clean base name without .exe, or None if invalid.""" if not raw_name: return "Byte Wrapper" if _INVALID_FILENAME_CHARS_RE.search(raw_name): return None name = raw_name.strip() if not name: return None if name.lower().endswith(".exe"): name = name[:-4] name = name.strip().rstrip(". ") if not name or _RESERVED_WIN_NAMES_RE.match(name): return None return name def _derive_auto_name_from_path(path_str: str, strip_ext: bool = True) -> str: base = os.path.basename(path_str.replace("/", "\\")) stem = (os.path.splitext(base)[0] if strip_ext else base).strip() return (_validate_package_name(stem) or "") if stem else "" def _relpath_if_within(path: str, directory: str) -> str: """Return relative path when path is inside directory ('.' if identical), else empty string.""" if not path or not directory: return "" try: path_real = os.path.realpath(path) dir_real = os.path.realpath(directory) path_nc = os.path.normcase(path_real) dir_nc = os.path.normcase(dir_real) if os.path.commonpath((path_nc, dir_nc)) == dir_nc: return os.path.relpath(path_real, dir_real) except (OSError, ValueError): pass return "" def _has_matching_url(event, allowed_exts: tuple[str, ...] = ()) -> bool: """Fast, zero-disk-I/O check used during dragEnterEvent.""" if not event.mimeData().hasUrls(): return False for url in event.mimeData().urls(): path = url.toLocalFile() if path and (not allowed_exts or path.lower().endswith(allowed_exts)): return True return False def _first_dropped_path(event, allowed_exts: tuple[str, ...] = ()) -> str: """Resolve the first valid local path on dropEvent.""" if not event.mimeData().hasUrls(): return "" for url in event.mimeData().urls(): path = url.toLocalFile() if not path: continue if not allowed_exts or (os.path.isfile(path) and path.lower().endswith(allowed_exts)): return expand_path(path) return "" def _parse_pe_version(raw_ver: str) -> tuple[str, str] | None: """Validate and normalize version into (4-part PE version, informational version), or None if invalid.""" cleaned = (raw_ver or "").strip() if not cleaned: return "1.0.0.0", "1.0.0.0" parts = cleaned.split(".") if len(parts) > 4: return None normalized: list[str] = [] for part in parts: if not (part.isascii() and part.isdigit()): return None val = int(part) if val > 65534: return None normalized.append(str(val)) while len(normalized) < 4: normalized.append("0") return ".".join(normalized), cleaned def _validate_metadata_fields( raw_name: str, raw_desc: str, raw_ver: str ) -> tuple[str, str, tuple[str, str]]: """Validate package name, description, and version in a single place.""" base_name = _validate_package_name(raw_name) if base_name is None: raise ValueError( "Package name contains invalid characters or is a reserved Windows file name." ) desc_clean = (raw_desc or "").strip() if _INVALID_FILENAME_CHARS_RE.search(desc_clean): raise ValueError("Description contains invalid characters.") parsed_ver = _parse_pe_version(raw_ver) if parsed_ver is None: raise ValueError("Version must be 1 to 4 numbers (0-65534) separated by dots.") return base_name, desc_clean, parsed_ver def _validate_password_input(pw: str) -> str: if pw and not pw.strip(): raise ValueError("Password cannot contain only whitespace.") return pw def _remove_path_4_steps(path: str, is_dir: bool = False): """Attempt to remove a specific temporary path up to 4 times without scanning directories.""" if not path: return for _ in range(4): try: if is_dir: if os.path.isdir(path): shutil.rmtree(path) elif os.path.lexists(path): os.unlink(path) return except OSError: pass def cleanup_temp_files_now(output_dir: str = ""): """Cleanup leftover Byte Wrapper preview files, build folders, and staging files.""" try: with os.scandir(tempfile.gettempdir()) as entries: for entry in entries: name_l = entry.name.lower() if name_l.startswith("byte_wrapper_code_") and name_l.endswith(".txt"): _remove_path_4_steps(entry.path, False) elif name_l.startswith("byte_wrapper_build_") and entry.is_dir(follow_symlinks=False): _remove_path_4_steps(entry.path, True) except OSError: pass norm_out = expand_path(output_dir) if norm_out and os.path.isdir(norm_out): try: with os.scandir(norm_out) as entries: for entry in entries: name_l = entry.name.lower() if (name_l.startswith((".bw_staging_", ".bw_cfg_")) and name_l.endswith(".tmp")): _remove_path_4_steps(entry.path, False) except OSError: pass def process_file(file_path): """Open 7-Zip SFX configs or Byte Wrapper's embedded C# source in the default viewer.""" cleanup_temp_files_now() sources = ( (b';!@Install@!UTF-8!', b';!@InstallEnd@!', '7-Zip SFX config'), (b';!@Install@!', b';!@InstallEnd@!', '7-Zip SFX config'), (_BW_SOURCE_START_MARKER, _BW_SOURCE_END_MARKER, 'Byte Wrapper generated C# source'), ) try: if not os.path.isfile(file_path): return False, 'The selected EXE / SFX file does not exist.' with open(file_path, 'rb') as f: data = f.read(_BW_HEADER_SCAN_LIMIT) if not data: return False, 'The selected file is empty.' content = None source_type = '' for start_marker, end_marker, description in sources: start_idx = data.find(start_marker) if start_idx < 0: continue end_idx = data.find(end_marker, start_idx + len(start_marker)) if end_idx < 0 or end_idx - start_idx > 2 * 1024 * 1024: continue content = data[start_idx:end_idx + len(end_marker)].decode('utf-8-sig', errors='replace') source_type = description break if content is None: return False, ('No supported 7-Zip SFX configuration or Byte Wrapper source was found. ' 'Protected or normal EXE files do not contain readable source code.') with tempfile.NamedTemporaryFile( mode='w', encoding='utf-8-sig', suffix='.txt', prefix='byte_wrapper_code_', delete=False ) as output: output.write(f'File: {os.path.basename(file_path)}\n') output.write(f'Type: {source_type}\n') output.write('View only: saving this text will not modify the EXE.\n') output.write('=' * 60 + '\n\n') output.write(content) output_file = output.name os.startfile(os.path.normpath(output_file)) return True, output_file except Exception as exc: return False, f'Unable to open SFX code: {exc}' def _xor_cipher(data: bytes) -> bytes: return bytes(b ^ SECRET_EXPORT_KEY[i % len(SECRET_EXPORT_KEY)] for i, b in enumerate(data)) def encrypt_password_for_export(plain_text: str) -> str: if not plain_text: return "" return "ENC:" + base64.urlsafe_b64encode(_xor_cipher(plain_text.encode("utf-8"))).decode("ascii") def decrypt_password_from_import(cipher_text: str) -> str: if not isinstance(cipher_text, str): raise ValueError("Password value must be a string.") if not cipher_text: return "" if cipher_text.startswith("ENC:"): payload = cipher_text[4:] if not payload: raise ValueError("Encrypted password data is empty.") try: raw_bytes = base64.urlsafe_b64decode(payload.encode("ascii")) if base64.urlsafe_b64encode(raw_bytes).decode("ascii") != payload: raise ValueError("Invalid base64 password payload.") plain = _xor_cipher(raw_bytes).decode("utf-8") except Exception as exc: raise ValueError("Corrupted encrypted password data.") from exc return _validate_password_input(plain) return _validate_password_input(cipher_text) def _split_command_and_args(raw: str) -> tuple[str, str]: """Separate Run File input into (file_path, parameters_string).""" s = (raw or "").strip() if not s: return "", "" if s.startswith('"'): end_q = s.find('"', 1) return (s[1:end_q].strip(), s[end_q + 1:].strip()) if end_q > 1 else (s.strip('"').strip(), "") m = _EXECUTABLE_EXT_RE.search(s) if m: return s[:m.end()].strip(), s[m.end():].strip() parts = s.split(None, 1) return (parts[0], "") if len(parts) == 1 else (parts[0], parts[1].strip()) def _resolve_run_file( source_path: str, cmd_part: str, source_resolved: bool = False, source_kind: str = "", ) -> tuple[str, str]: """Locate run file in source and compute its package-relative target in a single pass.""" src = source_path if source_resolved else expand_path(source_path, resolve_abs=True) if not src: return "", cmd_part.replace("/", "\\") if not source_kind: if os.path.isdir(src): source_kind = "dir" elif os.path.isfile(src): source_kind = "file" resolved_run_path = "" cand = expand_path(cmd_part) if cmd_part else "" cand_is_abs = os.path.isabs(cand) if cand else False if not cmd_part: if source_kind == "file": resolved_run_path = src elif cand_is_abs and os.path.isfile(cand): resolved_run_path = os.path.abspath(cand) elif source_kind == "dir": direct = os.path.join(src, cand.lstrip("\\/")) if os.path.isfile(direct): resolved_run_path = os.path.abspath(direct) elif source_kind == "file" and os.path.normcase(cand) == os.path.normcase(os.path.basename(src)): resolved_run_path = src target = cmd_part if resolved_run_path: if source_kind == "file": if resolved_run_path == src or _relpath_if_within(resolved_run_path, src): target = os.path.basename(src) elif not cand_is_abs: target = resolved_run_path elif source_kind == "dir": rel = _relpath_if_within(resolved_run_path, src) if rel and rel != ".": target = rel elif not cand_is_abs: target = resolved_run_path elif not cand_is_abs: target = resolved_run_path return resolved_run_path, target.replace("/", "\\") def _looks_like_self_elevating_script(path: str) -> bool: """Recognize common script self-elevation patterns so Hidden Run can elevate cleanly.""" try: if not path or not os.path.isfile(path) or os.path.splitext(path)[1].lower() not in (".bat", ".cmd", ".ps1"): return False with open(path, "rb") as f: content = f.read(65536).decode("utf-8-sig", errors="replace") active = "\n".join( line for line in content.splitlines() if not _COMMENT_LINE_RE.match(line) ) return bool( _SELF_ELEVATE_RE.search(active) or (_ADMIN_PROBE_RE.search(active) and _RUNAS_RE.search(active)) ) except (OSError, UnicodeError): return False # --- IN-MEMORY ICON UTILITIES --- def _to_square_rgba(img: Image.Image) -> Image.Image: src = img if img.mode == "RGBA" else img.convert("RGBA") if src.width != src.height: side = max(src.width, src.height) square = Image.new("RGBA", (side, side), (0, 0, 0, 0)) square.paste(src, ((side - src.width) // 2, (side - src.height) // 2)) return square return src def build_multi_ico_bytes(master_img: Image.Image) -> bytes: """Convert a PIL image into multi-resolution Windows .ico bytes in memory.""" try: src = ( master_img if (master_img.mode == "RGBA" and master_img.width == master_img.height) else _to_square_rgba(master_img) ) buf = io.BytesIO() src.save( buf, format="ICO", sizes=[(256, 256), (128, 128), (64, 64), (48, 48), (32, 32), (24, 24), (16, 16)], ) return buf.getvalue() except Exception: return b"" def _render_parcel_canvas(canvas: int = 512, stroke_scale: float = 1.08) -> Image.Image: factor = canvas / 214.0 img = Image.new("RGBA", (canvas, canvas), (0, 0, 0, 0)) draw = ImageDraw.Draw(img) ink = "#383838" def xy(x, y): return (round(x * factor), round(y * factor)) outer = [(12, 53), (105, 15), (198, 53), (198, 162), (106, 205), (12, 162)] draw.polygon([xy(*p) for p in outer], fill="#ffffff") strokes = ( (outer + [(12, 53)], 6.8), ([(12, 53), (106, 96), (198, 53)], 6.8), ([(106, 96), (106, 205)], 6.8), ([(43, 40), (138, 80)], 6.0), ([(76, 27), (166, 68)], 6.0), ([(139, 80), (139, 120), (166, 110), (166, 68)], 6.2), ) for points, width in strokes: w = max(1, round(width * stroke_scale * factor)) coords = [xy(*p) for p in points] draw.line(coords, fill=ink, width=w, joint="curve") r = w / 2.0 for cx, cy in (coords[0], coords[-1]): draw.ellipse((round(cx - r), round(cy - r), round(cx + r), round(cy + r)), fill=ink) return img _DEFAULT_PARCEL_IMAGE: Image.Image | None = None _DEFAULT_SFX_ICON_BYTES: bytes = b"" def get_default_parcel_image() -> Image.Image: global _DEFAULT_PARCEL_IMAGE if _DEFAULT_PARCEL_IMAGE is None: _DEFAULT_PARCEL_IMAGE = _render_parcel_canvas() return _DEFAULT_PARCEL_IMAGE def get_default_sfx_icon_bytes() -> bytes: global _DEFAULT_SFX_ICON_BYTES if not _DEFAULT_SFX_ICON_BYTES: _DEFAULT_SFX_ICON_BYTES = build_multi_ico_bytes(get_default_parcel_image()) return _DEFAULT_SFX_ICON_BYTES def load_pil_from_ico_bytes(data: bytes) -> Image.Image | None: """Load the highest-resolution, highest-bit-depth RGBA image from raw .ico bytes.""" if not data: return None try: with Image.open(io.BytesIO(data)) as source: ico_reader = getattr(source, "ico", None) entries = getattr(ico_reader, "entry", None) if ico_reader is not None and entries: def _entry_key(idx: int) -> tuple[int, int, int]: e = entries[idx] if isinstance(e, dict): w, h = e.get("dim") or (e.get("width", 0), e.get("height", 0)) bpp = e.get("bpp") or e.get("color_depth") or 32 sz = e.get("size", 0) else: w, h = getattr(e, "dim", (getattr(e, "width", 0), getattr(e, "height", 0))) bpp = getattr(e, "bpp", 0) or 32 sz = getattr(e, "size", 0) return ((w or 256) * (h or 256), bpp, sz) for idx in sorted(range(len(entries)), key=_entry_key, reverse=True): try: frame = ico_reader.frame(idx) frame.load() return _to_square_rgba(frame) except Exception: continue source.load() rgba = _to_square_rgba(source) return rgba.copy() if rgba is source else rgba except Exception: return None def pil_to_qpixmap(pil_img: Image.Image, logical_size: int, dpr: float = 1.0) -> QPixmap: target_px = max(1, round(logical_size * dpr)) img = ( pil_img if (pil_img.mode == "RGBA" and pil_img.width == pil_img.height) else _to_square_rgba(pil_img) ) if img.size != (target_px, target_px): img = img.resize((target_px, target_px), Image.Resampling.LANCZOS) raw_bgra = img.tobytes("raw", "BGRA") qimg = QImage(raw_bgra, target_px, target_px, target_px * 4, QImage.Format.Format_ARGB32) pixmap = QPixmap.fromImage(qimg) pixmap.setDevicePixelRatio(dpr) return pixmap def _extract_pe_icon_bytes(pe_path: str, icon_index: int = 0) -> bytes: """Extract raw RT_GROUP_ICON + RT_ICON bytes directly from a PE (.exe/.dll/.mun) file.""" if not pe_path: return b"" hmod = _KERNEL32.LoadLibraryExW(pe_path, None, 0x00000022) if not hmod: return b"" try: def _read_res(res_type: int, res_id) -> bytes: if isinstance(res_id, int): if not (0 < res_id < 0x10000): return b"" name_ptr = ctypes.c_void_p(res_id) else: if not res_id: return b"" name_ptr = ctypes.cast(ctypes.c_wchar_p(str(res_id)), ctypes.c_void_p) hres = _KERNEL32.FindResourceW(hmod, name_ptr, ctypes.c_void_p(res_type)) if not hres: return b"" size = _KERNEL32.SizeofResource(hmod, hres) hglob = _KERNEL32.LoadResource(hmod, hres) if size else None ptr = _KERNEL32.LockResource(hglob) if hglob else None return ctypes.string_at(ptr, size) if ptr else b"" grp_data = b"" if icon_index < 0: abs_id = abs(icon_index) grp_data = _read_res(14, abs_id) or _read_res(14, str(abs_id)) if not grp_data: return b"" else: target_res_id = None current_idx = 0 def _enum_cb(_hModule, _lpszType, lpszName, _lParam): nonlocal current_idx, target_res_id if not lpszName: return 0 if current_idx == icon_index: target_res_id = int(lpszName) if lpszName < 0x10000 else ctypes.wstring_at(lpszName) return 0 current_idx += 1 return 1 cb_func = _ENUMRESNAMEPROCW(_enum_cb) _KERNEL32.EnumResourceNamesW(hmod, ctypes.c_void_p(14), cb_func, 0) if target_res_id is None: return b"" grp_data = _read_res(14, target_res_id) if len(grp_data) < 6: return b"" reserved, res_type_hdr, count = struct.unpack_from("<HHH", grp_data, 0) if reserved != 0 or res_type_hdr != 1 or count == 0: return b"" valid_layers = [] for i in range(count): pos = 6 + i * 14 if pos + 14 > len(grp_data): break n_id, = struct.unpack_from("<H", grp_data, pos + 12) chunk = _read_res(3, n_id) if chunk: hdr_8 = bytearray(grp_data[pos:pos + 8]) hdr_8[3] = 0 # Ensure bReserved is 0 for strict ICO readers valid_layers.append((bytes(hdr_8) + struct.pack("<I", len(chunk)), chunk)) if not valid_layers: return b"" def _layer_sort_key(item: tuple[bytes, bytes]) -> tuple[int, int, int]: fixed_12, chunk = item w = fixed_12[0] or 256 h = fixed_12[1] or 256 bpp, = struct.unpack_from("<H", fixed_12, 6) return (w * h, bpp or 32, len(chunk)) valid_layers.sort(key=_layer_sort_key, reverse=True) offset = 6 + 16 * len(valid_layers) out = bytearray(struct.pack("<HHH", 0, 1, len(valid_layers))) for fixed_12, chunk in valid_layers: out.extend(fixed_12) out.extend(struct.pack("<I", offset)) offset += len(chunk) for _, chunk in valid_layers: out.extend(chunk) return bytes(out) finally: _KERNEL32.FreeLibrary(hmod) def _split_icon_source(file_path: str, icon_idx: int | None = None) -> tuple[str, int | None]: if not file_path: return "", None cleaned = expand_path(file_path) if os.path.isfile(cleaned): ext = os.path.splitext(cleaned)[1].lower() return cleaned, (None if ext in _DIRECT_ICON_EXTS else icon_idx) if icon_idx is None and "," in file_path: path_part, idx_part = file_path.rsplit(",", 1) cand_path = expand_path(path_part) try: cand_idx = int(idx_part.strip()) except ValueError: return "", None if os.path.isfile(cand_path): ext = os.path.splitext(cand_path)[1].lower() return cand_path, (None if ext in _DIRECT_ICON_EXTS else cand_idx) return "", None def extract_icon_from_file(resolved_path: str, icon_idx: int | None = None) -> tuple[bytes, Image.Image | None]: """Extract or convert .ico bytes from a validated path and return (ico_bytes, optional_cached_pil_img).""" if not resolved_path: return b"", None ext = os.path.splitext(resolved_path)[1].lower() if ext == ".ico": try: with open(resolved_path, "rb") as f: raw = f.read() pil_img = load_pil_from_ico_bytes(raw) return (raw, pil_img) if pil_img is not None else (b"", None) except OSError: return b"", None if ext in (".png", ".jpg", ".jpeg", ".jxl"): try: with Image.open(resolved_path) as img: img.load() rgba = _to_square_rgba(img) if rgba is img: rgba = img.copy() ico_bytes = build_multi_ico_bytes(rgba) return (ico_bytes, rgba) if ico_bytes else (b"", None) except Exception: return b"", None sys_root = os.environ.get("SystemRoot", r"C:\Windows") sys32 = os.path.join(sys_root, "System32") if icon_idx is None: known_map = { ".ps1": (os.path.join(sys32, r"WindowsPowerShell\v1.0\powershell.exe"), 0), ".reg": (os.path.join(sys_root, "regedit.exe"), 0), ".msi": (os.path.join(sys32, "msiexec.exe"), 0), ".bat": (os.path.join(sys32, "imageres.dll"), -68), ".cmd": (os.path.join(sys32, "imageres.dll"), -68), ".vbs": (os.path.join(sys32, "wscript.exe"), 0), ".js": (os.path.join(sys32, "wscript.exe"), 0), ".wsf": (os.path.join(sys32, "wscript.exe"), 0), ".msc": (os.path.join(sys32, "mmc.exe"), 0), } target_bin, icon_idx = known_map.get(ext, (resolved_path, 0)) else: target_bin = resolved_path mun_path = os.path.join(sys_root, "SystemResources", os.path.basename(target_bin) + ".mun") candidates = (mun_path, target_bin) if os.path.isfile(mun_path) else (target_bin,) indices = (icon_idx, 0) if icon_idx < 0 else (icon_idx,) for idx in indices: for candidate in candidates: ico_bytes = _extract_pe_icon_bytes(candidate, idx) if ico_bytes: pil_img = load_pil_from_ico_bytes(ico_bytes) if pil_img is not None: return ico_bytes, pil_img return b"", None def _find_executable(candidates: tuple[str, ...], cmd_name: str) -> str: cached = _CACHED_TOOL_PATHS.get(cmd_name) if cached and os.path.isfile(cached): return cached for cand in candidates: if os.path.isfile(cand): _CACHED_TOOL_PATHS[cmd_name] = cand return cand found = shutil.which(cmd_name) if found and os.path.isfile(found): _CACHED_TOOL_PATHS[cmd_name] = found return found return "" # ============================================================================== # 2. CORE ENGINE: BACKGROUND SFX BUILDER & C# STUB COMPILER # ============================================================================== class SFXBuilderWorker(QThread): progress = pyqtSignal(int) build_finished = pyqtSignal(bool, str) DICT_SCAN_CAP_BYTES = 65 * 1024 * 1024 # >64 MB selects -md=128m def __init__(self, config: dict): super().__init__() self.cfg = config self._cancelled = False self._proc_lock = threading.Lock() self._active_proc: subprocess.Popen | None = None self.output_exe_path: str = "" pw = config["password"] if pw: self._expected_user_pw_hash = hashlib.sha256(pw.encode("utf-8")).hexdigest() self._archive_7z_pw = pw self._stub_embedded_7z_pw = "" elif config["crypto"]: self._expected_user_pw_hash = "" self._archive_7z_pw = uuid.uuid4().hex self._stub_embedded_7z_pw = self._archive_7z_pw else: self._expected_user_pw_hash = "" self._archive_7z_pw = "" self._stub_embedded_7z_pw = "" def cancel(self): with self._proc_lock: self._cancelled = True proc = self._active_proc if proc is not None and proc.poll() is None and proc.pid: threading.Thread( target=lambda: subprocess.run( ["taskkill", "/F", "/T", "/PID", str(proc.pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, creationflags=subprocess.CREATE_NO_WINDOW, ), daemon=True, ).start() def _start_process(self, cmd, **kwargs): """Make cancellation and subprocess registration one synchronized operation.""" with self._proc_lock: if self._cancelled: return None proc = subprocess.Popen(cmd, **kwargs) self._active_proc = proc return proc @staticmethod def find_7zip() -> str: return _find_executable( ( r"C:\Program Files\7-Zip\7z.exe", r"C:\Program Files (x86)\7-Zip\7z.exe", ), "7z", ) @staticmethod def find_csc() -> str: win_dir = os.environ.get("SystemRoot", r"C:\Windows") return _find_executable( ( os.path.join(win_dir, r"Microsoft.NET\Framework64\v4.0.30319\csc.exe"), os.path.join(win_dir, r"Microsoft.NET\Framework\v4.0.30319\csc.exe"), ), "csc", ) def _get_source_total_size(self, source_path: str, is_dir: bool) -> int: """Scan source size up to DICT_SCAN_CAP_BYTES (65 MB) in a single scandir pass.""" if not is_dir: try: return os.path.getsize(source_path) except OSError: return 0 total = 0 stack = [source_path] while stack and not self._cancelled: current_dir = stack.pop() try: with os.scandir(current_dir) as entries: for entry in entries: if self._cancelled: return total try: if entry.is_dir(follow_symlinks=False): stack.append(entry.path) elif entry.is_file(follow_symlinks=False): total += entry.stat(follow_symlinks=False).st_size if total >= self.DICT_SCAN_CAP_BYTES: return total except OSError: pass except OSError: pass return total @staticmethod def _select_7z_dict_flag(total_bytes: int) -> str: """Choose an LZMA2 dictionary size proportional to the input size for fast startup and compression.""" mb = total_bytes / (1024 * 1024) if mb <= 8: return "-md=8m" if mb <= 16: return "-md=16m" if mb <= 32: return "-md=32m" if mb <= 64: return "-md=64m" return "-md=128m" def _iter_output_candidates(self, base_name: str): out_dir = self.cfg["output"] yield os.path.join(out_dir, f"{base_name}.exe") fs_stamp = datetime.now().strftime("%d.%m.%Y %H.%M.%S") yield os.path.join(out_dir, f"{base_name}_{fs_stamp}.exe") counter = 2 while True: yield os.path.join(out_dir, f"{base_name}_{fs_stamp} ({counter}).exe") counter += 1 def _commit_staging_exe(self, staging_exe: str, base_name: str) -> str: """Move staging file to final path; when Don't Replace is active, use non-overwriting os.rename.""" out_dir = self.cfg["output"] if not self.cfg["dont_replace"]: target_exe = os.path.join(out_dir, f"{base_name}.exe") os.replace(staging_exe, target_exe) return target_exe for cand in self._iter_output_candidates(base_name): try: os.rename(staging_exe, cand) return cand except FileExistsError: continue raise RuntimeError("Failed to commit output executable.") def build_7z_archive(self, seven_zip: str, source_path: str, archive_path: str) -> int: is_dir = self.cfg["source_kind"] == "dir" cmd = [seven_zip, "a", "-bso0", "-bsp1"] if self.cfg["compress"]: dict_flag = self._select_7z_dict_flag(self._get_source_total_size(source_path, is_dir)) cmd.extend(["-t7z", "-mx=9", "-m0=lzma2", dict_flag, "-mfb=64", "-ms=16g", "-mmt=on"]) else: cmd.extend(["-t7z", "-mx=0", "-mmt=on"]) if self._archive_7z_pw: cmd.extend([f"-p{self._archive_7z_pw}", "-mhe=on"]) cmd.extend(["-y", archive_path, "--"]) cmd.append(os.path.join(source_path, "*") if is_dir else source_path) proc = self._start_process( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, creationflags=subprocess.CREATE_NO_WINDOW ) if proc is None: return 255 last_mapped = 20 try: with proc: if proc.stdout is not None: fd = proc.stdout.fileno() while True: raw_chunk = os.read(fd, 1024) if not raw_chunk: break matches = _PCT_RE.findall(raw_chunk) if matches: pct = min(100, int(matches[-1])) mapped = 20 + (pct * 40) // 100 if mapped > last_mapped: last_mapped = mapped self.progress.emit(mapped) return proc.wait() finally: with self._proc_lock: self._active_proc = None def compile_csharp_sfx( self, csc_path: str, work_dir: str, raw_7z_path: str, seven_zip: str, staging_exe: str, base_name: str ) -> tuple[bool, str]: seven_zip_dll = os.path.join(os.path.dirname(seven_zip), "7z.dll") has_dll = os.path.isfile(seven_zip_dll) source_path = self.cfg["source"] cmd_part = self.cfg["cmd_part"] run_args_raw = self.cfg["run_args"] if self.cfg["extract_only"]: resolved_run_path, run_target_rel = "", "" else: resolved_run_path, run_target_rel = _resolve_run_file( source_path, cmd_part, source_resolved=True, source_kind=self.cfg["source_kind"], ) self_elevating = ( _looks_like_self_elevating_script(resolved_run_path) if (self.cfg["hidden_run"] and not self.cfg["run_as_admin"]) else False ) def cs_escape(s: str) -> str: return ( s.replace("\\", "\\\\") .replace('"', '\\"') .replace("\r", "\\r") .replace("\n", "\\n") .replace("\t", "\\t") ) prod_name_str = cs_escape(base_name) raw_desc = self.cfg["description"] file_desc_str = cs_escape(raw_desc if raw_desc else base_name) pe_ver_num, pe_ver_info = self.cfg["parsed_version"] pe_ver_num_str = cs_escape(pe_ver_num) pe_ver_info_str = cs_escape(pe_ver_info) extract_to_str = cs_escape(self.cfg["extract_to"]) run_target_str = cs_escape(run_target_rel) run_args_str = cs_escape(run_args_raw) ask_user_pw = "true" if self.cfg["password"] else "false" expected_user_pw_hash = cs_escape(self._expected_user_pw_hash) internal_7z_pw_str = cs_escape(self._stub_embedded_7z_pw) hidden_run = "true" if self.cfg["hidden_run"] else "false" hidden_script_needs_admin = "true" if self_elevating else "false" extract_only = "true" if self.cfg["extract_only"] else "false" instances = "true" if self.cfg["instances"] else "false" self_remove = "true" if self.cfg["self_remove"] else "false" show_progress = "true" if self.cfg["progress_bar"] else "false" extract_dll_line = 'ExtractResource("7z.dll", Path.Combine(workTemp, "7z.dll"));' if has_dll else "" exec_level = "requireAdministrator" if self.cfg["run_as_admin"] else "asInvoker" safe_xml_ver = xml_escape(pe_ver_num, {'"': '"', "'": '''}) manifest_path = os.path.join(work_dir, "app.manifest") manifest_xml = f"""<?xml version="1.0" encoding="utf-8"?> <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyIdentity version="{safe_xml_ver}" name="ByteWrapper.Package"/> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <requestedExecutionLevel level="{exec_level}" uiAccess="false" /> </requestedPrivileges> </security> </trustInfo> </assembly> """ with open(manifest_path, "w", encoding="utf-8") as f: f.write(manifest_xml) cs_code = rf""" using System; using System.IO; using System.Text; using System.Drawing; using System.Reflection; using System.Diagnostics; using System.ComponentModel; using System.Threading; using System.Runtime.InteropServices; using System.Security.Principal; using System.Security.Cryptography; using System.Windows.Forms; [assembly: AssemblyTitle("{file_desc_str}")] [assembly: AssemblyDescription("{file_desc_str}")] [assembly: AssemblyProduct("{prod_name_str}")] [assembly: AssemblyCompany("Byte Wrapper")] [assembly: AssemblyVersion("{pe_ver_num_str}")] [assembly: AssemblyFileVersion("{pe_ver_num_str}")] [assembly: AssemblyInformationalVersion("{pe_ver_info_str}")] namespace ByteWrapperStub {{ static class Program {{ private static readonly string EXTRACT_TO = "{extract_to_str}"; private static readonly string RUN_TARGET = "{run_target_str}"; private static readonly string RUN_ARGS = "{run_args_str}"; private static readonly string INTERNAL_7Z_PW = "{internal_7z_pw_str}"; private static readonly bool ASK_USER_PW = {ask_user_pw}; private static readonly string EXPECTED_USER_PW_HASH = "{expected_user_pw_hash}"; private static readonly bool HIDDEN_RUN = {hidden_run}; private static readonly bool HIDDEN_SCRIPT_NEEDS_ADMIN = {hidden_script_needs_admin}; private static readonly bool EXTRACT_ONLY = {extract_only}; private static readonly bool INSTANCES = {instances}; private static readonly bool SELF_REMOVE = {self_remove}; private static readonly bool SHOW_PROGRESS = {show_progress}; private const string ELEVATED_ARG_FLAG = "--bw-elevated"; private const int STARTF_USESHOWWINDOW = 0x00000001; private const short SW_HIDE = 0; private const uint CREATE_NO_WINDOW = 0x08000000; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct STARTUPINFO {{ public int cb; public string lpReserved; public string lpDesktop; public string lpTitle; public int dwX; public int dwY; public int dwXSize; public int dwXSize2; public int dwXCountChars; public int dwYCountChars; public int dwFillAttribute; public int dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; }} [StructLayout(LayoutKind.Sequential)] private struct PROCESS_INFORMATION {{ public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; }} [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern bool CreateProcessW( string lpApplicationName, StringBuilder lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation ); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); private static string QuoteCmdValue(string val) {{ StringBuilder sb = new StringBuilder("\""); int bs = 0; if (!string.IsNullOrEmpty(val)) {{ foreach (char c in val) {{ if (c == '\\') bs++; else if (c == '"') {{ sb.Append('\\', bs * 2 + 1).Append('"'); bs = 0; }} else {{ if (bs > 0) {{ sb.Append('\\', bs); bs = 0; }} sb.Append(c); }} }} if (bs > 0) sb.Append('\\', bs * 2); }} sb.Append('"'); return sb.ToString(); }} private static bool VerifyUserPassword(string inputPw) {{ using (SHA256 sha = SHA256.Create()) {{ string hex = BitConverter.ToString(sha.ComputeHash(Encoding.UTF8.GetBytes(inputPw ?? ""))).Replace("-", ""); return string.Equals(hex, EXPECTED_USER_PW_HASH, StringComparison.OrdinalIgnoreCase); }} }} private static bool IsRunningAsAdministrator() {{ try {{ return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); }} catch {{ return false; }} }} [STAThread] static void Main() {{ Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); if (HIDDEN_RUN && HIDDEN_SCRIPT_NEEDS_ADMIN && !IsRunningAsAdministrator() && Array.IndexOf(Environment.GetCommandLineArgs(), ELEVATED_ARG_FLAG) < 0) {{ try {{ ProcessStartInfo psi = new ProcessStartInfo(Application.ExecutablePath, ELEVATED_ARG_FLAG); psi.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory; psi.UseShellExecute = true; psi.Verb = "runas"; psi.WindowStyle = ProcessWindowStyle.Hidden; using (Process p = Process.Start(psi)) {{ }} }} catch {{ }} return; }} string runtime7zPw = INTERNAL_7Z_PW; if (ASK_USER_PW) {{ string verifiedPw = null; for (int attempt = 1; attempt <= 3; attempt++) {{ string inputPw = PromptPassword(); if (inputPw == null) return; if (VerifyUserPassword(inputPw)) {{ verifiedPw = inputPw; break; }} MessageBox.Show( attempt < 3 ? ("Invalid password! Remaining attempts: " + (3 - attempt)) : "Invalid password!", "{prod_name_str}", MessageBoxButtons.OK, MessageBoxIcon.Error ); }} if (verifiedPw == null) return; runtime7zPw = verifiedPw; }} string targetDir; try {{ string expanded = Environment.ExpandEnvironmentVariables(EXTRACT_TO.Trim().Trim('"').Replace("/", "\\")); if (expanded.Length == 2 && expanded[1] == ':') expanded += "\\"; targetDir = Path.GetFullPath(expanded); Directory.CreateDirectory(targetDir); }} catch (Exception ex) {{ if (!HIDDEN_RUN) MessageBox.Show("Failed to prepare target directory: " + ex.Message, "Byte Wrapper", MessageBoxButtons.OK, MessageBoxIcon.Error); return; }} string workTemp = Path.Combine(Path.GetTempPath(), "bw_rt_" + Guid.NewGuid().ToString("N").Substring(0, 8)); try {{ Directory.CreateDirectory(workTemp); }} catch (Exception ex) {{ if (!HIDDEN_RUN) MessageBox.Show("Failed to create working directory: " + ex.Message, "Byte Wrapper", MessageBoxButtons.OK, MessageBoxIcon.Error); return; }} Exception extractError = null; try {{ Action<Action<int>> extractAction = delegate(Action<int> reportProgress) {{ try {{ string sevenZExe = Path.Combine(workTemp, "7z.exe"); ExtractResource("7z.exe", sevenZExe); {extract_dll_line} // Native 7-Zip encryption can be read directly from the EXE overlay. string archivePath = Application.ExecutablePath; bool trackProgress = (reportProgress != null); string outSwitch = targetDir.TrimEnd('\\'); if (outSwitch.Length == 2 && outSwitch[1] == ':') outSwitch += "\\"; StringBuilder args = new StringBuilder(); args.Append(trackProgress ? "x -t7z -bso0 -bsp1 " : "x -t7z "); args.Append(QuoteCmdValue(archivePath)).Append(" -o").Append(QuoteCmdValue(outSwitch)).Append(' '); if (!string.IsNullOrEmpty(runtime7zPw)) {{ args.Append("-p").Append(QuoteCmdValue(runtime7zPw)).Append(' '); }} args.Append(INSTANCES ? "-aos -y" : "-aoa -y"); ProcessStartInfo psi7z = new ProcessStartInfo(sevenZExe, args.ToString()); psi7z.UseShellExecute = false; psi7z.CreateNoWindow = true; psi7z.WindowStyle = ProcessWindowStyle.Hidden; psi7z.RedirectStandardOutput = trackProgress; using (Process p = Process.Start(psi7z)) {{ if (p == null) throw new InvalidOperationException("7-Zip extractor could not start."); if (trackProgress) {{ char[] readBuf = new char[512]; int charsRead; while ((charsRead = p.StandardOutput.Read(readBuf, 0, readBuf.Length)) > 0) {{ for (int i = charsRead - 1; i >= 1; i--) {{ if (readBuf[i] == '%' && (i + 1 == charsRead || char.IsWhiteSpace(readBuf[i + 1]))) {{ int d = i - 1; while (d >= 0 && readBuf[d] >= '0' && readBuf[d] <= '9') d--; int digits = i - 1 - d; if (digits >= 1 && digits <= 3) {{ int s = d; while (s >= 0 && readBuf[s] == ' ') s--; if (s < 0 || readBuf[s] == '\r' || readBuf[s] == '\n' || readBuf[s] == '\b') {{ int pct = 0; for (int k = d + 1; k < i; k++) pct = pct * 10 + (readBuf[k] - '0'); reportProgress(Math.Min(100, pct)); break; }} }} }} }} }} }} p.WaitForExit(); if (p.ExitCode != 0) throw new InvalidDataException("7-Zip extraction failed with exit code " + p.ExitCode); if (trackProgress) reportProgress(100); }} }} catch (Exception ex) {{ extractError = ex; }} }}; if (SHOW_PROGRESS && !HIDDEN_RUN) {{ ProgressBar pbControl; Label lblControl; using (Form progressForm = CreateProgressForm(out pbControl, out lblControl)) {{ bool allowClose = false; progressForm.FormClosing += delegate(object sender, FormClosingEventArgs e) {{ if (!allowClose && e.CloseReason == CloseReason.UserClosing) e.Cancel = true; }}; int lastUiPct = -1; Action<int> uiReporter = delegate(int val) {{ int clamped = Math.Max(0, Math.Min(100, val)); if (clamped <= lastUiPct) return; lastUiPct = clamped; try {{ if (progressForm.IsHandleCreated && !progressForm.IsDisposed) {{ progressForm.BeginInvoke((MethodInvoker)delegate {{ if (!pbControl.IsDisposed) {{ pbControl.Value = clamped; lblControl.Text = "Extracting package... " + clamped + "%"; }} }}); }} }} catch {{ }} }}; Thread worker = new Thread(delegate() {{ extractAction(uiReporter); try {{ if (progressForm.IsHandleCreated && !progressForm.IsDisposed) progressForm.BeginInvoke((MethodInvoker)delegate {{ allowClose = true; if (!pbControl.IsDisposed) pbControl.Value = 100; progressForm.Close(); }}); }} catch {{ }} }}); worker.IsBackground = true; progressForm.Shown += delegate {{ worker.Start(); }}; progressForm.ShowDialog(); if (worker.IsAlive) worker.Join(); }} }} else {{ extractAction(null); }} if (extractError != null) throw extractError; if (!EXTRACT_ONLY && !string.IsNullOrWhiteSpace(RUN_TARGET)) {{ ExecuteRunFile(targetDir); }} }} catch (Exception ex) {{ if (!HIDDEN_RUN) MessageBox.Show("Package processing failed: " + ex.Message, "Byte Wrapper", MessageBoxButtons.OK, MessageBoxIcon.Error); return; }} finally {{ for (int i = 0; i < 4; i++) {{ try {{ if (!Directory.Exists(workTemp)) break; Directory.Delete(workTemp, true); break; }} catch {{ }} }} }} if (SELF_REMOVE) {{ SelfDeleteExe(); }} }} private static void ExecuteRunFile(string targetDir) {{ string relTarget = Environment.ExpandEnvironmentVariables(RUN_TARGET.Trim()).Replace("/", "\\"); string args = Environment.ExpandEnvironmentVariables(RUN_ARGS.Trim()); string directCand = Path.IsPathRooted(relTarget) ? Path.GetFullPath(relTarget) : Path.GetFullPath(Path.Combine(targetDir, relTarget)); string resolvedFile = (!File.Exists(directCand) && !Path.IsPathRooted(relTarget) && relTarget.IndexOf('\\') < 0) ? relTarget : directCand; string workDir = Path.IsPathRooted(resolvedFile) ? Path.GetDirectoryName(resolvedFile) : targetDir; if (string.IsNullOrEmpty(workDir) || !Directory.Exists(workDir)) workDir = targetDir; string suffix = string.IsNullOrEmpty(args) ? "" : (" " + args); string fileName = resolvedFile; string finalArgs = args; switch (Path.GetExtension(resolvedFile).ToLowerInvariant()) {{ case ".reg": fileName = "regedit.exe"; finalArgs = (HIDDEN_RUN ? "/s \"" : "\"") + resolvedFile + "\"" + suffix; break; case ".ps1": fileName = "powershell.exe"; finalArgs = "-NoProfile -ExecutionPolicy Bypass " + (HIDDEN_RUN ? "-NonInteractive -WindowStyle Hidden " : "") + "-File \"" + resolvedFile + "\"" + suffix; break; case ".bat": case ".cmd": fileName = "cmd.exe"; finalArgs = "/D /S /C \"\"" + resolvedFile + "\"" + suffix + "\""; break; case ".msi": fileName = "msiexec.exe"; finalArgs = "/i \"" + resolvedFile + "\"" + (HIDDEN_RUN ? " /qn /norestart" : "") + suffix; break; case ".vbs": case ".js": case ".wsf": fileName = "wscript.exe"; finalArgs = (HIDDEN_RUN ? "//B //Nologo \"" : "\"") + resolvedFile + "\"" + suffix; break; case ".msc": fileName = "mmc.exe"; finalArgs = "\"" + resolvedFile + "\"" + suffix; break; }} RunNativeProcess(fileName, finalArgs, workDir, HIDDEN_RUN); }} private static string ResolveExecutableForCreateProcess(string fileName, string workDir) {{ if (Path.IsPathRooted(fileName) && File.Exists(fileName)) return Path.GetFullPath(fileName); string sys32 = Environment.SystemDirectory; string[] candidates = new string[] {{ Path.Combine(workDir ?? "", fileName), Path.Combine(sys32, fileName), Path.Combine(sys32, @"WindowsPowerShell\v1.0\" + fileName), Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), fileName) }}; foreach (string cand in candidates) if (File.Exists(cand)) return Path.GetFullPath(cand); return null; }} private static bool TryRunHiddenNative(string fileName, string arguments, string workDir, out int win32Err) {{ STARTUPINFO startup = new STARTUPINFO(); startup.cb = Marshal.SizeOf(typeof(STARTUPINFO)); startup.dwFlags = STARTF_USESHOWWINDOW; startup.wShowWindow = SW_HIDE; string resolvedExe = ResolveExecutableForCreateProcess(fileName, workDir); string cmdTarget = resolvedExe ?? fileName; StringBuilder commandBuffer = new StringBuilder( "\"" + cmdTarget + "\"" + (string.IsNullOrWhiteSpace(arguments) ? "" : " " + arguments) ); PROCESS_INFORMATION processInfo; if (!CreateProcessW(resolvedExe, commandBuffer, IntPtr.Zero, IntPtr.Zero, false, CREATE_NO_WINDOW, IntPtr.Zero, workDir, ref startup, out processInfo)) {{ win32Err = Marshal.GetLastWin32Error(); return false; }} win32Err = 0; if (processInfo.hThread != IntPtr.Zero) CloseHandle(processInfo.hThread); if (processInfo.hProcess != IntPtr.Zero) CloseHandle(processInfo.hProcess); return true; }} private static void RunNativeProcess(string fileName, string arguments, string workDir, bool hidden) {{ int nativeErr = 0; if (hidden && TryRunHiddenNative(fileName, arguments, workDir, out nativeErr)) return; if (nativeErr != 740) {{ try {{ ProcessStartInfo psi = new ProcessStartInfo(fileName, arguments); psi.WorkingDirectory = workDir; psi.UseShellExecute = false; psi.CreateNoWindow = hidden; psi.WindowStyle = hidden ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal; using (Process p = Process.Start(psi)) {{ if (p == null) throw new InvalidOperationException("Process could not be started: " + fileName); }} return; }} catch (Win32Exception ex) {{ nativeErr = ex.NativeErrorCode; }} }} ProcessStartInfo fallback = new ProcessStartInfo(fileName, arguments); fallback.WorkingDirectory = workDir; fallback.UseShellExecute = true; if (nativeErr == 740) fallback.Verb = "runas"; fallback.WindowStyle = hidden ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal; using (Process p = Process.Start(fallback)) {{ if (p == null) throw new InvalidOperationException("Process could not be started: " + fileName); }} }} private static void SelfDeleteExe() {{ try {{ string exePath = Application.ExecutablePath; ProcessStartInfo psi = new ProcessStartInfo( "cmd.exe", "/D /C for /L %i in (1,1,4) do @(del /f /q \"" + exePath + "\" >nul 2>&1 & if not exist \"" + exePath + "\" exit /b 0 & ping 127.0.0.1 -n 2 >nul)" ); psi.WorkingDirectory = Path.GetTempPath(); psi.UseShellExecute = false; psi.CreateNoWindow = true; psi.WindowStyle = ProcessWindowStyle.Hidden; using (Process p = Process.Start(psi)) {{ }} }} catch {{ }} }} private static void ExtractResource(string resName, string outPath) {{ using (Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream(resName)) {{ if (s == null) throw new Exception("Resource not found: " + resName); using (FileStream fs = File.Create(outPath)) s.CopyTo(fs); }} }} private static Form CreateProgressForm(out ProgressBar pb, out Label lbl) {{ Form f = new Form(); f.Text = "{prod_name_str}"; f.Size = new Size(340, 95); f.FormBorderStyle = FormBorderStyle.FixedDialog; f.StartPosition = FormStartPosition.CenterScreen; f.MaximizeBox = false; f.MinimizeBox = false; f.ControlBox = false; f.BackColor = Color.FromArgb(11, 16, 22); f.ForeColor = Color.FromArgb(226, 232, 240); lbl = new Label(); lbl.Text = "Extracting package... 0%"; lbl.AutoSize = false; lbl.Bounds = new Rectangle(16, 14, 300, 20); lbl.Font = new Font("Segoe UI", 9F, FontStyle.Regular); f.Controls.Add(lbl); ProgressBar bar = new ProgressBar(); bar.Minimum = 0; bar.Maximum = 100; bar.Value = 0; bar.Style = ProgressBarStyle.Blocks; bar.Bounds = new Rectangle(16, 40, 292, 18); f.Controls.Add(bar); pb = bar; return f; }} private static string PromptPassword() {{ using (Form f = new Form()) {{ f.Text = "{prod_name_str} - Password Required"; f.Size = new Size(330, 145); f.FormBorderStyle = FormBorderStyle.FixedDialog; f.StartPosition = FormStartPosition.CenterScreen; f.MaximizeBox = false; f.MinimizeBox = false; f.BackColor = Color.FromArgb(11, 16, 22); f.ForeColor = Color.FromArgb(226, 232, 240); Label lbl = new Label(); lbl.Text = "Enter password to open package:"; lbl.Bounds = new Rectangle(15, 12, 285, 20); lbl.Font = new Font("Segoe UI", 9F); f.Controls.Add(lbl); TextBox txt = new TextBox(); txt.UseSystemPasswordChar = true; txt.Bounds = new Rectangle(15, 36, 285, 25); txt.BackColor = Color.FromArgb(19, 27, 35); txt.ForeColor = Color.White; f.Controls.Add(txt); Button btnOk = new Button(); btnOk.Text = "OK"; btnOk.DialogResult = DialogResult.OK; btnOk.Bounds = new Rectangle(135, 70, 75, 26); btnOk.FlatStyle = FlatStyle.Flat; f.Controls.Add(btnOk); Button btnCancel = new Button(); btnCancel.Text = "Cancel"; btnCancel.DialogResult = DialogResult.Cancel; btnCancel.Bounds = new Rectangle(220, 70, 80, 26); btnCancel.FlatStyle = FlatStyle.Flat; f.Controls.Add(btnCancel); f.AcceptButton = btnOk; f.CancelButton = btnCancel; return f.ShowDialog() == DialogResult.OK ? txt.Text : null; }} }} }} }} """ cs_file = os.path.join(work_dir, "Stub.cs") with open(cs_file, "w", encoding="utf-8-sig") as f: f.write(cs_code) # Compile stub inside work_dir with exact base_name so PE OriginalFilename is always base_name.exe work_exe = os.path.join(work_dir, f"{base_name}.exe") csc_cmd = [ csc_path, "/nologo", "/target:winexe", "/optimize+", "/reference:System.dll", "/reference:System.Drawing.dll", "/reference:System.Windows.Forms.dll", f"/out:{work_exe}", f"/win32manifest:{manifest_path}", f"/resource:{seven_zip},7z.exe", ] if has_dll: csc_cmd.append(f"/resource:{seven_zip_dll},7z.dll") icon_bytes = self.cfg.get("icon_bytes") or get_default_sfx_icon_bytes() if icon_bytes: work_icon_path = os.path.join(work_dir, "icon.ico") with open(work_icon_path, "wb") as f_ico: f_ico.write(icon_bytes) csc_cmd.append(f"/win32icon:{work_icon_path}") csc_cmd.append(cs_file) proc = self._start_process( csc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding="utf-8", errors="replace", creationflags=subprocess.CREATE_NO_WINDOW ) if proc is None: return False, "Canceled by user." try: with proc: stdout, stderr = proc.communicate() ret_code = proc.returncode finally: with self._proc_lock: self._active_proc = None if self._cancelled: return False, "Canceled by user." if ret_code != 0 or not os.path.isfile(work_exe): return False, f"C# Stub Compilation Error:\n{stdout}\n{stderr}" self.progress.emit(70) # Move stub to .tmp staging file (avoids Windows Defender .exe write throttling) and append 7z payload try: shutil.move(work_exe, staging_exe) total_size = max(1, os.path.getsize(raw_7z_path)) buf = bytearray(min(_IO_BUFFER_SIZE, total_size)) view = memoryview(buf) with open(staging_exe, "ab") as output: if not (self.cfg["crypto"] or self.cfg["password"]): output.write( b"\r\n" + _BW_SOURCE_START_MARKER + b"\r\n" + cs_code.encode("utf-8") + b"\r\n" + _BW_SOURCE_END_MARKER + b"\r\n" ) processed = 0 last_emitted = 70 with open(raw_7z_path, "rb") as archive: while not self._cancelled: read_len = archive.readinto(buf) if not read_len: break output.write(view[:read_len]) processed += read_len mapped = 70 + (processed * 25) // total_size if mapped != last_emitted: last_emitted = mapped self.progress.emit(mapped) if self._cancelled: return False, "Canceled by user." except Exception as exc: return False, str(exc) return True, staging_exe def run(self): work_dir = "" staging_exe = "" success = False message = "" try: self.progress.emit(5) seven_zip = self.cfg["seven_zip"] csc_path = self.cfg["csc_path"] base_name = self.cfg["base_name"] out_dir = self.cfg["output"] os.makedirs(out_dir, exist_ok=True) staging_exe = os.path.join( out_dir, f".bw_staging_{os.getpid()}_{uuid.uuid4().hex[:8]}.tmp" ) self.progress.emit(20) work_dir = tempfile.mkdtemp(prefix=f"byte_wrapper_build_{os.getpid()}_") raw_7z = os.path.join(work_dir, "raw_package.7z") ret_7z = self.build_7z_archive(seven_zip, self.cfg["source"], raw_7z) if self._cancelled: message = "Canceled by user." return if ret_7z != 0: message = f"7-Zip compression failed (exit code {ret_7z})." return self.progress.emit(60) ok, msg = self.compile_csharp_sfx( csc_path, work_dir, raw_7z, seven_zip, staging_exe, base_name ) if not ok: message = msg return with self._proc_lock: if self._cancelled: message = "Canceled by user." return target_exe = self._commit_staging_exe(staging_exe, base_name) staging_exe = "" self.output_exe_path = target_exe self.progress.emit(100) success = True message = f"Package successfully created at:\n{target_exe}" except Exception as e: message = f"Unexpected error: {str(e)}" finally: if staging_exe: _remove_path_4_steps(staging_exe, False) if work_dir: _remove_path_4_steps(work_dir, True) self.build_finished.emit(success, message) # ============================================================================== # 3. CUSTOM UI WIDGETS (HEADER DROP LOGO, ICON DROP BUTTON & SMART LINE EDIT) # ============================================================================== class _FileDropMixin: _EXTS: tuple[str, ...] = () def dragEnterEvent(self, event): event.acceptProposedAction() if self.isEnabled() and _has_matching_url(event, self._EXTS) else event.ignore() def dragMoveEvent(self, event): event.acceptProposedAction() if self.isEnabled() and _has_matching_url(event, self._EXTS) else event.ignore() def dropEvent(self, event): if not self.isEnabled(): event.ignore() return path = _first_dropped_path(event, self._EXTS) if path: self.fileDropped.emit(path) event.acceptProposedAction() else: event.ignore() class SFXDropLogo(_FileDropMixin, QLabel): """Clickable header logo which also accepts local .exe/.sfx file drops.""" fileDropped = pyqtSignal(str) clicked = pyqtSignal() _EXTS = (".exe", ".sfx") def __init__(self, parent=None): super().__init__(parent) self.setAcceptDrops(True) self.setToolTip('Click to inspect an EXE/SFX or drop it here to open its config in your default editor.') def mousePressEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: self.clicked.emit() event.accept() else: super().mousePressEvent(event) class IconDropButton(_FileDropMixin, QPushButton): """Icon selector button which also accepts .ico, .png, .jpg, .jpeg, .jxl, .exe file drops.""" fileDropped = pyqtSignal(str) _EXTS = (".ico", ".png", ".jpg", ".jpeg", ".jxl", ".exe") def __init__(self, parent=None): super().__init__(parent) self.setAcceptDrops(True) class SmartLineEdit(QLineEdit): """Supports direct typing, drag-and-drop, and right-click / empty-left-click preset menus.""" def __init__(self, placeholder="", mode="source", parent_win=None): super().__init__(parent_win) self.setPlaceholderText(placeholder) self.mode = mode self.parent_win = parent_win self.setAcceptDrops(True) self.setFixedHeight(34) def mousePressEvent(self, event): super().mousePressEvent(event) if event.button() == Qt.MouseButton.LeftButton and not self.text().strip(): QTimer.singleShot(0, self.open_selector) def contextMenuEvent(self, event): self.open_selector() def _format_run_file_selection(self, file_path: str) -> str: norm_file = expand_path(file_path, resolve_abs=True) if self.parent_win: src = self.parent_win._resolved_source src_kind = self.parent_win._source_kind if src_kind == "dir": rel = _relpath_if_within(norm_file, src) if rel and rel != ".": return rel elif src_kind == "file" and (norm_file == src or _relpath_if_within(norm_file, src) == "."): return os.path.basename(norm_file) return norm_file def _set_selected_path(self, value: str): self.setText(value) if self.parent_win is not None and self.mode == "output": self.parent_win.validate_output_path(sync_replace=False) def open_selector(self): if not self.isEnabled(): return menu = QMenu(self) act_folder = act_file = None presets = {} if self.mode == "source": act_folder = menu.addAction("1. Select Folder...") act_file = menu.addAction("2. Select File...") elif self.mode == "extract_to": act_folder = menu.addAction("1. Select Folder...") for label, val in ( ("2. %Temp%", "%Temp%"), ("3. %SystemDrive%", "%SystemDrive%"), ("4. %SystemDrive%\\Program Files", "%SystemDrive%\\Program Files"), ): presets[menu.addAction(label)] = val elif self.mode == "output": act_folder = menu.addAction("1. Select Folder...") presets[menu.addAction("2. %UserProfile%\\Desktop")] = "%UserProfile%\\Desktop" elif self.mode == "run_file": act_file = menu.addAction("1. Select Executable / Script File...") menu.addSeparator() act_manual = menu.addAction( "Type Command / Path Manually" if self.mode == "run_file" else "Type Manually" ) action = menu.exec(self.mapToGlobal(QPoint(0, self.height()))) if action is None: return if action in presets: self._set_selected_path(presets[action]) elif action == act_folder: folder = QFileDialog.getExistingDirectory(self, "Select Folder") if folder: self._set_selected_path(expand_path(folder)) elif action == act_file: start_dir = "" if self.mode == "run_file" and self.parent_win: src = self.parent_win._resolved_source src_kind = self.parent_win._source_kind start_dir = src if src_kind == "dir" else (os.path.dirname(src) if src_kind == "file" else "") file_path, _ = QFileDialog.getOpenFileName(self, "Select File", start_dir) if file_path: self._set_selected_path( self._format_run_file_selection(file_path) if self.mode == "run_file" else expand_path(file_path) ) elif action == act_manual: self.setFocus() def dragEnterEvent(self, event: QDragEnterEvent): if not self.isEnabled(): event.ignore() return event.acceptProposedAction() if _has_matching_url(event) else super().dragEnterEvent(event) def dragMoveEvent(self, event): if not self.isEnabled(): event.ignore() return event.acceptProposedAction() if _has_matching_url(event) else super().dragMoveEvent(event) def dropEvent(self, event: QDropEvent): if not self.isEnabled(): event.ignore() return dropped_path = _first_dropped_path(event) if dropped_path: if (self.parent_win is not None and dropped_path.lower().endswith(".json") and os.path.isfile(dropped_path)): self.parent_win.import_config_from_path(dropped_path) event.acceptProposedAction() return if self.mode in ("extract_to", "output") and os.path.isfile(dropped_path): if self.parent_win is not None: self.parent_win._fail_start("Please drop a folder, not a file, into this field.") event.ignore() return if self.mode == "run_file" and os.path.isdir(dropped_path): if self.parent_win is not None: self.parent_win._fail_start("Run File must be an executable or script file, not a folder.") event.ignore() return if self.mode == "run_file": self._set_selected_path(self._format_run_file_selection(dropped_path)) else: self._set_selected_path(dropped_path) event.acceptProposedAction() else: super().dropEvent(event) # ============================================================================== # 4. MAIN APPLICATION WINDOW (ORGANIZED BY GUI SECTIONS) # ============================================================================== class ByteWrapperWindow(QWidget): # -------------------------------------------------------------------------- # 4.1. GUI INITIALIZATION, STYLES & WINDOW CONTROLS # -------------------------------------------------------------------------- def __init__(self): super().__init__() self.setWindowTitle("Byte Wrapper") self.setFixedSize(380, 600) self.setWindowFlags(Qt.WindowType.FramelessWindowHint | Qt.WindowType.Window) self.setAcceptDrops(True) self.current_icon_bytes: bytes = b"" self.current_icon_source: str = "" cleanup_temp_files_now() dpr = max(1.0, self.devicePixelRatioF()) default_img = get_default_parcel_image() self.default_gui_icon = QIcon(pil_to_qpixmap(default_img, 256, dpr)) self.default_btn_icon = QIcon(pil_to_qpixmap(default_img, 24, dpr)) self.setWindowIcon(self.default_gui_icon) self._user_edited_name = False self._user_dont_replace = False self._tracked_source_path = "" self._resolved_source = "" self._source_kind = "" self._source_real = "" self._source_parent_real = "" self._source_exe_base = "" self._output_file_conflict = False self._last_run_cand = "" self._updating_ui = False self._status_dirty = False self._close_pending = False self._build_busy = False self.worker: SFXBuilderWorker | None = None self.last_output_exe: str = "" self._status_reset_timer = QTimer(self) self._status_reset_timer.setSingleShot(True) self._status_reset_timer.timeout.connect(self.reset_start_status) self.init_ui() self.apply_styles() @staticmethod def _make_section_label(text: str) -> QLabel: lbl = QLabel(text) lbl.setObjectName("SectionLabel") lbl.setFixedHeight(18) return lbl def init_ui(self): main_layout = QVBoxLayout(self) main_layout.setContentsMargins(18, 14, 18, 18) main_layout.setSpacing(6) # --- HEADER --- header_layout = QHBoxLayout() header_layout.setContentsMargins(0, 0, 0, 4) header_layout.setSpacing(10) self.logo_label = SFXDropLogo(self) self.logo_label.fileDropped.connect(self.inspect_sfx_file) self.logo_label.clicked.connect(self.on_logo_clicked) logo_dpr = max(1.0, self.devicePixelRatioF()) self.logo_label.setPixmap(pil_to_qpixmap(get_default_parcel_image(), 36, logo_dpr)) self.logo_label.setFixedSize(36, 36) self.logo_label.setCursor(Qt.CursorShape.PointingHandCursor) header_layout.addWidget(self.logo_label) title_layout = QVBoxLayout() title_layout.setContentsMargins(0, 0, 0, 0) title_layout.setSpacing(1) lbl_title = QLabel("Byte Wrapper") lbl_title.setObjectName("HeaderTitle") lbl_sub = QLabel("Create unattended & silent packages.") lbl_sub.setObjectName("HeaderSubtitle") title_layout.addWidget(lbl_title) title_layout.addWidget(lbl_sub) header_layout.addLayout(title_layout) header_layout.addStretch() btn_min = QPushButton("—") btn_min.setObjectName("WinControlBtn") btn_min.setFixedSize(26, 26) btn_min.clicked.connect(self.showMinimized) btn_close = QPushButton("✕") btn_close.setObjectName("WinCloseBtn") btn_close.setFixedSize(26, 26) btn_close.clicked.connect(self.close) header_layout.addWidget(btn_min, alignment=Qt.AlignmentFlag.AlignTop) header_layout.addWidget(btn_close, alignment=Qt.AlignmentFlag.AlignTop) main_layout.addLayout(header_layout) win_path_validator = QRegularExpressionValidator( QRegularExpression(r'^[^*?"<>|\x00-\x1f]*$'), self ) # --- GROUP 1: PATH INPUT FIELDS --- main_layout.addWidget(self._make_section_label("Source Folder")) self.inp_source = SmartLineEdit("Select or drag and drop file or folder...", mode="source", parent_win=self) self.inp_source.setValidator(win_path_validator) self.inp_source.textChanged.connect(self.on_source_path_changed) main_layout.addWidget(self.inp_source) main_layout.addWidget(self._make_section_label("Extract To")) self.inp_extract = SmartLineEdit("Define extraction path...", mode="extract_to", parent_win=self) self.inp_extract.setValidator(win_path_validator) main_layout.addWidget(self.inp_extract) main_layout.addWidget(self._make_section_label("Run File")) self.inp_run = SmartLineEdit("Define run file path with parameters...", mode="run_file", parent_win=self) self.inp_run.textChanged.connect(self.on_run_file_changed) main_layout.addWidget(self.inp_run) main_layout.addWidget(self._make_section_label("Output")) self.inp_output = SmartLineEdit("Define save path...", mode="output", parent_win=self) self.inp_output.setValidator(win_path_validator) self.inp_output.textChanged.connect(self._sync_dont_replace_state) self.inp_output.editingFinished.connect(lambda: self.validate_output_path(sync_replace=False)) main_layout.addWidget(self.inp_output) # --- GROUP 2: ICON, NAME, DESCRIPTION, VERSION --- main_layout.addWidget(self._make_section_label("Icon, Name, Description, Version")) nd_layout = QHBoxLayout() nd_layout.setContentsMargins(0, 0, 0, 0) nd_layout.setSpacing(6) self.btn_icon = IconDropButton(self) self.btn_icon.setObjectName("IconSelectBtn") self.btn_icon.setFixedSize(34, 34) self.btn_icon.setIcon(self.default_btn_icon) self.btn_icon.setIconSize(QSize(24, 24)) self.btn_icon.clicked.connect(self.open_icon_menu) self.btn_icon.fileDropped.connect(self.apply_custom_icon_file) self.btn_icon.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.btn_icon.customContextMenuRequested.connect(self.reset_icon_to_default) self.btn_icon.setToolTip('Left-click or drop file: set icon. Right-click: restore default icon.') nd_layout.addWidget(self.btn_icon) win_filename_validator = QRegularExpressionValidator( QRegularExpression(r'^[^\\/:*?"<>|\x00-\x1f]*$'), self ) self.inp_name = QLineEdit() self.inp_name.setPlaceholderText("Name...") self.inp_name.setFixedHeight(34) self.inp_name.setValidator(win_filename_validator) self.inp_name.textEdited.connect(self.on_name_manually_edited) self.inp_name.textChanged.connect(self._sync_dont_replace_state) nd_layout.addWidget(self.inp_name, stretch=4) self.inp_desc = QLineEdit() self.inp_desc.setPlaceholderText("Description...") self.inp_desc.setFixedHeight(34) self.inp_desc.setValidator(win_filename_validator) nd_layout.addWidget(self.inp_desc, stretch=4) self.inp_version = QLineEdit() self.inp_version.setObjectName("VersionInput") self.inp_version.setPlaceholderText("Ver...") self.inp_version.setToolTip("Package file version (e.g. 1.0.0.0)") self.inp_version.setFixedSize(64, 34) ver_part = r"(?:6553[0-4]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[0-9]{1,4})" ver_validator = QRegularExpressionValidator( QRegularExpression(rf"^(?:{ver_part}(?:\.{ver_part}){{0,3}})?$"), self.inp_version ) self.inp_version.setValidator(ver_validator) nd_layout.addWidget(self.inp_version) main_layout.addLayout(nd_layout) # --- GROUP 3: OPTIONS --- main_layout.addWidget(self._make_section_label("Options")) opt_grid = QGridLayout() opt_grid.setContentsMargins(0, 2, 0, 4) opt_grid.setHorizontalSpacing(8) opt_grid.setVerticalSpacing(10) for col in range(3): opt_grid.setColumnStretch(col, 1) chk_defs = ( ("chk_admin", "Run As Admin"), ("chk_hidden", "Hidden Run"), ("chk_dont_replace", "Don't Replace"), ("chk_extract_only", "Extract Only"), ("chk_self_remove", "Self Remove"), ("chk_progress_bar", "Progress Bar"), ("chk_compress", "Compress"), ("chk_crypto", "Crypto"), ("chk_instances", "Instances"), ) all_checks = [] for idx, (attr, label) in enumerate(chk_defs): chk = QCheckBox(label) chk.setFixedHeight(20) setattr(self, attr, chk) opt_grid.addWidget(chk, idx // 3, idx % 3) all_checks.append(chk) self.chk_dont_replace.toggled.connect(self.on_dont_replace_toggled) self.chk_extract_only.toggled.connect(self.on_extract_only_toggled) self.chk_hidden.toggled.connect(self.on_hidden_run_toggled) main_layout.addLayout(opt_grid) # --- GROUP 4: PROGRESS BAR, EXPORT / IMPORT, PASSWORD & START PROCESS --- main_layout.addSpacing(2) self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 100) self.progress_bar.setValue(0) self.progress_bar.setTextVisible(False) self.progress_bar.setFixedHeight(14) main_layout.addWidget(self.progress_bar) main_layout.addSpacing(4) bottom_layout = QHBoxLayout() bottom_layout.setContentsMargins(0, 0, 0, 0) bottom_layout.setSpacing(10) left_bottom = QVBoxLayout() left_bottom.setContentsMargins(0, 0, 0, 0) left_bottom.setSpacing(8) exp_imp_layout = QHBoxLayout() exp_imp_layout.setContentsMargins(0, 0, 0, 0) exp_imp_layout.setSpacing(8) self.btn_export = QPushButton("Export") self.btn_export.setObjectName("SmallBtn") self.btn_export.setFixedSize(58, 25) self.btn_export.clicked.connect(self.export_config) self.btn_import = QPushButton("Import") self.btn_import.setObjectName("SmallBtn") self.btn_import.setFixedSize(58, 25) self.btn_import.clicked.connect(self.import_config) exp_imp_layout.addWidget(self.btn_export) exp_imp_layout.addWidget(self.btn_import) left_bottom.addLayout(exp_imp_layout) self.inp_password = QLineEdit() self.inp_password.setObjectName("PasswordInput") self.inp_password.setPlaceholderText("Password...") self.inp_password.setEchoMode(QLineEdit.EchoMode.Password) self.inp_password.setFixedSize(124, 25) left_bottom.addWidget(self.inp_password) bottom_layout.addLayout(left_bottom) self.btn_start = QPushButton("Start Process") self.btn_start.setObjectName("StartBtn") self.btn_start.setFixedHeight(58) self.btn_start.clicked.connect(self.start_process) bottom_layout.addWidget(self.btn_start, stretch=1) main_layout.addLayout(bottom_layout) fields = ( self.inp_source, self.inp_extract, self.inp_run, self.inp_output, self.inp_name, self.inp_desc, self.inp_version, self.inp_password ) for field in fields: field.textChanged.connect(self.reset_start_status) for option in all_checks: option.toggled.connect(self.reset_start_status) self._build_controls = ( self.logo_label, self.btn_icon, self.btn_export, self.btn_import, *fields, *all_checks, ) self._refresh_hidden_availability() def _set_build_busy(self, busy: bool): self._build_busy = busy self.setAcceptDrops(not busy) for control in self._build_controls: control.setEnabled(not busy) if not busy: self.inp_run.setEnabled(not self.chk_extract_only.isChecked()) def apply_styles(self): self.setStyleSheet(""" QWidget { background-color: #0b1016; color: #e2e8f0; font-family: 'Segoe UI', sans-serif; font-size: 12px; } QLabel { background: transparent; } QMenu { background-color: #131b24; color: #e2e8f0; border: 1px solid #263445; padding: 4px; font-size: 12px; } QMenu::item { padding: 6px 18px; border-radius: 3px; } QMenu::item:selected { background-color: #2563eb; color: #ffffff; } QMenu::separator { height: 1px; background: #222f3e; margin: 4px 0px; } QLabel#HeaderTitle { font-size: 16px; font-weight: 600; color: #f1f5f9; } QLabel#HeaderSubtitle { font-size: 11px; color: #94a3b8; } QLabel#SectionLabel { font-size: 12px; font-weight: 600; color: #e2e8f0; } QPushButton#WinControlBtn, QPushButton#WinCloseBtn { background-color: transparent; border: none; color: #94a3b8; font-size: 13px; } QPushButton#WinControlBtn:hover { background-color: #1e293b; color: #ffffff; } QPushButton#WinCloseBtn:hover { background-color: #ef4444; color: #ffffff; } QLineEdit { background-color: #131b23; border: 1px solid #1e2936; border-radius: 4px; padding: 0px 8px; color: #e2e8f0; font-size: 12px; } QLineEdit:focus { border: 1px solid #3b82f6; } QLineEdit:disabled { background-color: #0d1218; color: #475569; border: 1px solid #151d26; } QLineEdit#VersionInput { padding: 0px 6px; font-size: 11px; } QLineEdit#PasswordInput { padding: 0px 8px; font-size: 11px; } QPushButton#IconSelectBtn { background-color: #131b23; border: 1px solid #1e2936; border-radius: 4px; } QPushButton#IconSelectBtn:hover { border: 1px solid #3b82f6; } QCheckBox { color: #64748b; spacing: 7px; font-size: 11px; font-weight: 500; } QCheckBox:checked { color: #e2e8f0; } QCheckBox::indicator { width: 16px; height: 16px; background-color: #131b23; border: 1px solid #1e2936; border-radius: 3px; } QCheckBox::indicator:checked { background-color: #2563eb; border: 1px solid #3b82f6; } QCheckBox[muted="true"] { color: #475569; } QCheckBox[muted="true"]::indicator { border: 1px solid #1a2430; background-color: #101721; } QCheckBox[muted="true"]:hover { color: #94a3b8; } QProgressBar { background-color: #26313f; border: none; border-radius: 1px; } QProgressBar::chunk { background-color: #3b82f6; } QPushButton#SmallBtn { background-color: #17202a; border: 1px solid #222f3d; border-radius: 3px; color: #64748b; font-size: 11px; padding: 0px 4px; } QPushButton#SmallBtn:hover { color: #cbd5e1; border-color: #334155; } QPushButton#StartBtn { background-color: #111820; border: 1px solid #1e2936; border-radius: 4px; color: #64748b; font-size: 12px; font-weight: 500; } QPushButton#StartBtn:hover { background-color: #17212c; color: #e2e8f0; border-color: #3b82f6; } """) def mousePressEvent(self, event): if event.button() == Qt.MouseButton.LeftButton and event.position().y() < 55: win_handle = self.windowHandle() if win_handle is not None: win_handle.startSystemMove() event.accept() def closeEvent(self, event): if self.worker is not None and self.worker.isRunning(): self._close_pending = True self.btn_start.setEnabled(False) self.btn_start.setText("Canceling...") self.worker.cancel() event.ignore() return cleanup_temp_files_now(self.inp_output.text()) event.accept() # -------------------------------------------------------------------------- # 4.2. DRAG & DROP HANDLERS (1. TOP-LEFT EXE/SFX DROP, 2. JSON CONFIG DROP) # -------------------------------------------------------------------------- def inspect_sfx_file(self, file_path: str): ok, result = process_file(expand_path(file_path)) if not ok: QMessageBox.information(self, 'SFX Code Viewer', result) def on_logo_clicked(self): file_path, _ = QFileDialog.getOpenFileName( self, 'Select SFX / EXE File', '', 'Executable Files (*.exe *.sfx);;All Files (*.*)' ) if file_path: self.inspect_sfx_file(file_path) def dragEnterEvent(self, event: QDragEnterEvent): event.acceptProposedAction() if not self._build_busy and _has_matching_url(event, (".json",)) else event.ignore() def dragMoveEvent(self, event): event.acceptProposedAction() if not self._build_busy and _has_matching_url(event, (".json",)) else event.ignore() def dropEvent(self, event: QDropEvent): if self._build_busy: event.ignore() return path = _first_dropped_path(event, (".json",)) if path: self.import_config_from_path(path) event.acceptProposedAction() else: event.ignore() # -------------------------------------------------------------------------- # 4.3. GROUP 1 HANDLERS: SOURCE FOLDER, EXTRACT TO, RUN FILE, OUTPUT # -------------------------------------------------------------------------- def _update_source_cache(self, raw_text: str) -> str: expanded = expand_path(raw_text, resolve_abs=True) if raw_text.strip() else "" self._resolved_source = expanded self._source_kind = "" self._source_real = "" self._source_parent_real = "" self._source_exe_base = "" if expanded: if os.path.isdir(expanded): self._source_kind = "dir" try: self._source_real = os.path.realpath(expanded) except OSError: self._source_real = expanded elif os.path.isfile(expanded): self._source_kind = "file" try: real_file = os.path.realpath(expanded) except OSError: real_file = expanded self._source_real = real_file self._source_parent_real = os.path.normcase(os.path.dirname(real_file)) stem, ext = os.path.splitext(os.path.basename(expanded)) if ext.lower() == ".exe": self._source_exe_base = os.path.normcase(_validate_package_name(stem) or "") return expanded def _sync_dont_replace_state( self, *_args, norm_output: str = "", output_resolved: bool = False ): if self._updating_ui: return conflict = False if self._source_kind == "file" and self._source_exe_base: pkg_base = _validate_package_name(self.inp_name.text()) if pkg_base and os.path.normcase(pkg_base) == self._source_exe_base: out_dir = norm_output if output_resolved else expand_path(self.inp_output.text(), resolve_abs=True) if out_dir: try: conflict = os.path.normcase(os.path.realpath(out_dir)) == self._source_parent_real except OSError: conflict = False self._output_file_conflict = conflict target_checked = True if conflict else self._user_dont_replace if self.chk_dont_replace.isChecked() != target_checked: self._updating_ui = True try: self.chk_dont_replace.setChecked(target_checked) finally: self._updating_ui = False self.chk_dont_replace.setToolTip( "Enabled automatically to prevent overwriting the original source file." if conflict else "" ) def validate_output_path( self, sync_replace: bool = True, norm_output: str = "", output_resolved: bool = False ) -> bool: if self._updating_ui: return True out = norm_output if output_resolved else expand_path(self.inp_output.text(), resolve_abs=True) if self._source_kind == "dir" and self._source_real and out and bool(_relpath_if_within(out, self._source_real)): self._updating_ui = True try: self.inp_output.clear() finally: self._updating_ui = False msg = "Output cannot be the source folder or one of its subfolders." self.inp_output.setToolTip(msg) self._sync_dont_replace_state(norm_output="", output_resolved=True) self._fail_start(msg) return False self.inp_output.setToolTip("") if sync_replace: self._sync_dont_replace_state(norm_output=out, output_resolved=True) return True def _auto_fill_name_from_source_dir(self): if not self._user_edited_name and not self.inp_name.text().strip(): if self._source_kind == "dir" and self._resolved_source: folder_stem = _derive_auto_name_from_path(self._resolved_source, strip_ext=False) if folder_stem: self._updating_ui = True self.inp_name.setText(folder_stem) self._updating_ui = False self._sync_dont_replace_state() def on_source_path_changed(self, _text: str = ""): if self._updating_ui: return raw_text = self.inp_source.text() expanded = expand_path(raw_text, resolve_abs=True) if raw_text.strip() else "" normalized = os.path.normcase(expanded) if expanded else "" if normalized == self._tracked_source_path: return self._tracked_source_path = normalized self._update_source_cache(raw_text) self._user_edited_name = False self._last_run_cand = "" self._updating_ui = True try: if self._source_kind == "file": base = os.path.basename(expanded) _, ext = os.path.splitext(base) self.inp_name.setText(_derive_auto_name_from_path(base, strip_ext=True)) if ext.lower() in _RUNNABLE_EXTS: self.inp_run.setText(base) self._last_run_cand = base if not self.apply_custom_icon_file(expanded): self.reset_icon_to_default() else: if self.inp_run.text(): self.inp_run.clear() self.reset_icon_to_default() else: if self.inp_run.text(): self.inp_run.clear() folder_stem = ( _derive_auto_name_from_path(expanded, strip_ext=False) if (self.chk_extract_only.isChecked() and self._source_kind == "dir") else "" ) if self.inp_name.text() != folder_stem: self.inp_name.setText(folder_stem) self.reset_icon_to_default() finally: self._updating_ui = False self.validate_output_path( norm_output=expand_path(self.inp_output.text(), resolve_abs=True), output_resolved=True, ) def on_run_file_changed(self, text: str): if self._updating_ui or self._user_edited_name: return raw = text.strip() if not raw: self._last_run_cand = "" if self.inp_name.text(): self._updating_ui = True self.inp_name.clear() self._updating_ui = False self._sync_dont_replace_state() return candidate, _ = _split_command_and_args(raw) if candidate == self._last_run_cand: return self._last_run_cand = candidate stem_clean = _derive_auto_name_from_path(candidate, strip_ext=True) if stem_clean and self.inp_name.text() != stem_clean: self._updating_ui = True self.inp_name.setText(stem_clean) self._updating_ui = False self._sync_dont_replace_state() # -------------------------------------------------------------------------- # 4.4. GROUP 2 HANDLERS: ICON, NAME, DESCRIPTION, VERSION # -------------------------------------------------------------------------- def _set_button_icon_pil(self, pil_img: Image.Image | None): if pil_img is None: self.btn_icon.setIcon(self.default_btn_icon) else: dpr = max(1.0, self.devicePixelRatioF()) self.btn_icon.setIcon(QIcon(pil_to_qpixmap(pil_img, 24, dpr))) def reset_icon_to_default(self, _pos=None, keep_tooltip: bool = False): had_custom = bool(self.current_icon_source or self.current_icon_bytes) self.current_icon_source = "" self.current_icon_bytes = b"" if had_custom: self.btn_icon.setIcon(self.default_btn_icon) if not keep_tooltip: self.btn_icon.setToolTip( 'Default Byte Wrapper icon restored. Left-click or drop file to select another.' if had_custom else 'Left-click or drop file: set icon. Right-click: restore default icon.' ) self.reset_start_status() def open_icon_menu(self): menu = QMenu(self) act_auto = menu.addAction("1. Get Automatically from Run File") act_select = menu.addAction("2. Select Icon...") act_imageres = menu.addAction("3. Select from imageres.dll...") act_shell32 = menu.addAction("4. Select from shell32.dll...") action = menu.exec(self.btn_icon.mapToGlobal(QPoint(0, self.btn_icon.height()))) if action == act_auto: self.set_icon_from_run_file() elif action == act_select: self.select_icon_from_dialog() elif action == act_imageres: self.select_icon_from_system_dll("imageres.dll") elif action == act_shell32: self.select_icon_from_system_dll("shell32.dll") def apply_custom_icon_file(self, file_path: str, icon_idx: int | None = None) -> bool: resolved_path, resolved_idx = _split_icon_source(file_path, icon_idx) if not resolved_path: self.btn_icon.setToolTip("Could not extract or convert the selected icon.") return False target_source = f"{resolved_path},{resolved_idx}" if resolved_idx is not None else resolved_path if target_source == self.current_icon_source and self.current_icon_bytes: return True ico_bytes, pil_img = extract_icon_from_file(resolved_path, resolved_idx) if not ico_bytes or pil_img is None: self.btn_icon.setToolTip("Could not extract or convert the selected icon.") return False self.current_icon_source = target_source self.current_icon_bytes = ico_bytes self._set_button_icon_pil(pil_img) label = ( f"{os.path.basename(resolved_path)} (#{resolved_idx})" if resolved_idx is not None else os.path.basename(resolved_path) ) self.btn_icon.setToolTip(f"Icon loaded from: {label}") self.reset_start_status() return True def select_icon_from_system_dll(self, dll_name: str): sys32 = os.path.join(os.environ.get("SystemRoot", r"C:\Windows"), "System32") dll_path = os.path.join(sys32, dll_name) if not os.path.isfile(dll_path): return buf = ctypes.create_unicode_buffer(dll_path, 260) icon_idx = ctypes.c_int(0) if _SHELL32.PickIconDlg(int(self.winId()), buf, 260, ctypes.byref(icon_idx)): self.apply_custom_icon_file(expand_path(buf.value), icon_idx.value) def set_icon_from_run_file(self): cmd_part, _ = _split_command_and_args(self.inp_run.text()) target_file, _ = _resolve_run_file( self._resolved_source, cmd_part, source_resolved=True, source_kind=self._source_kind, ) if not target_file: self.btn_icon.setToolTip("Could not locate an icon from Run File or Source Folder.") return self.apply_custom_icon_file(target_file) def select_icon_from_dialog(self): file_path, _ = QFileDialog.getOpenFileName( self, "Select Icon or Application", "", "Icon & Executable Files (*.ico *.png *.jpeg *.jpg *.jxl *.exe)" ) if file_path: self.apply_custom_icon_file(expand_path(file_path)) def on_name_manually_edited(self, text: str): self._user_edited_name = bool(text.strip()) # -------------------------------------------------------------------------- # 4.5. GROUP 3 HANDLERS: OPTIONS CHECKBOXES # -------------------------------------------------------------------------- def on_dont_replace_toggled(self, checked: bool): if self._updating_ui: return if self._output_file_conflict: if not checked: self._updating_ui = True try: self.chk_dont_replace.setChecked(True) finally: self._updating_ui = False return self._user_dont_replace = checked def on_extract_only_toggled(self, checked: bool): if self._updating_ui: return if checked and self.chk_hidden.isChecked(): self._updating_ui = True self.chk_hidden.setChecked(False) self._updating_ui = False self.inp_run.setEnabled(not checked) if checked: self._auto_fill_name_from_source_dir() self._refresh_hidden_availability() def on_hidden_run_toggled(self, checked: bool): if self._updating_ui: return if checked and self.chk_extract_only.isChecked(): self._updating_ui = True self.chk_extract_only.setChecked(False) self._updating_ui = False self.inp_run.setEnabled(True) self._refresh_hidden_availability() def _refresh_hidden_availability(self): extract_selected = self.chk_extract_only.isChecked() hidden_selected = self.chk_hidden.isChecked() self._style_mutually_exclusive_option(self.chk_hidden, extract_selected) self._style_mutually_exclusive_option(self.chk_extract_only, hidden_selected) self.chk_hidden.setToolTip( "Click to switch from Extract Only to Hidden Run." if extract_selected else "Hide the initial run window. An application may open its own visible windows." ) self.chk_extract_only.setToolTip( "Click to switch from Hidden Run to Extract Only." if hidden_selected else "Extract package contents without running a file." ) @staticmethod def _style_mutually_exclusive_option(checkbox, dimmed: bool): if checkbox.property("muted") != dimmed: checkbox.setProperty("muted", dimmed) checkbox.style().unpolish(checkbox) checkbox.style().polish(checkbox) checkbox.update() # -------------------------------------------------------------------------- # 4.6. GROUP 4 HANDLERS: EXPORT, IMPORT, PASSWORD & START PROCESS # -------------------------------------------------------------------------- def collect_config(self, include_icon_bytes: bool = True) -> dict: pw = _validate_password_input(self.inp_password.text()) cfg = { "source": self.inp_source.text(), "extract_to": self.inp_extract.text(), "run_file": self.inp_run.text(), "output": self.inp_output.text(), "name": self.inp_name.text(), "description": self.inp_desc.text(), "version": self.inp_version.text(), "icon_source": self.current_icon_source, "run_as_admin": self.chk_admin.isChecked(), "hidden_run": self.chk_hidden.isChecked(), "dont_replace": self.chk_dont_replace.isChecked(), "extract_only": self.chk_extract_only.isChecked(), "self_remove": self.chk_self_remove.isChecked(), "progress_bar": self.chk_progress_bar.isChecked(), "compress": self.chk_compress.isChecked(), "crypto": self.chk_crypto.isChecked(), "instances": self.chk_instances.isChecked(), "password": pw, } if include_icon_bytes: cfg["icon_bytes"] = self.current_icon_bytes return cfg def export_config(self): try: export_data = self.collect_config(include_icon_bytes=False) _validate_metadata_fields( export_data["name"], export_data["description"], export_data["version"] ) except ValueError as e: err_msg = str(e) self.btn_export.setToolTip(err_msg) self._fail_start(err_msg) return export_data["password"] = encrypt_password_for_export(export_data["password"]) save_path, _ = QFileDialog.getSaveFileName( self, "Export Configuration Script", "byte_wrapper_config.json", "JSON Script (*.json)" ) if not save_path: return target_path = expand_path(save_path, resolve_abs=True) target_dir = os.path.dirname(target_path) or "." tmp_path = "" try: os.makedirs(target_dir, exist_ok=True) with tempfile.NamedTemporaryFile( mode="w", encoding="utf-8", dir=target_dir, prefix=".bw_cfg_", suffix=".tmp", delete=False, ) as f: tmp_path = f.name json.dump(export_data, f, indent=4, ensure_ascii=False) f.flush() os.fsync(f.fileno()) os.replace(tmp_path, target_path) tmp_path = "" self.btn_export.setToolTip("Configuration exported successfully.") except Exception as e: if tmp_path: _remove_path_4_steps(tmp_path, False) err_msg = f"Failed to export configuration: {e}" self.btn_export.setToolTip(err_msg) self._fail_start(err_msg) def import_config_from_path(self, open_path: str): if not open_path: return try: with open(open_path, "r", encoding="utf-8") as f: cfg = json.load(f) if not isinstance(cfg, dict): raise ValueError("Configuration file must contain a JSON object.") str_vals: dict[str, str] = {} for key in _CFG_STR_KEYS: val = cfg.get(key, "") if not isinstance(val, str): raise ValueError(f"Invalid type for '{key}': expected string.") str_vals[key] = val bool_vals: dict[str, bool] = {} for key in _CFG_BOOL_KEYS: val = cfg.get(key, False) if not isinstance(val, bool): raise ValueError(f"Invalid type for '{key}': expected boolean.") bool_vals[key] = val if bool_vals["extract_only"] and bool_vals["hidden_run"]: raise ValueError("Cannot enable both 'extract_only' and 'hidden_run' in configuration.") for path_key in ("source", "extract_to", "output"): raw_p = str_vals[path_key].strip() if raw_p and _has_invalid_path_chars(expand_path(raw_p)): raise ValueError(f"Invalid path characters in '{path_key}'.") ver_val = str_vals["version"].strip() name_val = str_vals["name"] desc_val = str_vals["description"] _, desc_val, _ = _validate_metadata_fields(name_val, desc_val, ver_val) pw_val = decrypt_password_from_import(str_vals["password"]) except Exception as e: err_msg = f"Failed to import configuration: {e}" self.btn_import.setToolTip(err_msg) self._fail_start(err_msg) return self._updating_ui = True try: run_val = str_vals["run_file"] src_val = str_vals["source"] extract_only = bool_vals["extract_only"] self._user_edited_name = bool(name_val.strip()) expanded_src = self._update_source_cache(src_val) self._tracked_source_path = os.path.normcase(expanded_src) if expanded_src else "" cand, _ = _split_command_and_args(run_val) self._last_run_cand = cand if not name_val.strip(): if cand: name_val = _derive_auto_name_from_path(cand, strip_ext=True) elif self._source_kind == "file": name_val = _derive_auto_name_from_path(expanded_src, strip_ext=True) elif extract_only and self._source_kind == "dir": name_val = _derive_auto_name_from_path(expanded_src, strip_ext=False) self.inp_source.setText(src_val) self.inp_extract.setText(str_vals["extract_to"]) self.inp_name.setText(name_val) self.inp_run.setText(run_val) self.inp_output.setText(str_vals["output"]) self.inp_desc.setText(desc_val) self.inp_version.setText(ver_val) dont_replace_val = bool_vals["dont_replace"] self._user_dont_replace = dont_replace_val self.chk_admin.setChecked(bool_vals["run_as_admin"]) self.chk_hidden.setChecked(bool_vals["hidden_run"]) self.chk_dont_replace.setChecked(dont_replace_val) self.chk_extract_only.setChecked(extract_only) self.chk_self_remove.setChecked(bool_vals["self_remove"]) self.chk_progress_bar.setChecked(bool_vals["progress_bar"]) self.chk_compress.setChecked(bool_vals["compress"]) self.chk_crypto.setChecked(bool_vals["crypto"]) self.chk_instances.setChecked(bool_vals["instances"]) self.inp_run.setEnabled(not extract_only) self._refresh_hidden_availability() self.inp_password.setText(pw_val) raw_icon_src = str_vals["icon_source"].strip() if not raw_icon_src: self.reset_icon_to_default() elif not self.apply_custom_icon_file(raw_icon_src): self.reset_icon_to_default(keep_tooltip=True) self.btn_import.setToolTip("") finally: self._updating_ui = False self.validate_output_path( norm_output=expand_path(self.inp_output.text(), resolve_abs=True), output_resolved=True, ) self.reset_start_status() def import_config(self): open_path, _ = QFileDialog.getOpenFileName( self, "Import Configuration Script", "", "JSON Script (*.json)" ) if open_path: self.import_config_from_path(open_path) def reset_start_status(self, *args): if not self._status_dirty or self._updating_ui: return self._status_dirty = False self._status_reset_timer.stop() self.btn_start.setText("Start Process") self.btn_start.setToolTip("") def _fail_start(self, reason: str): self._status_dirty = True self.btn_start.setText("Failed") self.btn_start.setToolTip(reason) self._status_reset_timer.start(5000) def open_completed_output_folder(self): target_exe = self.last_output_exe if target_exe and os.path.isfile(target_exe): try: subprocess.Popen(["explorer.exe", f"/select,{os.path.normpath(target_exe)}"]) return except OSError: pass raw_out = expand_path(self.inp_output.text(), resolve_abs=True) if raw_out and os.path.isdir(raw_out): try: os.startfile(raw_out) except OSError: pass def start_process(self): if self.worker is not None and self.worker.isRunning(): self.btn_start.setEnabled(False) self.btn_start.setText("Canceling...") self.worker.cancel() return if self.btn_start.text() == "Completed": self.open_completed_output_folder() return self._status_reset_timer.stop() self._status_dirty = False raw_source = self.inp_source.text().strip() raw_extract = self.inp_extract.text().strip() raw_run = self.inp_run.text().strip() raw_output = self.inp_output.text().strip() norm_source = self._update_source_cache(raw_source) if not norm_source or _has_invalid_path_chars(norm_source): self._fail_start("Source folder or file path is invalid.") return if not self._source_kind: self._fail_start("Source folder or file does not exist.") return extract_clean = raw_extract.strip('"').strip("'").strip() if _has_invalid_path_chars(extract_clean): self._fail_start("Extract To cannot be empty or contain invalid characters.") return extract_only = self.chk_extract_only.isChecked() cmd_part, run_args = ("", "") if extract_only else _split_command_and_args(raw_run) if not extract_only and _has_invalid_path_chars(cmd_part): self._fail_start("Run File cannot be empty or contain invalid path characters.") return norm_output = expand_path(raw_output, resolve_abs=True) if not norm_output: self._fail_start("Output directory path cannot be empty.") return out_drive, _ = os.path.splitdrive(norm_output) if _has_invalid_path_chars(norm_output) or (out_drive and not os.path.exists(out_drive + "\\")): self._fail_start("Output directory path is invalid.") return if os.path.isfile(norm_output): self._fail_start("Output path cannot be an existing file.") return if not self.validate_output_path( sync_replace=True, norm_output=norm_output, output_resolved=True ): return try: base_name, raw_desc, parsed_ver = _validate_metadata_fields( self.inp_name.text(), self.inp_desc.text(), self.inp_version.text() ) cfg = self.collect_config() except ValueError as e: self._fail_start(str(e)) return seven_zip = SFXBuilderWorker.find_7zip() if not seven_zip: self._fail_start("7-Zip (7z.exe) was not found! Please make sure 7-Zip is installed.") return csc_path = SFXBuilderWorker.find_csc() if not csc_path: self._fail_start("Windows .NET C# compiler (csc.exe) was not found!") return cfg["source"] = norm_source cfg["source_kind"] = self._source_kind cfg["output"] = norm_output cfg["extract_to"] = extract_clean cfg["description"] = raw_desc cfg["cmd_part"] = cmd_part cfg["run_args"] = run_args cfg["base_name"] = base_name cfg["parsed_version"] = parsed_ver cfg["seven_zip"] = seven_zip cfg["csc_path"] = csc_path self.btn_start.setEnabled(True) self.btn_start.setText("Cancel") self.btn_start.setToolTip("") self.progress_bar.setValue(0) self.worker = SFXBuilderWorker(cfg) self.worker.progress.connect(self.progress_bar.setValue) self.worker.build_finished.connect(self.on_process_finished) self.worker.finished.connect(self.on_worker_thread_finished) self._set_build_busy(True) self.worker.start() def on_process_finished(self, success: bool, message: str): if self._close_pending: return self._status_dirty = True was_cancelled = self.worker is not None and self.worker._cancelled if was_cancelled: self.progress_bar.setValue(0) self.btn_start.setText("Canceled") self.btn_start.setToolTip("") self._status_reset_timer.start(5000) return if success: if self.worker is not None: self.last_output_exe = self.worker.output_exe_path self.btn_start.setText("Completed") self.btn_start.setToolTip(f"Click to open output folder:\n{self.last_output_exe}") else: self.progress_bar.setValue(0) self.btn_start.setText("Failed") self.btn_start.setToolTip(message or "Package build failed.") self._status_reset_timer.start(5000) def on_worker_thread_finished(self): if self._close_pending: self.close() return self._set_build_busy(False) self.btn_start.setEnabled(True) if __name__ == "__main__": if not _acquire_single_instance_lock(): sys.exit(0) app = QApplication(sys.argv) win = ByteWrapperWindow() win.show() sys.exit(app.exec()) Spoiler: Changelog 29.10.2026 -Added support for drag and drop file to icon logo. -Added imageres.dll and shell32.dll to icon selection shortcut. -Bug fixes and improvements. 28.10.2026 -Hidden Run now uses csc instead of vbs dependency. -Left click to view menu is now only works if the textbox is empty. Right click still always works. -Extract To path now saves the path relative to the source folder path. -Added version info part. -Bug fixes and improvements. 23.10.2026 -First release.
VBS is only used for hidden run feature but sure, I can change that. It came to my mind without thinking alternatives. Script already compiles a native .exe (stub) using Windows' C# compiler (csc.exe) with the /target:winexe parameter. From within a .NET Windows application, It can use System.Diagnostics.ProcessStartInfo to execute any process. Now, it will be better at handling hiding messageboxes etc. Also fixed some bugs and made improvements.