Skip to content

Instantly share code, notes, and snippets.

@goncalomb
Created December 29, 2022 20:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save goncalomb/fc0048e14a6cba63946996719d665e88 to your computer and use it in GitHub Desktop.
Save goncalomb/fc0048e14a6cba63946996719d665e88 to your computer and use it in GitHub Desktop.
Extract music/audio from 'Bully: Scholarship Edition' on PC.
#!/bin/bash
# Copyright (c) 2022 Gonçalo Baltazar <me@goncalomb.com>
# MIT License
# Extract music/audio from 'Bully: Scholarship Edition' on PC.
# This script expects the game to be installed on Linux using Steam.
# Use Proton to install Bully on Steam, then run the script:
# - Open the game properties
# - Select "COMPATIBILITY"
# - Select "Force the use of a specific Steam Play compatibility tool"
# - Install the game and wait for the download to complete
# - Run the script
# - Enjoy the awesome soundtrack
# Creates 2 directories 'aluigi' (extraction tools) and 'bully' (extracted music).
set -euo pipefail
cd -- "$(dirname -- "$0")"
# set to 1 for verbose extraction
DEBUG=
# set to 1 to extract all audio files, not just music
EXTRACT_ALL=
# bully files location
BULLY_FILES="$HOME/.steam/debian-installation/steamapps/common/Bully Scholarship Edition"
if [ ! -d "$BULLY_FILES" ]; then
echo "'Bully: Scholarship Edition' installation not found"
echo "expected game files at '$BULLY_FILES'"
exit 1
fi
show_debug() {
if [ -n "$DEBUG" ]; then cat
else cat >/dev/null; fi
}
echo "downloading and compiling dependencies..."
# download quickbms and unxwb
(
mkdir -p aluigi
cd aluigi
# https://aluigi.altervista.org/quickbms.htm
[ -f quickbms_linux.zip ] || curl -ROL https://aluigi.altervista.org/papers/quickbms_linux.zip
[ -d quickbms_linux ] || unzip -d quickbms_linux quickbms_linux.zip
chmod +x quickbms_linux/quickbms
# https://aluigi.altervista.org/papers.htm
[ -f unxwb.zip ] || curl -ROL https://aluigi.altervista.org/papers/unxwb.zip
[ -d unxwb ] || unzip -d unxwb unxwb.zip
)
# compile unxwb
(
cd aluigi/unxwb
# http://blog.ssokolow.com/archives/2014/04/21/extracting-music-from-xwb-files-on-linux/
# https://gist.github.com/ssokolow/11149295
[ -f Makefile ] || curl -ROL https://gist.githubusercontent.com/ssokolow/11149295/raw/26e01891905917a43b90bf693356ff5473f9ec54/Makefile
[ -f unxwb ] || make
)
mkdir -p bully/tmp
cd bully
# https://forum.xentax.com/viewtopic.php?t=25243
# https://forum.xentax.com/viewtopic.php?p=41076#p41076
[ -f tmp/bully_bin.bms ] || cat <<EOF >tmp/bully_bin.bms
open FDDE bin 0
open FDDE lst 1
idstring Hash
get FILES long
for i = 0 < FILES
get FILEHASH long
get OFFSET long
get SIZE long
getct NAME string 0x0D 1
get TXT byte 1
set XSBN string NAME
string XSBN -= 4
string XSBN += .xsb
set XWBN string NAME
string XWBN -= 4
string XWBN += .xwb
set XWBOFF long OFFSET
math XWBOFF += 0x800
set XWBSIZE long SIZE
math XWBSIZE -= 0x800
log XSBN OFFSET 0x800
log XWBN XWBOFF XWBSIZE
next i
EOF
echo "copying osd..."
rsync -a --info=progress2 "$BULLY_FILES/Bully Original Soundtrack" .
extract_audio() {(
cd tmp
[ -f "$1.bin" ] || rsync -a --info=progress2 "$BULLY_FILES/audio/PLAYLIST/$1.bin" .
[ -f "$1.lst" ] || rsync -a --info=progress2 "$BULLY_FILES/audio/PLAYLIST/$1.lst" .
[ -d "$1" ] || (
mkdir -p "$1"
../../aluigi/quickbms_linux/quickbms bully_bin.bms "$1.bin" "$1"
) 2>&1 | show_debug
[ -d "$1_wav" ] || (
mkdir -p "$1_wav"
cd "$1"
find -maxdepth 1 -type f -name "*.xwb" -printf "%f\n" | while IFS= read -r F; do
../../../aluigi/unxwb/unxwb -d "../$1_wav" -b "${F%.*}.xsb" "$F" || true
[ ! -f "../$1_wav/00000000.wav" ] || mv "../$1_wav/00000000.wav" "../$1_wav/${F%.*}.wav"
# TODO: some files are extracted with weird names like 'K,.wav'
# these get overwritten if another file has the same name
# TODO: fix file names (are the .xwb/.xsb files bad?)
done
) 2>&1 | show_debug
# XXX: something is not totally right the extraction, many files show
# the error 'the file contains unexpected data' while extracting
# but still appear to produce a valid .wav file
)}
echo "extracting audio..."
[ -z "$EXTRACT_ALL" ] || extract_audio Ambs
[ -z "$EXTRACT_ALL" ] || extract_audio Cuts
extract_audio Music
[ -z "$EXTRACT_ALL" ] || extract_audio Speech
echo "collecting extra music..."
mkdir -p "Bully Extra Music"
find ./tmp/Music_wav -type f -name "*.wav" -size +1M -print0 | xargs -0 cp -at "Bully Extra Music"
echo "'extra music' are just '.wav files larger than 1M'"
echo "for all extracted files see 'tmp' directory"
echo "done"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment