update_gi.sh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  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. # some help
  58. if [ "${1:-}" == "-h" -o "${1:-}" == "help" ]; then
  59. cat << HELP
  60. $0 [-h|help] [install]
  61. This script will modify the current working directory.
  62. Specify "install" for a new installation, or nothing to perform an update.
  63. See README.md for details and examples.
  64. HELP
  65. exit 0
  66. fi
  67. # option to install
  68. if [ "${1:-}" == "install" ]; then
  69. if [ `ls | wc -l` -ne 0 ]; then
  70. fatal "The current directory contain some files/subdirectories." \
  71. "Please install to the empty directory." \
  72. "If you would like to update or continue the installation, remove the 'install' option:" \
  73. " bash '$0'"
  74. fi
  75. echo -e '[General]\r\nchannel=1\r\ncps=mihoyo\r\ngame_version=0.0.0\r\nsub_channel=0\r\n' > config.ini
  76. shift
  77. install_mode=y
  78. fi
  79. # hidden option to download pre-release version
  80. if [ "x${1:-}" == "x-pre" ]; then
  81. game_element="pre_download_game"
  82. end_element="deprecated_packages"
  83. pre_release=1
  84. else
  85. game_element="game"
  86. end_element="plugin"
  87. pre_release=0
  88. fi
  89. ################ Find a supported download executable
  90. dl_app=""
  91. for appname in ${DOWNLOAD_APPS[@]}; do
  92. dl_app_try=${appname%%|*}
  93. if command -v $dl_app_try &>/dev/null; then
  94. dl_app=${appname#*|}
  95. break
  96. fi
  97. done
  98. test -n "$dl_app" || fatal \
  99. "No downloader application found." \
  100. "Please install 'aria2c' (recommended), 'wget' or 'curl'."
  101. # Check and read out the configuration file
  102. test -e "$CONFIG" || fatal \
  103. "Game information file $CONFIG not found." \
  104. "Make sure 'Genshin Impact Game' is the current working directory." \
  105. "If you would like to install the game append the 'install' option:" \
  106. " bash '$0' install"
  107. # In case people accidentally want to install the game into the launcher directory
  108. if [ $(ls -b1q *.dll 2>/dev/null | wc -l) -gt 1 ]; then
  109. fatal "This script is likely run in the wrong directory." \
  110. "Found more than one DLL file. (expected: 1 or 0)" \
  111. "Please run this script in a proper/clean game directory."
  112. fi
  113. installed_version=`sed -n 's/^game_version=\(.*\)/\1/p' "$CONFIG" | tr -d "\r\n"`
  114. test -n "$installed_version" || fatal "Cannot read game_version from $CONFIG"
  115. echo "Installed version: $installed_version"
  116. ################ Download the version information JSON file
  117. # wget and curl can download to stdout, but aria2c can't.
  118. # so we just create temp dir for it and read any downloaded file whose name can differ too
  119. tmp_path=$(mktemp -d)
  120. trap "rm -rf '$tmp_path'" EXIT
  121. update_content_src=$(cd "$tmp_path" && "$dl_app" "$UPDATE_URL" >"$LOG_FILE" 2>&1 && cat resource*) || fatal \
  122. "Failed to download version info. Check your internet connection." \
  123. "$LOG_FILE may contain some useful information."
  124. # trying to cut out the game info data. Tricky and not 100% realiable
  125. update_content=$(sed "s/^.*\"$game_element\":{//;s/,\"$end_element\":.*$//;s/{/&\n/g;s/}/\n&/g" <<< "$update_content_src")
  126. # find latest version: extract from JSON format
  127. latest_version_content=$(sed -n '/"latest":/,/^}/{/"version":/!d;s/,/\n/g;s/"//g;p}' <<< "$update_content")
  128. declare -A version_info
  129. while read -r keyvalue; do
  130. version_info[${keyvalue%%:*}]=${keyvalue#*:}
  131. done <<< "$latest_version_content"
  132. echo "Latest version: ${version_info[version]}"
  133. if [ "${version_info[version]}" = "$installed_version" ]; then
  134. echo
  135. echo "==> Client is up to date."
  136. if [[ "$update_content_src" == *'"pre_download_game":{'* ]]; then
  137. echo "NOTE: Pre-release client is available."
  138. fi
  139. exit 0
  140. fi
  141. # Patch script directory
  142. patcher_dir="${SCRIPT_PATH}/../${version_info[version]//./}"
  143. test -f "$patcher_dir/patch.sh" || fatal \
  144. "No suitable patch script found. Please check the bug tracker for details about the progress."
  145. # find suitable patch: extract version information
  146. diffs_content=$(sed -n '/"diffs":/,/^}]}]/{/"name":/!d;s/"//g;p}' <<< "$update_content")
  147. patch_strategy="full" # download full client
  148. patch_voicepacks=()
  149. while read -r diff; do
  150. # we're found the correct version, now reading voicepacks
  151. if [ "$patch_strategy" == "diff" ]; then
  152. # if line doesn't contain "language" it isn't the voicepack
  153. if [[ ! "$diff" =~ "language:" ]]; then
  154. break
  155. fi
  156. patch_voicepacks+=("$diff")
  157. continue
  158. fi
  159. diff_content=$(sed 's/,/\n/g' <<< "$diff")
  160. unset diff_info
  161. declare -A diff_info
  162. while read -r keyvalue; do
  163. diff_info[${keyvalue%%:*}]=${keyvalue#*:}
  164. done <<< "$diff_content"
  165. if [ "${diff_info[version]:-}" == "$installed_version" ]; then
  166. patch_strategy="diff"
  167. # continue reading voicepacks
  168. fi
  169. done <<< "$diffs_content"
  170. if [ "$patch_strategy" == "diff" ]; then
  171. download_url=${diff_info[path]}
  172. patch_size=${diff_info[size]}
  173. patch_md5=${diff_info[md5]}
  174. patch_type="incremental update"
  175. else
  176. download_url=${version_info[path]}
  177. patch_size=${version_info[size]}
  178. patch_md5=${version_info[md5]}
  179. patch_type="full client"
  180. patch_voicepacks=($(sed -n '/"latest":/,/"diffs":/{!d;/"language":/!d;s/"//g;p}' <<< "$update_content"))
  181. echo
  182. if [ "${install_mode:-}" != "y" ]; then
  183. echo "[WARNING] Cannot find any suitable upgrade to ${version_info[version]}. Will download full client!"
  184. else
  185. echo "Will download and install the full client version."
  186. fi
  187. fi
  188. ################ Add links to the download queue "download_files"
  189. echo
  190. patch_size_mib=$(($patch_size / (1024 * 1024 * 2)))
  191. echo "Main URL: $download_url"
  192. echo "Installed size: ${patch_size_mib} MiB (${patch_type})"
  193. # array of files to download "[url]=md5"
  194. unset download_files
  195. declare -A download_files
  196. # add main game file/patch
  197. download_files["$download_url"]="$patch_md5"
  198. # extract voicepacks versions
  199. for voicepack in "${patch_voicepacks[@]}"; do
  200. voicepack_content=$(sed 's/,/\n/g' <<< "$voicepack")
  201. unset voicepack_info
  202. declare -A voicepack_info
  203. while read -r keyvalue; do
  204. voicepack_info[${keyvalue%%:*}]=${keyvalue#*:}
  205. done <<< "$voicepack_content"
  206. # filter the installed languages ${voicepack_info[language]}
  207. # language:LANGUAGE_NAME,name:ARCHIVE_NAME,path:LINK_TO_ZIP,size:NUMBER,md5:MD5SUM_CAPS
  208. language=${voicepack_info[language]}
  209. voice_path=$VOICE_PACK_PATH/${LANG_MAP[$language]}
  210. if [ -d "$voice_path" ]; then
  211. download_files[${voicepack_info[path]}]=${voicepack_info[md5]}
  212. patch_size_mib=$((${voicepack_info[size]} / (1024 * 1024 * 2)))
  213. echo "$language voice pack URL: ${voicepack_info[path]}"
  214. echo "Installed size: ${patch_size_mib} MiB (${patch_type})"
  215. fi
  216. done
  217. echo
  218. if [ $pre_release -eq 1 ]; then
  219. echo "ATTENTION: You're going to install pre-release version."
  220. echo "The wine patch may not be yet available for this version."
  221. echo "Use on your own risk."
  222. echo
  223. fi
  224. # confirm update
  225. while :; do
  226. read -r -p "Start/continue update? [Y/n]: " input
  227. case "$input" in
  228. Y|y|'')
  229. break
  230. ;;
  231. n|N)
  232. exit 0
  233. ;;
  234. esac
  235. done
  236. echo "Download queue:"
  237. for url in "${!download_files[@]}"; do
  238. echo ">> $url"
  239. done
  240. echo
  241. ######## Start/continue downloading patch or full client and voice packs
  242. downloaded_files=()
  243. for url in "${!download_files[@]}"; do
  244. download_file "$url" "${download_files[$url]}"
  245. downloaded_files+=("$archive_file")
  246. done
  247. ######## Apply clean client update
  248. # Run 'patch_revert.sh' of the newest directory
  249. if [ -e launcher.bat ]; then
  250. echo
  251. echo "============== Reverting previous Wine patch ==============="
  252. bash "$patcher_dir/patch_revert.sh"
  253. echo "============================================================"
  254. echo
  255. fi
  256. # Unpack the game files and remove old ones according to deletefiles.txt
  257. echo "-- Updating game files...."
  258. for archive_file in "${downloaded_files[@]}"; do
  259. # Prevent deleting of the possibly necessary files in case of full update
  260. rm -f deletefiles.txt
  261. echo "Unpacking $archive_file..."
  262. unzip -o "$archive_file" >>"$LOG_FILE"
  263. if [ -f deletefiles.txt ]; then
  264. while read -r delme; do
  265. rm -f "$delme"
  266. done < deletefiles.txt
  267. rm deletefiles.txt
  268. fi
  269. done
  270. sed -i "s/game_version=$installed_version/game_version=${version_info[version]}/" "$CONFIG"
  271. # If we got this far, this was a successful update. Clean up old files.
  272. rm -r "$DOWNLOAD_DIR"
  273. rm -f "$LOG_FILE"
  274. echo "==> Update to version ${version_info[version]} completed"
  275. ######## Run wine compatibility patch script
  276. echo
  277. echo "================= Applying new Wine patch =================="
  278. bash "$patcher_dir/patch.sh"
  279. if [ -f "$patcher_dir/patch_anti_logincrash.sh" ]; then
  280. bash "$patcher_dir/patch_anti_logincrash.sh"
  281. fi
  282. echo "============================================================"
  283. echo "==> Update script completed successfully"
  284. exit 0