Skip to content

Instantly share code, notes, and snippets.

@connorads
Last active March 3, 2026 16:05
Show Gist options
  • Select an option

  • Save connorads/38488e1dde78f84cf73317f3369562a3 to your computer and use it in GitHub Desktop.

Select an option

Save connorads/38488e1dde78f84cf73317f3369562a3 to your computer and use it in GitHub Desktop.
Download StarCraft 1 unit 'wht' (first-selected) voice lines from The Sounds Resource
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "httpx",
# ]
# ///
"""Download StarCraft: Brood War unit 'wht' (What) sounds from The Sounds Resource.
Covers all units from both the original StarCraft and the Brood War expansion.
'wht' files are the voice lines played when a unit is first selected:
Marine: "You want a piece of me, boy?"
Firebat: "Need a light?"
Ghost: "Somebody call for an exterminator?"
Zealot: "My life for Aiur!"
Medic: "Where does it hurt?" (Brood War)
Lurker: *growl* (Brood War)
Downloads ZIPs for each unit, extracts only *wht*.wav files,
and organises them into folders by unit name.
Source: https://sounds.spriters-resource.com/pc_computer/starcraft/
Usage:
uv run download_wht.py [--output-dir DIR]
"""
import argparse
import io
import time
import zipfile
from pathlib import Path
import httpx
BASE_URL = "https://sounds.spriters-resource.com"
ZIP_PATH = "/media/assets/416/{asset_id}.zip"
# {name: asset_id} — units only (no buildings, advisors, music, misc)
# Includes both original StarCraft and Brood War expansion units.
# Brood War additions marked with (BW).
UNITS: dict[str, int] = {
# --- Protoss ---
"arbiter": 419545,
"archon": 419547,
"carrier": 419550,
"corsair": 419551, # (BW)
"dark-archon": 419553, # (BW)
"dark-templar": 419554, # (BW)
"dragoon": 419555,
"high-templar": 419565,
"interceptor": 419559,
"probe": 419560,
"reaver": 419567,
"scout": 419561,
"shuttle": 419562,
"zealot": 419570,
# --- Protoss heroes ---
"artanis": 419548, # (BW)
"fenix-dragoon": 419556,
"fenix-zealot": 419557,
"gantrithor": 419558,
"tassadar": 419564,
"witness-iv": 419569,
"zeratul": 419572,
# --- Terran ---
"battlecruiser": 419575,
"civilian": 419577,
"dropship": 419578,
"firebat": 419582,
"ghost": 419584,
"goliath": 419585,
"marine": 419587,
"medic": 419588, # (BW)
"science-vessel": 419594,
"scv": 419592,
"siege-tank": 419593,
"valkyrie": 419583, # (BW) listed as "Frigate" on source site
"vulture": 419595,
"wraith": 419589,
# --- Terran heroes ---
"edmund-duke-battlecruiser": 419579,
"edmund-duke-siege-tank": 419580,
"jim-raynor-marine": 419590,
"jim-raynor-vulture": 419591,
"samir-duran": 419581, # (BW)
"sarah-kerrigan": 419586,
# --- Zerg ---
"broodling": 419599,
"defiler": 419601,
"devourer": 419602, # (BW)
"guardian": 419604,
"hydralisk": 419605,
"infested-terran": 419600,
"lurker": 419607, # (BW)
"mutalisk": 419608,
"overlord": 419609,
"queen": 419610,
"scourge": 419597,
"ultralisk": 419611,
"zergling": 419614,
# --- Zerg heroes ---
"samir-duran-infested": 419612, # (BW)
"sarah-kerrigan-infested": 419613,
}
# Units unlikely to have voice lines (non-speaking units / creatures)
SKIP_NO_VOICE = {
"interceptor",
"broodling",
"egg",
"larva",
"scourge",
}
def download_wht_sounds(output_dir: Path, *, dry_run: bool = False) -> None:
output_dir.mkdir(parents=True, exist_ok=True)
headers = {
"User-Agent": "Mozilla/5.0 (compatible; SC1SoundDL/1.0)",
"Referer": f"{BASE_URL}/pc_computer/starcraft/",
}
total_files = 0
units_with_sounds = 0
units_without = []
with httpx.Client(
base_url=BASE_URL,
headers=headers,
follow_redirects=True,
timeout=30.0,
) as client:
for name, asset_id in sorted(UNITS.items()):
if name in SKIP_NO_VOICE:
print(f" skip {name} (non-speaking)")
continue
url = ZIP_PATH.format(asset_id=asset_id)
print(f" fetch {name} ({asset_id})...", end=" ", flush=True)
if dry_run:
print("(dry run)")
continue
try:
resp = client.get(url)
resp.raise_for_status()
except httpx.HTTPError as e:
print(f"FAILED: {e}")
continue
with zipfile.ZipFile(io.BytesIO(resp.content)) as zf:
wht_files = [
f for f in zf.namelist() if "wht" in f.lower() and f.endswith(".wav")
]
if not wht_files:
print("no wht files")
units_without.append(name)
continue
unit_dir = output_dir / name
unit_dir.mkdir(exist_ok=True)
for fname in sorted(wht_files):
# Flatten nested paths — just use the filename
out_path = unit_dir / Path(fname).name
out_path.write_bytes(zf.read(fname))
count = len(wht_files)
total_files += count
units_with_sounds += 1
print(f"{count} wht files")
# Be polite
time.sleep(0.5)
print()
print(f"Done: {total_files} files from {units_with_sounds} units -> {output_dir}")
if units_without:
print(f"No wht files found for: {', '.join(units_without)}")
def main() -> None:
parser = argparse.ArgumentParser(description="Download StarCraft 1 unit 'wht' sounds")
parser.add_argument(
"--output-dir",
"-o",
type=Path,
default=Path("sc1-wht-sounds"),
help="Output directory (default: sc1-wht-sounds)",
)
parser.add_argument(
"--dry-run",
"-n",
action="store_true",
help="Show what would be downloaded without downloading",
)
args = parser.parse_args()
print(f"Downloading StarCraft unit 'wht' sounds to {args.output_dir}/")
print()
download_wht_sounds(args.output_dir, dry_run=args.dry_run)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment