update_gi.sh 9.2 KB

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