@@ -24,8 +24,10 @@ import androidx.core.view.WindowCompat
2424import androidx.core.view.WindowInsetsCompat
2525import androidx.core.view.WindowInsetsControllerCompat
2626import androidx.core.view.updatePadding
27+ import androidx.lifecycle.lifecycleScope
2728import com.customRobTop.BaseRobTopActivity
2829import com.customRobTop.JniToCpp
30+ import com.geode.launcher.main.FullScreenLoadingIndicator
2931import com.geode.launcher.main.LaunchNotification
3032import com.geode.launcher.main.determineDisplayedCards
3133import com.geode.launcher.utils.Constants
@@ -35,13 +37,14 @@ import com.geode.launcher.utils.GamePackageUtils
3537import com.geode.launcher.utils.GeodeUtils
3638import com.geode.launcher.utils.LaunchUtils
3739import com.geode.launcher.utils.PreferenceUtils
40+ import kotlinx.coroutines.Dispatchers
41+ import kotlinx.coroutines.launch
42+ import kotlinx.coroutines.withContext
3843import org.cocos2dx.lib.Cocos2dxGLSurfaceView
3944import org.cocos2dx.lib.Cocos2dxHelper
4045import org.cocos2dx.lib.Cocos2dxRenderer
4146import org.fmod.FMOD
4247import java.io.File
43- import java.io.FileInputStream
44- import java.io.FileOutputStream
4548import java.io.IOException
4649import javax.microedition.khronos.egl.EGL10
4750import javax.microedition.khronos.egl.EGLConfig
@@ -91,97 +94,88 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
9194 return
9295 }
9396
94- try {
95- createVersionFile()
96- tryLoadGame()
97- } catch (e: UnsatisfiedLinkError ) {
98- Log .e(" GeodeLauncher" , " Library linkage failure" , e)
99-
100- // generates helpful information for use in debugging library load failures
101- val gdPackageInfo = packageManager.getPackageInfo(Constants .PACKAGE_NAME , 0 )
102- val abiMismatch = GamePackageUtils .detectAbiMismatch(this , gdPackageInfo, e)
103-
104- val is64bit = LaunchUtils .is64bit
105- val errorMessage = when {
106- abiMismatch && is64bit -> LaunchUtils .LauncherError .LINKER_NEEDS_32BIT
107- abiMismatch -> LaunchUtils .LauncherError .LINKER_NEEDS_64BIT
108- e.message?.contains(" __gxx_personality_v0" ) == true ->
109- LaunchUtils .LauncherError .LINKER_FAILED_STL
110- else -> LaunchUtils .LauncherError .LINKER_FAILED
97+ val rootLayout = setupContext()
98+ val loadingView = ComposeView (this ).apply {
99+ setContent {
100+ FullScreenLoadingIndicator ()
111101 }
102+ }
112103
113- returnToMain(errorMessage, e.message, e.stackTraceToString())
104+ if (rootLayout != null ) {
105+ rootLayout.addView(loadingView)
106+ } else {
107+ setContentView(loadingView)
108+ }
114109
115- return
116- } catch (e: Exception ) {
117- Log .e(" GeodeLauncher" , " Uncaught error during game load" , e)
110+ lifecycleScope.launch {
111+ val error = preloadGame(rootLayout)
112+ if (error != null ) {
113+ returnToMain(error)
114+ }
118115
119- returnToMain(
120- LaunchUtils .LauncherError .GENERIC ,
121- e.message ? : " Unknown Exception" ,
122- e.stackTraceToString()
123- )
116+ rootLayout?.removeView(loadingView)
117+ }
118+ }
124119
125- return
120+ suspend fun preloadGame (rootLayout : FrameLayout ? ): LaunchUtils .FullLauncherError ? = try {
121+ createVersionFile()
122+ val loadFailed = tryLoadGame()
123+
124+ val baseView = createView()
125+
126+ if (rootLayout != null ) {
127+ rootLayout.addView(baseView)
128+ } else {
129+ setContentView(baseView)
126130 }
127131
128- onBackPressedDispatcher.addCallback(this , object : OnBackPressedCallback (true ) {
129- override fun handleOnBackPressed () {
130- mGLSurfaceView?.sendKeyBack()
131- }
132- })
133132 mGLSurfaceView?.manualBackEvents = true
134- }
135133
136- private fun createVersionFile () {
137- val versionPath = File (filesDir, " game_version.txt" )
138- val gameVersion = GamePackageUtils .getGameVersionCode(packageManager)
134+ setupNotificationView(baseView, loadFailed)
139135
140- versionPath.writeText(" $gameVersion " )
141- }
136+ null
137+ } catch (e: UnsatisfiedLinkError ) {
138+ Log .e(" GeodeLauncher" , " Library linkage failure" , e)
142139
143- private fun returnToMain (
144- error : LaunchUtils .LauncherError ? = null,
145- returnMessage : String? = null,
146- returnExtendedMessage : String? = null
147- ) {
148- val launchIntent = Intent (this , MainActivity ::class .java).apply {
149- flags = Intent .FLAG_ACTIVITY_CLEAR_TASK or Intent .FLAG_ACTIVITY_NEW_TASK
140+ // generates helpful information for use in debugging library load failures
141+ val gdPackageInfo = packageManager.getPackageInfo(Constants .PACKAGE_NAME , 0 )
142+ val abiMismatch = GamePackageUtils .detectAbiMismatch(this , gdPackageInfo, e)
150143
151- if (error != null && ! returnMessage.isNullOrEmpty()) {
152- putExtra(LaunchUtils .LAUNCHER_KEY_RETURN_ERROR , error)
153- putExtra(LaunchUtils .LAUNCHER_KEY_RETURN_MESSAGE , returnMessage)
154- putExtra(LaunchUtils .LAUNCHER_KEY_RETURN_EXTENDED_MESSAGE , returnExtendedMessage)
155- }
144+ val is64bit = LaunchUtils .is64bit
145+ val errorMessage = when {
146+ abiMismatch && is64bit -> LaunchUtils .LauncherError .LINKER_NEEDS_32BIT
147+ abiMismatch -> LaunchUtils .LauncherError .LINKER_NEEDS_64BIT
148+ e.message?.contains(" __gxx_personality_v0" ) == true ->
149+ LaunchUtils .LauncherError .LINKER_FAILED_STL
150+ else -> LaunchUtils .LauncherError .LINKER_FAILED
156151 }
157152
158- startActivity(launchIntent)
159- }
160-
161- private fun tryLoadGame () {
162- val gdPackageInfo = packageManager.getPackageInfo(Constants .PACKAGE_NAME , 0 )
153+ LaunchUtils .FullLauncherError (
154+ errorMessage,
155+ e.message ? : " Unknown exception" ,
156+ e.stackTraceToString()
157+ )
158+ } catch (e: Exception ) {
159+ Log .e(" GeodeLauncher" , " Uncaught error during game load" , e)
163160
164- setupRedirection(gdPackageInfo)
161+ LaunchUtils .FullLauncherError (
162+ LaunchUtils .LauncherError .GENERIC ,
163+ e.message ? : " Unknown Exception" ,
164+ e.stackTraceToString()
165+ )
166+ }
165167
168+ private fun setupContext (): FrameLayout ? {
166169 Cocos2dxHelper .init (this , this )
167170
168171 GeodeUtils .setContext(this )
169172 GeodeUtils .setCapabilityListener(this )
170173
171- tryLoadLibrary(gdPackageInfo, Constants .FMOD_LIB_NAME )
172- tryLoadLibrary(gdPackageInfo, Constants .COCOS_LIB_NAME )
173-
174- if (GamePackageUtils .getGameVersionCode(packageManager) >= 39L ) {
175- // this fix requires geode v3, which is 2.206+
176- // there is a short period in which 2.206 users will still have geode v2, but whatever. ig
177- LauncherFix .enableExceptionsRenaming()
178- }
179-
180- LauncherFix .performPatches()
181-
182- loadInternalMods()
183-
184- val baseView = createView()
174+ onBackPressedDispatcher.addCallback(this , object : OnBackPressedCallback (true ) {
175+ override fun handleOnBackPressed () {
176+ mGLSurfaceView?.sendKeyBack()
177+ }
178+ })
185179
186180 if (! GeodeUtils .handleSafeArea && Build .VERSION .SDK_INT >= Build .VERSION_CODES .BAKLAVA ) {
187181 val frameLayoutParams = ViewGroup .LayoutParams (
@@ -204,15 +198,61 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
204198 WindowInsets .CONSUMED
205199 }
206200
207- frameLayout.addView(baseView)
208201 setContentView(frameLayout)
209- } else {
210- setContentView(baseView)
202+ return frameLayout
203+ }
204+
205+ return null
206+ }
207+
208+ private suspend fun createVersionFile () = runCatching {
209+ val gameVersion = GamePackageUtils .getGameVersionCode(packageManager)
210+
211+ val versionPath = File (filesDir, " game_version.txt" )
212+
213+ withContext(Dispatchers .IO ) {
214+ if (versionPath.exists()) {
215+ versionPath.delete()
216+ }
217+
218+ versionPath.writeText(" $gameVersion " )
219+ }
220+ }
221+
222+ private fun returnToMain (message : LaunchUtils .FullLauncherError ? = null, ) {
223+ val launchIntent = Intent (this , MainActivity ::class .java).apply {
224+ flags = Intent .FLAG_ACTIVITY_CLEAR_TASK or Intent .FLAG_ACTIVITY_NEW_TASK
225+
226+ if (message != null ) {
227+ putExtra(LaunchUtils .LAUNCHER_KEY_RETURN_ERROR , message.error)
228+ putExtra(LaunchUtils .LAUNCHER_KEY_RETURN_MESSAGE , message.returnMessage)
229+ putExtra(LaunchUtils .LAUNCHER_KEY_RETURN_EXTENDED_MESSAGE , message.extendedMessage)
230+ }
211231 }
212232
233+ startActivity(launchIntent)
234+ }
235+
236+ private suspend fun tryLoadGame (): Boolean {
237+ val gdPackageInfo = packageManager.getPackageInfo(Constants .PACKAGE_NAME , 0 )
238+ setupRedirection(gdPackageInfo)
239+
240+ tryLoadLibrary(gdPackageInfo, Constants .FMOD_LIB_NAME )
241+ tryLoadLibrary(gdPackageInfo, Constants .COCOS_LIB_NAME )
242+
243+ if (GamePackageUtils .getGameVersionCode(packageManager) >= 39L ) {
244+ // this fix requires geode v3, which is 2.206+
245+ // there is a short period in which 2.206 users will still have geode v2, but whatever. ig
246+ LauncherFix .enableExceptionsRenaming()
247+ }
248+
249+ LauncherFix .performPatches()
250+
251+ loadInternalMods()
252+
213253 setupPostLibraryLoad(gdPackageInfo)
214254
215- val geodeFailed = try {
255+ return try {
216256 loadGeodeLibrary()
217257 false
218258 } catch (e: UnsatisfiedLinkError ) {
@@ -222,8 +262,6 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
222262 handleGeodeException(e)
223263 true
224264 }
225-
226- setupNotificationView(baseView, geodeFailed)
227265 }
228266
229267 private fun setupNotificationView (baseView : FrameLayout , geodeFailed : Boolean ) {
@@ -283,7 +321,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
283321 }
284322
285323 @SuppressLint(" UnsafeDynamicallyLoadedCode" )
286- private fun tryLoadLibrary (packageInfo : PackageInfo , libraryName : String ) {
324+ private suspend fun tryLoadLibrary (packageInfo : PackageInfo , libraryName : String ) {
287325 val nativeDir = getNativeLibraryDirectory(packageInfo.applicationInfo!! )
288326 val libraryPath = if (nativeDir.endsWith(' /' )) " ${nativeDir} lib$libraryName .so" else " $nativeDir /lib$libraryName .so"
289327
@@ -302,7 +340,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
302340 }
303341
304342 @SuppressLint(" UnsafeDynamicallyLoadedCode" )
305- private fun loadLibraryCopy (libraryName : String , libraryPath : String ) {
343+ private suspend fun loadLibraryCopy (libraryName : String , libraryPath : String ) {
306344 if (libraryPath.contains(" !/" )) {
307345 // library in apk can't be loaded directly
308346 loadLibraryFromAssetsCopy(libraryName)
@@ -312,19 +350,15 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
312350 val library = File (libraryPath)
313351 val libraryCopy = File (cacheDir, " lib$libraryName .so" )
314352
315- libraryCopy.outputStream().use { libraryOutput ->
316- library.inputStream().use { inputStream ->
317- DownloadUtils .copyFile(inputStream, libraryOutput)
318- }
319- }
353+ DownloadUtils .copyFile(library, libraryCopy)
320354
321355 System .load(libraryCopy.path)
322356
323357 return
324358 }
325359
326360 @SuppressLint(" UnsafeDynamicallyLoadedCode" )
327- private fun loadLibraryFromAssetsCopy (libraryName : String ) {
361+ private suspend fun loadLibraryFromAssetsCopy (libraryName : String ) {
328362 // loads a library loaded in assets
329363 // this copies the library to a non-compressed directory
330364
@@ -339,9 +373,11 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
339373 // there doesn't seem to be a way to load a library from a file descriptor
340374 val libraryCopy = File (cacheDir, " lib$libraryName .so" )
341375
342- libraryCopy.outputStream().use { libraryOutput ->
343- libraryFd.createInputStream().use { inputStream ->
344- DownloadUtils .copyFile(inputStream, libraryOutput)
376+ withContext(Dispatchers .IO ) {
377+ libraryCopy.outputStream().use { libraryOutput ->
378+ libraryFd.createInputStream().use { inputStream ->
379+ DownloadUtils .copyFile(inputStream, libraryOutput)
380+ }
345381 }
346382 }
347383
@@ -376,7 +412,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
376412 }
377413
378414 @SuppressLint(" UnsafeDynamicallyLoadedCode" )
379- private fun loadGeodeLibrary () {
415+ private suspend fun loadGeodeLibrary () {
380416 // Load Geode if exists
381417 // bundling the object with the application allows for nicer backtraces
382418 try {
@@ -397,22 +433,23 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
397433 // also i get 20 million permission denied errors
398434 val externalGeodePath = LaunchUtils .getInstalledGeodePath(this )!!
399435
400- val copiedPath = File (filesDir.path , " copied" )
436+ val copiedPath = File (filesDir, " copied" )
401437 if (copiedPath.exists()) {
402438 copiedPath.deleteRecursively()
403439 }
404440 copiedPath.mkdir()
405441
406- val copiedGeodePath = File (copiedPath.path , " Geode.so" )
442+ val copiedGeodePath = File (copiedPath, " Geode.so" )
407443
408444 if (externalGeodePath.exists()) {
409445 DownloadUtils .copyFile(
410- FileInputStream ( externalGeodePath) ,
411- FileOutputStream ( copiedGeodePath)
446+ externalGeodePath,
447+ copiedGeodePath
412448 )
413449
414450 if (copiedGeodePath.exists()) {
415451 println (" Loading Geode from ${externalGeodePath.name} " )
452+
416453 System .load(copiedGeodePath.path)
417454 return
418455 }
0 commit comments