468 lines
18 KiB
Python
468 lines
18 KiB
Python
"""
|
|
LOGAR Branding & Installer Graphics Generator
|
|
Generates clean, professional SVG, PNG, ICO, and BMP visual assets for
|
|
LOGAR installers (Inno Setup & NSIS), application icons, root /assets library, and WebUI branding.
|
|
Zero external dependencies required (uses standard library zlib and struct).
|
|
"""
|
|
|
|
import os
|
|
import math
|
|
import zlib
|
|
import struct
|
|
import shutil
|
|
|
|
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
ROOT_ASSETS_DIR = os.path.join(ROOT_DIR, "assets")
|
|
COMPILATION_ASSETS_DIR = os.path.join(ROOT_DIR, "compilation", "assets")
|
|
LICENSING_ASSETS_DIR = os.path.join(ROOT_DIR, "LOGAR-LICENSING", "assets")
|
|
|
|
for d in (ROOT_ASSETS_DIR, COMPILATION_ASSETS_DIR, LICENSING_ASSETS_DIR):
|
|
os.makedirs(d, exist_ok=True)
|
|
|
|
|
|
def create_bmp_24bit(width: int, height: int, pixel_func) -> bytes:
|
|
"""Generates an uncompressed 24-bit RGB BMP image."""
|
|
row_bytes = width * 3
|
|
padding = (4 - (row_bytes % 4)) % 4
|
|
image_size = (row_bytes + padding) * height
|
|
file_size = 14 + 40 + image_size
|
|
|
|
# BITMAPFILEHEADER (14 bytes)
|
|
header = struct.pack(
|
|
"<2sIHHI",
|
|
b"BM",
|
|
file_size,
|
|
0, 0,
|
|
54 # Offset to pixel data
|
|
)
|
|
|
|
# BITMAPINFOHEADER (40 bytes)
|
|
info = struct.pack(
|
|
"<IIIHHIIIIII",
|
|
40, # biSize
|
|
width, # biWidth
|
|
height, # biHeight (positive = bottom-to-top)
|
|
1, # biPlanes
|
|
24, # biBitCount (24-bit BGR)
|
|
0, # biCompression (BI_RGB)
|
|
image_size, # biSizeImage
|
|
2835, # biXPelsPerMeter (72 DPI)
|
|
2835, # biYPelsPerMeter
|
|
0, # biClrUsed
|
|
0 # biClrImportant
|
|
)
|
|
|
|
data = bytearray()
|
|
pad = b"\x00" * padding
|
|
# BMP is stored bottom-to-top
|
|
for y in range(height - 1, -1, -1):
|
|
for x in range(width):
|
|
r, g, b = pixel_func(x, y, width, height)
|
|
data.extend(bytes([int(b) & 0xFF, int(g) & 0xFF, int(r) & 0xFF]))
|
|
data.extend(pad)
|
|
|
|
return header + info + bytes(data)
|
|
|
|
|
|
def create_png_rgba(width: int, height: int, pixel_func) -> bytes:
|
|
"""Generates a standard 32-bit RGBA PNG image."""
|
|
raw_scanlines = bytearray()
|
|
for y in range(height):
|
|
raw_scanlines.append(0) # Filter type 0 (None)
|
|
for x in range(width):
|
|
r, g, b, a = pixel_func(x, y, width, height)
|
|
raw_scanlines.extend(bytes([int(r) & 0xFF, int(g) & 0xFF, int(b) & 0xFF, int(a) & 0xFF]))
|
|
|
|
compressed = zlib.compress(bytes(raw_scanlines), level=9)
|
|
|
|
def chunk(tag: bytes, data: bytes) -> bytes:
|
|
chk = tag + data
|
|
crc = zlib.crc32(chk) & 0xFFFFFFFF
|
|
return struct.pack(">I", len(data)) + chk + struct.pack(">I", crc)
|
|
|
|
ihdr = struct.pack(">IIBBBBB", width, height, 8, 6, 0, 0, 0)
|
|
png_bytes = b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", ihdr) + chunk(b"IDAT", compressed) + chunk(b"IEND", b"")
|
|
return png_bytes
|
|
|
|
|
|
def draw_logo_rgba(x: int, y: int, w: int, h: int) -> tuple:
|
|
"""Renders a sleek modern hexagon shield and pulse log streams."""
|
|
nx = (x - w / 2) / (w / 2)
|
|
ny = (y - h / 2) / (h / 2)
|
|
dist = math.sqrt(nx * nx + ny * ny)
|
|
|
|
if dist > 0.98:
|
|
return 0, 0, 0, 0
|
|
|
|
# Hexagon radius formula
|
|
angle = math.atan2(ny, nx)
|
|
hex_radius = 0.88 / (math.cos((angle % (math.pi / 3)) - (math.pi / 6)) + 0.001)
|
|
|
|
if dist > hex_radius:
|
|
if dist < hex_radius + 0.04:
|
|
alpha = max(0.0, min(1.0, (hex_radius + 0.04 - dist) / 0.04))
|
|
return 11, 25, 44, int(alpha * 255)
|
|
return 0, 0, 0, 0
|
|
|
|
# Gradient background: Dark Slate Blue to Navy
|
|
grad_t = (ny + 1) / 2
|
|
bg_r = int(12 * (1 - grad_t) + 6 * grad_t)
|
|
bg_g = int(24 * (1 - grad_t) + 14 * grad_t)
|
|
bg_b = int(48 * (1 - grad_t) + 32 * grad_t)
|
|
|
|
# Hexagon outer border (Cyber Cyan / Emerald highlight)
|
|
if dist > hex_radius - 0.07:
|
|
glow_t = (nx + 1) / 2
|
|
br = int(0 * (1 - glow_t) + 0 * glow_t)
|
|
bg = int(229 * (1 - glow_t) + 160 * glow_t)
|
|
bb = int(255 * (1 - glow_t) + 255 * glow_t)
|
|
return br, bg, bb, 255
|
|
|
|
# Center stylized 'L' log symbol and dynamic telemetry bars
|
|
# Bar 1 (Left Log Stream): x in [-0.42, -0.22], y in [-0.45, 0.45]
|
|
if -0.42 <= nx <= -0.22 and -0.45 <= ny <= 0.45:
|
|
v = (ny + 0.45) / 0.9
|
|
return int(0), int(220 * (1 - v) + 140 * v), int(255), 255
|
|
|
|
# Bar 2 (Bottom Base of 'L'): x in [-0.42, 0.35], y in [0.25, 0.45]
|
|
if -0.42 <= nx <= 0.35 and 0.25 <= ny <= 0.45:
|
|
v = (nx + 0.42) / 0.77
|
|
return int(0), int(220 * (1 - v) + 180 * v), int(255), 255
|
|
|
|
# Bar 3 (Central Stream Wave 1): x in [-0.10, 0.08], y in [-0.30, 0.12]
|
|
if -0.10 <= nx <= 0.08 and -0.30 <= ny <= 0.12:
|
|
return 0, 240, 255, 255
|
|
|
|
# Bar 4 (Central Stream Wave 2): x in [0.18, 0.36], y in [-0.50, 0.12]
|
|
if 0.18 <= nx <= 0.36 and -0.50 <= ny <= 0.12:
|
|
return 0, 180, 255, 255
|
|
|
|
# Subtle inner grid dots
|
|
grid_x = (nx * 10) % 1
|
|
grid_y = (ny * 10) % 1
|
|
if 0.45 <= grid_x <= 0.55 and 0.45 <= grid_y <= 0.55:
|
|
return bg_r + 20, bg_g + 30, bg_b + 50, 255
|
|
|
|
return bg_r, bg_g, bg_b, 255
|
|
|
|
|
|
def draw_wizard_header_white_bgr(x: int, y: int, w: int, h: int) -> tuple:
|
|
"""
|
|
Renders header BMP graphic with a pure white background (255, 255, 255)
|
|
matching the installer wizard dialog background seamlessly.
|
|
The logo is positioned on the left with clean alpha-blended edges.
|
|
"""
|
|
bg_r, bg_g, bg_b = 255, 255, 255
|
|
|
|
# Position emblem cleanly on the left (center x = 28, center y = h / 2)
|
|
icon_center_x = 28
|
|
icon_center_y = h / 2
|
|
icon_radius = 22
|
|
dx = (x - icon_center_x) / icon_radius
|
|
dy = (y - icon_center_y) / icon_radius
|
|
d = math.sqrt(dx * dx + dy * dy)
|
|
|
|
if d < 1.0:
|
|
er, eg, eb, ea = draw_logo_rgba(int((dx + 1) * 32), int((dy + 1) * 32), 64, 64)
|
|
alpha = ea / 255.0
|
|
r = int(er * alpha + bg_r * (1.0 - alpha))
|
|
g = int(eg * alpha + bg_g * (1.0 - alpha))
|
|
b = int(eb * alpha + bg_b * (1.0 - alpha))
|
|
return r, g, b
|
|
|
|
return bg_r, bg_g, bg_b
|
|
|
|
|
|
def draw_wizard_small_bgr(x: int, y: int, w: int, h: int) -> tuple:
|
|
"""Renders 55x55 square header BMP with centered logo on pure white background."""
|
|
bg_r, bg_g, bg_b = 255, 255, 255
|
|
cx = w / 2
|
|
cy = h / 2
|
|
rad = (min(w, h) / 2) - 4
|
|
dx = (x - cx) / rad
|
|
dy = (y - cy) / rad
|
|
d = math.sqrt(dx * dx + dy * dy)
|
|
|
|
if d < 1.0:
|
|
er, eg, eb, ea = draw_logo_rgba(int((dx + 1) * 32), int((dy + 1) * 32), 64, 64)
|
|
alpha = ea / 255.0
|
|
r = int(er * alpha + bg_r * (1.0 - alpha))
|
|
g = int(eg * alpha + bg_g * (1.0 - alpha))
|
|
b = int(eb * alpha + bg_b * (1.0 - alpha))
|
|
return r, g, b
|
|
|
|
return bg_r, bg_g, bg_b
|
|
|
|
|
|
def draw_wizard_sidebar_bgr(x: int, y: int, w: int, h: int) -> tuple:
|
|
"""Renders 164x314 BMP sidebar graphic for NSIS / Inno Setup."""
|
|
t_y = y / h
|
|
|
|
# Deep navy to midnight blue vertical gradient
|
|
r = int(10 * (1 - t_y) + 4 * t_y)
|
|
g = int(22 * (1 - t_y) + 12 * t_y)
|
|
b = int(48 * (1 - t_y) + 26 * t_y)
|
|
|
|
# Subtle telemetry grid / stream lines
|
|
if int(x) % 18 == 0 or int(y) % 24 == 0:
|
|
r = min(255, r + 8)
|
|
g = min(255, g + 14)
|
|
b = min(255, b + 25)
|
|
|
|
# Decorative node glow in top region
|
|
emblem_cx = w / 2
|
|
emblem_cy = 90
|
|
edx = (x - emblem_cx) / 44
|
|
edy = (y - emblem_cy) / 44
|
|
ed = math.sqrt(edx * edx + edy * edy)
|
|
|
|
if ed < 0.98:
|
|
er, eg, eb, ea = draw_logo_rgba(int((edx + 1) * 64), int((edy + 1) * 64), 128, 128)
|
|
if ea > 128:
|
|
return er, eg, eb
|
|
|
|
# Glowing pulse lines streaming downwards from the emblem
|
|
if 55 <= x <= 110 and y > 150:
|
|
line_idx = int((x - 55) / 14)
|
|
line_offset = (line_idx * 37 + int(y * 0.4)) % 100
|
|
if line_offset < 30:
|
|
glow = math.sin((line_offset / 30) * math.pi)
|
|
g = min(255, int(g + 160 * glow))
|
|
b = min(255, int(b + 220 * glow))
|
|
|
|
# Bottom accent band
|
|
if y >= h - 4:
|
|
accent_t = x / w
|
|
return 0, int(220 * accent_t), 255
|
|
|
|
return r, g, b
|
|
|
|
|
|
def generate_ico_file(png_images: list) -> bytes:
|
|
"""Packs multiple PNG images into a valid Windows .ico container."""
|
|
count = len(png_images)
|
|
header = struct.pack("<HHH", 0, 1, count) # Reserved=0, Type=1 (ICO), Count
|
|
entries = bytearray()
|
|
offset = 6 + (16 * count)
|
|
|
|
image_data_blocks = []
|
|
for w, h, png_bytes in png_images:
|
|
size = len(png_bytes)
|
|
b_w = w if w < 256 else 0
|
|
b_h = h if h < 256 else 0
|
|
entry = struct.pack(
|
|
"<BBBBHHII",
|
|
b_w,
|
|
b_h,
|
|
0, # Color count
|
|
0, # Reserved
|
|
1, # Color planes
|
|
32, # Bits per pixel
|
|
size,
|
|
offset
|
|
)
|
|
entries.extend(entry)
|
|
image_data_blocks.append(png_bytes)
|
|
offset += size
|
|
|
|
return header + bytes(entries) + b"".join(image_data_blocks)
|
|
|
|
|
|
def generate_svg_logo(transparent_bg: bool = False) -> str:
|
|
"""Generates a high-quality SVG vector graphic for LOGAR."""
|
|
bg_polygon = "" if transparent_bg else """ <!-- Base Hexagon Shield -->
|
|
<polygon points="256,24 456,139 456,373 256,488 56,373 56,139"
|
|
fill="url(#bgGrad)" stroke="url(#cyanGrad)" stroke-width="8" filter="url(#glow)"/>"""
|
|
|
|
return f"""<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="100%" height="100%">
|
|
<defs>
|
|
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
<stop offset="0%" stop-color="#0E1A2E"/>
|
|
<stop offset="50%" stop-color="#08101E"/>
|
|
<stop offset="100%" stop-color="#040810"/>
|
|
</linearGradient>
|
|
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
<stop offset="0%" stop-color="#00E5FF"/>
|
|
<stop offset="100%" stop-color="#0072FF"/>
|
|
</linearGradient>
|
|
<linearGradient id="emeraldGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
<stop offset="0%" stop-color="#00FFA3"/>
|
|
<stop offset="100%" stop-color="#00E5FF"/>
|
|
</linearGradient>
|
|
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
|
|
<feGaussianBlur stdDeviation="12" result="blur"/>
|
|
<feMerge>
|
|
<feMergeNode in="blur"/>
|
|
<feMergeNode in="SourceGraphic"/>
|
|
</feMerge>
|
|
</filter>
|
|
</defs>
|
|
|
|
{bg_polygon}
|
|
|
|
<!-- Inner Telemetry Grid / Ring -->
|
|
<polygon points="256,58 426,156 426,356 256,454 86,356 86,156"
|
|
fill="none" stroke="#162A45" stroke-width="3" stroke-dasharray="8 6"/>
|
|
|
|
<!-- Stylized L and Log Streams -->
|
|
<rect x="140" y="140" width="46" height="232" rx="10" fill="url(#cyanGrad)" filter="url(#glow)"/>
|
|
<rect x="140" y="326" width="220" height="46" rx="10" fill="url(#cyanGrad)" filter="url(#glow)"/>
|
|
<rect x="220" y="210" width="36" height="80" rx="8" fill="url(#emeraldGrad)"/>
|
|
<rect x="286" y="150" width="36" height="140" rx="8" fill="url(#cyanGrad)"/>
|
|
|
|
<!-- Connection Nodes / Spark Points -->
|
|
<circle cx="238" cy="190" r="7" fill="#00FFA3" filter="url(#glow)"/>
|
|
<circle cx="304" cy="130" r="7" fill="#00E5FF" filter="url(#glow)"/>
|
|
<circle cx="380" cy="349" r="8" fill="#00E5FF" filter="url(#glow)"/>
|
|
</svg>"""
|
|
|
|
|
|
def generate_svg_banner() -> str:
|
|
"""Generates a wide SVG banner with emblem and modern typography."""
|
|
return """<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 240" width="100%" height="100%">
|
|
<defs>
|
|
<linearGradient id="bannerBg" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
<stop offset="0%" stop-color="#0E1A2E"/>
|
|
<stop offset="100%" stop-color="#070C18"/>
|
|
</linearGradient>
|
|
<linearGradient id="textCyan" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
<stop offset="0%" stop-color="#00E5FF"/>
|
|
<stop offset="100%" stop-color="#00FFA3"/>
|
|
</linearGradient>
|
|
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
<stop offset="0%" stop-color="#00E5FF"/>
|
|
<stop offset="100%" stop-color="#0072FF"/>
|
|
</linearGradient>
|
|
<linearGradient id="emeraldGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
<stop offset="0%" stop-color="#00FFA3"/>
|
|
<stop offset="100%" stop-color="#00E5FF"/>
|
|
</linearGradient>
|
|
<filter id="bannerGlow" x="-20%" y="-20%" width="140%" height="140%">
|
|
<feGaussianBlur stdDeviation="8" result="blur"/>
|
|
<feMerge>
|
|
<feMergeNode in="blur"/>
|
|
<feMergeNode in="SourceGraphic"/>
|
|
</feMerge>
|
|
</filter>
|
|
</defs>
|
|
|
|
<!-- Background Card -->
|
|
<rect width="960" height="240" rx="16" fill="url(#bannerBg)" stroke="#162A45" stroke-width="2"/>
|
|
|
|
<!-- Left Emblem Group (scaled & positioned) -->
|
|
<g transform="translate(40, 20) scale(0.39)">
|
|
<!-- Hexagon Shield -->
|
|
<polygon points="256,24 456,139 456,373 256,488 56,373 56,139"
|
|
fill="#0E1A2E" stroke="url(#cyanGrad)" stroke-width="12" filter="url(#bannerGlow)"/>
|
|
<!-- Inner Telemetry Ring -->
|
|
<polygon points="256,58 426,156 426,356 256,454 86,356 86,156"
|
|
fill="none" stroke="#162A45" stroke-width="4" stroke-dasharray="10 8"/>
|
|
<!-- Stylized L & Telemetry Bars -->
|
|
<rect x="140" y="140" width="46" height="232" rx="10" fill="url(#cyanGrad)"/>
|
|
<rect x="140" y="326" width="220" height="46" rx="10" fill="url(#cyanGrad)"/>
|
|
<rect x="220" y="210" width="36" height="80" rx="8" fill="url(#emeraldGrad)"/>
|
|
<rect x="286" y="150" width="36" height="140" rx="8" fill="url(#cyanGrad)"/>
|
|
<!-- Connection Points -->
|
|
<circle cx="238" cy="190" r="9" fill="#00FFA3"/>
|
|
<circle cx="304" cy="130" r="9" fill="#00E5FF"/>
|
|
<circle cx="380" cy="349" r="10" fill="#00E5FF"/>
|
|
</g>
|
|
|
|
<!-- Typography & Tagline -->
|
|
<text x="270" y="98" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif" font-size="56" font-weight="900" letter-spacing="4" fill="url(#textCyan)" filter="url(#bannerGlow)">LOGAR</text>
|
|
<text x="272" y="142" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif" font-size="20" font-weight="600" fill="#E2E8F0" letter-spacing="0.5">Fleet Log Aggregation & Telemetry Hub</text>
|
|
<text x="272" y="174" font-family="'JetBrains Mono', 'Fira Code', monospace" font-size="13" font-weight="400" fill="#00E5FF" letter-spacing="1">ZERO-STATE FORWARDERS • MUTUAL TLS 1.3 • TEMPORAL PERSISTENCE</text>
|
|
</svg>"""
|
|
|
|
|
|
def main():
|
|
print("[*] Generating comprehensive branding assets for LOGAR...")
|
|
|
|
# 1. Generate SVG Logos
|
|
svg_logo_dark = generate_svg_logo(transparent_bg=False)
|
|
svg_logo_trans = generate_svg_logo(transparent_bg=True)
|
|
svg_banner = generate_svg_banner()
|
|
|
|
# 2. Generate PNG images across multiple dimensions
|
|
png_sizes = [16, 32, 48, 64, 128, 256, 512]
|
|
png_buffers = []
|
|
png_dict = {}
|
|
for s in png_sizes:
|
|
png_data = create_png_rgba(s, s, draw_logo_rgba)
|
|
png_buffers.append((s, s, png_data))
|
|
png_dict[s] = png_data
|
|
|
|
# 3. Generate Windows ICO File (multi-res 16..256)
|
|
ico_data = generate_ico_file(png_buffers[:-1])
|
|
|
|
# 4. Generate BMP files for installers
|
|
header_bmp_data = create_bmp_24bit(150, 57, draw_wizard_header_white_bgr)
|
|
small_bmp_data = create_bmp_24bit(55, 55, draw_wizard_small_bgr)
|
|
sidebar_bmp_data = create_bmp_24bit(164, 314, draw_wizard_sidebar_bgr)
|
|
|
|
# 5. Generate Palette Swatch SVGs
|
|
swatches = {
|
|
"00e5ff": "#00E5FF",
|
|
"00ffa3": "#00FFA3",
|
|
"0072ff": "#0072FF",
|
|
"0e1a2e": "#0E1A2E",
|
|
"070c18": "#070C18",
|
|
"ef4444": "#EF4444",
|
|
"f59e0b": "#F59E0B",
|
|
"94a3b8": "#94A3B8",
|
|
"ffffff": "#FFFFFF",
|
|
}
|
|
swatch_svgs = {}
|
|
for name, hex_code in swatches.items():
|
|
border = "#334155" if hex_code.upper() != "#070C18" else "#1e293b"
|
|
swatch_svgs[name] = f'<svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="16" height="16" rx="4" fill="{hex_code}" stroke="{border}" stroke-width="1.5"/></svg>'
|
|
|
|
# 6. Write to target directories
|
|
for target_dir in (ROOT_ASSETS_DIR, COMPILATION_ASSETS_DIR, LICENSING_ASSETS_DIR):
|
|
swatch_dir = os.path.join(target_dir, "swatches")
|
|
os.makedirs(swatch_dir, exist_ok=True)
|
|
|
|
# Swatches
|
|
for name, svg_content in swatch_svgs.items():
|
|
with open(os.path.join(swatch_dir, f"swatch_{name}.svg"), "w", encoding="utf-8") as f:
|
|
f.write(svg_content)
|
|
|
|
# SVGs
|
|
with open(os.path.join(target_dir, "logo.svg"), "w", encoding="utf-8") as f:
|
|
f.write(svg_logo_dark)
|
|
with open(os.path.join(target_dir, "logo_dark.svg"), "w", encoding="utf-8") as f:
|
|
f.write(svg_logo_dark)
|
|
with open(os.path.join(target_dir, "logo_transparent.svg"), "w", encoding="utf-8") as f:
|
|
f.write(svg_logo_trans)
|
|
with open(os.path.join(target_dir, "logo_banner.svg"), "w", encoding="utf-8") as f:
|
|
f.write(svg_banner)
|
|
|
|
# PNGs
|
|
with open(os.path.join(target_dir, "logo.png"), "wb") as f:
|
|
f.write(png_dict[512])
|
|
for s in png_sizes:
|
|
with open(os.path.join(target_dir, f"logo_{s}x{s}.png"), "wb") as f:
|
|
f.write(png_dict[s])
|
|
|
|
# ICOs
|
|
with open(os.path.join(target_dir, "logo.ico"), "wb") as f:
|
|
f.write(ico_data)
|
|
with open(os.path.join(target_dir, "favicon.ico"), "wb") as f:
|
|
f.write(ico_data)
|
|
|
|
# BMPs
|
|
with open(os.path.join(target_dir, "wizard_header.bmp"), "wb") as f:
|
|
f.write(header_bmp_data)
|
|
with open(os.path.join(target_dir, "wizard_small.bmp"), "wb") as f:
|
|
f.write(small_bmp_data)
|
|
with open(os.path.join(target_dir, "wizard_sidebar.bmp"), "wb") as f:
|
|
f.write(sidebar_bmp_data)
|
|
|
|
print(f"[+] Populated asset repository: {target_dir}")
|
|
|
|
print("[+] All LOGAR branding assets, icons, and banners generated successfully!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|