Compare commits

...

3 Commits

Author SHA1 Message Date
b5c86787ab jdlo modifications
Some checks failed
Check Strings / check-strings (push) Has been cancelled
tx-src / sources (push) Has been cancelled
2025-12-26 13:28:47 -03:00
xbzk
cfae726289 [video_core] nvn descriptor layout fix (#3206)
Yxzx presumes this:
// The NVN driver buffer (index 0) is known to pack the SSBO address followed by its size.
But in MCI i`ve discovered that there are no sizes, both registers are GPU addresses (hence the 2.8gb allocation, it was an address actually)

Method could be much simpler but for safety i`ve routed both old and new worlds.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3206
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2025-12-26 04:54:14 +01:00
Gamer64
bb94cff886 [chore] Fixed a couple memory leaks using up ~15 MB each iteration (#398)
Co-authored-by: Jarrod Norwell <official.antique@gmail.com>
Co-authored-by: Gamer64 <76565986+Gamer64ytb@users.noreply.github.com>
Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/398
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: Gamer64 <gamer64@eden-emu.dev>
Co-committed-by: Gamer64 <gamer64@eden-emu.dev>
2025-12-26 02:55:52 +01:00
27 changed files with 605 additions and 65 deletions

View File

@@ -39,7 +39,6 @@ android {
buildFeatures {
viewBinding = true
buildConfig = true
}
compileOptions {

View File

@@ -14,6 +14,8 @@ android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
kotlin.parallel.tasks.in.project=true
android.defaults.buildfeatures.buildconfig=true
# Android Gradle plugin 8.0.2
android.suppressUnsupportedCompileSdk=34
android.native.buildOutput=verbose

View File

@@ -34,8 +34,8 @@ struct Member {
struct RoomInformation {
std::string name; ///< Name of the server
std::string description; ///< Server description
u32 member_slots; ///< Maximum number of members in this room
u16 port; ///< The port of this room
u32 member_slots{}; ///< Maximum number of members in this room
u16 port{}; ///< The port of this room
GameInfo preferred_game; ///< Game to advertise that you want to play
std::string host_username; ///< Forum username of the host
};
@@ -46,8 +46,8 @@ struct Room {
std::string id;
std::string verify_uid; ///< UID used for verification
std::string ip;
u32 net_version;
bool has_password;
u32 net_version{};
bool has_password = false;
std::vector<Member> members;
};

View File

@@ -17,8 +17,6 @@ add_library(core STATIC
constants.h
core.cpp
core.h
game_settings.cpp
game_settings.h
core_timing.cpp
core_timing.h
cpu_manager.cpp
@@ -45,7 +43,11 @@ add_library(core STATIC
device_memory.cpp
device_memory.h
device_memory_manager.h
device_memory.h
device_memory_manager.h
device_memory_manager.inc
internal_network/legacy_online.cpp
internal_network/legacy_online.h
file_sys/bis_factory.cpp
file_sys/bis_factory.h
file_sys/card_image.cpp

View File

@@ -49,6 +49,7 @@
#include "core/hle/service/services.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
#include "core/internal_network/legacy_online.h"
#include "core/internal_network/network.h"
#include "core/loader/loader.h"
#include "core/memory.h"
@@ -137,6 +138,13 @@ struct System::Impl {
kernel.SetMulticore(is_multicore);
cpu_manager.SetMulticore(is_multicore);
cpu_manager.SetAsyncGpu(is_async_gpu);
cpu_manager.SetMulticore(is_multicore);
cpu_manager.SetAsyncGpu(is_async_gpu);
if (!legacy_online) {
legacy_online = std::make_unique<Network::LegacyOnlineService>();
legacy_online->Start();
}
}
void ReinitializeIfNecessary(System& system) {
@@ -293,6 +301,48 @@ struct System::Impl {
return SystemResultStatus::Success;
}
void LoadOverrides(u64 programId) const {
std::string vendor = gpu_core->Renderer().GetDeviceVendor();
LOG_INFO(Core, "GPU Vendor: {}", vendor);
// Reset all per-game flags
Settings::values.use_squashed_iterated_blend = false;
// Insert PC overrides here
#ifdef ANDROID
// Example on how to set a setting based on the program ID and vendor
if (programId == 0x010028600EBDA000 && vendor == "Mali") { // Mario 3d World
// Settings::values.example = true;
}
// Example array of program IDs
const std::array<u64, 10> example_array = {
//0xprogramId
0x0004000000033400, // Game 1
0x0004000000033500 // Game 2
// And so on
};
for (auto id : example_array) {
if (programId == id) {
// Settings::values.example = true;
break;
}
}
#endif
// Ninja Gaiden Ragebound
constexpr u64 ngr = 0x0100781020710000ULL;
if (programId == ngr) {
LOG_INFO(Core, "Enabling game specifc override: use_squashed_iterated_blend");
Settings::values.use_squashed_iterated_blend = true;
}
}
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath,
Service::AM::FrontendAppletParameters& params) {
@@ -378,8 +428,7 @@ struct System::Impl {
LOG_ERROR(Core, "Failed to find program id for ROM");
}
GameSettings::LoadOverrides(program_id, gpu_core->Renderer());
LoadOverrides(program_id);
if (auto room_member = Network::GetRoomMember().lock()) {
Network::GameInfo game_info;
game_info.name = name;
@@ -428,6 +477,11 @@ struct System::Impl {
stop_event = {};
Network::RestartSocketOperations();
if (legacy_online) {
legacy_online->Stop();
legacy_online.reset();
}
if (auto room_member = Network::GetRoomMember().lock()) {
Network::GameInfo game_info{};
room_member->SendGameInfo(game_info);
@@ -516,6 +570,9 @@ struct System::Impl {
/// Network instance
Network::NetworkInstance network_instance;
/// Legacy Online Service
std::unique_ptr<Network::LegacyOnlineService> legacy_online;
/// Debugger
std::unique_ptr<Core::Debugger> debugger;

View File

@@ -11,6 +11,8 @@
#include "core/core.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/hle/service/filesystem/filesystem.h"
namespace FileSys {
@@ -74,20 +76,97 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut
VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
u64 target_program_id = meta.program_id;
// CRITICAL FIX: If the game requests Cache/Temp with ProgramID 0 (generic),
// we MUST redirect it to the actual running TitleID, otherwise it looks in '.../0000000000000000'.
if ((meta.type == SaveDataType::Cache || meta.type == SaveDataType::Temporary) && target_program_id == 0) {
target_program_id = system.GetApplicationProcessProgramID();
LOG_INFO(Service_FS, "Redirecting generic Cache request (ID 0) to active TitleID: {:016X}", target_program_id);
}
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, target_program_id,
meta.user_id, meta.system_save_data_id);
auto out = dir->GetDirectoryRelative(save_directory);
// Fallback for Ryujinx-style cache paths: sdcard/Nintendo/save/{TitleID} (No /cache/ subdir)
// Also include Temporary, as games like Just Dance use Temporary storage for cache-like data (MapBaseCache),
// and Ryujinx stores this in the same simple 'save/{ID}' structure.
// Fallback logic removed. We now enforce standard paths.
// User instructions will direct them to the correct folder.
if (out == nullptr) {
LOG_WARNING(Service_FS, "Cache/Save path NOT FOUND: '{}'. Auto-create={}", save_directory, auto_create);
} else {
LOG_INFO(Service_FS, "Cache/Save path FOUND: '{}'", save_directory);
}
if (out == nullptr && (ShouldSaveDataBeAutomaticallyCreated(space, meta) && auto_create)) {
LOG_INFO(Service_FS, "Auto-creating save directory...");
return Create(space, meta);
}
if (out != nullptr) {
// Some emulators (Ryujinx) or even different firmware versions may rely on the commit
// directories /0 or /1 being present for cache or save data.
// We prioritizing /1 as it usually implies a newer commit if both exist,
// but /0 is what's commonly used by Ryujinx for cache.
// Ryujinx behavior: If 0 exists and 1 does not, copy 0 to 1.
auto dir_0 = out->GetSubdirectory("0");
auto dir_1 = out->GetSubdirectory("1");
if (dir_0) LOG_INFO(Service_FS, "Found subdirectory '0' in save path.");
if (dir_1) LOG_INFO(Service_FS, "Found subdirectory '1' in save path.");
if (dir_0 != nullptr && dir_1 == nullptr) {
LOG_INFO(Service_FS, "Ryujinx structure detected: '0' exists, '1' missing. Copying 0->1 for compatibility.");
dir_1 = out->CreateSubdirectory("1");
if (dir_1 != nullptr) {
// Copy contents from 0 to 1
VfsRawCopyD(dir_0, dir_1);
}
}
// Check for 'Addressables' and 'Addressables2' and delete 'json.cache' if present.
// This is a specific workaround for games (e.g. Just Dance) that freeze if they find old cache metadata.
// We force them to regenerate it.
const auto CleanCache = [](VirtualDir root) {
if (root == nullptr) return;
const char* subdirs[] = {"Addressables", "Addressables2"};
for (const char* subdir_name : subdirs) {
auto subdir = root->GetSubdirectory(subdir_name);
if (subdir != nullptr) {
if (subdir->DeleteFile("json.cache")) {
LOG_INFO(Service_FS, "Deleted stale 'json.cache' in '{}'", subdir_name);
}
}
}
};
if (dir_1 != nullptr) {
LOG_INFO(Service_FS, "Returning subdirectory '1' as Save Root.");
CleanCache(dir_1);
return dir_1;
}
if (dir_0 != nullptr) {
LOG_INFO(Service_FS, "Returning subdirectory '0' as Save Root.");
CleanCache(dir_0);
return dir_0;
}
LOG_INFO(Service_FS, "No '0' or '1' subdirectories found. Returning parent folder as Save Root.");
CleanCache(out);
}
return out;
}
VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const {
return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space));
const auto path = GetSaveDataSpaceIdPath(space);
// Ensure the directory exists, otherwise FindAllSaves fails.
return GetOrCreateDirectoryRelative(dir, path);
// return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space));
}
std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
@@ -96,12 +175,12 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
return "/system/";
case SaveDataSpaceId::User:
case SaveDataSpaceId::SdUser:
case SaveDataSpaceId::Temporary: // Map into User so we can find the save/ folder
return "/user/";
case SaveDataSpaceId::Temporary:
return "/temp/";
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
return "/unrecognized/"; ///< To prevent corruption when ignoring asserts.
// ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
LOG_WARNING(Service_FS, "Unrecognized SaveDataSpaceId: {:02X}, defaulting to /user/", static_cast<u8>(space));
return "/user/";
}
}
@@ -137,8 +216,9 @@ std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir,
return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
case SaveDataType::Temporary:
return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
// Unified Cache/Temporary Path: Always use save/cache/{TitleID}
// This simplifies user instructions and avoids UUID/Permission issues.
return fmt::format("{}save/cache/{:016X}", out, title_id);
case SaveDataType::Cache:
return fmt::format("{}save/cache/{:016X}", out, title_id);
default:

View File

@@ -21,7 +21,7 @@ namespace Core::Frontend {
struct CabinetParameters {
Service::NFP::TagInfo tag_info;
Service::NFP::RegisterInfo register_info;
Service::NFP::CabinetMode mode;
Service::NFP::CabinetMode mode{};
};
using CabinetCallback = std::function<void(bool, const std::string&)>;

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -20,9 +23,9 @@ struct KeyboardInitializeParameters {
std::u16string initial_text;
char16_t left_optional_symbol_key;
char16_t right_optional_symbol_key;
u32 max_text_length;
u32 min_text_length;
s32 initial_cursor_position;
u32 max_text_length{};
u32 min_text_length{};
s32 initial_cursor_position{};
Service::AM::Frontend::SwkbdType type;
Service::AM::Frontend::SwkbdPasswordMode password_mode;
Service::AM::Frontend::SwkbdTextDrawType text_draw_type;
@@ -34,12 +37,12 @@ struct KeyboardInitializeParameters {
};
struct InlineAppearParameters {
u32 max_text_length;
u32 min_text_length;
f32 key_top_scale_x;
f32 key_top_scale_y;
f32 key_top_translate_x;
f32 key_top_translate_y;
u32 max_text_length{};
u32 min_text_length{};
f32 key_top_scale_x{};
f32 key_top_scale_y{};
f32 key_top_translate_x{};
f32 key_top_translate_y{};
Service::AM::Frontend::SwkbdType type;
Service::AM::Frontend::SwkbdKeyDisableFlags key_disable_flags;
bool key_top_as_floating;
@@ -50,7 +53,7 @@ struct InlineAppearParameters {
struct InlineTextParameters {
std::u16string input_text;
s32 cursor_position;
s32 cursor_position{};
};
class SoftwareKeyboardApplet : public Applet {

View File

@@ -686,6 +686,154 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
using EdenPath = Common::FS::EdenPath;
const auto sdmc_dir_path = Common::FS::GetEdenPath(EdenPath::SDMCDir);
const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents";
// If the NAND user save location doesn't exist but the SDMC contains
// Nintendo/save (common portable save structure), create a host-side
// symlink so the emulator will see those saves under the expected NAND path.
// This helps users who placed saves under `<eden>/user/sdmc/Nintendo/save/...`.
try {
const auto nand_fs_path = Common::FS::GetEdenPath(EdenPath::NANDDir);
const auto sdmc_fs_path = sdmc_dir_path;
const auto nand_user_save_path = nand_fs_path / "user" / "save";
const auto sdmc_nintendo_save_path = sdmc_fs_path / "Nintendo" / "save";
std::error_code ec;
if (!std::filesystem::exists(nand_user_save_path) &&
std::filesystem::exists(sdmc_nintendo_save_path)) {
std::filesystem::create_directory_symlink(sdmc_nintendo_save_path, nand_user_save_path, ec);
if (ec) {
LOG_WARNING(Service_FS, "Could not create symlink {} -> {}: {}",
Common::FS::PathToUTF8String(nand_user_save_path),
Common::FS::PathToUTF8String(sdmc_nintendo_save_path), ec.message());
// Fallback: copy the SDMC saves into the NAND save folder so the emulator can see them
try {
if (!std::filesystem::exists(nand_user_save_path)) {
std::filesystem::create_directories(nand_user_save_path);
}
std::filesystem::copy(sdmc_nintendo_save_path, nand_user_save_path,
std::filesystem::copy_options::recursive |
std::filesystem::copy_options::skip_existing,
ec);
if (ec) {
LOG_WARNING(Service_FS, "Failed to copy SDMC saves to {}: {}",
Common::FS::PathToUTF8String(nand_user_save_path), ec.message());
} else {
LOG_INFO(Service_FS, "Copied SDMC saves to NAND save path: {} -> {}",
Common::FS::PathToUTF8String(sdmc_nintendo_save_path),
Common::FS::PathToUTF8String(nand_user_save_path));
}
} catch (const std::exception& ex) {
LOG_WARNING(Service_FS, "Exception while copying SDMC saves: {}", ex.what());
}
} else {
LOG_INFO(Service_FS, "Linked NAND save path to SDMC saves: {} -> {}",
Common::FS::PathToUTF8String(nand_user_save_path),
Common::FS::PathToUTF8String(sdmc_nintendo_save_path));
}
}
// Also support official-style SD cache directory name used by some Switch setups.
// If users placed saves or cache under `<eden>/user/sdmc/Nintendo/SD_Cache.0000`,
// expose it to the NAND user save path as well so the emulator can find them.
const auto sdmc_nintendo_cache_path = sdmc_fs_path / "Nintendo" / "SD_Cache.0000";
if (!std::filesystem::exists(nand_user_save_path) &&
std::filesystem::exists(sdmc_nintendo_cache_path)) {
std::error_code ec2;
std::filesystem::create_directory_symlink(sdmc_nintendo_cache_path, nand_user_save_path,
ec2);
if (ec2) {
LOG_WARNING(Service_FS,
"Could not create symlink {} -> {}: {}",
Common::FS::PathToUTF8String(nand_user_save_path),
Common::FS::PathToUTF8String(sdmc_nintendo_cache_path), ec2.message());
// Fallback: copy the SDMC cache into the NAND save folder so the emulator can see them
try {
if (!std::filesystem::exists(nand_user_save_path)) {
std::filesystem::create_directories(nand_user_save_path);
}
std::filesystem::copy(sdmc_nintendo_cache_path, nand_user_save_path,
std::filesystem::copy_options::recursive |
std::filesystem::copy_options::skip_existing,
ec2);
if (ec2) {
LOG_WARNING(Service_FS, "Failed to copy SDMC cache to {}: {}",
Common::FS::PathToUTF8String(nand_user_save_path), ec2.message());
} else {
LOG_INFO(Service_FS, "Copied SDMC cache to NAND save path: {} -> {}",
Common::FS::PathToUTF8String(sdmc_nintendo_cache_path),
Common::FS::PathToUTF8String(nand_user_save_path));
}
} catch (const std::exception& ex) {
LOG_WARNING(Service_FS, "Exception while copying SDMC cache: {}", ex.what());
}
} else {
LOG_INFO(Service_FS, "Linked NAND save path to SDMC cache: {} -> {}",
Common::FS::PathToUTF8String(nand_user_save_path),
Common::FS::PathToUTF8String(sdmc_nintendo_cache_path));
}
}
// If the NAND save folder already exists, ensure individual entries from
// SDMC (both `Nintendo/save` and `Nintendo/SD_Cache.0000`) are visible
// inside it: create per-entry symlinks (with copy fallback) for missing
// title/account directories so saves placed in SDMC are reachable.
auto try_merge_sdmc_entries = [&](const std::filesystem::path& sdmc_src) {
try {
if (!std::filesystem::exists(sdmc_src))
return;
std::error_code ec;
if (!std::filesystem::exists(nand_user_save_path))
std::filesystem::create_directories(nand_user_save_path, ec);
for (auto& ent : std::filesystem::directory_iterator(sdmc_src)) {
const auto name = ent.path().filename();
const auto target = nand_user_save_path / name;
if (std::filesystem::exists(target))
continue;
std::error_code ec2;
std::filesystem::create_directory_symlink(ent.path(), target, ec2);
if (ec2) {
LOG_WARNING(Service_FS, "Could not create symlink {} -> {}: {}",
Common::FS::PathToUTF8String(target),
Common::FS::PathToUTF8String(ent.path()), ec2.message());
try {
std::filesystem::copy(ent.path(), target,
std::filesystem::copy_options::recursive |
std::filesystem::copy_options::skip_existing,
ec2);
if (ec2) {
LOG_WARNING(Service_FS, "Failed to copy {} -> {}: {}",
Common::FS::PathToUTF8String(ent.path()),
Common::FS::PathToUTF8String(target), ec2.message());
} else {
LOG_INFO(Service_FS, "Copied SDMC entry to NAND: {} -> {}",
Common::FS::PathToUTF8String(ent.path()),
Common::FS::PathToUTF8String(target));
}
} catch (const std::exception& ex) {
LOG_WARNING(Service_FS, "Exception while copying SDMC entry: {}",
ex.what());
}
} else {
LOG_INFO(Service_FS, "Linked NAND entry to SDMC: {} -> {}",
Common::FS::PathToUTF8String(target),
Common::FS::PathToUTF8String(ent.path()));
}
}
} catch (const std::exception& ex) {
LOG_WARNING(Service_FS, "Exception while merging SDMC entries: {}", ex.what());
}
};
try_merge_sdmc_entries(sdmc_nintendo_save_path);
try_merge_sdmc_entries(sdmc_nintendo_cache_path);
} catch (const std::exception& e) {
LOG_WARNING(Service_FS, "Exception while linking SDMC saves: {}", e.what());
}
const auto rw_mode = FileSys::OpenMode::ReadWrite;
auto nand_directory =

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -79,7 +82,7 @@ using DeviceHandle = u64;
// This is nn::nfc::TagInfo
struct TagInfo {
UniqueSerialNumber uuid;
UniqueSerialNumber uuid{};
u8 uuid_length;
INSERT_PADDING_BYTES(0x15);
NfcProtocol protocol;

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -315,7 +318,7 @@ static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
// This is nn::nfp::RegisterInfo
struct RegisterInfo {
Service::Mii::CharInfo mii_char_info;
WriteDate creation_date;
WriteDate creation_date{};
AmiiboName amiibo_name;
u8 font_region;
INSERT_PADDING_BYTES(0x7A);

View File

@@ -164,7 +164,7 @@ IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const ch
// Rebuild shared fonts from data ncas or synthesize
impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE);
for (auto font : SHARED_FONTS) {
for (auto& font : SHARED_FONTS) {
FileSys::VirtualFile romfs;
const auto nca =
nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data);
@@ -261,7 +261,7 @@ Result IPlatformServiceManager::GetSharedFontInOrderOfPriority(
out_font_sizes.size(), impl->shared_font_regions.size()});
for (size_t i = 0; i < max_size; i++) {
auto region = impl->GetSharedFontRegion(i);
auto& region = impl->GetSharedFontRegion(i);
out_font_codes[i] = static_cast<u32>(i);
out_font_offsets[i] = region.offset;

View File

@@ -0,0 +1,164 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/internal_network/legacy_online.h"
#include <cstring>
#include <iostream>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#include "common/logging/log.h"
namespace Network {
LegacyOnlineService::LegacyOnlineService() = default;
LegacyOnlineService::~LegacyOnlineService() {
Stop();
}
void LegacyOnlineService::Start() {
if (is_running) {
return;
}
is_running = true;
worker_thread = std::thread(&LegacyOnlineService::ServerLoop, this);
}
void LegacyOnlineService::Stop() {
if (!is_running) {
return;
}
is_running = false;
// Close socket to wake up the thread if it's blocked on recvfrom
if (socket_fd != ~0ULL) {
#ifdef _WIN32
closesocket(static_cast<SOCKET>(socket_fd));
#else
close(static_cast<int>(socket_fd));
#endif
socket_fd = ~0ULL;
}
if (worker_thread.joinable()) {
worker_thread.join();
}
}
void LegacyOnlineService::ServerLoop() {
LOG_INFO(Network, "Starting Legacy Online UDP Server on port {}", PORT);
#ifdef _WIN32
WSADATA wsa_data;
if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) {
LOG_ERROR(Network, "WSAStartup failed");
is_running = false;
return;
}
#endif
auto s = socket(AF_INET, SOCK_DGRAM, 0);
#ifdef _WIN32
if (s == INVALID_SOCKET) {
#else
if (s == -1) {
#endif
LOG_ERROR(Network, "Failed to create socket");
is_running = false;
return;
}
socket_fd = static_cast<uintptr_t>(s);
int opt = 1;
#ifdef _WIN32
setsockopt(static_cast<SOCKET>(socket_fd), SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
#else
setsockopt(static_cast<int>(socket_fd), SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#endif
sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
int res = -1;
#ifdef _WIN32
res = bind(static_cast<SOCKET>(socket_fd), (sockaddr*)&server_addr, sizeof(server_addr));
#else
res = bind(static_cast<int>(socket_fd), (sockaddr*)&server_addr, sizeof(server_addr));
#endif
if (res < 0) {
LOG_ERROR(Network, "Failed to bind to port {}", PORT);
#ifdef _WIN32
closesocket(static_cast<SOCKET>(socket_fd));
#else
close(static_cast<int>(socket_fd));
#endif
socket_fd = ~0ULL;
is_running = false;
return;
}
LOG_INFO(Network, "Legacy Online Server waiting for messages...");
// Set a timeout for recvfrom so check is_running periodically if not closed via socket
// Alternatively, closing the socket (as done in Stop) will cause recvfrom to return error
char buffer[2048];
while (is_running) {
sockaddr_in client_addr{};
#ifdef _WIN32
int client_len = sizeof(client_addr);
#else
socklen_t client_len = sizeof(client_addr);
#endif
int len = -1;
#ifdef _WIN32
len = recvfrom(static_cast<SOCKET>(socket_fd), buffer, sizeof(buffer), 0, (sockaddr*)&client_addr, &client_len);
#else
len = recvfrom(static_cast<int>(socket_fd), buffer, sizeof(buffer), 0, (sockaddr*)&client_addr, &client_len);
#endif
if (!is_running) break;
if (len > 0) {
// Send ACK
const char* ack_msg = "ACK";
#ifdef _WIN32
sendto(static_cast<SOCKET>(socket_fd), ack_msg, static_cast<int>(strlen(ack_msg)), 0, (sockaddr*)&client_addr, client_len);
#else
sendto(static_cast<int>(socket_fd), ack_msg, strlen(ack_msg), 0, (sockaddr*)&client_addr, client_len);
#endif
} else {
// Error or closed
// If we closed the socket in Stop(), this will likely trigger.
break;
}
}
#ifdef _WIN32
if (socket_fd != ~0ULL) closesocket(static_cast<SOCKET>(socket_fd));
WSACleanup();
#else
if (socket_fd != ~0ULL) close(static_cast<int>(socket_fd));
#endif
socket_fd = ~0ULL;
LOG_INFO(Network, "Legacy Online Server stopped");
}
} // namespace Network

View File

@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <atomic>
#include <cstdint>
#include <memory>
#include <thread>
namespace Network {
class LegacyOnlineService {
public:
LegacyOnlineService();
~LegacyOnlineService();
void Start();
void Stop();
private:
void ServerLoop();
std::atomic_bool is_running{false};
std::thread worker_thread;
uintptr_t socket_fd{~0ULL}; // ~0ULL is approx -1 equivalent for unsigned
static constexpr int PORT = 6000;
};
} // namespace Network

View File

@@ -48,7 +48,7 @@ private:
void Save();
PlayTimeDatabase database;
u64 running_program_id;
u64 running_program_id{};
std::jthread play_time_thread;
};

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -53,7 +56,7 @@ struct ChatEntry {
/// Represents a system status message.
struct StatusMessageEntry {
StatusMessageTypes type; ///< Type of the message
StatusMessageTypes type{}; ///< Type of the message
/// Subject of the message. i.e. the user who is joining/leaving/being banned, etc.
std::string nickname;
std::string username;

View File

@@ -1705,21 +1705,26 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
return NULL_BINDING;
}
// xbzk: New size logic. Fixes MCI.
// If ever the * comment below prove wrong, the 'if' block may be removed.
const auto size = [&]() {
const bool is_nvn_cbuf = cbuf_index == 0;
// The NVN driver buffer (index 0) is known to pack the SSBO address followed by its size.
if (is_nvn_cbuf) {
const u32 ssbo_size = gpu_memory->Read<u32>(ssbo_addr + 8);
if (ssbo_size != 0) {
return ssbo_size;
// * The NVN driver buffer (index 0) is known to pack the SSBO address followed by its size.
const u64 next_qword = gpu_memory->Read<u64>(ssbo_addr + 8);
const u32 upper_32 = static_cast<u32>(next_qword >> 32);
// Hardware-based detection: GPU addresses have non-zero upper bits
if (upper_32 == 0) {
// This is a size field, not a GPU address
return static_cast<u32>(next_qword); // Return lower_32
}
}
// Other titles (notably Doom Eternal) may use STG/LDG on buffer addresses in custom defined
// cbufs, which do not store the sizes adjacent to the addresses, so use the fully
// mapped buffer size for now.
// Fall through: either not NVN cbuf (Doom Eternal & +), or NVN but ssbo_addr+8 is a GPU address (MCI)
const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr));
// Cap at 8MB to prevent allocator overflow from misinterpreted addresses
return (std::min)(memory_layout_size, static_cast<u32>(8_MiB));
}();
// Alignment only applies to the offset of the buffer
const u32 alignment = runtime.GetStorageBufferAlignment();
const GPUVAddr aligned_gpu_addr = Common::AlignDown(gpu_addr, alignment);

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -17,7 +20,7 @@ void Scheduler::Push(s32 channel, CommandList&& entries) {
std::unique_lock lk(scheduling_guard);
auto it = channels.find(channel);
ASSERT(it != channels.end());
auto channel_state = it->second;
auto& channel_state = it->second;
gpu.BindChannel(channel_state->bind_id);
channel_state->dma_pusher->Push(std::move(entries));
channel_state->dma_pusher->DispatchCalls();

View File

@@ -107,9 +107,19 @@ void Vic::Execute() {
auto output_height{config.output_surface_config.out_surface_height + 1};
output_surface.resize_destructive(output_width * output_height);
// Initialize the surface with the appropriate black pixel
Pixel black_pixel{};
if (config.output_surface_config.out_pixel_format == VideoPixelFormat::Y8__V8U8_N420) {
// Y=0, U=512, V=512 (10-bit), A=0
black_pixel = {0, 512, 512, 0};
} else {
// R=0, G=0, B=0, A=0
black_pixel = {0, 0, 0, 0};
}
std::fill(output_surface.begin(), output_surface.end(), black_pixel);
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Off) [[unlikely]] {
// Fill the frame with black, as otherwise they can have random data and be very glitchy.
std::fill(output_surface.begin(), output_surface.end(), Pixel{});
} else {
for (size_t i = 0; i < config.slot_structs.size(); i++) {
auto& slot_config{config.slot_structs[i]};
@@ -122,7 +132,18 @@ void Vic::Execute() {
nvdec_id = frame_queue.VicFindNvdecFdFromOffset(luma_offset);
}
if (auto frame = frame_queue.GetFrame(nvdec_id, luma_offset); frame) {
auto frame = frame_queue.GetFrame(nvdec_id, luma_offset);
if (!frame) {
// We might've failed to find the frame, or the nvdec id is stale/wrong.
// Try to find the nvdec id again.
const s32 new_id = frame_queue.VicFindNvdecFdFromOffset(luma_offset);
if (new_id != -1) {
nvdec_id = new_id;
frame = frame_queue.GetFrame(nvdec_id, luma_offset);
}
}
if (frame) {
if (frame.get()) {
switch (frame->GetPixelFormat()) {
case AV_PIX_FMT_YUV420P:
@@ -170,6 +191,7 @@ void Vic::ReadProgressiveY8__V8U8_N420(const SlotStruct& slot, std::span<const P
}
slot_surface.resize_destructive(out_luma_width * out_luma_height);
std::fill(slot_surface.begin(), slot_surface.end(), Pixel{0, 512, 512, 0});
const auto in_luma_width{(std::min)(frame->GetWidth(), s32(out_luma_width))};
const auto in_luma_height{(std::min)(frame->GetHeight(), s32(out_luma_height))};
@@ -219,6 +241,7 @@ void Vic::ReadInterlacedY8__V8U8_N420(const SlotStruct& slot, std::span<const Pl
const auto out_luma_stride{out_luma_width};
slot_surface.resize_destructive(out_luma_width * out_luma_height);
std::fill(slot_surface.begin(), slot_surface.end(), Pixel{0, 512, 512, 0});
const auto in_luma_width{(std::min)(frame->GetWidth(), s32(out_luma_width))};
[[maybe_unused]] const auto in_luma_height{

View File

@@ -108,7 +108,7 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in
VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u32 down_shift = 0) {
const auto& src = regs.scissor_test[index];
VkRect2D scissor;
VkRect2D scissor{};
const auto scale_up = [&](s32 value) -> s32 {
if (value == 0) {
return 0U;
@@ -374,7 +374,7 @@ void RasterizerVulkan::Clear(u32 layer_count) {
}
UpdateViewportsState(regs);
VkRect2D default_scissor;
VkRect2D default_scissor{};
default_scissor.offset.x = 0;
default_scissor.offset.y = 0;
default_scissor.extent.width = (std::numeric_limits<s32>::max)();

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -71,11 +74,11 @@ private:
std::unique_ptr<InputProfiles> profiles;
std::array<ConfigureInputPlayer*, 8> player_controllers;
std::array<QWidget*, 8> player_tabs;
std::array<ConfigureInputPlayer*, 8> player_controllers{};
std::array<QWidget*, 8> player_tabs{};
// Checkboxes representing the "Connected Controllers".
std::array<QCheckBox*, 8> connected_controller_checkboxes;
ConfigureInputAdvanced* advanced;
std::array<QCheckBox*, 8> connected_controller_checkboxes{};
ConfigureInputAdvanced* advanced = nullptr;
Core::System& system;
};

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -689,10 +692,10 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
{
// Draw joysticks
using namespace Settings::NativeAnalog;
const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
const auto l_button = button_values[Settings::NativeButton::LStick];
const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
const auto r_button = button_values[Settings::NativeButton::RStick];
const auto& l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
const auto& l_button = button_values[Settings::NativeButton::LStick];
const auto& r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
const auto& r_button = button_values[Settings::NativeButton::RStick];
DrawJoystick(p, center + QPointF(-171, -41) + (l_stick * 4), 1.0f, l_button);
DrawJoystick(p, center + QPointF(171, 8) + (r_stick * 4), 1.0f, r_button);

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -214,7 +217,7 @@ private:
bool mapping_active{};
int blink_counter{};
int callback_key;
int callback_key{};
QColor button_color{};
ColorMapping colors{};
Core::HID::LedPattern led_pattern{0, 0, 0, 0};

View File

@@ -1495,7 +1495,7 @@ void MainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
(state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) {
auto_paused = true;
OnPauseGame();
} else if (!emu_thread->IsRunning() && auto_paused && state == Qt::ApplicationActive) {
} else if (!emu_thread->IsRunning() && auto_paused && (state & Qt::ApplicationActive)) {
auto_paused = false;
OnStartGame();
}
@@ -1505,7 +1505,7 @@ void MainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
(state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) {
Settings::values.audio_muted = true;
auto_muted = true;
} else if (auto_muted && state == Qt::ApplicationActive) {
} else if (auto_muted && (state & Qt::ApplicationActive)) {
Settings::values.audio_muted = false;
auto_muted = false;
}

View File

@@ -482,13 +482,13 @@ private:
MultiplayerState* multiplayer_state = nullptr;
GRenderWindow* render_window;
GameList* game_list;
LoadingScreen* loading_screen;
GRenderWindow* render_window = nullptr;
GameList* game_list = nullptr;
LoadingScreen* loading_screen = nullptr;
QTimer shutdown_timer;
OverlayDialog* shutdown_dialog{};
GameListPlaceholder* game_list_placeholder;
GameListPlaceholder* game_list_placeholder = nullptr;
std::vector<VkDeviceInfo::Record> vk_device_records;
@@ -531,7 +531,7 @@ private:
QString startup_icon_theme;
// Debugger panes
ControllerDialog* controller_dialog;
ControllerDialog* controller_dialog = nullptr;
QAction* actions_recent_files[max_recent_files_item];
@@ -543,7 +543,7 @@ private:
QTranslator translator;
// Install progress dialog
QProgressDialog* install_progress;
QProgressDialog* install_progress = nullptr;
// Last game booted, used for multi-process apps
QString last_filename_booted;

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -175,7 +178,7 @@ public:
private:
QString username;
QString nickname;
u64 title_id;
u64 title_id{};
QString game_name;
};