update_gi.sh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #!/usr/bin/env bash
  2. # Disable undeclared variables, exit immediately when exit code != 0
  3. set -eu -o pipefail
  4. ############# Const
  5. UPDATE_URL="https://sdk-os-static.mihoyo.com/hk4e_global/mdk/launcher/api/resource?key=gcStgarh&launcher_id=10"
  6. #UPDATE_URL=http://localhost:8000/resource.json
  7. #UPDATE_URL=http://127.0.0.1/resource.json
  8. CONFIG=config.ini
  9. ANCHOR=UnityPlayer.dll
  10. LOG_FILE=_error.txt
  11. # Voice pack information
  12. declare -A LANG_MAP=(
  13. ["zh-cn"]="Chinese"
  14. ["en-us"]="English(US)"
  15. ["ja-jp"]="Japanese"
  16. ["ko-kr"]="Korean"
  17. )
  18. VOICE_PACK_PATH="GenshinImpact_Data/StreamingAssets/Audio/GeneratedSoundBanks/Windows"
  19. SCRIPT_PATH=$(
  20. realpath "$(dirname "$0")"
  21. )
  22. # Dedicated download directory for aria2c: it stores the index file in the same directory as target file
  23. DOWNLOAD_DIR=$PWD/../_update_gi_download
  24. DOWNLOAD_APPS=(aria2c wget curl)
  25. DL_curl_ARGS=(--disable)
  26. DL_aria2c_ARGS=(--no-conf -c)
  27. DL_wget_ARGS=(-c)
  28. ############# Functions
  29. fatal() {
  30. echo
  31. for arg in "$@"; do
  32. echo " * $arg" >&2
  33. done
  34. echo
  35. exit 1
  36. }
  37. download_file() {
  38. url=$1; md5=$2
  39. test -n "$url" -a -n "$md5" || fatal "argument missing: download_file <url> <md5>"
  40. echo "Downloading $url ..."
  41. archive_file="$DOWNLOAD_DIR/${url##*/}"
  42. archive_file_completed="${archive_file}.completed"
  43. if [ -f "$archive_file_completed" ]; then
  44. # patch was already downloaded but something went wrong when applying it
  45. echo "-- Found existing archive: ${download_url##*/} is already downloaded"
  46. else
  47. mkdir -p "$DOWNLOAD_DIR"
  48. cd "$DOWNLOAD_DIR"
  49. "$dl_app" "${dl_args[@]}" "$url"
  50. cd - >/dev/null # Return to previous dir
  51. touch "$archive_file_completed"
  52. fi
  53. echo "-- Verifying MD5 checksum..."
  54. md5sum -c <<< "$md5 $archive_file"
  55. }
  56. # Perform path sanity checks
  57. # There is a good reason for this check. Do not pollute the game directory.
  58. test \! -e "$SCRIPT_PATH/$ANCHOR" || fatal \
  59. "Please move this script outside the game directory prior executing." \
  60. " -> See README.md for proper installation instructions"
  61. # IDEA: Check whether this is a git clone and check for updates
  62. # some help
  63. if [ "${1:-}" == "-h" -o "${1:-}" == "help" ]; then
  64. cat << HELP
  65. $0 [-h|help] [install]
  66. This script will modify the current working directory.
  67. Specify "install" for a new installation, or nothing to perform an update.
  68. See README.md for details and examples.
  69. HELP
  70. exit 0
  71. fi
  72. # option to install
  73. if [ "${1:-}" == "install" ]; then
  74. if [ `ls | wc -l` -ne 0 ]; then
  75. fatal "The current directory contain some files/subdirectories." \
  76. "Please install to the empty directory." \
  77. "If you would like to update or continue the installation, remove the 'install' option:" \
  78. " bash '$0'"
  79. fi
  80. echo -e '[General]\r\nchannel=1\r\ncps=mihoyo\r\ngame_version=0.0.0\r\nsub_channel=0\r\n' > config.ini
  81. shift
  82. install_mode=y
  83. fi
  84. # hidden option to download pre-release version
  85. if [ "x${1:-}" == "x-pre" ]; then
  86. game_element="pre_download_game"
  87. end_element="deprecated_packages"
  88. pre_release=1
  89. else
  90. game_element="game"
  91. end_element="plugin"
  92. pre_release=0
  93. fi
  94. ################ Find a supported download executable
  95. dl_app=""
  96. for appname in "${DOWNLOAD_APPS[@]}"; do
  97. if command -v "$appname" &>/dev/null; then
  98. dl_app="$appname"
  99. declare -n dl_args="DL_${dl_app}_ARGS"
  100. break
  101. fi
  102. done
  103. test -n "$dl_app" || fatal \
  104. "No downloader application found." \
  105. "Please install 'aria2c' (recommended), 'wget' or 'curl'."
  106. # Check and read out the configuration file
  107. test -e "$CONFIG" || fatal \
  108. "Game information file $CONFIG not found." \
  109. "Make sure 'Genshin Impact Game' is the current working directory." \
  110. "If you would like to install the game append the 'install' option:" \
  111. " bash '$0' install"
  112. # In case people accidentally want to install the game into the launcher directory
  113. if [ $(ls -b1q *.dll 2>/dev/null | wc -l) -gt 1 ]; then
  114. fatal "This script is likely run in the wrong directory." \
  115. "Found more than one DLL file. (expected: 1 or 0)" \
  116. "Please run this script in a proper/clean game directory."
  117. fi
  118. installed_version=$(sed -n 's/^game_version=\(.*\)/\1/p' "$CONFIG" | tr -d "\r\n")
  119. test -n "$installed_version" || fatal "Cannot read game_version from $CONFIG"
  120. echo "Installed version: $installed_version"
  121. ################ Download the version information JSON file
  122. # wget and curl can download to stdout, but aria2c can't.
  123. # so we just create temp dir for it and read any downloaded file whose name can differ too
  124. tmp_path=$(mktemp -d)
  125. trap "rm -rf '$tmp_path'" EXIT
  126. update_content_src=$(cd "$tmp_path" && "$dl_app" "${dl_args[@]}" "$UPDATE_URL" >"$LOG_FILE" 2>&1 && cat resource*) || fatal \
  127. "Failed to download version info. Check your internet connection." \
  128. "$LOG_FILE may contain some useful information."
  129. # trying to cut out the game info data. Tricky and not 100% realiable
  130. update_content=$(sed "s/^.*\"$game_element\":{//;s/,\"$end_element\":.*$//;s/{/&\n/g;s/}/\n&/g" <<< "$update_content_src")
  131. # find latest version: extract from JSON format
  132. latest_version_content=$(sed -n '/"latest":/,/^}/{/"version":/!d;s/,/\n/g;s/"//g;p}' <<< "$update_content")
  133. declare -A version_info
  134. while read -r keyvalue; do
  135. version_info[${keyvalue%%:*}]=${keyvalue#*:}
  136. done <<< "$latest_version_content"
  137. echo "Latest version: ${version_info[version]}"
  138. if [ "${version_info[version]}" = "$installed_version" ]; then
  139. echo
  140. echo "==> Client is up to date."
  141. if [[ "$update_content_src" == *'"pre_download_game":{'* ]]; then
  142. echo "NOTE: Pre-release client is available."
  143. fi
  144. exit 0
  145. fi
  146. # Patch script directory
  147. patcher_dir="${SCRIPT_PATH}/../${version_info[version]//./}"
  148. test -f "$patcher_dir/patch.sh" || fatal \
  149. "No suitable patch script found. Please check the bug tracker for details about the progress."
  150. # find suitable patch: extract version information
  151. diffs_content=$(sed -n '/"diffs":/,/^}]}]/{/"name":/!d;s/"//g;p}' <<< "$update_content")
  152. patch_strategy="full" # download full client
  153. patch_voicepacks=()
  154. while read -r diff; do
  155. # we're found the correct version, now reading voicepacks
  156. if [ "$patch_strategy" == "diff" ]; then
  157. # if line doesn't contain "language" it isn't the voicepack
  158. if [[ ! "$diff" =~ "language:" ]]; then
  159. break
  160. fi
  161. patch_voicepacks+=("$diff")
  162. continue
  163. fi
  164. diff_content=$(sed 's/,/\n/g' <<< "$diff")
  165. unset diff_info
  166. declare -A diff_info
  167. while read -r keyvalue; do
  168. diff_info[${keyvalue%%:*}]=${keyvalue#*:}
  169. done <<< "$diff_content"
  170. if [ "${diff_info[version]:-}" == "$installed_version" ]; then
  171. patch_strategy="diff"
  172. # continue reading voicepacks
  173. fi
  174. done <<< "$diffs_content"
  175. if [ "$patch_strategy" == "diff" ]; then
  176. download_url=${diff_info[path]}
  177. patch_size=${diff_info[size]}
  178. patch_md5=${diff_info[md5]}
  179. patch_type="incremental update"
  180. else
  181. download_url=${version_info[path]}
  182. patch_size=${version_info[size]}
  183. patch_md5=${version_info[md5]}
  184. patch_type="full client"
  185. patch_voicepacks=($(sed -n '/"latest":/,/"diffs":/{!d;/"language":/!d;s/"//g;p}' <<< "$update_content"))
  186. echo
  187. if [ "${install_mode:-}" != "y" ]; then
  188. echo "[WARNING] Cannot find any suitable upgrade to ${version_info[version]}. Will download full client!"
  189. else
  190. echo "Will download and install the full client version."
  191. fi
  192. fi
  193. ################ Add links to the download queue "download_files"
  194. echo
  195. patch_size_mib=$(($patch_size / (1024 * 1024 * 2)))
  196. echo "Main URL: $download_url"
  197. echo "Installed size: ${patch_size_mib} MiB (${patch_type})"
  198. # array of files to download "[url]=md5"
  199. unset download_files
  200. declare -A download_files
  201. # add main game file/patch
  202. download_files["$download_url"]="$patch_md5"
  203. # extract voicepacks versions
  204. for voicepack in "${patch_voicepacks[@]}"; do
  205. voicepack_content=$(sed 's/,/\n/g' <<< "$voicepack")
  206. unset voicepack_info
  207. declare -A voicepack_info
  208. while read -r keyvalue; do
  209. voicepack_info[${keyvalue%%:*}]=${keyvalue#*:}
  210. done <<< "$voicepack_content"
  211. # filter the installed languages ${voicepack_info[language]}
  212. # language:LANGUAGE_NAME,name:ARCHIVE_NAME,path:LINK_TO_ZIP,size:NUMBER,md5:MD5SUM_CAPS
  213. language=${voicepack_info[language]}
  214. voice_path=$VOICE_PACK_PATH/${LANG_MAP[$language]}
  215. if [ -d "$voice_path" ]; then
  216. download_files[${voicepack_info[path]}]=${voicepack_info[md5]}
  217. patch_size_mib=$((${voicepack_info[size]} / (1024 * 1024 * 2)))
  218. echo "$language voice pack URL: ${voicepack_info[path]}"
  219. echo "Installed size: ${patch_size_mib} MiB (${patch_type})"
  220. fi
  221. done
  222. echo
  223. if [ $pre_release -eq 1 ]; then
  224. echo "ATTENTION: You're going to install pre-release version."
  225. echo "The wine patch may not be yet available for this version."
  226. echo "Use on your own risk."
  227. echo
  228. fi
  229. # confirm update
  230. while :; do
  231. read -r -p "Start/continue update? [Y/n]: " input
  232. case "$input" in
  233. Y|y|'')
  234. break
  235. ;;
  236. n|N)
  237. exit 0
  238. ;;
  239. esac
  240. done
  241. echo "Download queue:"
  242. for url in "${!download_files[@]}"; do
  243. echo ">> $url"
  244. done
  245. echo
  246. ######## Start/continue downloading patch or full client and voice packs
  247. downloaded_files=()
  248. for url in "${!download_files[@]}"; do
  249. download_file "$url" "${download_files[$url]}"
  250. downloaded_files+=("$archive_file")
  251. done
  252. ######## Apply clean client update
  253. # Run 'patch_revert.sh' of the newest directory
  254. if [ -e launcher.bat ]; then
  255. echo
  256. echo "============== Reverting previous Wine patch ==============="
  257. bash "$patcher_dir/patch_revert.sh"
  258. echo "============================================================"
  259. echo
  260. fi
  261. # Unpack the game files and remove old ones according to deletefiles.txt
  262. echo "-- Updating game files...."
  263. for archive_file in "${downloaded_files[@]}"; do
  264. # Prevent deleting of the possibly necessary files in case of full update
  265. rm -f deletefiles.txt
  266. echo "Unpacking $archive_file..."
  267. unzip -o "$archive_file" >>"$LOG_FILE"
  268. if [ -f deletefiles.txt ]; then
  269. while read -r delme; do
  270. rm -f "$delme"
  271. done < deletefiles.txt
  272. rm deletefiles.txt
  273. fi
  274. done
  275. sed -i "s/game_version=$installed_version/game_version=${version_info[version]}/" "$CONFIG"
  276. # If we got this far, this was a successful update. Clean up old files.
  277. rm -r "$DOWNLOAD_DIR"
  278. rm -f "$LOG_FILE"
  279. echo "==> Update to version ${version_info[version]} completed"
  280. ######## Run wine compatibility patch script
  281. echo
  282. echo "================= Applying new Wine patch =================="
  283. bash "$patcher_dir/patch.sh"
  284. if [ -f "$patcher_dir/patch_anti_logincrash.sh" ]; then
  285. bash "$patcher_dir/patch_anti_logincrash.sh"
  286. fi
  287. echo "============================================================"
  288. echo "==> Update script completed successfully"
  289. exit 0