Installer.qml 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. import QtQuick 2.0
  2. import Qt.labs.settings 1.0
  3. import Qt.labs.platform 1.1
  4. import FileIO 1.0
  5. import "components"
  6. Item {
  7. id: installer
  8. readonly property string homePath: FileIO.userHomePath()
  9. readonly property string defaultGamePath: "drive_c/Program Files/Genshin Impact/Genshin Impact Game"
  10. readonly property string defaultLocation: `${homePath}/.wine/${defaultGamePath}`
  11. readonly property string configIni: "config.ini"
  12. readonly property string versionNotInstalled: "Not installed"
  13. readonly property var possibleLocations: [
  14. `.wine*/${defaultGamePath}`,
  15. `.steam/steam/steamapps/common/Proton */dist/share/default_pfx/${defaultGamePath}`
  16. ]
  17. property string patchRepo: "https://notabug.org/Krock/dawn"
  18. property alias installedVersion: localVersionLoader.version
  19. property string availableVersion
  20. property string preDownloadVersion
  21. property bool isPreDownloading: false
  22. property bool quitAfterCancel: false
  23. anchors.fill: parent
  24. InstallerUi {
  25. id: ui
  26. anchors.fill: parent
  27. progressText: clientInstaller.progressText
  28. progress: clientInstaller.progressPercent / 100.0
  29. onBrowseClicked: {
  30. fileDialog.open()
  31. }
  32. onInstallClicked: {
  33. ui.uiState = ui.uiStateInstalling
  34. dependenciesInstaller.check()
  35. }
  36. onCancelConfirmed: {
  37. clientInstaller.cancel()
  38. ui.log(qsTr("Canceled"))
  39. ui.log(qsTr(""))
  40. revertUiState()
  41. if (quitAfterCancel) {
  42. quitAfterCancel = false
  43. window.close()
  44. }
  45. }
  46. onCancelRejected: {
  47. quitAfterCancel = false
  48. }
  49. onPreDownloadClicked: {
  50. isPreDownloading = true
  51. dependenciesInstaller.check()
  52. }
  53. onUpdateClicked: {
  54. ui.uiState = ui.uiStateUpdating
  55. dependenciesInstaller.check()
  56. }
  57. onRetryClicked: checkForUpdates()
  58. onRepatchClicked: clientInstaller.reapplyWinePatch()
  59. onStartClicked: {
  60. ui.uiState = ui.uiStateStarting
  61. clientLauncher.start()
  62. }
  63. onStopConfirmed: {
  64. ui.uiState = ui.uiStateStopping
  65. clientLauncher.stop()
  66. }
  67. onKillWineServerClicked: clientLauncher.killWineServer()
  68. onCheckSelfUpdateClicked: selfUpdate.check()
  69. }
  70. Settings {
  71. id: settings
  72. category: "game"
  73. property string gamePath: ""
  74. property alias isLogVisible: ui.isLogVisible
  75. property bool multiConnections: false
  76. property bool limitDownload: false
  77. property string limitDownloadValue: "1M"
  78. property bool hideBackground: false
  79. property bool firstRun: true
  80. }
  81. LocalVersionLoader {
  82. id: localVersionLoader
  83. iniFilePath: settings.gamePath + "/" + configIni
  84. }
  85. WebVersionLoader {
  86. id: webVersionLoader
  87. onJsonChanged: tryCheckVersion()
  88. onError: {
  89. ui.showLog()
  90. ui.uiState = ui.uiStateRetry
  91. ui.log(qsTr("Failed to check version info. Check your Internet connection and try again."))
  92. }
  93. }
  94. DependenciesInstaller {
  95. id: dependenciesInstaller
  96. anchors.fill: parent
  97. onDependenciesInstallCanceled: {
  98. isPreDownloading = false
  99. ui.uiState = ui.uiStateInstall
  100. }
  101. onDependenciesInstallError: {
  102. ui.showLog()
  103. ui.log(qsTr("Failed to install dependencies"))
  104. revertUiState()
  105. }
  106. onDependenciesInstalled: startInstallUpdate()
  107. }
  108. ClientInstaller {
  109. id: clientInstaller
  110. anchors.fill: parent
  111. onPredownloadFinished: {
  112. isPreDownloading = false
  113. ui.hasPreDownload = false
  114. }
  115. onInstallError: {
  116. ui.showLog()
  117. switch (ui.uiState) {
  118. case ui.uiStateInstalling:
  119. ui.uiState = ui.uiStateInstall
  120. ui.log(qsTr("Error installing client"))
  121. return
  122. case ui.uiStateUpdating:
  123. ui.log(qsTr("Error updating client"))
  124. break
  125. }
  126. ui.log(qsTr(""))
  127. revertUiState()
  128. }
  129. onInstallCanceled: {
  130. switch (ui.uiState) {
  131. case ui.uiStateInstalling:
  132. ui.log(qsTr("Installation canceled"))
  133. return
  134. case ui.uiStateUpdating:
  135. ui.log(qsTr("Update canceled"))
  136. break
  137. }
  138. ui.log(qsTr(""))
  139. revertUiState()
  140. }
  141. onInstallFinished: {
  142. switch (ui.uiState) {
  143. case ui.uiStateInstalling:
  144. ui.log(qsTr("Installation completed!"))
  145. break
  146. case ui.uiStateUpdating:
  147. ui.log(qsTr("Update completed!"))
  148. break
  149. }
  150. ui.log(qsTr(""))
  151. checkFinished()
  152. isPreDownloading = false
  153. }
  154. onInstallSelectLanguagesCanceled: {
  155. revertUiState()
  156. }
  157. }
  158. ClientLauncher {
  159. id: clientLauncher
  160. onIsRunningChanged: {
  161. if (isRunning) {
  162. ui.uiState = ui.uiStateRunning
  163. } else {
  164. checkFinished()
  165. }
  166. }
  167. }
  168. SelfUpdate {
  169. id: selfUpdate
  170. anchors.fill: parent
  171. }
  172. FolderDialog {
  173. id: fileDialog
  174. title: qsTr(`Please choose 'Genshin Impact Game' directory`)
  175. folder: "file://" + (settings.gamePath || shortcuts.home)
  176. onAccepted: {
  177. let path = fileDialog.folder.toString().replace("file://", "")
  178. settings.gamePath = path
  179. ui.clearLog()
  180. if (!bootstrap()) {
  181. showInstallState()
  182. }
  183. }
  184. }
  185. function checkFinished() {
  186. ui.hasUpdate = !!availableVersion && availableVersion !== installedVersion
  187. if (clientInstaller.isPatchInstalled()) {
  188. ui.uiState = ui.uiStateStart
  189. } else {
  190. ui.uiState = ui.uiStatePatch
  191. }
  192. }
  193. function tryCheckVersion() {
  194. if (!webVersionLoader.json) {
  195. return
  196. }
  197. let game = webVersionLoader.json.data.game
  198. availableVersion = game.latest.version
  199. ui.log(qsTr("Web Version:") + " " + availableVersion)
  200. ui.hasUpdate = availableVersion !== installedVersion
  201. if (!ui.hasUpdate) {
  202. ui.log(qsTr("The game is up to date!"))
  203. checkFinished()
  204. let existingPreDownloadedVersion = clientInstaller.getPreDownloadedVersion()
  205. let preDownload = webVersionLoader.json.data.pre_download_game
  206. let hasPreDownload = preDownload && preDownload.latest && preDownload.latest.version || false
  207. if (hasPreDownload) {
  208. let latestPreDownloadVersion = preDownload.latest.version
  209. if (latestPreDownloadVersion !== existingPreDownloadedVersion) {
  210. preDownloadVersion = preDownload.latest.version
  211. ui.log(qsTr("Pre-download available") + " " + latestPreDownloadVersion)
  212. } else {
  213. ui.log(qsTr("The version") + " " + existingPreDownloadedVersion + " " + qsTr("is pre-downloaded"))
  214. hasPreDownload = false
  215. }
  216. }
  217. ui.hasPreDownload = hasPreDownload
  218. return
  219. }
  220. ui.log(qsTr("Update available"))
  221. switch (ui.uiState) {
  222. case ui.uiStateInstalling:
  223. clientInstaller.start()
  224. break
  225. case ui.uiStateLoading:
  226. checkFinished()
  227. break
  228. }
  229. }
  230. function tryLoadGameSettings() {
  231. if (!settings.gamePath) {
  232. console.debug("settings.gamePath is not set")
  233. return false
  234. }
  235. ui.log(qsTr("Game path: ") + settings.gamePath)
  236. const gameConfigPath = settings.gamePath + "/" + configIni
  237. if (!FileIO.isFileExists(gameConfigPath)) {
  238. console.debug(`${gameConfigPath} doesn't exist`)
  239. return false
  240. }
  241. localVersionLoader.load()
  242. return true
  243. }
  244. function checkForUpdates() {
  245. ui.log(qsTr("Installed Version:") + " " + installedVersion)
  246. ui.log(qsTr("Checking for updates..."))
  247. ui.uiState = ui.uiStateLoading
  248. webVersionLoader.loadVersion()
  249. }
  250. function detectLocation() {
  251. let foundPath
  252. for (let location of possibleLocations) {
  253. let foundLocations = FileIO.matchByWildcard(homePath + "/" + location + "/" + configIni)
  254. var foundLocation = foundLocations[0]
  255. if (foundLocation) {
  256. foundPath = foundLocation.substr(0, foundLocation.length - configIni.length)
  257. break
  258. }
  259. }
  260. return foundPath
  261. }
  262. function bootstrap() {
  263. if (tryLoadGameSettings()) {
  264. if (!FileIO.isFileExists(settings.gamePath + "/" + "GenshinImpact.exe")) {
  265. ui.log(qsTr("⚠ Wrong directory set as game directory."))
  266. ui.log(qsTr("⚠ Please choose 'Genshin Imact Game' dir, not the 'Genshin Impact'!"))
  267. ui.uiState = ui.uiStateWrongDir
  268. return true
  269. }
  270. if (!localVersionLoader.version) {
  271. ui.log(qsTr("⚠ Can't read game_version from config.ini, update wouldn't be possible."))
  272. checkFinished()
  273. return true
  274. }
  275. checkForUpdates()
  276. return true
  277. }
  278. return false
  279. }
  280. function handleAppExit() {
  281. if (!clientInstaller.isActive) {
  282. return true
  283. }
  284. quitAfterCancel = true
  285. ui.confirmCancelInstall()
  286. return false
  287. }
  288. function revertUiState() {
  289. switch (ui.uiState) {
  290. case ui.uiStateInstalling:
  291. ui.uiState = ui.uiStateInstall
  292. break
  293. case ui.uiStateInstall:
  294. break
  295. default:
  296. checkFinished()
  297. }
  298. isPreDownloading = false
  299. }
  300. function startInstallUpdate() {
  301. if (!webVersionLoader.json && !webVersionLoader.isLoading) {
  302. webVersionLoader.loadVersion()
  303. return
  304. }
  305. clientInstaller.start()
  306. }
  307. function showInstallState() {
  308. // in case we have empty directory as game path
  309. ui.log(qsTr("Install to specified location or press 'Browse...' to browse for game dir"))
  310. ui.uiState = ui.uiStateInstall
  311. }
  312. Component.onCompleted: {
  313. ui.log(qsTr("Please refrain from sharing this project in public."))
  314. ui.log("")
  315. if (bootstrap()) {
  316. return
  317. }
  318. if (settings.gamePath) {
  319. showInstallState()
  320. return
  321. }
  322. let foundPath = detectLocation()
  323. if (foundPath) {
  324. ui.log(qsTr("Location detected:") + " " + foundPath)
  325. settings.gamePath = foundPath
  326. if (bootstrap()) {
  327. return
  328. }
  329. }
  330. if (!foundPath) {
  331. ui.log(qsTr("No previously installed game detected."))
  332. } else {
  333. ui.log(qsTr("Failed to load game config."))
  334. }
  335. ui.log(qsTr("Install to default location or press 'Browse...' to browse for game dir"))
  336. ui.log(qsTr("Location to install:"))
  337. ui.log(defaultLocation)
  338. settings.gamePath = defaultLocation
  339. ui.uiState = ui.uiStateInstall
  340. }
  341. }