Merge pull request #10691 from t895/nro-check
android: Add proper homebrew check
This commit is contained in:
commit
f759ff3a5c
8 changed files with 51 additions and 13 deletions
|
@ -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(
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue