Skip to content

Instantly share code, notes, and snippets.

@toolness
Last active October 9, 2021 16:31
Show Gist options
  • Save toolness/a620ad1eec97ce69553a6ab83d1fca59 to your computer and use it in GitHub Desktop.
Save toolness/a620ad1eec97ce69553a6ab83d1fca59 to your computer and use it in GitHub Desktop.
A script to download id's original Quake maps and make them easy to load in TrenchBroom.
# This Python script downloads the map sources for the
# original id Quake levels and does a few things to make
# them easier to load in TrenchBroom:
#
# * It adds comments to the top of each map to let
# TrenchBroom know that the map is a Quake map, and
# that it uses the Standard map format. (Without
# these comments, TrenchBroom will ask the user, and
# choosing the default response will raise a very
# confusing error.)
#
# * It rewrites the WAD file to reference "QUAKE101.WAD".
# This WAD file is automatically downloaded from
# Quaddicted and placed in the same directory as the map
# files, and contains all the textures used by Quake.
# This ensures that the map doesn't load with blank
# textures.
#
# These original Quake levels are an excellent learning
# resource. For more details on how to use them, see
# dumptruck_ds's "Mapping for Quake: TrenchBroom 2.0 -
# The id Maps" tutorial:
#
# https://youtu.be/sg0iKjYsoBg
import re
from pathlib import Path
from urllib.parse import urlparse
import zipfile
import http.client
MY_DIR = Path(__file__).parent.resolve()
MAP_DIR = MY_DIR / "quake_map_source"
QUAKE_MAP_SOURCE_ZIP = MY_DIR / "quake_map_source.zip"
QUAKE_WAD_ZIP = MY_DIR / "quakewad.zip"
MAP_PREPEND_LINES = [
"// Game: Quake",
"// Format: Standard",
]
def extract_zip(zipfile_path):
print(f"Extracting {zipfile_path} to {MAP_DIR}.")
with zipfile.ZipFile(zipfile_path) as zf:
zf.extractall(MAP_DIR)
def download(url, outfile):
if outfile.exists():
return
print(f"Downloading {url} to {outfile}...")
parsed = urlparse(url)
assert parsed.scheme == "https"
conn = http.client.HTTPSConnection(parsed.netloc)
conn.request("GET", parsed.path, headers={
"Host": parsed.netloc,
"User-Agent": "Mozilla/5.0 (compatible; download_quake_map_source.py/0.1; +https://gist.github.com/toolness/a620ad1eec97ce69553a6ab83d1fca59)",
})
res = conn.getresponse()
data = res.read()
if res.status == 302:
print("Redirect found, following it...")
return download(res.headers["Location"], outfile)
if res.status != 200:
raise Exception(f"Got HTTP {res.status}")
outfile.write_bytes(data)
download(
# https://rome.ro/news/2016/2/14/quake-map-sources-released
"https://rome.ro/s/quake_map_source.zip",
QUAKE_MAP_SOURCE_ZIP,
)
download(
"https://www.quaddicted.com/files/wads/quakewad.zip",
QUAKE_WAD_ZIP,
)
MAP_DIR.mkdir(exist_ok=True)
extract_zip(QUAKE_MAP_SOURCE_ZIP)
extract_zip(QUAKE_WAD_ZIP)
for mapfile in MAP_DIR.glob('*.map'):
print(f"Rewriting {mapfile} for easy opening in TrenchBroom...")
map_content = mapfile.read_text()
map_content = '\n'.join([*MAP_PREPEND_LINES, map_content])
wad_replaced = re.sub(
r'^\"wad\"\s\".+\"$',
'"wad"\t"QUAKE101.WAD"',
map_content,
count=1,
flags=re.MULTILINE
)
if wad_replaced == map_content:
raise AssertionError(f"Unable to find 'wad' property!")
mapfile.write_text(wad_replaced)
print("Done.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment