Compare commits

..

5 Commits

Author SHA1 Message Date
CamilleLaVey 62c4ab026d [Vulkan] Re-adjusting ASTC resolve 2026-08-04 19:30:37 -04:00
lizzie ba9130fbf9 Revert "[externals] remove SPIRV-Headers and SPIRV-Tools (#3989)" (#4247)
This reverts commit ee197e6222.

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4247
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-08-04 02:03:18 +02:00
lizzie 7d5f390ffb [android] fix crash when loader for invalid file is found (#4003)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4003
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-08-04 01:52:32 +02:00
lizzie 8fe1e6efa2 [core/file_sys] implement IApplicationFunctions::GetPseudoDeviceId; common PatchManager::GetNACP for base|updates (#4159)
a bit of refactoring so there's less code duplication...
probably a good idea to make it a lambda anyways
maybe this is why QLauncher didn't have proper DLCs and updates?

either way "GetPseudoDeviceId" is now implemented (still stubbed) with a proper hash instead of just 0

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4159
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-08-04 01:50:54 +02:00
lizzie ee197e6222 [externals] remove SPIRV-Headers and SPIRV-Tools (#3989)
only dep that uses it is sirit, but sirit already has it's own bundled SPIRV-Headers... so

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3989
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-08-03 00:00:55 +02:00
65 changed files with 385 additions and 2222 deletions
@@ -50,7 +50,6 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
GPU_UNSWIZZLE_TEXTURE_SIZE("gpu_unswizzle_texture_size"),
GPU_UNSWIZZLE_STREAM_SIZE("gpu_unswizzle_stream_size"),
GPU_UNSWIZZLE_CHUNK_SIZE("gpu_unswizzle_chunk_size"),
HOMEBREW_NXLINK_SERVER_MODE("homebrew_nxlink_server_mode"),
BAT_TEMPERATURE_UNIT("bat_temperature_unit"),
CABINET_APPLET("cabinet_applet_mode"),
CONTROLLER_APPLET("controller_applet_mode"),
@@ -132,15 +132,6 @@ abstract class SettingsItem(
descriptionId = R.string.program_args_description
)
)
put(
SingleChoiceSetting(
IntSetting.HOMEBREW_NXLINK_SERVER_MODE,
titleId = R.string.nxlink_server_mode,
descriptionId = R.string.nxlink_server_mode_description,
choicesId = R.array.nxlinkServerModeEntries,
valuesId = R.array.nxlinkServerModeValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_USE_SPEED_LIMIT,
@@ -1288,7 +1288,6 @@ class SettingsFragmentPresenter(
add(ShortSetting.DEBUG_KNOBS.key)
add(StringSetting.PROGRAM_ARGS.key)
add(IntSetting.HOMEBREW_NXLINK_SERVER_MODE.key)
if (!NativeConfig.isPerGameConfigLoaded()) {
add(HeaderSetting(R.string.gpu_logging_header))
+61 -97
View File
@@ -20,132 +20,96 @@ struct RomMetadata {
std::vector<u8> icon;
bool isHomebrew;
};
static ankerl::unordered_dense::map<std::string, RomMetadata> m_rom_metadata_cache;
ankerl::unordered_dense::map<std::string, RomMetadata> m_rom_metadata_cache;
static RomMetadata CacheRomMetadata(const std::string& path) {
auto& instance = EmulationSession::GetInstance();
const auto file = Core::GetGameFileFromPath(instance.System().GetFilesystem(), path);
if (auto loader = Loader::GetLoader(instance.System(), file, 0, 0); loader) {
RomMetadata entry;
loader->ReadTitle(entry.title);
loader->ReadProgramId(entry.programId);
loader->ReadIcon(entry.icon);
RomMetadata CacheRomMetadata(const std::string& path) {
const auto file =
Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
const FileSys::PatchManager pm{
entry.programId,
instance.System().GetFileSystemController(),
instance.System().GetContentProvider()
};
const auto control = pm.GetControlMetadata();
RomMetadata entry;
loader->ReadTitle(entry.title);
loader->ReadProgramId(entry.programId);
loader->ReadIcon(entry.icon);
const FileSys::PatchManager pm{
entry.programId, EmulationSession::GetInstance().System().GetFileSystemController(),
EmulationSession::GetInstance().System().GetContentProvider()};
const auto control = pm.GetControlMetadata();
if (control.first != nullptr) {
entry.developer = control.first->GetDeveloperName();
entry.version = control.first->GetVersionString();
} else {
FileSys::NACP nacp;
if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
entry.developer = nacp.GetDeveloperName();
if (control.first != nullptr) {
entry.developer = control.first->GetDeveloperName();
entry.version = control.first->GetVersionString();
} else {
entry.developer = "";
FileSys::NACP nacp{};
entry.developer = loader->ReadControlData(nacp) == Loader::ResultStatus::Success
? nacp.GetDeveloperName()
: "";
entry.version = "1.0.0";
}
entry.version = "1.0.0";
if (loader->GetFileType() == Loader::FileType::NRO) {
auto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
entry.isHomebrew = loader_nro->IsHomebrew();
} else {
entry.isHomebrew = false;
}
m_rom_metadata_cache[path] = entry;
return entry;
}
if (loader->GetFileType() == Loader::FileType::NRO) {
auto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
entry.isHomebrew = loader_nro->IsHomebrew();
} else {
entry.isHomebrew = false;
}
m_rom_metadata_cache[path] = entry;
return entry;
return {};
}
RomMetadata GetRomMetadata(const std::string& path, bool reload = false) {
if (reload) {
static RomMetadata GetRomMetadata(const std::string& path, bool reload = false) {
if (reload)
return CacheRomMetadata(path);
}
if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
return search->second;
}
if (auto it = m_rom_metadata_cache.find(path); it != m_rom_metadata_cache.end())
return it->second;
return CacheRomMetadata(path);
}
extern "C" {
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj,
jstring jpath) {
const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
Common::Android::GetJString(env, jpath), FileSys::OpenMode::Read);
if (!file) {
return false;
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj, jstring jpath) {
if (auto const file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(Common::Android::GetJString(env, jpath), FileSys::OpenMode::Read); file) {
if (auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file); loader) {
auto const file_type = loader->GetFileType();
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error)
return false;
if ((file_type == Loader::FileType::NSP || file_type == Loader::FileType::XCI) && !Loader::IsBootableGameContainer(file, file_type))
return false;
u64 program_id = 0;
return loader->ReadProgramId(program_id) == Loader::ResultStatus::Success;
}
}
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file);
if (!loader) {
return false;
}
const auto file_type = loader->GetFileType();
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
return false;
}
if ((file_type == Loader::FileType::NSP || file_type == Loader::FileType::XCI) &&
!Loader::IsBootableGameContainer(file, file_type)) {
return false;
}
u64 program_id = 0;
Loader::ResultStatus res = loader->ReadProgramId(program_id);
if (res != Loader::ResultStatus::Success) {
return false;
}
return true;
return false;
}
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj,
jstring jpath) {
return Common::Android::ToJString(
env, GetRomMetadata(Common::Android::GetJString(env, jpath)).title);
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj, jstring jpath) {
return Common::Android::ToJString(env, GetRomMetadata(Common::Android::GetJString(env, jpath)).title);
}
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj,
jstring jpath) {
return Common::Android::ToJString(
env, std::to_string(GetRomMetadata(Common::Android::GetJString(env, jpath)).programId));
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj, jstring jpath) {
return Common::Android::ToJString(env, std::to_string(GetRomMetadata(Common::Android::GetJString(env, jpath)).programId));
}
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj,
jstring jpath) {
return Common::Android::ToJString(
env, GetRomMetadata(Common::Android::GetJString(env, jpath)).developer);
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj, jstring jpath) {
return Common::Android::ToJString(env, GetRomMetadata(Common::Android::GetJString(env, jpath)).developer);
}
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj,
jstring jpath, jboolean jreload) {
return Common::Android::ToJString(
env, GetRomMetadata(Common::Android::GetJString(env, jpath), jreload).version);
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj, jstring jpath, jboolean jreload) {
return Common::Android::ToJString(env, GetRomMetadata(Common::Android::GetJString(env, jpath), jreload).version);
}
jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj,
jstring jpath) {
jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj, jstring jpath) {
auto icon_data = GetRomMetadata(Common::Android::GetJString(env, jpath)).icon;
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
reinterpret_cast<jbyte*>(icon_data.data()));
jbyteArray icon = env->NewByteArray(jsize(icon_data.size()));
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), reinterpret_cast<jbyte*>(icon_data.data()));
return icon;
}
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj,
jstring jpath) {
return static_cast<jboolean>(
GetRomMetadata(Common::Android::GetJString(env, jpath)).isHomebrew);
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj, jstring jpath) {
return jboolean(GetRomMetadata(Common::Android::GetJString(env, jpath)).isHomebrew);
}
void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) {
@@ -630,16 +630,6 @@
<item>3</item>
</integer-array>
<string-array name="nxlinkServerModeEntries">
<item>Disabled</item>
<item>Eden Log</item>
</string-array>
<integer-array name="nxlinkServerModeValues">
<item>0</item>
<item>1</item>
</integer-array>
<string-array name="installKeysResults">
<item>""</item>
<item>""</item>
@@ -436,8 +436,6 @@
<string name="program_args">Homebrew Args</string>
<string name="program_args_description">Command-line arguments passed to homebrew at launch (e.g. -noglsl).</string>
<string name="nxlink_server_mode">nxlink Server</string>
<string name="nxlink_server_mode_description">Starts a local nxlink server for homebrew stdout/stderr streams.</string>
<!-- System settings strings -->
<string name="device_name">Device name</string>
+15 -19
View File
@@ -336,22 +336,22 @@ ALLOC_MEMBER(VaType)::Allocate(VaType size) {
current_linear_alloc_end = alloc_start + size;
} else { // If linear allocation overflows the AS then find a gap
if (this->blocks.size() <= 2) {
alloc_start = virt_start;
ASSERT_MSG(false, "Unexpected allocator state!");
}
auto search_predecessor{std::next(this->blocks.begin())};
auto search_successor{std::next(search_predecessor)};
while (search_successor != this->blocks.end() &&
(search_successor->virt - search_predecessor->virt < size ||
search_predecessor->Mapped())) {
search_predecessor = search_successor++;
}
if (search_successor != this->blocks.end()) {
alloc_start = search_predecessor->virt;
} else {
auto search_predecessor{std::next(this->blocks.begin())};
auto search_successor{std::next(search_predecessor)};
while (search_successor != this->blocks.end() &&
(search_successor->virt - search_predecessor->virt < size ||
search_predecessor->Mapped())) {
search_predecessor = search_successor++;
}
if (search_successor != this->blocks.end()) {
alloc_start = search_predecessor->virt;
} else {
return {}; // AS is full
}
return {}; // AS is full
}
}
@@ -364,10 +364,6 @@ ALLOC_MEMBER(void)::AllocateFixed(VaType virt, VaType size) {
}
ALLOC_MEMBER(void)::Free(VaType virt, VaType size) {
const VaType virt_end = virt + size;
this->Unmap(virt, size);
if (virt_end >= virt && virt_end == current_linear_alloc_end) {
current_linear_alloc_end = virt < virt_start ? virt_start : virt;
}
}
} // namespace Common
+1 -62
View File
@@ -4,8 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cerrno>
#include <cstdint>
#include <vector>
#include "common/assert.h"
@@ -17,10 +15,8 @@
#include "common/logging.h"
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
#include <share.h>
#include <windows.h>
#else
#include <unistd.h>
#endif
@@ -99,65 +95,10 @@ namespace {
case FileShareFlag::ShareWriteOnly:
return _SH_DENYRD;
case FileShareFlag::ShareReadWrite:
case FileShareFlag::ShareReadWriteDelete:
return _SH_DENYNO;
}
}
[[nodiscard]] std::FILE* OpenWithWindowsShareDelete(const fs::path& path, FileAccessMode mode,
FileType type) {
DWORD desired_access{};
DWORD creation_disposition{OPEN_EXISTING};
int open_flags = type == FileType::BinaryFile ? _O_BINARY : _O_TEXT;
switch (mode) {
case FileAccessMode::Read:
desired_access = GENERIC_READ;
open_flags |= _O_RDONLY;
break;
case FileAccessMode::Write:
desired_access = GENERIC_WRITE;
creation_disposition = CREATE_ALWAYS;
open_flags |= _O_WRONLY;
break;
case FileAccessMode::Append:
desired_access = GENERIC_WRITE;
creation_disposition = OPEN_ALWAYS;
open_flags |= _O_WRONLY | _O_APPEND;
break;
case FileAccessMode::ReadWrite:
desired_access = GENERIC_READ | GENERIC_WRITE;
open_flags |= _O_RDWR;
break;
case FileAccessMode::ReadAppend:
desired_access = GENERIC_READ | GENERIC_WRITE;
creation_disposition = OPEN_ALWAYS;
open_flags |= _O_RDWR | _O_APPEND;
break;
}
const auto handle =
CreateFileW(path.c_str(), desired_access,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
creation_disposition, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
errno = EACCES;
return nullptr;
}
const auto fd = _open_osfhandle(reinterpret_cast<intptr_t>(handle), open_flags);
if (fd == -1) {
CloseHandle(handle);
return nullptr;
}
auto* const file = _wfdopen(fd, AccessModeToWStr(mode, type));
if (file == nullptr) {
_close(fd);
}
return file;
}
#else
/**
@@ -313,9 +254,7 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File
errno = 0;
#ifdef _WIN32
if (flag == FileShareFlag::ShareReadWriteDelete) {
file = OpenWithWindowsShareDelete(path, mode, type);
} else if (flag != FileShareFlag::ShareNone) {
if (flag != FileShareFlag::ShareNone) {
file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag));
} else {
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
+5 -6
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -49,11 +49,10 @@ enum class FileType {
};
enum class FileShareFlag {
ShareNone, // Provides exclusive access to the file.
ShareReadOnly, // Provides read only shared access to the file.
ShareWriteOnly, // Provides write only shared access to the file.
ShareReadWrite, // Provides read and write shared access to the file.
ShareReadWriteDelete, // Provides read, write, and delete shared access to the file.
ShareNone, // Provides exclusive access to the file.
ShareReadOnly, // Provides read only shared access to the file.
ShareWriteOnly, // Provides write only shared access to the file.
ShareReadWrite, // Provides read and write shared access to the file.
};
enum class DirEntryFilter {
+1 -12
View File
@@ -702,15 +702,7 @@ struct Values {
// Controls
InputSetting<std::array<PlayerInput, 10>> players;
Setting<bool> disable_wgi_xinput{
linkage, false, "disable_wgi_xinput", Category::Controls, Specialization::Default,
// Only read/write disable_wgi_xinput on Windows platforms
#ifdef _WIN32
true
#else
false
#endif
};
Setting<bool> enable_raw_input{
linkage, false, "enable_raw_input", Category::Controls, Specialization::Default,
// Only read/write enable_raw_input on Windows platforms
@@ -840,9 +832,6 @@ struct Values {
Setting<bool> gpu_log_driver_debug{linkage, true, "gpu_log_driver_debug", Category::Debugging};
Setting<s32> gpu_log_ring_buffer_size{linkage, 512, "gpu_log_ring_buffer_size",
Category::Debugging};
Setting<HomebrewNxlinkServerMode> homebrew_nxlink_server_mode{
linkage, HomebrewNxlinkServerMode::Disabled, "homebrew_nxlink_server_mode",
Category::Debugging};
SwitchableSetting<u16, true> debug_knobs{linkage,
0,
-1
View File
@@ -159,7 +159,6 @@ ENUM(GpuUnswizzleChunk, VeryLow, Low, Normal, Medium, High)
ENUM(TemperatureUnits, Celsius, Fahrenheit)
ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2, EDS3);
ENUM(GpuLogLevel, Off, Errors, Standard, Verbose, All)
ENUM(HomebrewNxlinkServerMode, Disabled, EdenLog, HostStdout, File)
ENUM(GameListMode, TreeView, GridView, CarouselView);
ENUM(SpeedMode, Standard, Turbo, Slow);
+8
View File
@@ -208,4 +208,12 @@ UUID UUID::MakeRandomRFC4122V4() {
return uuid;
}
UUID UUID::MakeRFC4122V5(std::span<u8, 20> sha1) {
UUID uuid{};
std::memcpy(&uuid.uuid, sha1.data(), sizeof(UUID));
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
return uuid;
}
} // namespace Common
+16 -20
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 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
@@ -5,6 +8,7 @@
#include <array>
#include <functional>
#include <span>
#include <string>
#include "common/common_types.h"
@@ -86,28 +90,20 @@ struct UUID {
};
}
/**
* Creates a random UUID.
*
* @returns A random UUID.
*/
static UUID MakeRandom();
/// @brief Creates a random UUID.
/// @returns A random UUID.
[[nodiscard]] static UUID MakeRandom();
/**
* Creates a random UUID with a seed.
*
* @param seed A seed to initialize the Mersenne-Twister RNG
*
* @returns A random UUID.
*/
static UUID MakeRandomWithSeed(u32 seed);
/// @brief Creates a random UUID with a seed.
/// @param seed A seed to initialize the Mersenne-Twister RNG
/// @returns A random UUID.
[[nodiscard]] static UUID MakeRandomWithSeed(u32 seed);
/**
* Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant.
*
* @returns A random UUID that is RFC 4122 Version 4 compliant.
*/
static UUID MakeRandomRFC4122V4();
/// @brief Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant.
/// @returns A random UUID that is RFC 4122 Version 4 compliant.
[[nodiscard]] static UUID MakeRandomRFC4122V4();
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 20> sha1);
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
};
-2
View File
@@ -1139,8 +1139,6 @@ add_library(core STATIC
launch_timestamp_cache.h
loader/deconstructed_rom_directory.cpp
loader/deconstructed_rom_directory.h
loader/homebrew_nxlink.cpp
loader/homebrew_nxlink.h
loader/kip.cpp
loader/kip.h
loader/loader.cpp
-2
View File
@@ -52,7 +52,6 @@
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
#include "core/internal_network/network.h"
#include "core/loader/homebrew_nxlink.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h"
@@ -398,7 +397,6 @@ struct System::Impl {
stop_event.request_stop();
core_timing.SyncPause(false);
Loader::HomebrewNxlink::StopServer();
Network::CancelPendingSocketOperations();
kernel.SuspendEmulation(true);
kernel.CloseServices();
+15 -16
View File
@@ -274,23 +274,22 @@ public:
explicit NACP(VirtualFile file);
~NACP();
const LanguageEntry& GetLanguageEntry() const;
std::string GetApplicationName() const;
std::string GetDeveloperName() const;
u64 GetTitleId() const;
u64 GetDLCBaseTitleId() const;
std::string GetVersionString() const;
u64 GetDefaultNormalSaveSize() const;
u64 GetDefaultJournalSaveSize() const;
u32 GetSupportedLanguages() const;
std::vector<std::string> GetApplicationNames() const;
std::vector<u8> GetRawBytes() const;
bool GetUserAccountSwitchLock() const;
u64 GetDeviceSaveDataSize() const;
u32 GetParentalControlFlag() const;
const std::array<u8, 0x20>& GetRatingAge() const;
[[nodiscard]] const LanguageEntry& GetLanguageEntry() const;
[[nodiscard]] std::string GetApplicationName() const;
[[nodiscard]] std::string GetDeveloperName() const;
[[nodiscard]] u64 GetTitleId() const;
[[nodiscard]] u64 GetDLCBaseTitleId() const;
[[nodiscard]] std::string GetVersionString() const;
[[nodiscard]] u64 GetDefaultNormalSaveSize() const;
[[nodiscard]] u64 GetDefaultJournalSaveSize() const;
[[nodiscard]] u32 GetSupportedLanguages() const;
[[nodiscard]] std::vector<std::string> GetApplicationNames() const;
[[nodiscard]] std::vector<u8> GetRawBytes() const;
[[nodiscard]] bool GetUserAccountSwitchLock() const;
[[nodiscard]] u64 GetDeviceSaveDataSize() const;
[[nodiscard]] u32 GetParentalControlFlag() const;
[[nodiscard]] const std::array<u8, 0x20>& GetRatingAge() const;
private:
RawNACP raw{};
std::vector<LanguageEntry> language_entries;
};
-1
View File
@@ -12,7 +12,6 @@
namespace FileSys {
enum class OpenMode : u32 {
Default = 0,
Read = (1 << 0),
Write = (1 << 1),
AllowAppend = (1 << 2),
+10
View File
@@ -1156,4 +1156,14 @@ PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
return {std::move(nacp), icon_file};
}
[[nodiscard]] PatchManager::Metadata PatchManager::GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept {
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), system.GetContentProvider()};
auto metadata = pm.GetControlMetadata();
if (metadata.first != nullptr)
return metadata;
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(application_id), system.GetFileSystemController(), system.GetContentProvider()};
return pm_update.GetControlMetadata();
}
} // namespace FileSys
+3
View File
@@ -105,6 +105,9 @@ public:
// Version of GetControlMetadata that takes an arbitrary NCA
[[nodiscard]] Metadata ParseControlNCA(const NCA& nca) const;
/// @brief Gets NACP metadata (accounting for any patches or updates)
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
private:
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const;
+4 -7
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -40,7 +40,7 @@ VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
VirtualFile VfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) {
const auto path = Common::FS::SanitizePath(path_);
return root->GetFileRelative(path, perms);
return root->GetFileRelative(path);
}
VirtualFile VfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) {
@@ -201,7 +201,7 @@ std::string VfsFile::GetFullPath() const {
return GetContainingDirectory()->GetFullPath() + '/' + GetName();
}
VirtualFile VfsDirectory::GetFileRelative(std::string_view path, OpenMode perms) const {
VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
auto vec = Common::FS::SplitPathComponents(path);
if (vec.empty()) {
return nullptr;
@@ -224,10 +224,7 @@ VirtualFile VfsDirectory::GetFileRelative(std::string_view path, OpenMode perms)
return nullptr;
}
if (perms == OpenMode::Default) {
return dir->GetFile(vec.back());
}
return dir->GetFileRelative(vec.back(), perms);
return dir->GetFile(vec.back());
}
VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const {
+1 -2
View File
@@ -201,8 +201,7 @@ public:
// Retrieves the file located at path as if the current directory was root. Returns nullptr if
// not found.
virtual VirtualFile GetFileRelative(std::string_view path,
OpenMode perms = OpenMode::Default) const;
virtual VirtualFile GetFileRelative(std::string_view path) const;
// Calls GetFileRelative(path) on the root of the current directory.
virtual VirtualFile GetFileAbsolute(std::string_view path) const;
+2 -2
View File
@@ -27,9 +27,9 @@ VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dir
return VirtualDir(new LayeredVfsDirectory(std::move(dirs), std::move(name)));
}
VirtualFile LayeredVfsDirectory::GetFileRelative(std::string_view path, OpenMode perms) const {
VirtualFile LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
for (const auto& layer : dirs) {
const auto file = layer->GetFileRelative(path, perms);
const auto file = layer->GetFileRelative(path);
if (file != nullptr)
return file;
}
+1 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -23,8 +20,7 @@ public:
/// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = "");
VirtualFile GetFileRelative(std::string_view path,
OpenMode perms = OpenMode::Default) const override;
VirtualFile GetFileRelative(std::string_view path) const override;
VirtualDir GetDirectoryRelative(std::string_view path) const override;
VirtualFile GetFile(std::string_view file_name) const override;
VirtualDir GetSubdirectory(std::string_view subdir_name) const override;
+23 -51
View File
@@ -12,6 +12,7 @@
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/vfs/vfs_real.h"
@@ -45,11 +46,17 @@ bool IsWithinRoot(std::string_view root, std::string_view full_path) {
}
constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) {
if (True(mode & OpenMode::Write) || True(mode & OpenMode::AllowAppend)) {
switch (mode) {
case OpenMode::Read:
return FS::FileAccessMode::Read;
case OpenMode::Write:
case OpenMode::ReadWrite:
case OpenMode::AllowAppend:
case OpenMode::All:
return FS::FileAccessMode::ReadWrite;
default:
return {};
}
return FS::FileAccessMode::Read;
}
} // Anonymous namespace
@@ -87,11 +94,9 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
std::optional<std::string> parent_path,
OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
const auto open_perms = perms == OpenMode::Default ? OpenMode::Read : perms;
std::scoped_lock lk{list_lock};
const CacheKey cache_key{path, open_perms};
if (auto it = cache.find(cache_key); it != cache.end()) {
if (auto it = cache.find(path); it != cache.end()) {
if (auto file = it->second.lock(); file) {
return file;
}
@@ -105,9 +110,8 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
this->InsertReferenceIntoListLocked(*reference);
auto file = std::shared_ptr<RealVfsFile>(
new RealVfsFile(*this, std::move(reference), path, open_perms, size,
std::move(parent_path)));
cache[cache_key] = file;
new RealVfsFile(*this, std::move(reference), path, perms, size, std::move(parent_path)));
cache[path] = file;
return file;
}
@@ -120,7 +124,7 @@ VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, OpenMode perms
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
{
std::scoped_lock lk{list_lock};
CloseCachedFileReferenceLocked(path);
cache.erase(path);
}
// Current usages of CreateFile expect to delete the contents of an existing file.
@@ -153,8 +157,8 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
{
std::scoped_lock lk{list_lock};
CloseCachedFileReferenceLocked(old_path);
CloseCachedFileReferenceLocked(new_path);
cache.erase(old_path);
cache.erase(new_path);
}
if (!FS::RenameFile(old_path, new_path)) {
return nullptr;
@@ -166,15 +170,14 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
{
std::scoped_lock lk{list_lock};
CloseCachedFileReferenceLocked(path);
cache.erase(path);
}
return FS::RemoveFile(path);
}
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
return std::shared_ptr<RealVfsDirectory>(
new RealVfsDirectory(*this, path, perms == OpenMode::Default ? OpenMode::Read : perms));
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
}
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
@@ -219,8 +222,8 @@ std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::stri
if (!reference.file) {
this->EvictSingleReferenceLocked();
reference.file = FS::FileOpen(path, ModeFlagsToFileAccessMode(perms),
FS::FileType::BinaryFile, FS::FileShareFlag::ShareReadWriteDelete);
reference.file =
FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile);
if (reference.file) {
num_open_files++;
}
@@ -294,45 +297,15 @@ RealVfsFile::~RealVfsFile() {
base.DropReference(std::move(reference));
}
void RealVfsFilesystem::CloseCachedFileReferenceLocked(const std::string& path) {
for (auto it = cache.lower_bound(CacheKey{path, OpenMode::Default});
it != cache.end() && it->first.first == path;) {
const auto cached_file = it->second.lock();
if (cached_file) {
auto* const real_file = static_cast<RealVfsFile*>(cached_file.get());
auto& reference = real_file->reference;
if (reference && reference->file) {
RemoveReferenceFromListLocked(*reference);
reference->file.reset();
num_open_files--;
InsertReferenceIntoListLocked(*reference);
}
}
it = cache.erase(it);
}
}
std::string RealVfsFile::GetName() const {
#ifdef __ANDROID__
if (!path.empty() && path[0] != '/') {
if (path[0] != '/') {
return FS::Android::GetFilename(path);
}
#endif
return path_components.empty() ? "" : std::string(path_components.back());
}
std::string RealVfsFile::GetFullPath() const {
#ifdef __ANDROID__
if (!path.empty() && path[0] != '/') {
auto out = path;
std::replace(out.begin(), out.end(), '\\', '/');
return out;
}
#endif
return VfsFile::GetFullPath();
}
std::size_t RealVfsFile::GetSize() const {
if (size) {
return *size;
@@ -438,14 +411,13 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string&
RealVfsDirectory::~RealVfsDirectory() = default;
VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path,
OpenMode open_perms) const {
VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const {
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::Exists(full_path) || FS::IsDir(full_path)
|| !IsWithinRoot(FS::SanitizePath(path), full_path)) {
return nullptr;
}
return base.OpenFile(full_path, open_perms == OpenMode::Default ? perms : open_perms);
return base.OpenFile(full_path, perms);
}
VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const {
+2 -7
View File
@@ -10,7 +10,6 @@
#include <mutex>
#include <optional>
#include <string_view>
#include <utility>
#include "common/intrusive_list.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/vfs/vfs.h"
@@ -50,9 +49,8 @@ public:
bool DeleteDirectory(std::string_view path) override;
private:
using CacheKey = std::pair<std::string, OpenMode>;
using ReferenceListType = Common::IntrusiveListBaseTraits<FileReference>::ListType;
std::map<CacheKey, std::weak_ptr<VfsFile>, std::less<>> cache;
std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache;
ReferenceListType open_references;
ReferenceListType closed_references;
std::mutex list_lock;
@@ -65,7 +63,6 @@ private:
std::unique_lock<std::mutex> RefreshReference(const std::string& path, OpenMode perms,
FileReference& reference);
void DropReference(std::unique_ptr<FileReference>&& reference);
void CloseCachedFileReferenceLocked(const std::string& path);
private:
friend class RealVfsDirectory;
@@ -88,7 +85,6 @@ public:
~RealVfsFile() override;
std::string GetName() const override;
std::string GetFullPath() const override;
std::size_t GetSize() const override;
bool Resize(std::size_t new_size) override;
VirtualDir GetContainingDirectory() const override;
@@ -119,8 +115,7 @@ class RealVfsDirectory : public VfsDirectory {
public:
~RealVfsDirectory() override;
VirtualFile GetFileRelative(std::string_view relative_path,
OpenMode perms = OpenMode::Default) const override;
VirtualFile GetFileRelative(std::string_view relative_path) const override;
VirtualDir GetDirectoryRelative(std::string_view relative_path) const override;
VirtualFile GetFile(std::string_view name) const override;
VirtualDir GetSubdirectory(std::string_view name) const override;
-5
View File
@@ -670,11 +670,6 @@ public:
size_t GetHeapRegionSize() const {
return m_heap_region_end - m_heap_region_start;
}
size_t GetCurrentHeapSize() const {
KScopedLightLock lk(m_general_lock);
return m_current_heap_end - m_heap_region_start;
}
size_t GetAliasRegionSize() const {
return m_alias_region_end - m_alias_region_start;
}
-11
View File
@@ -210,11 +210,6 @@ Result KProcess::Initialize(KernelCore& kernel, const Svc::CreateProcessParamete
m_arg_pointer = 0;
m_arg_return_address = 0;
m_main_thread_handle_addr = 0;
m_process_handle_addr = 0;
m_homebrew_next_load_path_addr = 0;
m_homebrew_next_load_argv_addr = 0;
m_is_homebrew_in_place_next_load = false;
m_has_homebrew_nxlink_argv_marker = false;
m_code_size = params.code_num_pages * PageSize;
m_is_application = True(params.flags & Svc::CreateProcessFlag::IsApplication);
@@ -952,7 +947,6 @@ Result KProcess::Run(KernelCore& kernel, s32 priority, size_t stack_size) {
stack_top = stack_bottom + stack_size;
m_main_thread_stack_size = stack_size;
m_main_thread_stack_top = stack_top;
}
// Ensure our stack is safe to clean up on exit.
@@ -1011,11 +1005,6 @@ Result KProcess::Run(KernelCore& kernel, s32 priority, size_t stack_size) {
if (GetInteger(m_main_thread_handle_addr) != 0) {
this->GetMemory().Write32(m_main_thread_handle_addr, thread_handle);
}
if (GetInteger(m_process_handle_addr) != 0) {
Handle process_handle;
R_TRY(m_handle_table.Add(kernel, std::addressof(process_handle), this));
this->GetMemory().Write32(m_process_handle_addr, process_handle);
}
} else {
main_thread->GetContext().r[0] = 0;
main_thread->GetContext().r[1] = thread_handle;
-52
View File
@@ -6,9 +6,7 @@
#pragma once
#include <array>
#include <map>
#include <string_view>
#include "core/arm/arm_interface.h"
#include "core/file_sys/program_metadata.h"
@@ -89,10 +87,6 @@ private:
KProcessAddress m_arg_pointer{};
KProcessAddress m_arg_return_address{};
KProcessAddress m_main_thread_handle_addr{};
KProcessAddress m_process_handle_addr{};
KProcessAddress m_homebrew_next_load_path_addr{};
KProcessAddress m_homebrew_next_load_argv_addr{};
std::array<char, 16> m_homebrew_nxlink_argv_marker{};
KHandleTable m_handle_table;
KProcessAddress m_plr_address{};
ThreadList m_thread_list{};
@@ -118,7 +112,6 @@ private:
size_t m_code_size{};
size_t m_main_thread_stack_size{};
KProcessAddress m_main_thread_stack_top{};
size_t m_max_process_memory{};
size_t m_memory_release_hint{};
s64 m_schedule_count{};
@@ -146,8 +139,6 @@ private:
bool m_is_suspended : 1 = false;
bool m_is_immortal : 1 = false;
bool m_is_handle_table_initialized : 1 = false;
bool m_is_homebrew_in_place_next_load : 1 = false;
bool m_has_homebrew_nxlink_argv_marker : 1 = false;
private:
Result StartTermination(KernelCore& kernel);
@@ -240,49 +231,6 @@ public:
void SetMainThreadHandleAddr(KProcessAddress addr) {
m_main_thread_handle_addr = addr;
}
void SetProcessHandleAddr(KProcessAddress addr) {
m_process_handle_addr = addr;
}
void SetHomebrewNextLoadBufferAddrs(KProcessAddress path_addr, KProcessAddress argv_addr) {
m_homebrew_next_load_path_addr = path_addr;
m_homebrew_next_load_argv_addr = argv_addr;
}
void SetHomebrewInPlaceNextLoad(bool enabled) {
m_is_homebrew_in_place_next_load = enabled;
}
void SetHomebrewNxlinkArgvMarker(std::string_view marker) {
if (marker.size() != m_homebrew_nxlink_argv_marker.size()) {
m_has_homebrew_nxlink_argv_marker = false;
return;
}
marker.copy(m_homebrew_nxlink_argv_marker.data(), m_homebrew_nxlink_argv_marker.size());
m_has_homebrew_nxlink_argv_marker = true;
}
void ClearHomebrewNxlinkArgvMarker() {
m_has_homebrew_nxlink_argv_marker = false;
}
std::string_view GetHomebrewNxlinkArgvMarker() const {
if (!m_has_homebrew_nxlink_argv_marker) {
return {};
}
return {m_homebrew_nxlink_argv_marker.data(), m_homebrew_nxlink_argv_marker.size()};
}
bool IsHomebrewInPlaceNextLoad() const {
return m_is_homebrew_in_place_next_load;
}
KProcessAddress GetHomebrewNextLoadPathAddr() const {
return m_homebrew_next_load_path_addr;
}
KProcessAddress GetHomebrewNextLoadArgvAddr() const {
return m_homebrew_next_load_argv_addr;
}
size_t GetCodeSize() const {
return m_code_size;
}
KProcessAddress GetMainThreadStackTop() const {
return m_main_thread_stack_top;
}
size_t GetMainStackSize() const {
return m_main_thread_stack_size;
+4 -5
View File
@@ -1,8 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// 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-2.0-or-later
// SPDX-License-Identifier: GPL-2.0-or-late
// This file is automatically generated using svc_generator.py.
// DO NOT MODIFY IT MANUALLY
@@ -129,7 +128,7 @@ static void SvcWrap_QueryMemory64From32(Core::System& system, std::span<uint64_t
}
static void SvcWrap_ExitProcess64From32(Core::System& system, std::span<uint64_t, 8> args) {
ExitProcess64From32(system, args);
ExitProcess64From32(system);
}
static void SvcWrap_CreateThread64From32(Core::System& system, std::span<uint64_t, 8> args) {
@@ -1299,7 +1298,7 @@ static void SvcWrap_QueryMemory64(Core::System& system, std::span<uint64_t, 8> a
}
static void SvcWrap_ExitProcess64(Core::System& system, std::span<uint64_t, 8> args) {
ExitProcess64(system, args);
ExitProcess64(system);
}
static void SvcWrap_CreateThread64(Core::System& system, std::span<uint64_t, 8> args) {
+5 -6
View File
@@ -1,8 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// 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-2.0-or-later
// SPDX-License-Identifier: GPL-2.0-or-late
// This file is automatically generated using svc_generator.py.
// DO NOT MODIFY IT MANUALLY
@@ -26,7 +25,7 @@ Result SetMemoryAttribute(Core::System& system, uint64_t address, uint64_t size,
Result MapMemory(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
Result UnmapMemory(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
Result QueryMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, uint64_t address);
void ExitProcess(Core::System& system, std::span<uint64_t, 8> args);
void ExitProcess(Core::System& system);
Result CreateThread(Core::System& system, Handle* out_handle, uint64_t func, uint64_t arg, uint64_t stack_bottom, int32_t priority, int32_t core_id);
Result StartThread(Core::System& system, Handle thread_handle);
void ExitThread(Core::System& system);
@@ -147,7 +146,7 @@ Result SetMemoryAttribute64From32(Core::System& system, uint32_t address, uint32
Result MapMemory64From32(Core::System& system, uint32_t dst_address, uint32_t src_address, uint32_t size);
Result UnmapMemory64From32(Core::System& system, uint32_t dst_address, uint32_t src_address, uint32_t size);
Result QueryMemory64From32(Core::System& system, uint32_t out_memory_info, PageInfo* out_page_info, uint32_t address);
void ExitProcess64From32(Core::System& system, std::span<uint64_t, 8> args);
void ExitProcess64From32(Core::System& system);
Result CreateThread64From32(Core::System& system, Handle* out_handle, uint32_t func, uint32_t arg, uint32_t stack_bottom, int32_t priority, int32_t core_id);
Result StartThread64From32(Core::System& system, Handle thread_handle);
void ExitThread64From32(Core::System& system);
@@ -268,7 +267,7 @@ Result SetMemoryAttribute64(Core::System& system, uint64_t address, uint64_t siz
Result MapMemory64(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
Result UnmapMemory64(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
Result QueryMemory64(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, uint64_t address);
void ExitProcess64(Core::System& system, std::span<uint64_t, 8> args);
void ExitProcess64(Core::System& system);
Result CreateThread64(Core::System& system, Handle* out_handle, uint64_t func, uint64_t arg, uint64_t stack_bottom, int32_t priority, int32_t core_id);
Result StartThread64(Core::System& system, Handle thread_handle);
void ExitThread64(Core::System& system);
+6 -215
View File
@@ -4,9 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <vector>
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"
@@ -25,179 +22,6 @@ constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
}
}
bool IsHomebrewInPlaceNextLoadCodeRange(const KProcess& process, u64 address, u64 size) {
if (!process.IsHomebrewInPlaceNextLoad()) {
return false;
}
const u64 code_start = GetInteger(process.GetEntryPoint());
const size_t code_size = process.GetCodeSize();
if (code_start == 0 || code_size == 0 || size > code_size || address < code_start) {
return false;
}
return address - code_start <= code_size - size;
}
struct HomebrewInPlaceMemoryBlock {
u64 address;
u64 size;
KMemoryState state;
KMemoryPermission permission;
KMemoryAttribute attribute;
bool use_process_permission;
};
Result SetHomebrewInPlaceMemoryPermissionByBlocks(KProcess& process, u64 address, u64 size,
MemoryPermission perm, Result original_result) {
auto& page_table = process.GetPageTable();
const u64 end = address + size;
const auto requested_permission = ConvertToKMemoryPermission(perm);
std::vector<HomebrewInPlaceMemoryBlock> blocks;
for (u64 cursor = address; cursor < end;) {
KMemoryInfo info;
PageInfo page_info;
const auto query_result = page_table.QueryInfo(std::addressof(info),
std::addressof(page_info), cursor);
if (query_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: split permission query failed "
"address=0x{:016X}, result={:#X}, original={:#X}",
cursor, query_result.raw, original_result.raw);
R_RETURN(original_result);
}
const u64 block_end = (std::min<u64>)(info.GetEndAddress(), end);
if (block_end <= cursor) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: split permission walk stalled "
"cursor=0x{:016X}, block=0x{:016X}/0x{:X}, original={:#X}",
cursor, info.GetAddress(), info.GetSize(), original_result.raw);
R_RETURN(original_result);
}
const bool can_reprotect = True(info.GetState() & KMemoryState::FlagCanReprotect);
const bool can_process_reprotect = True(info.GetState() & KMemoryState::FlagCode);
if (!can_reprotect && !can_process_reprotect) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: split permission unsupported block "
"address=0x{:016X}, size=0x{:X}, state=0x{:08X}, svc_state={}, "
"perm=0x{:08X}, attr=0x{:08X}, original={:#X}",
cursor, block_end - cursor, static_cast<u32>(info.GetState()),
static_cast<u32>(info.GetSvcState()),
static_cast<u32>(info.GetPermission()),
static_cast<u32>(info.GetAttribute()), original_result.raw);
R_RETURN(original_result);
}
blocks.push_back({
.address = cursor,
.size = block_end - cursor,
.state = info.GetState(),
.permission = info.GetPermission(),
.attribute = info.GetAttribute(),
.use_process_permission = !can_reprotect && can_process_reprotect,
});
cursor = block_end;
}
for (const auto& block : blocks) {
if (block.permission == requested_permission) {
continue;
}
const auto block_result =
block.use_process_permission
? page_table.SetProcessMemoryPermission(block.address, block.size, perm)
: page_table.SetMemoryPermission(block.address, block.size, perm);
if (block_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: split permission block failed "
"address=0x{:016X}, size=0x{:X}, state=0x{:08X}, perm=0x{:08X}, "
"attr=0x{:08X}, result={:#X}, original={:#X}",
block.address, block.size, static_cast<u32>(block.state),
static_cast<u32>(block.permission), static_cast<u32>(block.attribute),
block_result.raw, original_result.raw);
R_RETURN(block_result);
}
}
R_SUCCEED();
}
struct HomebrewInPlaceDeviceSharedBlock {
u64 address;
u64 size;
u16 device_use_count;
};
Result UnlockHomebrewInPlaceDeviceSharedSource(KProcess& process, u64 address, u64 size,
Result original_result) {
auto& page_table = process.GetPageTable();
const u64 end = address + size;
std::vector<HomebrewInPlaceDeviceSharedBlock> blocks;
for (u64 cursor = address; cursor < end;) {
KMemoryInfo info;
PageInfo page_info;
const auto query_result = page_table.QueryInfo(std::addressof(info),
std::addressof(page_info), cursor);
if (query_result.IsError()) {
R_RETURN(original_result);
}
const u64 block_end = (std::min<u64>)(info.GetEndAddress(), end);
if (block_end <= cursor) {
R_RETURN(original_result);
}
const bool can_device_map = True(info.GetState() & KMemoryState::FlagCanDeviceMap);
const bool is_clean_memory =
can_device_map && info.GetPermission() == KMemoryPermission::UserReadWrite &&
info.GetAttribute() == KMemoryAttribute::None && info.m_device_use_count == 0;
const bool is_stale_device_shared =
can_device_map && info.GetPermission() == KMemoryPermission::UserReadWrite &&
info.GetAttribute() == KMemoryAttribute::DeviceShared && info.m_device_use_count > 0;
if (is_clean_memory) {
cursor = block_end;
continue;
}
if (!is_stale_device_shared) {
R_RETURN(original_result);
}
blocks.push_back({
.address = cursor,
.size = block_end - cursor,
.device_use_count = info.m_device_use_count,
});
cursor = block_end;
}
if (blocks.empty()) {
R_RETURN(original_result);
}
for (const auto& block : blocks) {
for (u16 unlock = 0; unlock < block.device_use_count; unlock++) {
const auto unlock_result =
page_table.UnlockForDeviceAddressSpace(block.address, block.size);
if (unlock_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: device-shared source unlock failed "
"address=0x{:016X}, size=0x{:X}, remaining={}, result={:#X}, "
"original={:#X}",
block.address, block.size, block.device_use_count - unlock - 1,
unlock_result.raw, original_result.raw);
R_RETURN(original_result);
}
}
}
R_SUCCEED();
}
// Checks if address + size is greater than the given address
// This can return false if the size causes an overflow of a 64-bit type
// or if the given size is zero.
@@ -268,19 +92,11 @@ Result SetMemoryPermission(Core::System& system, u64 address, u64 size, MemoryPe
R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
// Validate that the region is in range for the current process.
auto& process = GetCurrentProcess(system.Kernel());
auto& page_table = process.GetPageTable();
auto& page_table = GetCurrentProcess(system.Kernel()).GetPageTable();
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
// Set the memory attribute.
const auto result = page_table.SetMemoryPermission(address, size, perm);
if (result.raw == ResultInvalidCurrentMemory.raw &&
IsHomebrewInPlaceNextLoadCodeRange(process, address, size)) {
R_RETURN(
SetHomebrewInPlaceMemoryPermissionByBlocks(process, address, size, perm, result));
}
R_RETURN(result);
R_RETURN(page_table.SetMemoryPermission(address, size, perm));
}
Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask, u32 attr) {
@@ -319,30 +135,14 @@ Result MapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr={:#x}, src_addr={:#x}, size={:#x}", dst_addr,
src_addr, size);
auto& process = GetCurrentProcess(system.Kernel());
auto& page_table = process.GetPageTable();
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
result.IsError()) {
return result;
}
const auto result = page_table.MapMemory(dst_addr, src_addr, size);
if (result.raw == ResultInvalidCurrentMemory.raw && process.IsHomebrewInPlaceNextLoad()) {
if (UnlockHomebrewInPlaceDeviceSharedSource(process, src_addr, size, result).IsSuccess()) {
const auto retry_result = page_table.MapMemory(dst_addr, src_addr, size);
if (retry_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: svcMapMemory retry failed after "
"DeviceShared cleanup dst=0x{:016X}, src=0x{:016X}, size=0x{:X}, "
"result={:#X}",
dst_addr, src_addr, size, retry_result.raw);
}
R_RETURN(retry_result);
}
}
R_RETURN(result);
R_RETURN(page_table.MapMemory(dst_addr, src_addr, size));
}
/// Unmaps a region that was previously mapped with svcMapMemory
@@ -350,23 +150,14 @@ Result UnmapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr={:#x}, src_addr={:#x}, size={:#x}", dst_addr,
src_addr, size);
auto& process = GetCurrentProcess(system.Kernel());
auto& page_table = process.GetPageTable();
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
result.IsError()) {
return result;
}
const auto result = page_table.UnmapMemory(dst_addr, src_addr, size);
if (result.raw == ResultInvalidCurrentMemory.raw && process.IsHomebrewInPlaceNextLoad()) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: svcUnmapMemory failed dst=0x{:016X}, "
"src=0x{:016X}, size=0x{:X}, result={:#X}",
dst_addr, src_addr, size, result.raw);
}
R_RETURN(result);
R_RETURN(page_table.UnmapMemory(dst_addr, src_addr, size));
}
Result SetMemoryPermission64(Core::System& system, uint64_t address, uint64_t size,
+5 -204
View File
@@ -4,216 +4,17 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <filesystem>
#include <string>
#include <string_view>
#include <vector>
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/input.h"
#include "core/core.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/nvdrv/nvdrv_interface.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/nro.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/resource_manager.h"
namespace Kernel::Svc {
namespace {
constexpr size_t HomebrewNextLoadPathSize = 0x200;
constexpr size_t HomebrewNextLoadArgvSize = 0x800;
std::string ReadHomebrewString(Core::Memory::Memory& memory, KProcessAddress address,
size_t max_size) {
if (GetInteger(address) == 0) {
return {};
}
return memory.ReadCString(Common::ProcessAddress{GetInteger(address)}, max_size);
}
} // namespace
/// Exits the current process
void ExitProcess(Core::System& system, std::span<uint64_t, 8> args) {
void ExitProcess(Core::System& system) {
auto* current_process = GetCurrentProcessPointer(system.Kernel());
auto* current_thread = GetCurrentThreadPointer(system.Kernel());
LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessId());
const auto next_load_path_addr = current_process->GetHomebrewNextLoadPathAddr();
const auto next_load_argv_addr = current_process->GetHomebrewNextLoadArgvAddr();
if (GetInteger(next_load_path_addr) != 0) {
auto& memory = current_process->GetMemory();
const auto next_load_path =
ReadHomebrewString(memory, next_load_path_addr, HomebrewNextLoadPathSize);
const auto next_load_argv =
ReadHomebrewString(memory, next_load_argv_addr, HomebrewNextLoadArgvSize);
if (!next_load_path.empty()) {
auto guest_path = Common::FS::SanitizePath(next_load_path);
constexpr std::string_view SdmcPrefix{"sdmc:"};
FileSys::VirtualFile file{};
const bool is_sdmc_path = guest_path.rfind(SdmcPrefix, 0) == 0;
const bool is_absolute_guest_path = !guest_path.empty() && guest_path.front() == '/';
if (is_sdmc_path || is_absolute_guest_path) {
auto relative_path =
is_sdmc_path ? guest_path.substr(SdmcPrefix.size()) : guest_path;
while (!relative_path.empty() && relative_path.front() == '/') {
relative_path.erase(relative_path.begin());
}
const auto host_path = Common::FS::GetEdenPath(Common::FS::EdenPath::SDMCDir) /
std::filesystem::path{Common::FS::ToU8String(relative_path)};
const auto host_path_string = Common::FS::PathToUTF8String(host_path);
file = Core::GetGameFileFromPath(system.GetFilesystem(), host_path_string);
if (!file) {
LOG_WARNING(Kernel_SVC,
"NextLoad: failed to open guest_path='{}', host_path='{}'",
next_load_path, host_path_string);
}
} else {
file = Core::GetGameFileFromPath(system.GetFilesystem(), guest_path);
if (!file) {
LOG_WARNING(Kernel_SVC, "NextLoad: failed to open guest_path='{}'",
next_load_path);
}
}
if (file) {
const auto nvdrv =
system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv:s");
if (!nvdrv) {
LOG_WARNING(Kernel_SVC, "NextLoad: NVDRV service unavailable for reset");
} else {
nvdrv->GetModule()->ResetForProcess(current_process);
}
auto& page_table = current_process->GetPageTable();
const u64 heap_start = GetInteger(page_table.GetHeapRegionStart());
const u64 heap_size = page_table.GetHeapRegionSize();
const u64 heap_end = heap_start + heap_size;
if (heap_start != 0 && heap_size != 0 && heap_end > heap_start) {
struct DeviceSharedBlock {
u64 address;
u64 size;
u16 device_use_count;
};
std::vector<DeviceSharedBlock> blocks;
for (u64 cursor = heap_start; cursor < heap_end;) {
KMemoryInfo info;
PageInfo page_info;
const auto query_result =
page_table.QueryInfo(std::addressof(info), std::addressof(page_info),
cursor);
if (query_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad: DeviceShared heap cleanup query failed "
"address=0x{:016X}, result={:#X}",
cursor, query_result.raw);
break;
}
const u64 block_end = (std::min<u64>)(info.GetEndAddress(), heap_end);
if (block_end <= cursor) {
LOG_WARNING(Kernel_SVC,
"NextLoad: DeviceShared heap cleanup walk stalled "
"cursor=0x{:016X}, block=0x{:016X}/0x{:X}",
cursor, info.GetAddress(), info.GetSize());
break;
}
const bool is_device_shared =
True(info.GetState() & KMemoryState::FlagCanDeviceMap) &&
info.GetAttribute() == KMemoryAttribute::DeviceShared &&
info.m_device_use_count > 0;
if (is_device_shared) {
blocks.push_back(DeviceSharedBlock{
.address = cursor,
.size = block_end - cursor,
.device_use_count = info.m_device_use_count,
});
}
cursor = block_end;
}
for (const auto& block : blocks) {
for (u16 unlock = 0; unlock < block.device_use_count; unlock++) {
const auto unlock_result =
page_table.UnlockForDeviceAddressSpace(block.address, block.size);
if (unlock_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad: DeviceShared heap cleanup unlock failed "
"address=0x{:016X}, size=0x{:X}, remaining={}, "
"result={:#X}",
block.address, block.size,
block.device_use_count - unlock - 1, unlock_result.raw);
break;
}
}
}
}
if (Loader::LoadNroInPlace(system, *current_process, *current_thread, file,
next_load_path, next_load_argv)) {
const auto aruid = current_process->GetProcessId();
if (const auto hid =
system.ServiceManager().GetService<Service::HID::IHidServer>("hid")) {
const auto resource_manager = hid->GetResourceManager();
resource_manager->UnregisterAppletResourceUserId(aruid);
const auto register_result =
resource_manager->RegisterAppletResourceUserId(aruid, true);
if (register_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad: failed to register HID applet resource "
"aruid={}, result={:#X}",
aruid, register_result.raw);
}
} else {
LOG_WARNING(Kernel_SVC, "NextLoad: HID service unavailable for reset");
}
auto& hid_core = system.HIDCore();
hid_core.DisableAllControllerConfiguration();
hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
hid_core.ReloadInputDevices();
const auto activate_controller = [&](Core::HID::NpadIdType npad_id) {
auto* controller = hid_core.GetEmulatedController(npad_id);
if (controller == nullptr) {
return;
}
(void)controller->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
Common::Input::PollingMode::Active);
};
activate_controller(Core::HID::NpadIdType::Player1);
activate_controller(Core::HID::NpadIdType::Handheld);
system.Kernel().CurrentPhysicalCore().LoadContext(current_thread);
const auto& context = current_thread->GetContext();
for (size_t i = 0; i < args.size(); i++) {
args[i] = context.r[i];
}
return;
}
}
}
}
ASSERT_MSG(current_process->GetState() == KProcess::State::Running,
"Process has already exited");
@@ -331,8 +132,8 @@ Result TerminateProcess(Core::System& system, Handle process_handle) {
R_THROW(ResultNotImplemented);
}
void ExitProcess64(Core::System& system, std::span<uint64_t, 8> args) {
ExitProcess(system, args);
void ExitProcess64(Core::System& system) {
ExitProcess(system);
}
Result GetProcessId64(Core::System& system, uint64_t* out_process_id, Handle process_handle) {
@@ -363,8 +164,8 @@ Result GetProcessInfo64(Core::System& system, int64_t* out_info, Handle process_
R_RETURN(GetProcessInfo(system, out_info, process_handle, info_type));
}
void ExitProcess64From32(Core::System& system, std::span<uint64_t, 8> args) {
ExitProcess(system, args);
void ExitProcess64From32(Core::System& system) {
ExitProcess(system);
}
Result GetProcessId64From32(Core::System& system, uint64_t* out_process_id, Handle process_handle) {
@@ -4,6 +4,8 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <openssl/evp.h>
#include "common/settings.h"
#include "common/uuid.h"
#include "core/file_sys/control_metadata.h"
@@ -154,20 +156,7 @@ Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) {
// Default to 0 (all languages supported)
u32 supported_languages = 0;
const auto res = [this] {
const FileSys::PatchManager pm{m_applet->program_id, system.GetFileSystemController(),
system.GetContentProvider()};
auto metadata = pm.GetControlMetadata();
if (metadata.first != nullptr) {
return metadata;
}
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(m_applet->program_id),
system.GetFileSystemController(),
system.GetContentProvider()};
return pm_update.GetControlMetadata();
}();
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
if (res.first != nullptr) {
supported_languages = res.first->GetSupportedLanguages();
}
@@ -205,20 +194,7 @@ Result IApplicationFunctions::SetTerminateResult(Result terminate_result) {
Result IApplicationFunctions::GetDisplayVersion(Out<DisplayVersion> out_display_version) {
LOG_DEBUG(Service_AM, "called");
const auto res = [this] {
const FileSys::PatchManager pm{m_applet->program_id, system.GetFileSystemController(),
system.GetContentProvider()};
auto metadata = pm.GetControlMetadata();
if (metadata.first != nullptr) {
return metadata;
}
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(m_applet->program_id),
system.GetFileSystemController(),
system.GetContentProvider()};
return pm_update.GetControlMetadata();
}();
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
if (res.first != nullptr) {
const auto& version = res.first->GetVersionString();
std::memcpy(out_display_version->string.data(), version.data(),
@@ -347,8 +323,21 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
}
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
LOG_WARNING(Service_AM, "(STUBBED) called");
*out_pseudo_device_id = {};
LOG_WARNING(Service_AM, "(stubbed)");
// This should be hashed with the device specific hash
// for now this will do
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
u8 hash[EVP_MAX_MD_SIZE];
unsigned int hash_len = 0;
auto const seed = res.first->raw.seed_for_pseudo_device_id;
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
auto const algorithm = EVP_sha1();
EVP_DigestInit_ex(ctx, algorithm, nullptr);
EVP_DigestUpdate(ctx, &seed, sizeof(seed));
EVP_DigestFinal_ex(ctx, hash, &hash_len);
EVP_MD_CTX_free(ctx);
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 20>{hash, std::size(hash)});
R_SUCCEED();
}
@@ -252,19 +252,7 @@ Result ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage(
// Default to 0 (all languages supported)
u32 supported_languages = 0;
const auto res = [this, identity] {
const FileSys::PatchManager pm{identity.application_id, system.GetFileSystemController(),
system.GetContentProvider()};
auto metadata = pm.GetControlMetadata();
if (metadata.first != nullptr) {
return metadata;
}
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(identity.application_id),
system.GetFileSystemController(),
system.GetContentProvider()};
return pm_update.GetControlMetadata();
}();
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, identity.application_id);
if (res.first != nullptr) {
supported_languages = res.first->GetSupportedLanguages();
+8 -15
View File
@@ -4,7 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string_view>
#include <utility>
#include "common/assert.h"
@@ -258,7 +257,7 @@ Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file,
npath.remove_prefix(1);
}
auto file = backing->GetFileRelative(npath, mode);
auto file = backing->GetFileRelative(npath);
if (file == nullptr) {
return FileSys::ResultPathNotFound;
}
@@ -334,16 +333,14 @@ FileSystemController::~FileSystemController() = default;
Result FileSystemController::RegisterProcess(
ProcessId process_id, ProgramId program_id,
std::shared_ptr<FileSys::RomFSFactory>&& romfs_factory, std::string homebrew_initial_cwd) {
std::shared_ptr<FileSys::RomFSFactory>&& romfs_factory) {
std::scoped_lock lk{registration_lock};
registrations.insert_or_assign(process_id,
Registration{
.program_id = program_id,
.romfs_factory = std::move(romfs_factory),
.save_data_factory = CreateSaveDataFactory(program_id),
.homebrew_initial_cwd = std::move(homebrew_initial_cwd),
});
registrations.emplace(process_id, Registration{
.program_id = program_id,
.romfs_factory = std::move(romfs_factory),
.save_data_factory = CreateSaveDataFactory(program_id),
});
LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
return ResultSuccess;
@@ -351,8 +348,7 @@ Result FileSystemController::RegisterProcess(
Result FileSystemController::OpenProcess(
ProgramId* out_program_id, std::shared_ptr<SaveDataController>* out_save_data_controller,
std::shared_ptr<RomFsController>* out_romfs_controller, ProcessId process_id,
std::string* out_homebrew_initial_cwd) {
std::shared_ptr<RomFsController>* out_romfs_controller, ProcessId process_id) {
std::scoped_lock lk{registration_lock};
const auto it = registrations.find(process_id);
@@ -365,9 +361,6 @@ Result FileSystemController::OpenProcess(
std::make_shared<SaveDataController>(system, it->second.save_data_factory);
*out_romfs_controller =
std::make_shared<RomFsController>(it->second.romfs_factory, it->second.program_id);
if (out_homebrew_initial_cwd != nullptr) {
*out_homebrew_initial_cwd = it->second.homebrew_initial_cwd;
}
return ResultSuccess;
}
+4 -7
View File
@@ -8,7 +8,6 @@
#include <memory>
#include <mutex>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/fs_directory.h"
#include "core/file_sys/fs_filesystem.h"
@@ -72,12 +71,11 @@ public:
~FileSystemController();
Result RegisterProcess(ProcessId process_id, ProgramId program_id,
std::shared_ptr<FileSys::RomFSFactory>&& factory,
std::string homebrew_initial_cwd = {});
std::shared_ptr<FileSys::RomFSFactory>&& factory);
Result OpenProcess(ProgramId* out_program_id,
std::shared_ptr<SaveDataController>* out_save_data_controller,
std::shared_ptr<RomFsController>* out_romfs_controller,
ProcessId process_id, std::string* out_homebrew_initial_cwd = nullptr);
std::shared_ptr<SaveDataController>* out_save_data_controller,
std::shared_ptr<RomFsController>* out_romfs_controller,
ProcessId process_id);
void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw);
std::shared_ptr<SaveDataController> OpenSaveDataController();
@@ -138,7 +136,6 @@ private:
ProgramId program_id;
std::shared_ptr<FileSys::RomFSFactory> romfs_factory;
std::shared_ptr<FileSys::SaveDataFactory> save_data_factory;
std::string homebrew_initial_cwd;
};
std::mutex registration_lock;
@@ -4,9 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string_view>
#include "common/fs/path_util.h"
#include "common/string_util.h"
#include "core/file_sys/fssrv/fssrv_sf_path.h"
#include "core/hle/service/cmif_serialization.h"
@@ -16,29 +13,10 @@
namespace Service::FileSystem {
static std::string ResolveHomebrewCwdRootAlias(std::string path,
std::string_view homebrew_initial_cwd) {
if (homebrew_initial_cwd.empty()) {
return path;
}
const std::string normalized_path = Common::FS::SanitizePath(path);
if (normalized_path.empty() || normalized_path == "/" ||
normalized_path != homebrew_initial_cwd ||
path.size() != normalized_path.size() + 2 ||
path.substr(0, normalized_path.size()) != normalized_path ||
path.substr(normalized_path.size()) != "//") {
return path;
}
return "/";
}
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_,
std::string homebrew_initial_cwd_)
: ServiceFramework{system_, "IFileSystem"},
backend{std::make_unique<FileSys::Fsa::IFileSystem>(dir_)},
size_getter{std::move(size_getter_)}, homebrew_initial_cwd{std::move(homebrew_initial_cwd_)} {
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_)
: ServiceFramework{system_, "IFileSystem"}, backend{std::make_unique<FileSys::Fsa::IFileSystem>(
dir_)},
size_getter{std::move(size_getter_)} {
static const FunctionInfo functions[] = {
{0, D<&IFileSystem::CreateFile>, "CreateFile"},
{1, D<&IFileSystem::DeleteFile>, "DeleteFile"},
@@ -65,8 +43,7 @@ Result IFileSystem::CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_H
s32 option, s64 size) {
LOG_DEBUG(Service_FS, "called. file={}, option={:#x}, size={:#08x}", path->str, option, size);
const auto fs_path = ResolveHomebrewCwdRootAlias(path->str, homebrew_initial_cwd);
R_RETURN(backend->CreateFile(FileSys::Path(fs_path.c_str()), size));
R_RETURN(backend->CreateFile(FileSys::Path(path->str), size));
}
Result IFileSystem::DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
@@ -117,8 +94,7 @@ Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
LOG_DEBUG(Service_FS, "called. file={}, mode={}", path->str, mode);
FileSys::VirtualFile vfs_file{};
const auto fs_path = ResolveHomebrewCwdRootAlias(path->str, homebrew_initial_cwd);
R_TRY(backend->OpenFile(&vfs_file, FileSys::Path(fs_path.c_str()),
R_TRY(backend->OpenFile(&vfs_file, FileSys::Path(path->str),
static_cast<FileSys::OpenMode>(mode)));
*out_interface = std::make_shared<IFile>(system, vfs_file);
@@ -131,8 +107,7 @@ Result IFileSystem::OpenDirectory(OutInterface<IDirectory> out_interface,
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", path->str, mode);
FileSys::VirtualDir vfs_dir{};
const auto fs_path = ResolveHomebrewCwdRootAlias(path->str, homebrew_initial_cwd);
R_TRY(backend->OpenDirectory(&vfs_dir, FileSys::Path(fs_path.c_str()),
R_TRY(backend->OpenDirectory(&vfs_dir, FileSys::Path(path->str),
static_cast<FileSys::OpenDirectoryMode>(mode)));
*out_interface = std::make_shared<IDirectory>(system, vfs_dir,
@@ -145,8 +120,7 @@ Result IFileSystem::GetEntryType(
LOG_DEBUG(Service_FS, "called. file={}", path->str);
FileSys::DirectoryEntryType vfs_entry_type{};
const auto fs_path = ResolveHomebrewCwdRootAlias(path->str, homebrew_initial_cwd);
R_TRY(backend->GetEntryType(&vfs_entry_type, FileSys::Path(fs_path.c_str())));
R_TRY(backend->GetEntryType(&vfs_entry_type, FileSys::Path(path->str)));
*out_type = static_cast<u32>(vfs_entry_type);
R_SUCCEED();
@@ -1,13 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include "common/common_funcs.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/fsa/fs_i_filesystem.h"
@@ -28,8 +23,7 @@ class IDirectory;
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_,
std::string homebrew_initial_cwd_ = {});
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_);
Result CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, s32 option,
s64 size);
@@ -61,7 +55,6 @@ public:
private:
std::unique_ptr<FileSys::Fsa::IFileSystem> backend;
SizeGetter size_getter;
std::string homebrew_initial_cwd;
};
} // namespace Service::FileSystem
@@ -192,9 +192,8 @@ Result FSP_SRV::SetCurrentProcess(ClientProcessId pid) {
LOG_DEBUG(Service_FS, "called. current_process_id={:#016x}", current_process_id);
homebrew_initial_cwd.clear();
R_RETURN(fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller,
current_process_id, &homebrew_initial_cwd));
R_RETURN(
fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id));
}
Result FSP_SRV::OpenFileSystemWithPatch(OutInterface<IFileSystem> out_interface,
@@ -225,8 +224,7 @@ Result FSP_SRV::OpenSdCardFileSystem(OutInterface<IFileSystem> out_interface) {
fsc.OpenSDMC(&sdmc_dir);
*out_interface = std::make_shared<IFileSystem>(
system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard),
homebrew_initial_cwd);
system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
R_SUCCEED();
}
@@ -7,7 +7,6 @@
#pragma once
#include <memory>
#include <string>
#include "core/file_sys/fs_save_data_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/filesystem/fsp/fsp_types.h"
@@ -124,7 +123,6 @@ private:
u32 access_log_program_index = 0;
AccessLogMode access_log_mode = AccessLogMode::None;
u64 program_id = 0;
std::string homebrew_initial_cwd;
std::shared_ptr<SaveDataController> save_data_controller;
std::shared_ptr<RomFsController> romfs_controller;
};
+1 -91
View File
@@ -1,15 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <atomic>
#include <deque>
#include <mutex>
#include <vector>
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/nvdrv/core/container.h"
@@ -45,16 +40,6 @@ Container::Container(Tegra::Host1x::Host1x& host1x_) {
Container::~Container() = default;
static bool IsSameProcess(Kernel::KProcess* lhs, Kernel::KProcess* rhs) {
if (lhs == rhs) {
return true;
}
if (lhs == nullptr || rhs == nullptr) {
return false;
}
return lhs->GetProcessId() == rhs->GetProcessId();
}
SessionId Container::OpenSession(Kernel::KProcess* process) {
using namespace Common::Literals;
@@ -63,7 +48,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
if (!session.is_active) {
continue;
}
if (IsSameProcess(session.process, process)) {
if (session.process == process) {
session.ref_count++;
return session.id;
}
@@ -131,15 +116,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
void Container::CloseSession(SessionId session_id) {
std::scoped_lock lk(impl->session_guard);
if (session_id.id >= impl->sessions.size()) {
return;
}
auto& session = impl->sessions[session_id.id];
if (!session.is_active || session.ref_count <= 0) {
return;
}
if (--session.ref_count > 0) {
return;
}
@@ -157,73 +134,6 @@ void Container::CloseSession(SessionId session_id) {
impl->id_pool.emplace_front(session_id.id);
}
size_t Container::CloseSessions(std::span<const SessionId> session_ids) {
std::vector<SessionId> valid_session_ids;
valid_session_ids.reserve(session_ids.size());
{
std::scoped_lock lk(impl->session_guard);
for (const auto session_id : session_ids) {
if (session_id.id >= impl->sessions.size()) {
continue;
}
auto& session = impl->sessions[session_id.id];
if (!session.is_active) {
continue;
}
const auto duplicate = std::ranges::any_of(
valid_session_ids, [session_id](const auto candidate) {
return candidate.id == session_id.id;
});
if (duplicate) {
continue;
}
session.ref_count = 1;
valid_session_ids.push_back(session_id);
}
}
for (const auto session_id : valid_session_ids) {
CloseSession(session_id);
}
return valid_session_ids.size();
}
std::vector<SessionId> Container::GetSessionIdsForProcess(Kernel::KProcess* process) {
std::vector<SessionId> session_ids;
std::scoped_lock lk(impl->session_guard);
for (const auto& session : impl->sessions) {
if (!session.is_active || !IsSameProcess(session.process, process)) {
continue;
}
session_ids.push_back(session.id);
}
return session_ids;
}
std::vector<SessionId> Container::GetActiveSessionIds() const {
std::vector<SessionId> session_ids;
std::scoped_lock lk(impl->session_guard);
for (const auto& session : impl->sessions) {
if (session.is_active) {
session_ids.push_back(session.id);
}
}
return session_ids;
}
bool Container::IsSessionActive(SessionId session_id) const {
std::scoped_lock lk(impl->session_guard);
return session_id.id < impl->sessions.size() && impl->sessions[session_id.id].is_active;
}
Session* Container::GetSession(SessionId session_id) {
std::atomic_thread_fence(std::memory_order_acquire);
return &impl->sessions[session_id.id];
@@ -9,10 +9,7 @@
#include <deque>
#include <memory>
#include <span>
#include <cstddef>
#include <ankerl/unordered_dense.h>
#include <vector>
#include "core/device_memory_manager.h"
#include "core/hle/service/nvdrv/nvdata.h"
@@ -62,10 +59,6 @@ public:
SessionId OpenSession(Kernel::KProcess* process);
void CloseSession(SessionId id);
size_t CloseSessions(std::span<const SessionId> session_ids);
std::vector<SessionId> GetSessionIdsForProcess(Kernel::KProcess* process);
std::vector<SessionId> GetActiveSessionIds() const;
bool IsSessionActive(SessionId id) const;
Session* GetSession(SessionId id);
+8 -56
View File
@@ -6,12 +6,10 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <functional>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
@@ -328,65 +326,19 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
}
void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
auto* session = core.GetSession(session_id);
auto* process = session != nullptr ? session->process : nullptr;
auto handle_ids = [&] {
auto handles_copy = [&] {
std::scoped_lock lk{handles_lock};
std::vector<Handle::Id> ids;
ids.reserve(handles.size());
for (const auto& entry : handles) {
ids.push_back(entry.first);
}
return ids;
return handles;
}();
for (const auto id : handle_ids) {
bool unlocked_pages = false;
while (true) {
bool last_user_reference = false;
VAddr address = 0;
size_t size = 0;
{
const auto handle = GetHandle(id);
if (!handle) {
break;
}
std::scoped_lock lk{handle->mutex};
if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
break;
}
last_user_reference = handle->dupes == 1;
address = handle->address;
size = handle->size;
}
const auto free_info = FreeHandle(id, false);
if (!free_info) {
break;
}
if (!unlocked_pages && process != nullptr && address != 0 && size != 0 &&
(free_info->can_unlock || last_user_reference)) {
const auto unlock_result =
process->GetPageTable().UnlockForDeviceAddressSpace(address, size);
if (unlock_result.IsError()) {
LOG_WARNING(Service_NVDRV,
"NextLoad: nvmap session cleanup unlock failed, "
"handle={}, session={}, address=0x{:016X}, size=0x{:X}, "
"result={:#X}",
id, session_id.id, address, size, unlock_result.raw);
}
unlocked_pages = true;
}
if (last_user_reference) {
break;
for (auto& [id, handle] : handles_copy) {
{
std::scoped_lock lk{handle->mutex};
if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
continue;
}
}
FreeHandle(id, false);
}
}
+1 -104
View File
@@ -1,17 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <utility>
#include <vector>
#include <fmt/ranges.h>
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
@@ -138,9 +133,6 @@ DeviceFD Module::Open(const std::string& device_name, NvCore::SessionId session_
auto device = builder(fd)->second;
device->OnOpen(session_id, fd);
if (container.IsSessionActive(session_id)) {
open_file_sessions.emplace(fd, session_id);
}
return fd;
}
@@ -212,7 +204,6 @@ NvResult Module::Close(DeviceFD fd) {
itr->second->OnClose(fd);
open_files.erase(itr);
open_file_sessions.erase(fd);
return NvResult::Success;
}
@@ -237,98 +228,4 @@ NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) {
return NvResult::Success;
}
static bool ContainsSession(std::span<const NvCore::SessionId> session_ids,
NvCore::SessionId session_id) {
return std::ranges::any_of(session_ids, [session_id](const auto candidate) {
return candidate.id == session_id.id;
});
}
static void AppendUniqueSession(std::vector<NvCore::SessionId>& session_ids,
NvCore::SessionId session_id) {
if (!ContainsSession(session_ids, session_id)) {
session_ids.push_back(session_id);
}
}
size_t Module::CloseFilesForSessions(std::span<const NvCore::SessionId> session_ids) {
std::vector<DeviceFD> fds;
fds.reserve(open_file_sessions.size());
for (const auto& [fd, session_id] : open_file_sessions) {
if (ContainsSession(session_ids, session_id)) {
fds.push_back(fd);
}
}
for (const auto fd : fds) {
Close(fd);
}
return fds.size();
}
void Module::CloseSession(NvCore::SessionId session_id) {
container.CloseSession(session_id);
}
void Module::TrackSessionAruid(NvCore::SessionId session_id, u64 aruid) {
const bool active = container.IsSessionActive(session_id);
if (active) {
session_aruids[session_id.id] = aruid;
}
}
std::vector<NvCore::SessionId> Module::GetSessionIdsForAruid(u64 aruid) const {
std::vector<NvCore::SessionId> session_ids;
for (const auto& [session_id, session_aruid] : session_aruids) {
if (session_aruid == aruid) {
session_ids.push_back(NvCore::SessionId{session_id});
}
}
return session_ids;
}
size_t Module::ResetForProcess(Kernel::KProcess* process) {
const auto process_id = process != nullptr ? process->GetProcessId() : 0;
auto session_ids = container.GetSessionIdsForProcess(process);
if (process_id != 0) {
for (const auto session_id : GetSessionIdsForAruid(process_id)) {
AppendUniqueSession(session_ids, session_id);
}
}
const auto active_session_ids = container.GetActiveSessionIds();
const auto active_before = active_session_ids.size();
const bool has_active_candidate =
std::ranges::any_of(session_ids, [this](const auto session_id) {
return container.IsSessionActive(session_id);
});
bool used_active_sessions = false;
if (!has_active_candidate && !active_session_ids.empty()) {
for (const auto session_id : active_session_ids) {
AppendUniqueSession(session_ids, session_id);
}
used_active_sessions = true;
}
const auto closed_files = CloseFilesForSessions(session_ids);
const auto closed_sessions = container.CloseSessions(session_ids);
for (const auto session_id : session_ids) {
if (!container.IsSessionActive(session_id)) {
session_aruids.erase(session_id.id);
}
}
if (used_active_sessions) {
LOG_WARNING(Service_NVDRV,
"NextLoad: NVDRV reset used active sessions because process-owned "
"sessions were not found, process_id={}, sessions={}, files={}, active_before={}",
process_id, closed_sessions, closed_files, active_before);
}
return closed_sessions;
}
} // namespace Service::Nvidia
-10
View File
@@ -12,7 +12,6 @@
#include <memory>
#include <span>
#include <string>
#include <vector>
#include <ankerl/unordered_dense.h>
#include "common/common_types.h"
@@ -27,7 +26,6 @@ class System;
namespace Kernel {
class KEvent;
class KProcess;
}
namespace Service::Nvidia {
@@ -91,9 +89,6 @@ public:
NvResult Close(DeviceFD fd);
NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event);
void CloseSession(NvCore::SessionId session_id);
void TrackSessionAruid(NvCore::SessionId session_id, u64 aruid);
size_t ResetForProcess(Kernel::KProcess* process);
NvCore::Container& GetContainer() {
return container;
@@ -111,17 +106,12 @@ private:
using FilesContainerType = ankerl::unordered_dense::map<DeviceFD, std::shared_ptr<Devices::nvdevice>>;
/// Mapping of file descriptors to the devices they reference.
FilesContainerType open_files;
ankerl::unordered_dense::map<DeviceFD, NvCore::SessionId> open_file_sessions;
ankerl::unordered_dense::map<size_t, u64> session_aruids;
KernelHelpers::ServiceContext service_context;
EventInterface events_interface;
ankerl::unordered_dense::map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders;
size_t CloseFilesForSessions(std::span<const NvCore::SessionId> session_ids);
std::vector<NvCore::SessionId> GetSessionIdsForAruid(u64 aruid) const;
};
void LoopProcess(Core::System& system);
@@ -212,10 +212,7 @@ void NVDRV::QueryEvent(HLERequestContext& ctx) {
void NVDRV::SetAruid(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
pid = rp.Pop<u64>();
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid={:#X}", pid);
if (is_initialized) {
nvdrv->TrackSessionAruid(session_id, pid);
}
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid={:#x}", pid);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
+2 -4
View File
@@ -40,7 +40,6 @@ namespace Network {
namespace {
enum class CallType {
Connect,
Send,
Other,
};
@@ -132,7 +131,7 @@ Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
case WSAENOTCONN:
return Errno::NOTCONN;
case WSAEWOULDBLOCK:
return call_type == CallType::Connect ? Errno::INPROGRESS : Errno::AGAIN;
return Errno::AGAIN;
case WSAECONNREFUSED:
return Errno::CONNREFUSED;
case WSAECONNABORTED:
@@ -564,7 +563,6 @@ int TranslateTypeToNative(Type type) {
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE)
#elif defined(_WIN32)
#define NETWORK_PROTOCOL_TRANSLATE_LIST \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \
/*NETWORK_PROTOCOL_TRANSLATE_ELEM(HOPOPTS)*/ \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \
@@ -890,7 +888,7 @@ Errno Socket::Connect(SockAddrIn addr_in) {
return Errno::SUCCESS;
}
return GetAndLogLastError(CallType::Connect);
return GetAndLogLastError();
}
std::pair<SockAddrIn, Errno> Socket::GetPeerName() {
-311
View File
@@ -1,311 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/loader/homebrew_nxlink.h"
#include <array>
#include <cctype>
#include <memory>
#include <mutex>
#include <optional>
#include <stop_token>
#include <utility>
#include <boost/asio.hpp>
#include "common/logging.h"
#include "common/polyfill_thread.h"
#include "common/thread.h"
namespace Loader::HomebrewNxlink {
namespace {
using boost::asio::ip::tcp;
constexpr u16 NxlinkClientPort = 28771;
constexpr size_t ReceiveBufferSize = 1024;
constexpr size_t MaxLogLineSize = 16 * 1024;
struct LogConnection {
explicit LogConnection(boost::asio::io_context& io_context_) : socket{io_context_} {}
tcp::socket socket;
std::array<char, ReceiveBufferSize> buffer{};
std::string line;
};
struct LogServerState {
LogServerState() : acceptor{io_context} {}
boost::asio::io_context io_context;
tcp::acceptor acceptor;
};
std::mutex server_mutex;
std::shared_ptr<LogServerState> server_state;
std::optional<std::jthread> server_thread;
std::optional<std::string> GetLastArgvToken(std::string_view argv_string) {
while (!argv_string.empty() && argv_string.back() == '\0') {
argv_string.remove_suffix(1);
}
std::optional<std::string_view> last_token;
bool in_token = false;
bool quoted = false;
size_t token_begin = 0;
size_t token_size = 0;
for (size_t i = 0; i <= argv_string.size(); i++) {
const char c = i < argv_string.size() ? argv_string[i] : '\0';
if (!in_token) {
if (c == '\0' || std::isspace(static_cast<unsigned char>(c))) {
continue;
}
in_token = true;
token_size = 0;
if (c == '"') {
quoted = true;
token_begin = i + 1;
} else {
quoted = false;
token_begin = i;
token_size = 1;
}
continue;
}
const bool token_end =
quoted ? c == '"' || c == '\0'
: c == '\0' || std::isspace(static_cast<unsigned char>(c));
if (token_end) {
if (token_size != 0) {
last_token = argv_string.substr(token_begin, token_size);
}
in_token = false;
quoted = false;
token_size = 0;
continue;
}
token_size++;
}
if (!last_token) {
return std::nullopt;
}
return std::string{*last_token};
}
void AppendArgvToken(std::string& argv_string, std::string_view token) {
while (!argv_string.empty() && argv_string.back() == '\0') {
argv_string.pop_back();
}
if (!argv_string.empty()) {
argv_string.push_back(' ');
}
argv_string.append(token);
}
void FlushLogLine(std::string& line) {
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
if (!line.empty()) {
LOG_INFO(Loader, "{}", line);
line.clear();
}
}
void ConsumeLogBytes(LogConnection& connection, std::string_view bytes) {
for (const char byte : bytes) {
if (byte == '\n') {
FlushLogLine(connection.line);
continue;
}
connection.line.push_back(byte);
if (connection.line.size() >= MaxLogLineSize) {
FlushLogLine(connection.line);
}
}
}
void StartRead(std::shared_ptr<LogConnection> connection) {
connection->socket.async_read_some(
boost::asio::buffer(connection->buffer),
[connection](const boost::system::error_code& error, size_t bytes_read) {
if (error.failed()) {
FlushLogLine(connection->line);
return;
}
ConsumeLogBytes(*connection, {connection->buffer.data(), bytes_read});
StartRead(connection);
});
}
void StartAccept(std::shared_ptr<LogServerState> state) {
auto connection = std::make_shared<LogConnection>(state->io_context);
state->acceptor.async_accept(
connection->socket,
[state, connection](const boost::system::error_code& error) {
if (!error.failed()) {
LOG_INFO(Loader, "Homebrew nxlink log client connected");
StartRead(connection);
}
if (state->acceptor.is_open()) {
StartAccept(state);
}
});
}
void StartLogServer() {
std::scoped_lock lock{server_mutex};
if (server_thread) {
return;
}
auto state = std::make_shared<LogServerState>();
const tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), NxlinkClientPort};
boost::system::error_code error;
state->acceptor.open(endpoint.protocol(), error);
if (!error.failed()) {
state->acceptor.set_option(tcp::acceptor::reuse_address(true), error);
}
if (!error.failed()) {
state->acceptor.bind(endpoint, error);
}
if (!error.failed()) {
state->acceptor.listen(boost::asio::socket_base::max_listen_connections, error);
}
if (error.failed()) {
LOG_WARNING(Loader, "Homebrew nxlink log server could not listen on 127.0.0.1:{}: {}",
NxlinkClientPort, error.message());
return;
}
StartAccept(state);
server_state = state;
server_thread.emplace([state](std::stop_token stop_token) {
Common::SetCurrentThreadName("HomebrewNxlink");
std::stop_callback stop_callback{stop_token, [state] {
boost::system::error_code ignored;
state->acceptor.close(ignored);
state->io_context.stop();
}};
LOG_INFO(Loader, "Homebrew nxlink log server listening on 127.0.0.1:{}",
NxlinkClientPort);
state->io_context.run();
LOG_INFO(Loader, "Homebrew nxlink log server stopped");
});
}
} // namespace
bool IsArgvMarker(std::string_view token) {
if (token.size() != ArgvMarkerSize || token.substr(8) != ArgvMarkerSuffix) {
return false;
}
for (size_t i = 0; i < 8; i++) {
if (!std::isxdigit(static_cast<unsigned char>(token[i]))) {
return false;
}
}
return true;
}
bool IsLoopbackArgvMarker(std::string_view token) {
if (!IsArgvMarker(token)) {
return false;
}
for (size_t i = 0; i < 8; i++) {
if (std::tolower(static_cast<unsigned char>(token[i])) !=
std::tolower(static_cast<unsigned char>(LoopbackArgvMarker[i]))) {
return false;
}
}
return true;
}
std::optional<std::string> GetArgvMarker(std::string_view argv_string) {
auto last_token = GetLastArgvToken(argv_string);
if (!last_token || !IsArgvMarker(*last_token)) {
return std::nullopt;
}
return last_token;
}
std::optional<std::string> PrepareArgv(std::string& argv_string,
std::string_view inherited_marker,
bool append_loopback_marker) {
auto marker = GetArgvMarker(argv_string);
if (!marker && IsArgvMarker(inherited_marker)) {
AppendArgvToken(argv_string, inherited_marker);
marker = std::string{inherited_marker};
}
if (!marker && append_loopback_marker) {
AppendArgvToken(argv_string, LoopbackArgvMarker);
marker = std::string{LoopbackArgvMarker};
}
return marker;
}
void ApplyServerMode(Settings::HomebrewNxlinkServerMode mode,
const std::optional<std::string>& active_marker) {
switch (mode) {
case Settings::HomebrewNxlinkServerMode::Disabled:
StopServer();
return;
case Settings::HomebrewNxlinkServerMode::EdenLog:
if (active_marker && IsLoopbackArgvMarker(*active_marker)) {
StartLogServer();
return;
}
StopServer();
if (active_marker) {
LOG_INFO(Loader,
"Homebrew nxlink server mode overridden by argv marker '{}'; not starting "
"local nxlink log server",
*active_marker);
} else {
LOG_WARNING(Loader,
"Homebrew nxlink log server requested, but no argv marker is active");
}
return;
case Settings::HomebrewNxlinkServerMode::HostStdout:
case Settings::HomebrewNxlinkServerMode::File:
StopServer();
LOG_WARNING(Loader, "Homebrew nxlink server mode {} is not implemented",
static_cast<int>(mode));
return;
}
}
void StopServer() {
std::shared_ptr<LogServerState> state;
std::optional<std::jthread> thread;
{
std::scoped_lock lock{server_mutex};
state = std::move(server_state);
thread = std::move(server_thread);
}
if (state) {
boost::system::error_code ignored;
state->acceptor.close(ignored);
state->io_context.stop();
}
if (thread) {
thread->request_stop();
}
}
} // namespace Loader::HomebrewNxlink
-30
View File
@@ -1,30 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <cstddef>
#include <optional>
#include <string>
#include <string_view>
#include "common/settings_enums.h"
namespace Loader::HomebrewNxlink {
constexpr size_t ArgvMarkerSize = 16;
constexpr std::string_view ArgvMarkerSuffix = "_NXLINK_";
constexpr std::string_view LoopbackArgvMarker = "0100007F_NXLINK_";
bool IsArgvMarker(std::string_view token);
bool IsLoopbackArgvMarker(std::string_view token);
std::optional<std::string> GetArgvMarker(std::string_view argv_string);
std::optional<std::string> PrepareArgv(std::string& argv_string,
std::string_view inherited_marker,
bool append_loopback_marker = false);
void ApplyServerMode(Settings::HomebrewNxlinkServerMode mode,
const std::optional<std::string>& active_marker);
void StopServer();
} // namespace Loader::HomebrewNxlink
+51 -503
View File
@@ -4,20 +4,17 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstring>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "common/alignment.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/fs/path_util.h"
#include "common/logging.h"
#include "common/settings.h"
#include "common/random.h"
@@ -26,14 +23,11 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs/vfs_offset.h"
#include "core/hardware_properties.h"
#include "core/hle/api_version.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/homebrew_nxlink.h"
#include "core/loader/nro.h"
#include "core/memory.h"
@@ -158,432 +152,8 @@ static constexpr u32 PageAlignSize(u32 size) {
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
}
static std::string MakeHomebrewSdmcPath(std::string path) {
std::replace(path.begin(), path.end(), '\\', '/');
while (!path.empty() && path.front() == '/') {
path.erase(path.begin());
}
return "sdmc:/" + path;
}
static std::optional<std::string> TryMakeHomebrewSdmcPathFromHostPath(std::string path) {
std::replace(path.begin(), path.end(), '\\', '/');
std::string sdmc_root =
Common::FS::PathToUTF8String(Common::FS::GetEdenPath(Common::FS::EdenPath::SDMCDir));
std::replace(sdmc_root.begin(), sdmc_root.end(), '\\', '/');
while (!sdmc_root.empty() && sdmc_root.back() == '/') {
sdmc_root.pop_back();
}
if (!sdmc_root.empty() && path.size() > sdmc_root.size() &&
path.compare(0, sdmc_root.size(), sdmc_root) == 0 && path[sdmc_root.size()] == '/') {
return MakeHomebrewSdmcPath(path.substr(sdmc_root.size() + 1));
}
constexpr std::string_view SdmcMarker{"/sdmc/"};
if (const auto pos = path.rfind(SdmcMarker); pos != std::string::npos) {
return MakeHomebrewSdmcPath(path.substr(pos + SdmcMarker.size()));
}
return std::nullopt;
}
static std::string MakeHomebrewArgv0(std::string nro_path, std::string file_name) {
if (nro_path.empty()) {
nro_path = std::move(file_name);
}
std::replace(nro_path.begin(), nro_path.end(), '\\', '/');
if (nro_path.rfind("sdmc:/", 0) == 0) {
return nro_path;
}
if (auto sdmc_path = TryMakeHomebrewSdmcPathFromHostPath(nro_path)) {
return *sdmc_path;
}
#ifdef __ANDROID__
if (nro_path.find('%') != std::string::npos) {
const auto percent_decode = [](std::string value) {
const auto hex_value = [](char c) -> int {
if (c >= '0' && c <= '9') {
return c - '0';
}
if (c >= 'a' && c <= 'f') {
return c - 'a' + 10;
}
if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
}
return -1;
};
std::string decoded;
decoded.reserve(value.size());
for (size_t i = 0; i < value.size(); i++) {
if (value[i] == '%' && i + 2 < value.size()) {
const int high = hex_value(value[i + 1]);
const int low = hex_value(value[i + 2]);
if (high >= 0 && low >= 0) {
decoded.push_back(static_cast<char>((high << 4) | low));
i += 2;
continue;
}
}
decoded.push_back(value[i]);
}
return decoded;
};
if (auto sdmc_path = TryMakeHomebrewSdmcPathFromHostPath(percent_decode(nro_path))) {
return *sdmc_path;
}
}
#endif
if (!nro_path.empty() && nro_path.front() == '/') {
return "sdmc:" + nro_path;
}
return nro_path.empty() ? "homebrew" : nro_path;
}
static std::string QuoteHomebrewArgvComponent(const std::string& argument) {
if (argument.find_first_of(" \t\r\n") == std::string::npos) {
return argument;
}
std::string quoted{"\""};
quoted += argument;
quoted.push_back('"');
return quoted;
}
static std::string GetHomebrewInitialCwd(const std::string& argv0) {
constexpr std::string_view SdmcPrefix = "sdmc:";
if (argv0.substr(0, SdmcPrefix.size()) != SdmcPrefix) {
return {};
}
const auto last_slash = argv0.find_last_of('/');
if (last_slash == std::string_view::npos || last_slash < SdmcPrefix.size()) {
return {};
}
std::string cwd = argv0.substr(SdmcPrefix.size(), last_slash - SdmcPrefix.size());
if (cwd.empty()) {
cwd = "/";
}
while (cwd.size() > 1 && cwd.back() == '/') {
cwd.pop_back();
}
return cwd;
}
constexpr size_t HomebrewNextLoadPathSize = 0x200;
constexpr size_t HomebrewNextLoadArgvSize = 0x800;
constexpr u32 HomebrewSvcExitProcessInstruction = 0xD40000E1;
constexpr u32 HomebrewEntryEndOfList = 0;
constexpr u32 HomebrewEntryMainThreadHandle = 1;
constexpr u32 HomebrewEntryNextLoadPath = 2;
constexpr u32 HomebrewEntryOverrideHeap = 3;
constexpr u32 HomebrewEntryArgv = 5;
constexpr u32 HomebrewEntrySyscallAvailableHint = 6;
constexpr u32 HomebrewEntryAppletType = 7;
constexpr u32 HomebrewEntryProcessHandle = 10;
constexpr u32 HomebrewEntryRandomSeed = 14;
constexpr u32 HomebrewEntryHosVersion = 16;
constexpr u32 HomebrewEntrySyscallAvailableHint2 = 17;
constexpr u32 HomebrewAppletTypeApplication = 0;
constexpr u64 HomebrewAllSvcHints = ~u64{0};
constexpr u32 HomebrewHosVersion = (u32{HLE::ApiVersion::HOS_VERSION_MAJOR} << 16) |
(u32{HLE::ApiVersion::HOS_VERSION_MINOR} << 8) |
u32{HLE::ApiVersion::HOS_VERSION_MICRO};
struct HomebrewConfigEntry {
u32_le key;
u32_le flags;
u64_le value[2];
};
static_assert(sizeof(HomebrewConfigEntry) == 0x18);
constexpr size_t HomebrewBaseConfigEntryCount = 10;
constexpr size_t HomebrewInPlaceConfigEntryCount = 11;
constexpr size_t HomebrewBaseConfigTableSize =
HomebrewBaseConfigEntryCount * sizeof(HomebrewConfigEntry);
constexpr size_t HomebrewInPlaceConfigTableSize =
HomebrewInPlaceConfigEntryCount * sizeof(HomebrewConfigEntry);
struct HomebrewNroImage {
Kernel::CodeSet codeset;
size_t image_size{};
size_t args_offset{};
std::optional<size_t> exit_process_offset;
std::optional<std::string> nxlink_argv_marker;
std::string argv_string;
};
static void SetHomebrewConfigPointers(Kernel::KProcess& process, u64 config_addr,
u64 next_load_path_addr, u64 next_load_argv_addr) {
constexpr size_t MainThreadHandleEntryIndex = 0;
constexpr size_t ProcessHandleEntryIndex = 1;
constexpr size_t EntryValueOffset = offsetof(HomebrewConfigEntry, value);
process.SetArgPointer(Kernel::KProcessAddress{config_addr});
process.SetMainThreadHandleAddr(Kernel::KProcessAddress{
config_addr + MainThreadHandleEntryIndex * sizeof(HomebrewConfigEntry) + EntryValueOffset});
process.SetProcessHandleAddr(Kernel::KProcessAddress{
config_addr + ProcessHandleEntryIndex * sizeof(HomebrewConfigEntry) + EntryValueOffset});
process.SetHomebrewNextLoadBufferAddrs(Kernel::KProcessAddress{next_load_path_addr},
Kernel::KProcessAddress{next_load_argv_addr});
}
static std::optional<HomebrewNroImage> BuildHomebrewNroImage(const std::vector<u8>& data,
std::string nro_path,
std::string file_name,
std::string launch_argv,
std::string_view inherited_marker,
bool append_loopback_nxlink_marker) {
if (data.size() < sizeof(NroHeader)) {
return std::nullopt;
}
NroHeader nro_header{};
std::memcpy(&nro_header, data.data(), sizeof(NroHeader));
if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
return std::nullopt;
}
if (data.size() < nro_header.file_size ||
nro_header.module_header_offset + sizeof(ModHeader) > PageAlignSize(nro_header.file_size)) {
return std::nullopt;
}
std::vector<u8> program_image(PageAlignSize(nro_header.file_size));
std::memcpy(program_image.data(), data.data(), nro_header.file_size);
Kernel::CodeSet codeset;
for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
codeset.segments[i].addr = nro_header.segments[i].offset;
codeset.segments[i].offset = nro_header.segments[i].offset;
codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
}
u32 bss_size{PageAlignSize(nro_header.bss_size)};
ModHeader mod_header{};
std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset,
sizeof(ModHeader));
if (mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')) {
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
}
codeset.DataSegment().size += bss_size;
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
HomebrewNroImage image{.codeset = std::move(codeset)};
const auto argv0 = MakeHomebrewArgv0(std::move(nro_path), std::move(file_name));
if (!launch_argv.empty()) {
image.argv_string = std::move(launch_argv);
} else {
image.argv_string = QuoteHomebrewArgvComponent(argv0);
}
image.nxlink_argv_marker = HomebrewNxlink::PrepareArgv(
image.argv_string, inherited_marker, append_loopback_nxlink_marker);
if (image.argv_string.empty() || image.argv_string.back() != '\0') {
image.argv_string.push_back('\0');
}
const auto& code = image.codeset.CodeSegment();
const size_t code_end = (std::min)(program_image.size(), code.offset + code.size);
for (size_t offset = code.offset; offset + sizeof(u32) <= code_end; offset += sizeof(u32)) {
u32 instruction{};
std::memcpy(&instruction, program_image.data() + offset, sizeof(instruction));
if (instruction == HomebrewSvcExitProcessInstruction) {
image.exit_process_offset = offset;
break;
}
}
const size_t entries_and_buffers =
Common::AlignUp(HomebrewInPlaceConfigTableSize + HomebrewNextLoadPathSize +
HomebrewNextLoadArgvSize + image.argv_string.size(),
Core::Memory::YUZU_PAGESIZE);
image.args_offset = program_image.size();
image.codeset.DataSegment().size += static_cast<u32>(entries_and_buffers);
program_image.resize(image.args_offset + entries_and_buffers);
image.image_size = program_image.size();
image.codeset.memory = std::move(program_image);
return image;
}
bool LoadNroInPlace(Core::System& system, Kernel::KProcess& process, Kernel::KThread& thread,
const FileSys::VirtualFile& nro_file, const std::string& nro_path,
const std::string& launch_argv) {
if (!nro_file) {
return false;
}
#ifdef HAS_NCE
if (Settings::IsNceEnabled()) {
LOG_WARNING(Loader,
"Homebrew next-load: in-place handoff unavailable because NCE is enabled");
return false;
}
#endif
size_t live_threads = 0;
for (auto& candidate : process.GetThreadList()) {
if (candidate.GetState() != Kernel::ThreadState::Terminated) {
live_threads++;
}
}
if (live_threads != 1) {
LOG_WARNING(Loader, "NextLoad: in-place handoff failed because live_threads={}",
live_threads);
return false;
}
const auto stack_top = process.GetMainThreadStackTop();
if (GetInteger(stack_top) == 0) {
LOG_WARNING(Loader, "NextLoad: in-place handoff failed because stack_top=0");
return false;
}
const auto nxlink_server_mode = Settings::values.homebrew_nxlink_server_mode.GetValue();
const bool append_loopback_nxlink_marker =
nxlink_server_mode != Settings::HomebrewNxlinkServerMode::Disabled;
auto image = BuildHomebrewNroImage(
nro_file->ReadAllBytes(), nro_path, nro_file->GetName(), launch_argv,
process.GetHomebrewNxlinkArgvMarker(), append_loopback_nxlink_marker);
if (!image) {
LOG_WARNING(Loader, "NextLoad: in-place handoff failed because '{}' is invalid",
nro_path);
return false;
}
const auto capacity = process.GetCodeSize();
if (image->image_size > capacity) {
LOG_WARNING(Loader,
"NextLoad: in-place handoff failed because image_size=0x{:X} exceeds "
"capacity=0x{:X}",
image->image_size, capacity);
return false;
}
const u64 base = GetInteger(process.GetEntryPoint());
const u64 heap_addr = GetInteger(process.GetPageTable().GetHeapRegionStart());
const size_t heap_size = process.GetPageTable().GetBasePageTable().GetCurrentHeapSize();
if (heap_addr == 0 || heap_size == 0) {
LOG_WARNING(Loader,
"NextLoad: in-place handoff failed because heap override is "
"unavailable (addr=0x{:016X}, size=0x{:X})",
heap_addr, heap_size);
return false;
}
const u64 config_addr = base + image->args_offset;
const u64 next_load_path_addr = config_addr + HomebrewInPlaceConfigTableSize;
const u64 next_load_argv_addr = next_load_path_addr + HomebrewNextLoadPathSize;
const u64 argv_addr = next_load_argv_addr + HomebrewNextLoadArgvSize;
const u64 argv_entry_addr = image->argv_string.empty() ? 0 : argv_addr;
const std::string argv0 = MakeHomebrewArgv0(nro_path, nro_file->GetName());
const std::string homebrew_initial_cwd = GetHomebrewInitialCwd(argv0);
u64 program_id{};
AppLoader_NRO loader{nro_file};
if (loader.ReadProgramId(program_id) != Loader::ResultStatus::Success) {
LOG_WARNING(Loader, "NextLoad: in-place handoff could not read NRO program id");
}
Kernel::Handle main_thread_handle{};
if (process.GetHandleTable().Add(system.Kernel(), std::addressof(main_thread_handle), &thread)
.IsError()) {
LOG_WARNING(Loader,
"NextLoad: in-place handoff failed because main thread handle failed");
return false;
}
Kernel::Handle process_handle{};
if (process.GetHandleTable().Add(system.Kernel(), std::addressof(process_handle), &process)
.IsError()) {
LOG_WARNING(Loader,
"NextLoad: in-place handoff failed because process handle failed");
return false;
}
if (!process.GetMemory().ZeroBlock(Common::ProcessAddress{heap_addr}, heap_size)) {
LOG_WARNING(Loader,
"NextLoad: in-place handoff failed because heap clear failed "
"(addr=0x{:016X}, size=0x{:X})",
heap_addr, heap_size);
return false;
}
process.LoadModule(system.Kernel(), std::move(image->codeset), process.GetEntryPoint());
const HomebrewConfigEntry entries[HomebrewInPlaceConfigEntryCount] = {
{HomebrewEntryMainThreadHandle, 0, {main_thread_handle, 0}},
{HomebrewEntryProcessHandle, 0, {process_handle, 0}},
{HomebrewEntryNextLoadPath, 0, {next_load_path_addr, next_load_argv_addr}},
{HomebrewEntryOverrideHeap, 0, {heap_addr, heap_size}},
{HomebrewEntryAppletType, 0, {HomebrewAppletTypeApplication, 0}},
{HomebrewEntryArgv, 0, {0, argv_entry_addr}},
{HomebrewEntrySyscallAvailableHint, 0, {HomebrewAllSvcHints, HomebrewAllSvcHints}},
{HomebrewEntryRandomSeed, 0,
{process.GetRandomEntropy(0), process.GetRandomEntropy(1)}},
{HomebrewEntryHosVersion, 0, {HomebrewHosVersion, 0}},
{HomebrewEntrySyscallAvailableHint2, 0, {HomebrewAllSvcHints, 0}},
{HomebrewEntryEndOfList, 0, {0, 0}},
};
process.GetMemory().WriteBlock(Common::ProcessAddress{config_addr}, entries, sizeof(entries));
process.GetMemory().WriteBlock(Common::ProcessAddress{argv_addr}, image->argv_string.data(),
image->argv_string.size());
process.GetMemory().Write32(Common::ProcessAddress{GetInteger(thread.GetTlsAddress()) + 0x110},
main_thread_handle);
process.SetArgReturnAddress(Kernel::KProcessAddress{
image->exit_process_offset ? base + *image->exit_process_offset : 0});
SetHomebrewConfigPointers(process, config_addr, next_load_path_addr, next_load_argv_addr);
if (image->nxlink_argv_marker) {
process.SetHomebrewNxlinkArgvMarker(*image->nxlink_argv_marker);
} else {
process.ClearHomebrewNxlinkArgvMarker();
}
HomebrewNxlink::ApplyServerMode(nxlink_server_mode, image->nxlink_argv_marker);
system.GetFileSystemController().RegisterProcess(
process.GetProcessId(), program_id,
std::make_unique<FileSys::RomFSFactory>(loader, system.GetContentProvider(),
system.GetFileSystemController()),
homebrew_initial_cwd);
process.SetHomebrewInPlaceNextLoad(true);
auto& context = thread.GetContext();
context = {};
context.r[0] = config_addr;
context.r[1] = UINT64_MAX;
context.r[18] = Common::Random::Random64(0) | 1;
context.lr = image->exit_process_offset ? base + *image->exit_process_offset : 0;
context.pc = base;
context.sp = GetInteger(stack_top);
context.fpcr = 0;
context.fpsr = 0;
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
if (auto* arm = process.GetArmInterface(core); arm != nullptr) {
arm->ClearInstructionCache();
}
}
return true;
}
static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
const std::vector<u8>& data, std::string nro_path,
std::string file_name) {
const std::vector<u8>& data) {
if (data.size() < sizeof(NroHeader)) {
return {};
}
@@ -625,49 +195,47 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
codeset.DataSegment().size += bss_size;
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
struct ConfigEntry {
u32_le key;
u32_le flags;
u64_le value[2];
};
static_assert(sizeof(ConfigEntry) == 0x18);
// AArch64 encoding for svc #0x7 (ExitProcess).
constexpr u32 kSvcExitProcessInstruction = 0xD40000E1;
constexpr size_t kNumEntries = 4; // MainThreadHandle, AppletType, Argv, EndOfList
constexpr size_t kConfigTableSize = kNumEntries * sizeof(ConfigEntry);
std::string argv_string;
size_t args_offset_in_image = 0;
std::optional<size_t> exit_process_offset_in_image;
const auto& program_args = Settings::values.program_args.GetValue();
const std::string argv0 = MakeHomebrewArgv0(std::move(nro_path), std::move(file_name));
argv_string = QuoteHomebrewArgvComponent(argv0);
if (!program_args.empty()) {
argv_string.push_back(' ');
argv_string = "homebrew ";
argv_string += program_args;
}
const auto nxlink_server_mode = Settings::values.homebrew_nxlink_server_mode.GetValue();
const bool append_loopback_nxlink_marker =
nxlink_server_mode != Settings::HomebrewNxlinkServerMode::Disabled;
const auto nxlink_argv_marker =
HomebrewNxlink::PrepareArgv(argv_string, {}, append_loopback_nxlink_marker);
if (argv_string.empty() || argv_string.back() != '\0') {
argv_string.push_back('\0');
}
const auto& code_segment = codeset.CodeSegment();
const size_t code_end =
(std::min)(program_image.size(), code_segment.offset + code_segment.size);
for (size_t offset = code_segment.offset; offset + sizeof(u32) <= code_end;
offset += sizeof(u32)) {
u32 instruction{};
std::memcpy(&instruction, program_image.data() + offset, sizeof(instruction));
if (instruction == HomebrewSvcExitProcessInstruction) {
exit_process_offset_in_image = offset;
break;
const auto& code = codeset.CodeSegment();
const size_t code_end = (std::min)(program_image.size(), code.offset + code.size);
for (size_t offset = code.offset; offset + sizeof(u32) <= code_end; offset += sizeof(u32)) {
u32 instruction{};
std::memcpy(&instruction, program_image.data() + offset, sizeof(instruction));
if (instruction == kSvcExitProcessInstruction) {
exit_process_offset_in_image = offset;
break;
}
}
if (!exit_process_offset_in_image) {
LOG_WARNING(Loader,
"Unable to find svcExitProcess in NRO; returning from main may fault");
}
}
if (!exit_process_offset_in_image) {
LOG_WARNING(Loader, "Unable to find svcExitProcess in NRO; returning from main may fault");
}
const size_t entries_and_buffers =
Common::AlignUp(HomebrewBaseConfigTableSize + HomebrewNextLoadPathSize +
HomebrewNextLoadArgvSize + argv_string.size(),
Core::Memory::YUZU_PAGESIZE);
const size_t entries_and_argv =
Common::AlignUp(kConfigTableSize + argv_string.size(), Core::Memory::YUZU_PAGESIZE);
args_offset_in_image = program_image.size();
codeset.DataSegment().size += static_cast<u32>(entries_and_buffers);
program_image.resize(args_offset_in_image + entries_and_buffers);
args_offset_in_image = program_image.size();
codeset.DataSegment().size += static_cast<u32>(entries_and_argv);
program_image.resize(args_offset_in_image + entries_and_argv);
}
size_t image_size = program_image.size();
#ifdef HAS_NCE
@@ -695,9 +263,6 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
image_size += patch_segment.size;
}
#endif
// In-place NextLoad reuses the original code mapping; leave room for larger homebrew cores.
constexpr size_t HomebrewCodeArenaSize = 192 * 1024 * 1024;
image_size = (std::max)(image_size, HomebrewCodeArenaSize);
// Enable direct memory mapping in case of NCE.
const u64 fastmem_base = [&]() -> size_t {
@@ -732,44 +297,31 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
// Load codeset for current process
codeset.memory = std::move(program_image);
process.LoadModule(system.Kernel(), std::move(codeset), process.GetEntryPoint());
process.SetHomebrewInPlaceNextLoad(false);
if (nxlink_argv_marker) {
process.SetHomebrewNxlinkArgvMarker(*nxlink_argv_marker);
} else {
process.ClearHomebrewNxlinkArgvMarker();
}
HomebrewNxlink::ApplyServerMode(nxlink_server_mode, nxlink_argv_marker);
{
if (!argv_string.empty()) {
constexpr u32 kEntryEndOfList = 0;
constexpr u32 kEntryMainThreadHandle = 1;
constexpr u32 kEntryArgv = 5;
constexpr u32 kEntryAppletType = 7;
constexpr u32 kAppletTypeApplication = 0;
const u64 base = GetInteger(process.GetEntryPoint());
const u64 config_addr = base + args_offset_in_image;
const u64 next_load_path_addr = config_addr + HomebrewBaseConfigTableSize;
const u64 next_load_argv_addr = next_load_path_addr + HomebrewNextLoadPathSize;
const u64 argv_addr = next_load_argv_addr + HomebrewNextLoadArgvSize;
const u64 argv_entry_addr = argv_string.empty() ? 0 : argv_addr;
const u64 argv_addr = config_addr + kConfigTableSize;
const HomebrewConfigEntry entries[HomebrewBaseConfigEntryCount] = {
{HomebrewEntryMainThreadHandle, 0, {0, 0}}, // Value[0] patched in Run()
{HomebrewEntryProcessHandle, 0, {0, 0}}, // Value[0] patched in Run()
{HomebrewEntryNextLoadPath, 0, {next_load_path_addr, next_load_argv_addr}},
{HomebrewEntryAppletType, 0, {HomebrewAppletTypeApplication, 0}},
{HomebrewEntryArgv, 0, {0, argv_entry_addr}},
{HomebrewEntrySyscallAvailableHint, 0, {HomebrewAllSvcHints, HomebrewAllSvcHints}},
{HomebrewEntryRandomSeed, 0,
{process.GetRandomEntropy(0), process.GetRandomEntropy(1)}},
{HomebrewEntryHosVersion, 0, {HomebrewHosVersion, 0}},
{HomebrewEntrySyscallAvailableHint2, 0, {HomebrewAllSvcHints, 0}},
{HomebrewEntryEndOfList, 0, {0, 0}},
const ConfigEntry entries[kNumEntries] = {
{kEntryMainThreadHandle, 0, {0, 0}}, // Value[0] patched in Run()
{kEntryAppletType, 0, {kAppletTypeApplication, 0}},
{kEntryArgv, 0, {0, argv_addr}},
{kEntryEndOfList, 0, {0, 0}},
};
process.GetMemory().WriteBlock(Common::ProcessAddress{config_addr}, entries,
sizeof(entries));
if (!argv_string.empty()) {
process.GetMemory().WriteBlock(Common::ProcessAddress{argv_addr}, argv_string.data(),
argv_string.size());
}
process.GetMemory().WriteBlock(Common::ProcessAddress{config_addr}, entries, sizeof(entries));
process.GetMemory().WriteBlock(Common::ProcessAddress{argv_addr}, argv_string.data(), argv_string.size());
constexpr size_t kMainThreadHandleValueOffset = offsetof(ConfigEntry, value);
process.SetArgPointer(Kernel::KProcessAddress{config_addr});
if (exit_process_offset_in_image) {
process.SetArgReturnAddress(Kernel::KProcessAddress{base + *exit_process_offset_in_image});
}
SetHomebrewConfigPointers(process, config_addr, next_load_path_addr, next_load_argv_addr);
process.SetMainThreadHandleAddr(Kernel::KProcessAddress{config_addr + kMainThreadHandleValueOffset});
}
return true;
@@ -777,8 +329,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
bool AppLoader_NRO::LoadNro(Core::System& system, Kernel::KProcess& process,
const FileSys::VfsFile& nro_file) {
return LoadNroImpl(system, process, nro_file.ReadAllBytes(), nro_file.GetFullPath(),
nro_file.GetName());
return LoadNroImpl(system, process, nro_file.ReadAllBytes());
}
AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) {
@@ -792,13 +343,10 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S
u64 program_id{};
ReadProgramId(program_id);
const std::string argv0 = MakeHomebrewArgv0(file->GetFullPath(), file->GetName());
const std::string homebrew_initial_cwd = GetHomebrewInitialCwd(argv0);
system.GetFileSystemController().RegisterProcess(
process.GetProcessId(), program_id,
std::make_unique<FileSys::RomFSFactory>(*this, system.GetContentProvider(),
system.GetFileSystemController()),
homebrew_initial_cwd);
system.GetFileSystemController()));
is_loaded = true;
return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
-8
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -22,15 +19,10 @@ class NACP;
namespace Kernel {
class KProcess;
class KThread;
}
namespace Loader {
[[nodiscard]] bool LoadNroInPlace(Core::System& system, Kernel::KProcess& process,
Kernel::KThread& thread, const FileSys::VirtualFile& nro_file,
const std::string& nro_path, const std::string& launch_argv);
/// Loads an NRO file
class AppLoader_NRO final : public AppLoader {
public:
-7
View File
@@ -648,13 +648,6 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, Settings::values.enable_raw_input ? "1" : "0");
#ifdef _WIN32
if (Settings::values.disable_wgi_xinput) {
SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_RAWINPUT_CORRELATE_XINPUT, "0", SDL_HINT_OVERRIDE);
SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_WGI, "0", SDL_HINT_OVERRIDE);
}
#endif
// SDL3 defaults Steam Controller Bluetooth HIDAPI support to off, which can disable gyro.
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1");
SDL_SetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION, "1");
@@ -302,8 +302,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
INSERT(Settings, device_name, tr("Device Name"), tr("The name of the console."));
INSERT(Settings, program_args, tr("Homebrew Args"),
tr("Command-line arguments passed to homebrew at launch (e.g. -noglsl)."));
INSERT(Settings, homebrew_nxlink_server_mode, tr("nxlink Server"),
tr("Starts a local nxlink server for homebrew stdout/stderr streams."));
INSERT(Settings, custom_rtc, tr("Custom RTC Date:"),
tr("This option allows to change the clock of the console.\n"
"Can be used to manipulate time in games."));
+1 -3
View File
@@ -159,9 +159,7 @@ void GameListModel::RemoveFavorite(u64 program_id) {
void GameListModel::Repopulate() {
current_worker.reset();
if (!QtCommon::system->IsPoweredOn()) {
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
}
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
PopulateAsync(UISettings::values.game_dirs);
}
+16 -16
View File
@@ -77,14 +77,8 @@ uvec4 local_buff;
uvec4 color_endpoint_data;
int color_bitsread = 0;
// Global "vector" to be pushed into when decoding
// At most will require BLOCK_WIDTH x BLOCK_HEIGHT in single plane mode
// At most will require BLOCK_WIDTH x BLOCK_HEIGHT x 2 in dual plane mode
// So the maximum would be 144 (12 x 12) elements, x 2 for two planes
#define DIVCEIL(number, divisor) (number + divisor - 1) / divisor
#define ARRAY_NUM_ELEMENTS 144
#define VECTOR_ARRAY_SIZE DIVCEIL(ARRAY_NUM_ELEMENTS * 2, 4)
uint result_vector[ARRAY_NUM_ELEMENTS * 2];
#define MAX_WEIGHT_VALUES 64
uint result_vector[MAX_WEIGHT_VALUES];
int result_index = 0;
uint result_vector_max_index;
@@ -492,7 +486,7 @@ void DecodeColorValues(uvec4 modes, uint num_partitions, uint color_data_bits, o
A = ReplicateBitTo9((bitval & 1));
switch (encoding) {
case JUST_BITS:
color_values[++out_index] = FastReplicateTo8(bitval, bitlen);
color_values[out_index++] = FastReplicateTo8(bitval, bitlen);
break;
case TRIT: {
D = QuintTritValue(val);
@@ -571,7 +565,7 @@ void DecodeColorValues(uvec4 modes, uint num_partitions, uint color_data_bits, o
uint T = (D * C) + B;
T ^= A;
T = (A & 0x80) | (T >> 2);
color_values[++out_index] = T;
color_values[out_index++] = T;
}
}
}
@@ -753,12 +747,12 @@ void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, ui
#define READ_UINT_VALUES(N) \
uvec4 V[2]; \
for (uint i = 0; i < N; i++) { \
V[i / 4][i % 4] = color_values[++colvals_index]; \
V[i / 4][i % 4] = color_values[colvals_index++]; \
}
#define READ_INT_VALUES(N) \
ivec4 V[2]; \
for (uint i = 0; i < N; i++) { \
V[i / 4][i % 4] = int(color_values[++colvals_index]); \
V[i / 4][i % 4] = int(color_values[colvals_index++]); \
}
switch (color_endpoint_mode) {
@@ -1225,6 +1219,10 @@ void DecompressBlock(ivec3 coord) {
FillError(coord);
return;
}
if (GetNumWeightValues(size_params, dual_plane) > MAX_WEIGHT_VALUES) {
FillError(coord);
return;
}
uint partition_index = 1;
uvec4 color_endpoint_mode = uvec4(0);
uint ced_pointer = 0;
@@ -1239,6 +1237,10 @@ void DecompressBlock(ivec3 coord) {
const uint base_mode = base_cem & 3;
const uint max_weight = DecodeMaxWeight(mode);
const uint weight_bits = GetPackedBitSize(size_params, dual_plane, max_weight);
if (weight_bits < 24 || weight_bits > 96) {
FillError(coord);
return;
}
uint remaining_bits = 128 - weight_bits - total_bitsread;
uint extra_cem_bits = 0;
if (base_mode > 0) {
@@ -1253,6 +1255,7 @@ void DecompressBlock(ivec3 coord) {
extra_cem_bits += 8;
break;
default:
FillError(coord);
return;
}
}
@@ -1262,6 +1265,7 @@ void DecompressBlock(ivec3 coord) {
if (remaining_bits > 128) {
// Bad data, more remaining bits than 4 bytes
// return early
FillError(coord);
return;
}
// Read color data...
@@ -1384,11 +1388,7 @@ void DecompressBlock(ivec3 coord) {
p = Cf / 65535.0f;
}
#ifdef VULKAN
imageStore(dest_image, coord + ivec3(i, j, 0), p.gbar);
#else
imageStore(dest_image, coord + ivec3(i, j, 0), clamp(p, 0.0f, 1.0f).gbar);
#endif
}
}
}
+22 -23
View File
@@ -782,17 +782,13 @@ void MacroJITx64Impl::Compile_ALU(Core::System& system, Macro::Opcode opcode) {
const bool valid_operation = !is_a_zero && !is_b_zero;
[[maybe_unused]] const bool is_move_operation = !is_a_zero && is_b_zero;
const bool has_zero_register = is_a_zero || is_b_zero;
const bool zero_reg_skip =
optimizer.zero_reg_skip && !is_a_zero && is_b_zero &&
(opcode.alu_operation == Macro::ALUOperation::Xor ||
opcode.alu_operation == Macro::ALUOperation::Or ||
(optimizer.can_skip_carry && (opcode.alu_operation == Macro::ALUOperation::Add ||
opcode.alu_operation == Macro::ALUOperation::Subtract)));
const bool no_zero_reg_skip = opcode.alu_operation == Macro::ALUOperation::AddWithCarry ||
opcode.alu_operation == Macro::ALUOperation::SubtractWithBorrow;
Xbyak::Reg32 src_a;
Xbyak::Reg32 src_b;
if (!zero_reg_skip) {
if (!optimizer.zero_reg_skip || no_zero_reg_skip) {
src_a = Compile_GetRegister(opcode.src_a, RESULT);
src_b = Compile_GetRegister(opcode.src_b, eax);
} else {
@@ -808,7 +804,7 @@ void MacroJITx64Impl::Compile_ALU(Core::System& system, Macro::Opcode opcode) {
switch (opcode.alu_operation) {
case Macro::ALUOperation::Add:
if (zero_reg_skip) {
if (optimizer.zero_reg_skip) {
if (valid_operation) {
add(src_a, src_b);
}
@@ -826,7 +822,7 @@ void MacroJITx64Impl::Compile_ALU(Core::System& system, Macro::Opcode opcode) {
setc(byte[STATE + offsetof(JITState, carry_flag)]);
break;
case Macro::ALUOperation::Subtract:
if (zero_reg_skip) {
if (optimizer.zero_reg_skip) {
if (valid_operation) {
sub(src_a, src_b);
has_emitted = true;
@@ -845,7 +841,7 @@ void MacroJITx64Impl::Compile_ALU(Core::System& system, Macro::Opcode opcode) {
setc(byte[STATE + offsetof(JITState, carry_flag)]);
break;
case Macro::ALUOperation::Xor:
if (zero_reg_skip) {
if (optimizer.zero_reg_skip) {
if (valid_operation) {
xor_(src_a, src_b);
}
@@ -854,7 +850,7 @@ void MacroJITx64Impl::Compile_ALU(Core::System& system, Macro::Opcode opcode) {
}
break;
case Macro::ALUOperation::Or:
if (zero_reg_skip) {
if (optimizer.zero_reg_skip) {
if (valid_operation) {
or_(src_a, src_b);
}
@@ -863,7 +859,7 @@ void MacroJITx64Impl::Compile_ALU(Core::System& system, Macro::Opcode opcode) {
}
break;
case Macro::ALUOperation::And:
if (zero_reg_skip) {
if (optimizer.zero_reg_skip) {
if (!has_zero_register) {
and_(src_a, src_b);
}
@@ -872,7 +868,7 @@ void MacroJITx64Impl::Compile_ALU(Core::System& system, Macro::Opcode opcode) {
}
break;
case Macro::ALUOperation::AndNot:
if (zero_reg_skip) {
if (optimizer.zero_reg_skip) {
if (!is_a_zero) {
not_(src_b);
and_(src_a, src_b);
@@ -883,7 +879,7 @@ void MacroJITx64Impl::Compile_ALU(Core::System& system, Macro::Opcode opcode) {
}
break;
case Macro::ALUOperation::Nand:
if (zero_reg_skip) {
if (optimizer.zero_reg_skip) {
if (!is_a_zero) {
and_(src_a, src_b);
not_(src_a);
@@ -1391,26 +1387,29 @@ void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u
std::span<const u32> code;
auto macro_code = uploaded_macro_code.find(method);
if (macro_code == uploaded_macro_code.end()) {
for (auto it = uploaded_macro_code.begin(); it != uploaded_macro_code.end(); ++it) {
const auto& [method_base, uploaded_code] = *it;
std::optional<u32> mid_method;
for (const auto& [method_base, uploaded_code] : uploaded_macro_code) {
if (method >= method_base && (method - method_base) < uploaded_code.size()) {
macro_code = it;
mid_method = method_base;
break;
}
}
if (macro_code == uploaded_macro_code.end()) {
if (!mid_method) {
ASSERT_MSG(false, "Macro 0x{0:x} was not uploaded", method);
return;
}
const auto rebased_method = method - macro_code->first;
std::vector<u32> rebased_code(macro_code->second.begin() + rebased_method,
macro_code->second.end());
const auto source = uploaded_macro_code.find(*mid_method);
ASSERT(source != uploaded_macro_code.end());
const auto rebased_method = method - *mid_method;
std::vector<u32> rebased_code(source->second.begin() + rebased_method,
source->second.end());
const auto [it, inserted] = uploaded_macro_code.emplace(method, std::move(rebased_code));
ASSERT(inserted);
macro_code = it;
code = it->second;
} else {
code = macro_code->second;
}
code = macro_code->second;
auto& ci = macro_cache[method];
ci.hash = Common::HashRange(code.begin(), code.end());
@@ -144,11 +144,6 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
info.size.depth == 1;
}
[[nodiscard]] bool WillUseWidenedAstcFormat(const Device& device, const ImageInfo& info) {
return WillUseAcceleratedAstcDecode(device, info) &&
!VideoCore::Surface::IsPixelFormatSRGB(info.format);
}
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info,
std::optional<VkFormat> format_override = {}) {
auto format_info =
@@ -269,6 +264,10 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
}
}
[[nodiscard]] bool IsLdrAstcFormat(VkFormat format) {
return format >= VK_FORMAT_ASTC_4x4_UNORM_BLOCK && format <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK;
}
[[nodiscard]] VkImageAspectFlags ImageViewAspectMask(const VideoCommon::ImageViewInfo& info) {
if (info.IsRenderTarget()) {
return ImageAspectMask(info.format);
@@ -1780,12 +1779,7 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
: VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler},
runtime{&runtime_},
original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info,
WillUseWidenedAstcFormat(runtime_.device, info)
? std::span<const VkFormat>{}
: runtime->ViewFormats(info.format),
WillUseWidenedAstcFormat(runtime_.device, info)
? std::make_optional(VK_FORMAT_R32G32B32A32_SFLOAT)
: std::nullopt)),
runtime->ViewFormats(info.format))),
aspect_mask(ImageAspectMask(info.format)) {
if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) {
switch (Settings::values.accelerate_astc.GetValue()) {
@@ -1812,13 +1806,9 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
}
current_image = &Image::original_image;
storage_image_views.resize(info.resources.levels);
if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported() &&
Settings::values.astc_recompression.GetValue() ==
Settings::AstcRecompression::Uncompressed) {
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
const auto& device = runtime->device.GetLogical();
const VkFormat storage_format = WillUseWidenedAstcFormat(runtime->device, info)
? VK_FORMAT_R32G32B32A32_SFLOAT
: VK_FORMAT_A8B8G8R8_UNORM_PACK32;
const VkFormat storage_format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
for (s32 level = 0; level < info.resources.levels; ++level) {
storage_image_views[level] =
MakeStorageView(device, level, *original_image, storage_format);
@@ -2204,9 +2194,7 @@ VkImageView Image::StorageImageView(s32 level) noexcept {
auto format_info =
MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format);
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
format_info.format = WillUseWidenedAstcFormat(runtime->device, info)
? VK_FORMAT_R32G32B32A32_SFLOAT
: VK_FORMAT_A8B8G8R8_UNORM_PACK32;
format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
}
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image),
format_info.format);
@@ -2382,11 +2370,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
SanitizeDepthStencilSwizzle(swizzle, device->SupportsDepthStencilSwizzleOne());
}
}
uses_widened_astc_format = WillUseWidenedAstcFormat(*device, image.info);
auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
if (uses_widened_astc_format) {
format_info.format = VK_FORMAT_R32G32B32A32_SFLOAT;
}
if (device->ApiVersion() >= VK_API_VERSION_1_3) {
const VkFormatProperties3 properties3 =
device->GetPhysical().GetFormatProperties3(format_info.format);
@@ -2404,9 +2388,18 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
.pNext = nullptr,
.usage = clamped_view_usage,
};
const VkImageViewASTCDecodeModeEXT astc_decode_mode{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_ASTC_DECODE_MODE_EXT,
.pNext = &image_view_usage,
.decodeMode = VK_FORMAT_R8G8B8A8_UNORM,
};
const void* view_next = &image_view_usage;
if (device->IsExtAstcDecodeModeSupported() && IsLdrAstcFormat(format_info.format)) {
view_next = &astc_decode_mode;
}
const VkImageViewCreateInfo create_info{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = &image_view_usage,
.pNext = view_next,
.flags = 0,
.image = image.Handle(),
.viewType = VkImageViewType{},
@@ -2531,9 +2524,6 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type,
if (image_format == Shader::ImageFormat::Typeless) {
if (!typeless_storage_view) {
auto info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
if (uses_widened_astc_format) {
info.format = VK_FORMAT_R32G32B32A32_SFLOAT;
}
typeless_storage_view = MakeView(info.format, VK_IMAGE_ASPECT_COLOR_BIT, texture_type);
}
return *typeless_storage_view;
@@ -436,7 +436,6 @@ private:
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
u32 buffer_size = 0;
bool uses_widened_astc_format = false;
bool supports_depth_comparison = false;
};
+11
View File
@@ -1769,6 +1769,12 @@ static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth,
return;
}
if (weightParams.GetNumWeightValues() > 64) {
assert(false && "Too many weights in the weight grid");
FillError(outBuf, blockWidth, blockHeight);
return;
}
// Read num partitions
u32 nPartitions = strm.ReadBits<2>() + 1;
assert(nPartitions <= 4);
@@ -1805,6 +1811,11 @@ static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth,
// Remaining bits are color endpoint data...
u32 nWeightBits = weightParams.GetPackedBitSize();
if (nWeightBits < 24 || nWeightBits > 96) {
assert(false && "Invalid weight bit count");
FillError(outBuf, blockWidth, blockHeight);
return;
}
s32 remainingBits = 128 - nWeightBits - static_cast<int>(strm.GetBitsRead());
// Consider extra bits prior to texel data...
@@ -820,14 +820,13 @@ bool Device::ComputeIsOptimalAstcSupported() const {
if (!features.features.textureCompressionASTC_LDR) {
return false;
}
const auto format_feature_usage{VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
VK_FORMAT_FEATURE_BLIT_SRC_BIT |
VK_FORMAT_FEATURE_BLIT_DST_BIT |
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
VK_FORMAT_FEATURE_TRANSFER_DST_BIT};
const VkFormatFeatureFlags format_feature_usage{
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT |
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT};
for (const auto format : astc_formats) {
const auto physical_format_properties{physical.GetFormatProperties(format)};
if ((physical_format_properties.optimalTilingFeatures & format_feature_usage) == 0) {
if ((physical_format_properties.optimalTilingFeatures & format_feature_usage) !=
format_feature_usage) {
return false;
}
}
+7 -5
View File
@@ -72,13 +72,12 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
pipeline_executable_properties) \
FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
workgroup_memory_explicit_layout) \
FEATURE(EXT, TextureCompressionASTCHDR, TEXTURE_COMPRESSION_ASTC_HDR, \
texture_compression_astc_hdr)
workgroup_memory_explicit_layout)
// Define miscellaneous extensions which may be used by the implementation here.
#define FOR_EACH_VK_EXTENSION(EXTENSION) \
EXTENSION(EXT, ASTC_DECODE_MODE, astc_decode_mode) \
EXTENSION(EXT, CONDITIONAL_RENDERING, conditional_rendering) \
EXTENSION(EXT, CONSERVATIVE_RASTERIZATION, conservative_rasterization) \
EXTENSION(EXT, DEPTH_RANGE_UNRESTRICTED, depth_range_unrestricted) \
@@ -369,8 +368,7 @@ FN_MAX_LIMIT_LIST
}
bool IsOptimalAstcSupported() const {
return features.features.textureCompressionASTC_LDR &&
features.texture_compression_astc_hdr.textureCompressionASTC_HDR;
return is_optimal_astc_supported;
}
/// Returns true if BCn is natively supported.
@@ -816,6 +814,10 @@ FN_MAX_LIMIT_LIST
return extensions.conditional_rendering;
}
bool IsExtAstcDecodeModeSupported() const {
return extensions.astc_decode_mode;
}
bool HasTimelineSemaphore() const;
/// Returns true if the device supports VK_KHR_synchronization2.
@@ -67,11 +67,6 @@ void ConfigureDebug::SetConfiguration() {
ui->homebrew_args_edit->setEnabled(runtime_lock);
ui->homebrew_args_edit->setText(
QString::fromStdString(Settings::values.program_args.GetValue()));
ui->nxlink_server_mode->setEnabled(runtime_lock);
const int nxlink_server_mode =
static_cast<int>(Settings::values.homebrew_nxlink_server_mode.GetValue());
ui->nxlink_server_mode->setCurrentIndex(
nxlink_server_mode < ui->nxlink_server_mode->count() ? nxlink_server_mode : 0);
ui->toggle_console->setEnabled(runtime_lock);
ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
ui->fs_access_log->setEnabled(runtime_lock);
@@ -121,8 +116,6 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.log_flush_line = ui->flush_line->isChecked();
Settings::values.censor_username = ui->censor_username->isChecked();
Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
Settings::values.homebrew_nxlink_server_mode =
static_cast<Settings::HomebrewNxlinkServerMode>(ui->nxlink_server_mode->currentIndex());
Settings::values.enable_fs_access_log = ui->fs_access_log->isChecked();
Settings::values.reporting_services = ui->reporting_services->isChecked();
Settings::values.dump_audio_commands = ui->dump_audio_commands->isChecked();
+9 -30
View File
@@ -309,40 +309,20 @@
<property name="title">
<string>Homebrew</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Arguments String</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="homebrew_args_edit"/>
</item>
<item>
<widget class="QLabel" name="nxlink_server_mode_label">
<property name="text">
<string>nxlink Server</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="nxlink_server_mode">
<property name="toolTip">
<string>Starts a local nxlink server for homebrew stdout/stderr streams.</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<property name="text">
<string>Disabled</string>
</property>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Arguments String</string>
</property>
</widget>
</item>
<item>
<property name="text">
<string>Eden Log</string>
</property>
<widget class="QLineEdit" name="homebrew_args_edit"/>
</item>
</widget>
</layout>
</item>
</layout>
</widget>
@@ -820,7 +800,6 @@
<tabstop>extended_logging</tabstop>
<tabstop>open_log_button</tabstop>
<tabstop>homebrew_args_edit</tabstop>
<tabstop>nxlink_server_mode</tabstop>
<tabstop>enable_graphics_debugging</tabstop>
<tabstop>enable_shader_feedback</tabstop>
<tabstop>enable_nsight_aftermath</tabstop>
@@ -99,7 +99,6 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QW
#ifndef _WIN32
ui->enable_raw_input->setVisible(false);
ui->disable_wgi_xinput->setVisible(false);
#endif
LoadConfiguration();
@@ -140,7 +139,6 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
Settings::values.emulate_analog_keyboard = ui->emulate_analog_keyboard->isChecked();
Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
Settings::values.disable_wgi_xinput = ui->disable_wgi_xinput->isChecked();
Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
Settings::values.controller_navigation = ui->controller_navigation->isChecked();
Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
@@ -176,7 +174,6 @@ void ConfigureInputAdvanced::LoadConfiguration() {
ui->emulate_analog_keyboard->setChecked(Settings::values.emulate_analog_keyboard.GetValue());
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
ui->disable_wgi_xinput->setChecked(Settings::values.disable_wgi_xinput.GetValue());
ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
@@ -2757,22 +2757,6 @@
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="disable_wgi_xinput">
<property name="toolTip">
<string>Aimed to disable SDL GUIDE button hack: synthetic GUIDE(HOME) event when SELECT(MINUS) + START(PLUS) pressed. May impact Win related trigger/rumble/etc stuff</string>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>23</height>
</size>
</property>
<property name="text">
<string>Disable SDL WGI/XInput (Requires restart)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>