mirror of https://git.h3cjp.net/H3cJP/citra.git
164 lines
5.7 KiB
Kotlin
164 lines
5.7 KiB
Kotlin
// Copyright 2023 Citra Emulator Project
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
package org.citra.citra_emu.utils
|
|
|
|
import android.content.Context
|
|
import android.net.Uri
|
|
import androidx.preference.PreferenceManager
|
|
import org.citra.citra_emu.BuildConfig
|
|
import org.citra.citra_emu.CitraApplication
|
|
import org.citra.citra_emu.NativeLibrary
|
|
import org.citra.citra_emu.utils.PermissionsHandler.hasWriteAccess
|
|
import java.io.File
|
|
import java.io.FileOutputStream
|
|
import java.io.IOException
|
|
import java.io.InputStream
|
|
import java.io.OutputStream
|
|
import java.util.concurrent.atomic.AtomicBoolean
|
|
|
|
/**
|
|
* A service that spawns its own thread in order to copy several binary and shader files
|
|
* from the Citra APK to the external file system.
|
|
*/
|
|
object DirectoryInitialization {
|
|
private const val SYS_DIR_VERSION = "sysDirectoryVersion"
|
|
|
|
@Volatile
|
|
private var directoryState: DirectoryInitializationState? = null
|
|
var userPath: String? = null
|
|
val internalUserPath
|
|
get() = CitraApplication.appContext.getExternalFilesDir(null)!!.canonicalPath
|
|
private val isCitraDirectoryInitializationRunning = AtomicBoolean(false)
|
|
|
|
val context: Context get() = CitraApplication.appContext
|
|
|
|
@JvmStatic
|
|
fun start(): DirectoryInitializationState? {
|
|
if (!isCitraDirectoryInitializationRunning.compareAndSet(false, true)) {
|
|
return null
|
|
}
|
|
|
|
if (directoryState != DirectoryInitializationState.CITRA_DIRECTORIES_INITIALIZED) {
|
|
directoryState = if (hasWriteAccess(context)) {
|
|
if (setCitraUserDirectory()) {
|
|
CitraApplication.documentsTree.setRoot(Uri.parse(userPath))
|
|
NativeLibrary.createLogFile()
|
|
NativeLibrary.logUserDirectory(userPath.toString())
|
|
NativeLibrary.createConfigFile()
|
|
GpuDriverHelper.initializeDriverParameters()
|
|
DirectoryInitializationState.CITRA_DIRECTORIES_INITIALIZED
|
|
} else {
|
|
DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE
|
|
}
|
|
} else {
|
|
DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED
|
|
}
|
|
}
|
|
isCitraDirectoryInitializationRunning.set(false)
|
|
return directoryState
|
|
}
|
|
|
|
private fun deleteDirectoryRecursively(file: File) {
|
|
if (file.isDirectory) {
|
|
for (child in file.listFiles()!!) {
|
|
deleteDirectoryRecursively(child)
|
|
}
|
|
}
|
|
file.delete()
|
|
}
|
|
|
|
@JvmStatic
|
|
fun areCitraDirectoriesReady(): Boolean {
|
|
return directoryState == DirectoryInitializationState.CITRA_DIRECTORIES_INITIALIZED
|
|
}
|
|
|
|
fun resetCitraDirectoryState() {
|
|
directoryState = null
|
|
isCitraDirectoryInitializationRunning.compareAndSet(true, false)
|
|
}
|
|
|
|
val userDirectory: String?
|
|
get() {
|
|
checkNotNull(directoryState) {
|
|
"DirectoryInitialization has to run at least once!"
|
|
}
|
|
check(!isCitraDirectoryInitializationRunning.get()) {
|
|
"DirectoryInitialization has to finish running first!"
|
|
}
|
|
return userPath
|
|
}
|
|
|
|
fun setCitraUserDirectory(): Boolean {
|
|
val dataPath = PermissionsHandler.citraDirectory
|
|
if (dataPath.toString().isNotEmpty()) {
|
|
userPath = dataPath.toString()
|
|
android.util.Log.d("[Citra Frontend]", "[DirectoryInitialization] User Dir: $userPath")
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
private fun copyAsset(asset: String, output: File, overwrite: Boolean, context: Context) {
|
|
Log.debug("[DirectoryInitialization] Copying File $asset to $output")
|
|
try {
|
|
if (!output.exists() || overwrite) {
|
|
val inputStream = context.assets.open(asset)
|
|
val outputStream = FileOutputStream(output)
|
|
copyFile(inputStream, outputStream)
|
|
inputStream.close()
|
|
outputStream.close()
|
|
}
|
|
} catch (e: IOException) {
|
|
Log.error("[DirectoryInitialization] Failed to copy asset file: $asset" + e.message)
|
|
}
|
|
}
|
|
|
|
private fun copyAssetFolder(
|
|
assetFolder: String,
|
|
outputFolder: File,
|
|
overwrite: Boolean,
|
|
context: Context
|
|
) {
|
|
Log.debug("[DirectoryInitialization] Copying Folder $assetFolder to $outputFolder")
|
|
try {
|
|
var createdFolder = false
|
|
for (file in context.assets.list(assetFolder)!!) {
|
|
if (!createdFolder) {
|
|
outputFolder.mkdir()
|
|
createdFolder = true
|
|
}
|
|
copyAssetFolder(
|
|
assetFolder + File.separator + file, File(outputFolder, file),
|
|
overwrite, context
|
|
)
|
|
copyAsset(
|
|
assetFolder + File.separator + file, File(outputFolder, file), overwrite,
|
|
context
|
|
)
|
|
}
|
|
} catch (e: IOException) {
|
|
Log.error(
|
|
"[DirectoryInitialization] Failed to copy asset folder: $assetFolder" +
|
|
e.message
|
|
)
|
|
}
|
|
}
|
|
|
|
@Throws(IOException::class)
|
|
private fun copyFile(inputStream: InputStream, outputStream: OutputStream) {
|
|
val buffer = ByteArray(1024)
|
|
var read: Int
|
|
while (inputStream.read(buffer).also { read = it } != -1) {
|
|
outputStream.write(buffer, 0, read)
|
|
}
|
|
}
|
|
|
|
enum class DirectoryInitializationState {
|
|
CITRA_DIRECTORIES_INITIALIZED,
|
|
EXTERNAL_STORAGE_PERMISSION_NEEDED,
|
|
CANT_FIND_EXTERNAL_STORAGE
|
|
}
|
|
}
|