Created
December 29, 2022 20:52
-
-
Save goncalomb/fc0048e14a6cba63946996719d665e88 to your computer and use it in GitHub Desktop.
Extract music/audio from 'Bully: Scholarship Edition' on PC.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Copyright (c) 2022 Gonçalo Baltazar <[email protected]> | |
# 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