diff options
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<List<Game>>(emptyList())      val games: LiveData<List<Game>> get() = _games @@ -49,7 +52,13 @@ class GamesViewModel : ViewModel() {          if (storedGames!!.isNotEmpty()) {              val deserializedGames = mutableSetOf<Game>()              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 <android/api-level.h>  #include <android/native_window_jni.h> +#include <core/loader/nro.h>  #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<u8> 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::AppLoader_NRO*>(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<u32>((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);      } | 
