Add data files

This commit is contained in:
SSimco
2025-06-15 20:00:06 +03:00
parent de58ae7d98
commit d5bb3e44e8
5 changed files with 163 additions and 5 deletions
+1
View File
@@ -13,3 +13,4 @@
.externalNativeBuild
.cxx
local.properties
app/src/main/assets/hash.txt
+70
View File
@@ -1,4 +1,9 @@
import com.android.build.gradle.internal.tasks.factory.dependsOn
import java.io.IOException
import java.security.MessageDigest
import java.util.regex.Pattern
import javax.xml.bind.DatatypeConverter
plugins {
alias(libs.plugins.android.application)
@@ -35,6 +40,8 @@ fun getVersionName(): String {
fun getVersionCode(): Int = System.getenv("VERSION_CODE")?.toIntOrNull() ?: 1
val cemuDataFilesFolder = "../../../bin"
android {
namespace = "info.cemu.cemu"
compileSdk = 35
@@ -47,6 +54,17 @@ android {
versionName = getVersionName()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
androidResources {
ignoreAssetsPattern = "!*cemu.mo:"
}
sourceSets.getByName("main") {
assets {
srcDir(cemuDataFilesFolder)
}
}
packaging {
jniLibs.useLegacyPackaging = true
}
@@ -139,6 +157,58 @@ android {
}
}
abstract class ComputeCemuDataFilesHashTask : DefaultTask() {
private val ignoreFilePatterns = arrayOf(
Pattern.compile(".*cemu\\.mo"),
Pattern.compile(".*Cemu_(?:debug|release)"),
)
@get:Input
abstract val cemuDataFolder: Property<String>
private fun isFileIgnored(file: File): Boolean {
return ignoreFilePatterns.any { pattern -> pattern.matcher(file.path).matches() }
}
@TaskAction
fun computeCemuDataFilesHash() {
val assetDir = File(project.projectDir, "src/main/assets")
if (!assetDir.exists()) {
assetDir.mkdirs()
}
val cemuDataFilesFile = File(project.projectDir, cemuDataFolder.get())
val hashFile = File(assetDir, "hash.txt")
val md = MessageDigest.getInstance("SHA-256")
if (!cemuDataFilesFile.isDirectory) {
hashFile.writeText("invalid")
return
}
val fileHashes = cemuDataFilesFile.walkTopDown()
.filter { it.isFile && !isFileIgnored(it) }
.sortedBy { it.path }
.map {
md.reset()
md.update(it.path.toByteArray())
md.update(it.readBytes())
md.digest()
}
.toList()
md.reset()
fileHashes.forEach { md.update(it) }
hashFile.writeText(DatatypeConverter.printHexBinary(md.digest()))
}
}
tasks.register<ComputeCemuDataFilesHashTask>("computeCemuDataFilesHash") {
cemuDataFolder = cemuDataFilesFolder
}
tasks.preBuild.dependsOn("computeCemuDataFilesHash")
dependencies {
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.kotlinx.serialization.json)
@@ -14,12 +14,13 @@ Java_info_cemu_cemu_nativeinterface_NativeActiveSettings_getUserDataPath(JNIEnv*
}
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
Java_info_cemu_cemu_nativeinterface_NativeActiveSettings_initializeActiveSettings(JNIEnv* env, [[maybe_unused]] jclass clazz, jstring data_path, jstring cache_path)
Java_info_cemu_cemu_nativeinterface_NativeActiveSettings_initializeActiveSettings(JNIEnv* env, [[maybe_unused]] jclass clazz, jstring user_data_path, jstring data_path, jstring cache_path)
{
std::string userDataPath = JNIUtils::toString(env, user_data_path);
std::string dataPath = JNIUtils::toString(env, data_path);
std::string cachePath = JNIUtils::toString(env, cache_path);
std::set<fs::path> failedWriteAccess;
ActiveSettings::SetPaths(false, {}, dataPath, dataPath, cachePath, dataPath, failedWriteAccess);
ActiveSettings::SetPaths(false, {}, userDataPath, userDataPath, cachePath, dataPath, failedWriteAccess);
}
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
@@ -1,7 +1,6 @@
package info.cemu.cemu
import android.app.Application
import android.util.Log
import info.cemu.cemu.nativeinterface.NativeActiveSettings.initializeActiveSettings
import info.cemu.cemu.nativeinterface.NativeActiveSettings.setInternalDir
import info.cemu.cemu.nativeinterface.NativeActiveSettings.setNativeLibDir
@@ -11,8 +10,10 @@ import info.cemu.cemu.nativeinterface.NativeGraphicPacks.refreshGraphicPacks
import info.cemu.cemu.nativeinterface.NativeLogging.crashLog
import info.cemu.cemu.nativeinterface.NativeSwkbd.initializeSwkbd
import java.io.File
import java.io.IOException
import java.io.PrintWriter
import java.io.StringWriter
import java.util.regex.Pattern
class CemuApplication : Application() {
init {
@@ -30,6 +31,78 @@ class CemuApplication : Application() {
override fun onCreate() {
super.onCreate()
configureExceptionHandler()
initializeCemu()
saveDataFiles()
}
private fun saveDataFiles() {
val dataFolder = File(internalCemuDataFolder)
if (!dataFolder.exists() && !dataFolder.mkdirs()) {
return
}
val hashFileName = "hash.txt"
val hashFile = dataFolder.resolve(hashFileName)
val oldHash = if (hashFile.isFile) hashFile.readText() else "invalid"
val assetsFileStream = try {
assets.open(hashFileName)
} catch (_: IOException) {
null
}
if (assetsFileStream == null) {
return
}
val newHash = assetsFileStream.reader().readText()
if (oldHash == newHash) {
return
}
dataFolder.deleteRecursively()
dataFolder.mkdirs()
dataFolder.resolve(hashFileName).writeText(newHash)
fun traverseAssets(path: String = ""): Iterator<String> = iterator {
val assetFiles = assets.list(path) ?: return@iterator
if (assetFiles.isEmpty()) {
yield(path)
}
for (assetFile in assetFiles) {
val assetPath = path + (if (path == "") "" else "/") + assetFile
for (file in traverseAssets(assetPath)) {
yield(file)
}
}
}
val filePatterns = arrayOf(
Pattern.compile("gameProfiles/.*"),
Pattern.compile("resources/.*"),
)
fun isFileValid(file: String): Boolean {
return filePatterns.any { pattern -> pattern.matcher(file).matches() }
}
for (assetFile in traverseAssets()) {
if (!isFileValid(assetFile)) {
continue
}
val outFile = dataFolder.resolve(assetFile)
outFile.parentFile?.mkdirs()
assets.open(assetFile).copyTo(outFile.outputStream())
}
}
private fun configureExceptionHandler() {
if (DefaultUncaughtExceptionHandler == null) {
DefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
}
@@ -44,9 +117,16 @@ class CemuApplication : Application() {
exception
)
}
}
private fun initializeCemu() {
val displayMetrics = resources.displayMetrics
setDPI(displayMetrics.density)
initializeActiveSettings(internalFolder.toString(), internalFolder.toString())
initializeActiveSettings(
userDataPath = internalCemuUserFolder,
dataPath = internalCemuDataFolder,
cachePath = internalCemuUserFolder,
)
setNativeLibDir(applicationInfo.nativeLibraryDir)
setInternalDir(dataDir.absolutePath)
initializeEmulation()
@@ -54,6 +134,12 @@ class CemuApplication : Application() {
refreshGraphicPacks()
}
private val internalCemuDataFolder: String
get() = internalFolder.resolve("data").toString()
private val internalCemuUserFolder: String
get() = internalFolder.toString()
companion object {
init {
System.loadLibrary("CemuAndroid")
@@ -2,7 +2,7 @@ package info.cemu.cemu.nativeinterface
object NativeActiveSettings {
@JvmStatic
external fun initializeActiveSettings(dataPath: String, cachePath: String)
external fun initializeActiveSettings(userDataPath: String, dataPath: String, cachePath: String)
@JvmStatic
external fun setNativeLibDir(nativeLibDir: String)