android: Add proper homebrew check

This commit is contained in:
Charles Lombardo 2023-06-09 16:11:30 -04:00
parent 4d395b3b72
commit 72d9dc9a3f
8 changed files with 51 additions and 13 deletions

View File

@ -223,6 +223,8 @@ object NativeLibrary {
external fun getCompany(filename: String): String external fun getCompany(filename: String): String
external fun isHomebrew(filename: String): Boolean
external fun setAppDirectory(directory: String) external fun setAppDirectory(directory: String)
external fun initializeGpuDriver( external fun initializeGpuDriver(

View File

@ -127,13 +127,7 @@ class SearchFragment : Fragment() {
} }
} }
R.id.chip_homebrew -> { R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
baseList.filter {
Log.error("Guh - ${it.path}")
FileUtil.hasExtension(it.path, "nro")
|| FileUtil.hasExtension(it.path, "nso")
}
}
R.id.chip_retail -> baseList.filter { R.id.chip_retail -> baseList.filter {
FileUtil.hasExtension(it.path, "xci") FileUtil.hasExtension(it.path, "xci")

View File

@ -16,7 +16,8 @@ class Game(
val regions: String, val regions: String,
val path: String, val path: String,
val gameId: String, val gameId: String,
val company: String val company: String,
val isHomebrew: Boolean
) : Parcelable { ) : Parcelable {
val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime" val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime"
val keyLastPlayedTime get() = "${gameId}_LastPlayed" val keyLastPlayedTime get() = "${gameId}_LastPlayed"
@ -31,6 +32,7 @@ class Game(
&& path == other.path && path == other.path
&& gameId == other.gameId && gameId == other.gameId
&& company == other.company && company == other.company
&& isHomebrew == other.isHomebrew
} }
companion object { companion object {

View File

@ -13,6 +13,8 @@ import androidx.preference.PreferenceManager
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.MissingFieldException
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
@ -20,6 +22,7 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.GameHelper import org.yuzu.yuzu_emu.utils.GameHelper
import java.util.Locale import java.util.Locale
@OptIn(ExperimentalSerializationApi::class)
class GamesViewModel : ViewModel() { class GamesViewModel : ViewModel() {
private val _games = MutableLiveData<List<Game>>(emptyList()) private val _games = MutableLiveData<List<Game>>(emptyList())
val games: LiveData<List<Game>> get() = _games val games: LiveData<List<Game>> get() = _games
@ -49,7 +52,13 @@ class GamesViewModel : ViewModel() {
if (storedGames!!.isNotEmpty()) { if (storedGames!!.isNotEmpty()) {
val deserializedGames = mutableSetOf<Game>() val deserializedGames = mutableSetOf<Game>()
storedGames.forEach { storedGames.forEach {
val game: Game = Json.decodeFromString(it) val game: Game
try {
game = Json.decodeFromString(it)
} catch (e: MissingFieldException) {
return@forEach
}
val gameExists = val gameExists =
DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path)) DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path))
?.exists() ?.exists()

View File

@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.utils
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.Uri import android.net.Uri
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
@ -83,7 +82,8 @@ object GameHelper {
NativeLibrary.getRegions(filePath), NativeLibrary.getRegions(filePath),
filePath, filePath,
gameId, gameId,
NativeLibrary.getCompany(filePath) NativeLibrary.getCompany(filePath),
NativeLibrary.isHomebrew(filePath)
) )
val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L) val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L)

View File

@ -13,6 +13,7 @@
#include <android/api-level.h> #include <android/api-level.h>
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
#include <core/loader/nro.h>
#include "common/detached_tasks.h" #include "common/detached_tasks.h"
#include "common/dynamic_library.h" #include "common/dynamic_library.h"
@ -281,6 +282,10 @@ public:
return GetRomMetadata(path).icon; return GetRomMetadata(path).icon;
} }
bool GetIsHomebrew(const std::string& path) {
return GetRomMetadata(path).isHomebrew;
}
void ResetRomMetadata() { void ResetRomMetadata() {
m_rom_metadata_cache.clear(); m_rom_metadata_cache.clear();
} }
@ -348,6 +353,7 @@ private:
struct RomMetadata { struct RomMetadata {
std::string title; std::string title;
std::vector<u8> icon; std::vector<u8> icon;
bool isHomebrew;
}; };
RomMetadata GetRomMetadata(const std::string& path) { RomMetadata GetRomMetadata(const std::string& path) {
@ -360,11 +366,17 @@ private:
RomMetadata CacheRomMetadata(const std::string& path) { RomMetadata CacheRomMetadata(const std::string& path) {
const auto file = Core::GetGameFileFromPath(m_vfs, 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; RomMetadata entry;
loader->ReadTitle(entry.title); loader->ReadTitle(entry.title);
loader->ReadIcon(entry.icon); 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; m_rom_metadata_cache[path] = entry;
@ -662,6 +674,12 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv
return env->NewStringUTF(""); 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 void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation
[[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) { [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {
// Create the default config.ini. // Create the default config.ini.

View File

@ -33,7 +33,8 @@ static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect s
struct NroHeader { struct NroHeader {
INSERT_PADDING_BYTES(0x4); INSERT_PADDING_BYTES(0x4);
u32_le module_header_offset; u32_le module_header_offset;
INSERT_PADDING_BYTES(0x8); u32 magic_ext1;
u32 magic_ext2;
u32_le magic; u32_le magic;
INSERT_PADDING_BYTES(0x4); INSERT_PADDING_BYTES(0x4);
u32_le file_size; u32_le file_size;
@ -124,6 +125,16 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) {
return FileType::Error; 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) { static constexpr u32 PageAlignSize(u32 size) {
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
} }

View File

@ -38,6 +38,8 @@ public:
*/ */
static FileType IdentifyType(const FileSys::VirtualFile& nro_file); static FileType IdentifyType(const FileSys::VirtualFile& nro_file);
bool IsHomebrew();
FileType GetFileType() const override { FileType GetFileType() const override {
return IdentifyType(file); return IdentifyType(file);
} }