Skip to content

Instantly share code, notes, and snippets.

@segfault-bilibili
Last active July 8, 2023 02:09
Show Gist options
  • Save segfault-bilibili/d1cfaecd94ad9d5f5377471ee6406bc8 to your computer and use it in GitHub Desktop.
Save segfault-bilibili/d1cfaecd94ad9d5f5377471ee6406bc8 to your computer and use it in GitHub Desktop.
#!/bin/bash
# workaround audio bug since Magia Record 3.0.1
# author: segfault-bilibili
# English README
#
# Since Magia Record 3.0.1, there appears to be a bug affecting minor fraction of players.
#
# Such bug makes the game audio (including BGM, sound effects etc) sound strange:
# (1) the pitch sounds to be lower than normal;
# (2) the time sounds to be "stretched" longer/slower than normal.
#
# There's currently an experimental modification to workaround this bug.
# To distinguish from other EX versions without this experimental modification,
# APK files which contain this mod have "-audiofix" suffix in its file name, like:
# "magireco-3.0.2-EX-audiofix.apk".
#
# The root cause of this bug is still unclear. It should be some kind of sample rate mismatch.
#
# It's observed that the audio is "stretched" exactly 8.84% longer than it should be,
# which matches exactly with 48 / 44.1 = 108.84%;
# plus the currently observed fact that only environments with a 44.1kHz system audio output
# sample rate seem to have this problem;
# it's guessed that deceiving the game to make it think the system audio output sample rate
# was 48kHz (instead of actual 44.1kHz) might make this problem go away - and luckily, it does,
# at least in our limited tests.
#
# However, it's still confusing why such trick seems to work.
# 中文说明
#
# 自从魔法纪录3.0.1版开始,出现了一个影响少数玩家的bug。
#
# 这个bug会让音频(包括背景音乐、音效等等)听起来很奇怪:
# (1) 音调听起来比正常低;
# (2) 时间听起来也被拉长/变慢了。
#
# 目前有一个实验性的小修改来绕过这个bug。
# 为了与不带这个修改的其他EX版区分,进行过这种修改的APK文件名含有"-audiofix"后缀,比如:
# "magireco-3.0.2-EX-audiofix.apk"。
#
# 导致这个bug的根本原因还不太清楚。可能是某种采样率不匹配。
#
# 据观察,音频被拉长到正好108.84%,和 48 / 44.1 = 108.84% 吻合。
# 再加上目前观察到只有系统音频输出采样率是44.1kHz的环境才有这个问题;
# 就可以猜测,如果欺骗游戏、使其认为系统音频输出采样率是48kHz(而不是实际值44.1kHz)
# 就能让问题消失——实际上也确实消失了,至少在有限的测试里是这样。
#
# 然而,现在还并不清楚为什么这一招看上去能奏效。
# usage:
# apktool d --no-src --no-res magireco-3.0.2-EX.apk
# audiofix.sh --wdir magireco-3.0.2-EX
# apktool b magireco-3.0.2-EX
# zipalign -p -f -v 4 magireco-3.0.2-EX/dist/magireco-3.0.2-EX.apk magireco-3.0.2-EX-audiofix.apk
# apksigner sign --ks keystore.jks --ks-pass pass:12345678 magireco-3.0.2-EX-audiofix.apk
set -e
check_function() {
local filename=$1
local funcname=$2
local in_func_offset=$3
local data=$4
local read_len=$(( ${#data} / 2 ))
local foundline="$(readelf --wide --dyn-sym "${filename}" | grep -E -m1 "\s+${funcname}$")"
local offset=$(( "0x$(echo "${foundline}" | awk '{print $2}')" + in_func_offset ))
local size=$(( "$(echo "${foundline}" | awk '{print $3}')" ))
if (( in_func_offset < 0 )) || (( read_len <= 0 )) || (( in_func_offset + read_len > size )); then
echo "invalid in_func_offset or read_len" >&2
exit 1
fi
local func_hex="$(xxd -p -s "${offset}" -l "${read_len}" "${filename}")"
[[ "${data}" == "${func_hex}" ]]
local ret=$?
echo "check funcname=${funcname} offset=${offset} read_len=${read_len} data=${data} func_hex=${func_hex} ret=${ret}" >&2
return $ret
}
patch_function() {
local filename=$1
local funcname=$2
local in_func_offset=$3
local data=$4
local write_len=$(( ${#data} / 2 ))
local foundline="$(readelf --wide --dyn-sym "${filename}" | grep -E -m1 "\s+${funcname}$")"
local offset=$(( "0x$(echo "${foundline}" | awk '{print $2}')" + in_func_offset ))
local size=$(( "$(echo "${foundline}" | awk '{print $3}')" ))
if (( in_func_offset < 0 )) || (( write_len <= 0 )) || (( in_func_offset + write_len > size )); then
echo "invalid in_func_offset or write_len" >&2
exit 1
fi
echo "patch funcname=${funcname} offset=${offset} write_len=${write_len} data=${data}" >&2
echo "${data}" | xxd -p -s "${offset}" -l "${write_len}" -r - "${filename}"
local ret=$?
if (( ret != 0 )); then
exit $ret;
fi
}
patch_for_arch() {
local arch=$1
local check_offset=$2
local check_data=$3
local patch_offset=$4
local patch_data=$5
local func_to_patch="criNcv_GetHardwareSamplingRate_ANDROID"
local file_to_patch="./lib/${arch}/libmadomagi_native.so"
if [ -d "./lib/$arch" ]; then
if check_function "${file_to_patch}" "${func_to_patch}" "${check_offset}" "${check_data}"; then
patch_function "${file_to_patch}" "${func_to_patch}" "${patch_offset}" "${patch_data}"
check_function "${file_to_patch}" "${func_to_patch}" "${patch_offset}" "${patch_data}" || exit 1
else
echo "check failed"
exit 1;
fi
else
echo "skip arch ${arch}"
fi
}
wdir=""
while (($# > 0)); do
case "$1" in
--wdir)
if [ $# -gt 1 ] && [ -n "$2" ]; then
wdir=$2
fi
;;
esac
shift 1
done
if [ ! -n "$wdir" ]; then
echo "please specify --wdir work_dir"
exit 2
fi
cd "$wdir"
patch_for_arch arm64-v8a 8 "c0035fd6" 4 "00709752"
patch_for_arch armeabi-v7a 8 "1eff2fe1" 4 "800b0be3"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment