Last active
July 8, 2023 02:09
-
-
Save segfault-bilibili/d1cfaecd94ad9d5f5377471ee6406bc8 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 | |
# 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