mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-23 07:56:52 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ac0651a420 | |||
| 5b86242313 | |||
| d24e79c991 |
@@ -31,6 +31,7 @@ object GameHelper {
|
||||
|
||||
fun getGames(): List<Game> {
|
||||
val games = mutableListOf<Game>()
|
||||
val gamesByProgramId = mutableMapOf<String, Game>()
|
||||
val context = YuzuApplication.appContext
|
||||
preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
@@ -63,6 +64,7 @@ object GameHelper {
|
||||
|
||||
addGamesRecursive(
|
||||
games,
|
||||
gamesByProgramId,
|
||||
FileUtil.listFiles(gameDirUri),
|
||||
scanDepth,
|
||||
mountedContainerUris
|
||||
@@ -136,6 +138,7 @@ object GameHelper {
|
||||
|
||||
private fun addGamesRecursive(
|
||||
games: MutableList<Game>,
|
||||
gamesByProgramId: MutableMap<String, Game>,
|
||||
files: Array<MinimalDocumentFile>,
|
||||
depth: Int,
|
||||
mountedContainerUris: MutableSet<String>
|
||||
@@ -148,6 +151,7 @@ object GameHelper {
|
||||
if (it.isDirectory) {
|
||||
addGamesRecursive(
|
||||
games,
|
||||
gamesByProgramId,
|
||||
FileUtil.listFiles(it.uri),
|
||||
depth - 1,
|
||||
mountedContainerUris
|
||||
@@ -156,8 +160,9 @@ object GameHelper {
|
||||
val extension = FileUtil.getExtension(it.uri).lowercase()
|
||||
val filePath = it.uri.toString()
|
||||
|
||||
if (externalContentExtensions.contains(extension) &&
|
||||
mountedContainerUris.add(filePath)) {
|
||||
val mountedContainer = externalContentExtensions.contains(extension) &&
|
||||
mountedContainerUris.add(filePath)
|
||||
if (mountedContainer) {
|
||||
NativeLibrary.addGameFolderFileToFilesystemProvider(filePath)
|
||||
}
|
||||
|
||||
@@ -165,6 +170,20 @@ object GameHelper {
|
||||
val game = getGame(it.uri, true, false)
|
||||
if (game != null) {
|
||||
games.add(game)
|
||||
if (game.programId != "0") {
|
||||
gamesByProgramId[game.programId] = game
|
||||
}
|
||||
} else if (mountedContainer) {
|
||||
GameMetadata.getProgramId(filePath).toLongOrNull()?.let { programId ->
|
||||
gamesByProgramId[(programId and 0x800L.inv()).toString()]
|
||||
}?.let { existingGame ->
|
||||
NativeLibrary.getPatchesForFile(existingGame.path, existingGame.programId)
|
||||
existingGame.version = GameMetadata.getVersion(
|
||||
existingGame.path,
|
||||
true
|
||||
)
|
||||
GameIconUtils.refreshGameIcon(existingGame)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -24,6 +27,15 @@ import coil.request.Options
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.model.Game
|
||||
import java.util.Collections
|
||||
import java.util.WeakHashMap
|
||||
|
||||
private val gameIconHashes = Collections.synchronizedMap(mutableMapOf<String, Int>())
|
||||
private val gameIconTargets = Collections.synchronizedMap(WeakHashMap<ImageView, GameIconTarget>())
|
||||
|
||||
private fun Game.iconCacheKey(): String = "$path|$version"
|
||||
|
||||
private data class GameIconTarget(val game: Game, var iconHash: Int? = null)
|
||||
|
||||
class GameIconFetcher(
|
||||
private val game: Game,
|
||||
@@ -31,14 +43,15 @@ class GameIconFetcher(
|
||||
) : Fetcher {
|
||||
override suspend fun fetch(): FetchResult {
|
||||
return DrawableResult(
|
||||
drawable = decodeGameIcon(game.path)!!.toDrawable(options.context.resources),
|
||||
drawable = decodeGameIcon(game)!!.toDrawable(options.context.resources),
|
||||
isSampled = false,
|
||||
dataSource = DataSource.DISK
|
||||
)
|
||||
}
|
||||
|
||||
private fun decodeGameIcon(uri: String): Bitmap? {
|
||||
val data = GameMetadata.getIcon(uri)
|
||||
private fun decodeGameIcon(game: Game): Bitmap? {
|
||||
val data = GameMetadata.getIcon(game.path)
|
||||
gameIconHashes[game.iconCacheKey()] = data.contentHashCode()
|
||||
return BitmapFactory.decodeByteArray(
|
||||
data,
|
||||
0,
|
||||
@@ -54,7 +67,7 @@ class GameIconFetcher(
|
||||
}
|
||||
|
||||
class GameIconKeyer : Keyer<Game> {
|
||||
override fun key(data: Game, options: Options): String = data.path
|
||||
override fun key(data: Game, options: Options): String = data.iconCacheKey()
|
||||
}
|
||||
|
||||
object GameIconUtils {
|
||||
@@ -71,14 +84,58 @@ object GameIconUtils {
|
||||
.build()
|
||||
|
||||
fun loadGameIcon(game: Game, imageView: ImageView) {
|
||||
gameIconTargets[imageView] = GameIconTarget(game)
|
||||
val request = ImageRequest.Builder(YuzuApplication.appContext)
|
||||
.data(game)
|
||||
.target(imageView)
|
||||
.error(R.drawable.default_icon)
|
||||
.listener(
|
||||
onSuccess = { _, _ ->
|
||||
val target = gameIconTargets[imageView]
|
||||
if (target?.game?.iconCacheKey() == game.iconCacheKey()) {
|
||||
gameIconHashes[game.iconCacheKey()]?.let {
|
||||
target.iconHash = it
|
||||
}
|
||||
}
|
||||
},
|
||||
onError = { _, _ ->
|
||||
gameIconTargets[imageView]?.iconHash = null
|
||||
}
|
||||
)
|
||||
.build()
|
||||
imageLoader.enqueue(request)
|
||||
}
|
||||
|
||||
fun refreshGameIcon(game: Game) {
|
||||
val targets = synchronized(gameIconTargets) {
|
||||
gameIconTargets
|
||||
.filterValues { it.game.path == game.path && it.game.programId == game.programId }
|
||||
.keys
|
||||
.toList()
|
||||
}
|
||||
if (targets.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val iconHash = GameMetadata.getIcon(game.path).contentHashCode()
|
||||
val targetsToRefresh = targets.filter { gameIconTargets[it]?.iconHash != iconHash }
|
||||
if (targetsToRefresh.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
imageLoader.memoryCache?.remove(MemoryCache.Key(game.iconCacheKey()))
|
||||
targetsToRefresh.forEach { imageView ->
|
||||
imageView.post {
|
||||
val target = gameIconTargets[imageView] ?: return@post
|
||||
if (target.game.path == game.path && target.game.programId == game.programId) {
|
||||
if (target.iconHash != iconHash) {
|
||||
loadGameIcon(game, imageView)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap {
|
||||
val request = ImageRequest.Builder(YuzuApplication.appContext)
|
||||
.data(game)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:color="?attr/colorControlHighlight" />
|
||||
<item android:state_focused="true" android:color="@android:color/transparent" />
|
||||
<item android:state_selected="true" android:color="@android:color/transparent" />
|
||||
<item android:state_hovered="true" android:color="@android:color/transparent" />
|
||||
<item android:color="@android:color/transparent" />
|
||||
</selector>
|
||||
@@ -10,6 +10,7 @@
|
||||
android:clipChildren="true"
|
||||
android:layout_margin="0dp"
|
||||
app:cardBackgroundColor="@color/eden_card_background"
|
||||
app:rippleColor="@color/game_card_ripple"
|
||||
app:strokeWidth="1dp"
|
||||
app:strokeColor="@color/eden_border">
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardPreventCornerOverlap="true"
|
||||
android:clipChildren="true"
|
||||
app:rippleColor="@color/game_card_ripple"
|
||||
android:layout_margin="4dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
android:focusable="true"
|
||||
android:transitionName="card_game"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:rippleColor="@color/game_card_ripple"
|
||||
android:foreground="@color/eden_border_gradient_start">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
android:focusable="true"
|
||||
android:transitionName="card_game_compact"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:rippleColor="@color/game_card_ripple"
|
||||
android:foreground="@color/eden_border_gradient_start">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="0dp"
|
||||
app:cardBackgroundColor="@android:color/transparent"
|
||||
app:rippleColor="@color/game_card_ripple"
|
||||
app:strokeWidth="0dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/common_types.h"
|
||||
#include "dynarmic/backend/exception_handler.h"
|
||||
#include "dynarmic/common/context.h"
|
||||
@@ -54,21 +53,23 @@ class SigHandler {
|
||||
return e.first <= offset && e.first + e.second.size > offset;
|
||||
});
|
||||
}
|
||||
static void SigAction(int sig, siginfo_t* info, void* raw_context);
|
||||
|
||||
bool supports_fast_mem = true;
|
||||
void* signal_stack_memory = nullptr;
|
||||
ankerl::unordered_dense::map<u64, CodeBlockInfo> code_block_infos;
|
||||
std::shared_mutex code_block_infos_mutex;
|
||||
struct sigaction old_sa_segv;
|
||||
struct sigaction old_sa_bus;
|
||||
std::unique_ptr<uint8_t[]> signal_stack_memory;
|
||||
bool supports_fast_mem = true;
|
||||
std::size_t signal_stack_size;
|
||||
public:
|
||||
SigHandler() noexcept {
|
||||
auto const stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024);
|
||||
signal_stack_memory = std::make_unique<uint8_t[]>(stack_size);
|
||||
signal_stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024);
|
||||
signal_stack_memory = mmap(nullptr, signal_stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
stack_t signal_stack{};
|
||||
signal_stack.ss_sp = signal_stack_memory.get();
|
||||
signal_stack.ss_size = stack_size;
|
||||
signal_stack.ss_sp = signal_stack_memory;
|
||||
signal_stack.ss_size = signal_stack_size;
|
||||
signal_stack.ss_flags = 0;
|
||||
if (sigaltstack(&signal_stack, nullptr) != 0) {
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n");
|
||||
@@ -86,7 +87,7 @@ public:
|
||||
supports_fast_mem = false;
|
||||
return;
|
||||
}
|
||||
#if defined(__APPLE__)
|
||||
#ifdef __APPLE__
|
||||
if (sigaction(SIGBUS, &sa, &old_sa_bus) != 0) {
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGBUS handler\n");
|
||||
supports_fast_mem = false;
|
||||
@@ -95,6 +96,10 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
~SigHandler() noexcept {
|
||||
munmap(signal_stack_memory, signal_stack_size);
|
||||
}
|
||||
|
||||
void AddCodeBlock(u64 offset, CodeBlockInfo cbi) noexcept {
|
||||
std::unique_lock guard(code_block_infos_mutex);
|
||||
code_block_infos.insert_or_assign(offset, cbi);
|
||||
@@ -104,17 +109,14 @@ public:
|
||||
code_block_infos.erase(offset);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool SupportsFastmem() const noexcept {
|
||||
return supports_fast_mem;
|
||||
}
|
||||
|
||||
static void RegisterHandler();
|
||||
static void SigAction(int sig, siginfo_t* info, void* raw_context);
|
||||
bool SupportsFastmem() const noexcept { return supports_fast_mem; }
|
||||
};
|
||||
|
||||
std::mutex handler_lock;
|
||||
std::optional<SigHandler> sig_handler;
|
||||
|
||||
void SigHandler::RegisterHandler() {
|
||||
void RegisterHandler() {
|
||||
std::lock_guard<std::mutex> guard(handler_lock);
|
||||
if (!sig_handler) {
|
||||
sig_handler.emplace();
|
||||
}
|
||||
@@ -123,27 +125,51 @@ void SigHandler::RegisterHandler() {
|
||||
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||
DEBUG_ASSERT(sig == SIGSEGV || sig == SIGBUS);
|
||||
CTX_DECLARE(raw_context);
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
{
|
||||
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||
if (auto const iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
|
||||
FakeCall fc = iter->second.cb(CTX_PC);
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
CTX_SP -= sizeof(u64);
|
||||
*std::bit_cast<u64*>(CTX_SP) = fc.ret_rip;
|
||||
CTX_PC = fc.call_rip;
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
CTX_PC = fc.call_pc;
|
||||
#elif defined(ARCHITECTURE_riscv64)
|
||||
CTX_PC = fc.call_sepc;
|
||||
#elif defined(ARCHITECTURE_loongarch64)
|
||||
CTX_PC = fc.call_pc;
|
||||
#else
|
||||
ASSERT(false);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG_ERROR(Core, "Unhandled {} at {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||
fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
{
|
||||
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
|
||||
FakeCall fc = iter->second.cb(CTX_PC);
|
||||
CTX_PC = fc.call_pc;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||
#elif defined(ARCHITECTURE_riscv64)
|
||||
{
|
||||
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_SEPC); iter != sig_handler->code_block_infos.end()) {
|
||||
FakeCall fc = iter->second.cb(CTX_SEPC);
|
||||
CTX_SEPC = fc.call_sepc;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_SEPC);
|
||||
#elif defined(ARCHITECTURE_loongarch64)
|
||||
{
|
||||
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
|
||||
FakeCall fc = iter->second.cb(CTX_PC);
|
||||
CTX_PC = fc.call_pc;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus;
|
||||
if (retry_sa->sa_flags & SA_SIGINFO) {
|
||||
@@ -164,10 +190,9 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||
|
||||
struct ExceptionHandler::Impl final {
|
||||
Impl(u64 offset_, u64 size_)
|
||||
: offset(offset_)
|
||||
, size(size_)
|
||||
{
|
||||
SigHandler::RegisterHandler();
|
||||
: offset(offset_)
|
||||
, size(size_) {
|
||||
RegisterHandler();
|
||||
}
|
||||
|
||||
void SetCallback(std::function<FakeCall(u64)> cb) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <variant>
|
||||
#include "dynarmic/backend/loongarch64/emit_loongarch64.h"
|
||||
|
||||
#include "dynarmic/backend/loongarch64/a32_jitstate.h"
|
||||
@@ -95,7 +96,8 @@ EmittedBlockInfo EmitLoongArch64(lagoon_assembler_t& as, IR::Block block, const
|
||||
|
||||
// TODO: Emit Terminal
|
||||
const auto term = block.GetTerminal();
|
||||
const IR::Term::LinkBlock* link_block_term = boost::get<IR::Term::LinkBlock>(&term);
|
||||
const IR::Term::LeafTerminal* leaft_term = std::get_if<IR::Term::LeafTerminal>(&term);
|
||||
const IR::Term::LinkBlock* link_block_term = std::get_if<IR::Term::LinkBlock>(leaft_term);
|
||||
ASSERT(link_block_term);
|
||||
la_load_immediate64(&as, Xscratch0, link_block_term->next.Value());
|
||||
la_st_w(&as, Xscratch0, Xstate, static_cast<int32_t>(offsetof(A32JitState, regs) + sizeof(u32) * 15));
|
||||
|
||||
@@ -136,14 +136,14 @@
|
||||
# endif
|
||||
#elif defined(ARCHITECTURE_riscv64)
|
||||
# if defined(__FreeBSD__)
|
||||
# define CTX_PC (mctx.mc_gpregs.gp_sepc)
|
||||
# define CTX_SEPC (mctx.mc_gpregs.gp_sepc)
|
||||
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
||||
# elif defined(__linux__)
|
||||
# define CTX_PC (mctx.__gregs[REG_PC])
|
||||
# define CTX_SEPC (mctx.__gregs[REG_PC])
|
||||
# define CTX_SP (mctx.__gregs[REG_SP])
|
||||
# elif defined(__OpenBSD__)
|
||||
// https://github.com/openbsd/src/blob/master/sys/arch/riscv64/include/signal.h
|
||||
# define CTX_PC (ucontext->sc_sepc)
|
||||
# define CTX_SEPC (ucontext->sc_sepc)
|
||||
# define CTX_SP (ucontext->sc_sp)
|
||||
# else
|
||||
# error "unknown platform"
|
||||
|
||||
Reference in New Issue
Block a user