#!/usr/bin/env python3 """Enxuga a arvore do Super Tux Party para a web (fredygames.com). Uso: python3 -I slim.py Tudo aqui e feito sobre arquivos que o repositorio ja traz; nada e inventado: - tira o que nao serve no navegador ou nao pode ir: fontes do Blender (.blend, .blend1), fontes do GIMP (.xcf), o personagem Beastie (o diabinho do BSD e marca registrada), o tabuleiro de teste e a musica dele (51 MB), a captura de tela do repositorio; - reduz as texturas das arvores (assets/models/nature/Textures, CC0 do Quaternius, 2048 x 2048, 200 MB de PNG) para 512 x 512 com o Pillow (LANCZOS), e limita o resto na importacao (process/size_limit do proprio Godot, nos .import do repositorio): capturas de tela dos minijogos em 640 e as demais texturas em 512; - troca a sombra paraboloide dual da luz do minijogo Kernel Compiling por cubemap (o Compatibility so tem cubemap); - converte a musica de 20 MB do tabuleiro (WAV) em Ogg Vorbis e recodifica as outras musicas (acima de 300 KB) com qualidade 0,2 (o oggdec e o wav2ogg de Builds/pygame-music, variaveis STP_OGGDEC e STP_WAV2OGG; numero de serie do stream fixo, mesma entrada, mesmos bytes). O texto dos creditos (licenses/*.md) perde so as secoes do que saiu. """ import os import re import shutil import subprocess import sys from PIL import Image tree = sys.argv[1] os.chdir(tree) REMOVE_DIRS = [ 'plugins/characters/Beastie', 'plugins/boards/test', ] REMOVE_FILES = [ 'assets/music/boards/test board bgm.wav', 'screenshot.png', ] REMOVE_EXT = ('.blend', '.blend1', '.xcf') for d in REMOVE_DIRS: shutil.rmtree(d) for f in REMOVE_FILES: os.remove(f) os.remove(f + '.import') removed = 0 for dp, dn, fn in os.walk('.'): for f in fn: if f.endswith(REMOVE_EXT): os.remove(os.path.join(dp, f)) removed += 1 print('removidos %d arquivos de fonte' % removed) # .import de arquivos que nao existem mais (o repositorio guarda alguns orfaos) orphans = 0 for dp, dn, fn in os.walk('.'): for f in fn: if f.endswith('.import') and not os.path.exists(os.path.join(dp, f[:-7])): os.remove(os.path.join(dp, f)) orphans += 1 print('removidos %d .import orfaos' % orphans) def set_param(text, key, value): pat = re.compile(r'^%s=.*$' % re.escape(key), re.M) if not pat.search(text): raise SystemExit('parametro ausente: ' + key) return pat.sub('%s=%s' % (key, value), text, count=1) for dp, dn, fn in sorted(os.walk('.')): for f in sorted(fn): if not re.search(r'\.(png|jpg|jpeg)\.import$', f): continue p = os.path.join(dp, f)[2:] limit = 512 if p.startswith('assets/models/nature/Textures/'): src = p[:-7] im = Image.open(src) im.load() im = im.resize((512, 512), Image.LANCZOS) im.save(src, optimize=True) limit = 512 elif re.match(r'plugins/minigames/[^/]+/screenshot\.png\.import$', p): limit = 640 t = open(p).read() open(p, 'w').write(set_param(t, 'process/size_limit', limit)) # A musica de 20 MB do tabuleiro (WAV) vira Ogg Vorbis (qualidade 0,2); a cena do tabuleiro aponta para o novo arquivo. oggdec, wav2ogg = os.environ['STP_OGGDEC'], os.environ['STP_WAV2OGG'] subprocess.run([wav2ogg, 'assets/music/boards/kdevalley.wav', 'assets/music/boards/kdevalley.ogg', '0.2'], check=True) os.remove('assets/music/boards/kdevalley.wav') os.remove('assets/music/boards/kdevalley.wav.import') board = 'plugins/boards/KDEValley/board.tscn' t = open(board).read() old = 'uid="uid://dv2o7xmoruojj" path="res://assets/music/boards/kdevalley.wav"' assert old in t open(board, 'w').write(t.replace(old, 'path="res://assets/music/boards/kdevalley.ogg"')) # O Compatibility nao tem sombra "dual paraboloid" (erro a cada quadro no console): a luz da sala do Kernel Compiling usa cubemap. kc = 'plugins/minigames/kernel_compiling/minigame.tscn' t = open(kc).read() assert 'omni_shadow_mode = 0' in t open(kc, 'w').write(t.replace('omni_shadow_mode = 0', 'omni_shadow_mode = 1')) # creditos: tira as secoes do que saiu for md in ('licenses/LICENSE-ART.md', 'licenses/LICENSE-MUSIC.md'): t = open(md, encoding='utf8').read() parts = re.split(r'(?m)^(?=## )', t) out = [] for s in parts: if re.match(r'## (plugins/characters/Beastie|plugins/boards/test)\b', s): continue if s.startswith('## assets/music/boards'): # a secao tem duas musicas; so a do tabuleiro de teste sai s = re.sub(r'(?s)### test board bgm\.wav.*?(?=### )', '', s) out.append(s) open(md, 'w', encoding='utf8').write(''.join(out)) # musicas: Ogg Vorbis recodificado em qualidade 0,2 (arquivo que nao decodifica em estereo fica como esta) before = after = 0 for dp, dn, fn in sorted(os.walk('.')): for f in sorted(fn): p = os.path.join(dp, f) if not f.endswith('.ogg') or os.path.getsize(p) < 300000: continue size = os.path.getsize(p) tmpwav, tmpogg = p + '.tmp.wav', p + '.tmp.ogg' ok = subprocess.run([oggdec, p, tmpwav], capture_output=True).returncode == 0 and \ subprocess.run([wav2ogg, tmpwav, tmpogg, '0.2'], capture_output=True).returncode == 0 if ok and os.path.getsize(tmpogg) < size: os.replace(tmpogg, p) print('ogg %s: %d -> %d' % (p, size, os.path.getsize(p))) else: print('ogg %s: mantido (%d)' % (p, size)) if os.path.exists(tmpogg): os.remove(tmpogg) os.remove(tmpwav) if os.path.exists(tmpwav) else None before += size after += os.path.getsize(p) print('musica: %d -> %d' % (before, after)) print('ok')