From 72d9dc9a3f63b314cea7b166b6f81000e20a14c3 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Fri, 9 Jun 2023 16:11:30 -0400 Subject: [PATCH] android: Add proper homebrew check --- .../java/org/yuzu/yuzu_emu/NativeLibrary.kt | 2 ++ .../yuzu/yuzu_emu/fragments/SearchFragment.kt | 8 +------- .../main/java/org/yuzu/yuzu_emu/model/Game.kt | 4 +++- .../org/yuzu/yuzu_emu/model/GamesViewModel.kt | 11 +++++++++- .../org/yuzu/yuzu_emu/utils/GameHelper.kt | 4 ++-- src/android/app/src/main/jni/native.cpp | 20 ++++++++++++++++++- src/core/loader/nro.cpp | 13 +++++++++++- src/core/loader/nro.h | 2 ++ 8 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index c11b6bc16..22af9e435 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -223,6 +223,8 @@ object NativeLibrary { external fun getCompany(filename: String): String + external fun isHomebrew(filename: String): Boolean + external fun setAppDirectory(directory: String) external fun initializeGpuDriver( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt index ebc0f164a..adbe3696b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt @@ -127,13 +127,7 @@ class SearchFragment : Fragment() { } } - R.id.chip_homebrew -> { - baseList.filter { - Log.error("Guh - ${it.path}") - FileUtil.hasExtension(it.path, "nro") - || FileUtil.hasExtension(it.path, "nso") - } - } + R.id.chip_homebrew -> baseList.filter { it.isHomebrew } R.id.chip_retail -> baseList.filter { FileUtil.hasExtension(it.path, "xci") diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index 2a17653b2..3d6782c49 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt @@ -16,7 +16,8 @@ class Game( val regions: String, val path: String, val gameId: String, - val company: String + val company: String, + val isHomebrew: Boolean ) : Parcelable { val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime" val keyLastPlayedTime get() = "${gameId}_LastPlayed" @@ -31,6 +32,7 @@ class Game( && path == other.path && gameId == other.gameId && company == other.company + && isHomebrew == other.isHomebrew } companion object { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt index 7059856f1..d9b301210 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt @@ -13,6 +13,8 @@ import androidx.preference.PreferenceManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.MissingFieldException import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import org.yuzu.yuzu_emu.NativeLibrary @@ -20,6 +22,7 @@ import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.utils.GameHelper import java.util.Locale +@OptIn(ExperimentalSerializationApi::class) class GamesViewModel : ViewModel() { private val _games = MutableLiveData>(emptyList()) val games: LiveData> get() = _games @@ -49,7 +52,13 @@ class GamesViewModel : ViewModel() { if (storedGames!!.isNotEmpty()) { val deserializedGames = mutableSetOf() storedGames.forEach { - val game: Game = Json.decodeFromString(it) + val game: Game + try { + game = Json.decodeFromString(it) + } catch (e: MissingFieldException) { + return@forEach + } + val gameExists = DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path)) ?.exists() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt index ba6b5783e..42b207618 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt @@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.utils import android.content.SharedPreferences import android.net.Uri import androidx.preference.PreferenceManager -import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import org.yuzu.yuzu_emu.NativeLibrary @@ -83,7 +82,8 @@ object GameHelper { NativeLibrary.getRegions(filePath), filePath, gameId, - NativeLibrary.getCompany(filePath) + NativeLibrary.getCompany(filePath), + NativeLibrary.isHomebrew(filePath) ) val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index b87e04b3d..03cb0b74b 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "common/detached_tasks.h" #include "common/dynamic_library.h" @@ -281,6 +282,10 @@ public: return GetRomMetadata(path).icon; } + bool GetIsHomebrew(const std::string& path) { + return GetRomMetadata(path).isHomebrew; + } + void ResetRomMetadata() { m_rom_metadata_cache.clear(); } @@ -348,6 +353,7 @@ private: struct RomMetadata { std::string title; std::vector icon; + bool isHomebrew; }; RomMetadata GetRomMetadata(const std::string& path) { @@ -360,11 +366,17 @@ private: RomMetadata CacheRomMetadata(const std::string& path) { const auto file = Core::GetGameFileFromPath(m_vfs, path); - const auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); + auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); RomMetadata entry; loader->ReadTitle(entry.title); loader->ReadIcon(entry.icon); + if (loader->GetFileType() == Loader::FileType::NRO) { + auto loader_nro = dynamic_cast(loader.get()); + entry.isHomebrew = loader_nro->IsHomebrew(); + } else { + entry.isHomebrew = false; + } m_rom_metadata_cache[path] = entry; @@ -662,6 +674,12 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv return env->NewStringUTF(""); } +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + [[maybe_unused]] jstring j_filename) { + return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename)); +} + void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) { // Create the default config.ini. diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 73d04d7ee..7be6cf5f3 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -33,7 +33,8 @@ static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect s struct NroHeader { INSERT_PADDING_BYTES(0x4); u32_le module_header_offset; - INSERT_PADDING_BYTES(0x8); + u32 magic_ext1; + u32 magic_ext2; u32_le magic; INSERT_PADDING_BYTES(0x4); u32_le file_size; @@ -124,6 +125,16 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) { return FileType::Error; } +bool AppLoader_NRO::IsHomebrew() { + // Read NSO header + NroHeader nro_header{}; + if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { + return false; + } + return nro_header.magic_ext1 == Common::MakeMagic('H', 'O', 'M', 'E') && + nro_header.magic_ext2 == Common::MakeMagic('B', 'R', 'E', 'W'); +} + static constexpr u32 PageAlignSize(u32 size) { return static_cast((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); } diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index ccb77b581..8de6eebc6 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -38,6 +38,8 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& nro_file); + bool IsHomebrew(); + FileType GetFileType() const override { return IdentifyType(file); }