early-access version 3837

main
pineappleEA 2023-08-27 02:56:33 +02:00
parent d07529eada
commit c807f0cfc8
17 changed files with 252 additions and 94 deletions

View File

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 3836. This is the source code for early-access 3837.
## Legal Notice ## Legal Notice

View File

@ -78,6 +78,11 @@ QPushButton#buttonRefreshDevices {
max-height: 21px; max-height: 21px;
} }
QPushButton#button_reset_defaults {
min-width: 57px;
padding: 4px 8px;
}
QWidget#bottomPerGameInput, QWidget#bottomPerGameInput,
QWidget#topControllerApplet, QWidget#topControllerApplet,
QWidget#bottomControllerApplet, QWidget#bottomControllerApplet,

View File

@ -2228,6 +2228,10 @@ QPushButton#buttonRefreshDevices {
padding: 0px 0px; padding: 0px 0px;
} }
QPushButton#button_reset_defaults {
padding: 3px 6px;
}
QSpinBox#spinboxLStickRange, QSpinBox#spinboxLStickRange,
QSpinBox#spinboxRStickRange, QSpinBox#spinboxRStickRange,
QSpinBox#vibrationSpinPlayer1, QSpinBox#vibrationSpinPlayer1,

View File

@ -42,6 +42,11 @@ endif()
# mbedtls # mbedtls
add_subdirectory(mbedtls) add_subdirectory(mbedtls)
target_include_directories(mbedtls PUBLIC ./mbedtls/include) target_include_directories(mbedtls PUBLIC ./mbedtls/include)
if (NOT MSVC)
target_compile_options(mbedcrypto PRIVATE
-Wno-unused-but-set-variable
-Wno-string-concatenation)
endif()
# MicroProfile # MicroProfile
add_library(microprofile INTERFACE) add_library(microprofile INTERFACE)
@ -94,6 +99,12 @@ if (ENABLE_CUBEB AND NOT TARGET cubeb::cubeb)
set(BUILD_TOOLS OFF) set(BUILD_TOOLS OFF)
add_subdirectory(cubeb) add_subdirectory(cubeb)
add_library(cubeb::cubeb ALIAS cubeb) add_library(cubeb::cubeb ALIAS cubeb)
if (NOT MSVC)
if (TARGET speex)
target_compile_options(speex PRIVATE -Wno-sign-compare)
endif()
target_compile_options(cubeb PRIVATE -Wno-implicit-const-int-float-conversion)
endif()
endif() endif()
# DiscordRPC # DiscordRPC
@ -151,6 +162,9 @@ endif()
if (NOT TARGET LLVM::Demangle) if (NOT TARGET LLVM::Demangle)
add_library(demangle demangle/ItaniumDemangle.cpp) add_library(demangle demangle/ItaniumDemangle.cpp)
target_include_directories(demangle PUBLIC ./demangle) target_include_directories(demangle PUBLIC ./demangle)
if (NOT MSVC)
target_compile_options(demangle PRIVATE -Wno-deprecated-declarations) # std::is_pod
endif()
add_library(LLVM::Demangle ALIAS demangle) add_library(LLVM::Demangle ALIAS demangle)
endif() endif()

View File

@ -114,16 +114,19 @@ else()
-Wno-attributes -Wno-attributes
-Wno-invalid-offsetof -Wno-invalid-offsetof
-Wno-unused-parameter -Wno-unused-parameter
$<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init>
$<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field>
$<$<CXX_COMPILER_ID:Clang>:-Werror=shadow-uncaptured-local>
$<$<CXX_COMPILER_ID:Clang>:-Werror=implicit-fallthrough>
$<$<CXX_COMPILER_ID:Clang>:-Werror=type-limits>
$<$<CXX_COMPILER_ID:AppleClang>:-Wno-braced-scalar-init>
$<$<CXX_COMPILER_ID:AppleClang>:-Wno-unused-private-field>
) )
if (CMAKE_CXX_COMPILER_ID MATCHES Clang) # Clang or AppleClang
add_compile_options(
-Wno-braced-scalar-init
-Wno-unused-private-field
-Wno-nullability-completeness
-Werror=shadow-uncaptured-local
-Werror=implicit-fallthrough
-Werror=type-limits
)
endif()
if (ARCHITECTURE_x86_64) if (ARCHITECTURE_x86_64)
add_compile_options("-mcx16") add_compile_options("-mcx16")
add_compile_options("-fwrapv") add_compile_options("-fwrapv")

View File

@ -460,11 +460,6 @@ S operator&(const S& i, const swap_struct_t<T, F> v) {
return i & v.swap(); return i & v.swap();
} }
template <typename S, typename T, typename F>
S operator&(const swap_struct_t<T, F> v, const S& i) {
return static_cast<S>(v.swap() & i);
}
// Comparison // Comparison
template <typename S, typename T, typename F> template <typename S, typename T, typename F>
bool operator<(const S& p, const swap_struct_t<T, F> v) { bool operator<(const S& p, const swap_struct_t<T, F> v) {

View File

@ -273,7 +273,8 @@ struct System::Impl {
time_manager.Initialize(); time_manager.Initialize();
is_powered_on = true; is_powered_on = true;
exit_lock = false; exit_locked = false;
exit_requested = false;
microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0); microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0);
microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1); microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1);
@ -398,12 +399,14 @@ struct System::Impl {
} }
is_powered_on = false; is_powered_on = false;
exit_lock = false; exit_locked = false;
exit_requested = false;
if (gpu_core != nullptr) { if (gpu_core != nullptr) {
gpu_core->NotifyShutdown(); gpu_core->NotifyShutdown();
} }
Network::CancelPendingSocketOperations();
kernel.SuspendApplication(true); kernel.SuspendApplication(true);
if (services) { if (services) {
services->KillNVNFlinger(); services->KillNVNFlinger();
@ -425,6 +428,7 @@ struct System::Impl {
debugger.reset(); debugger.reset();
kernel.Shutdown(); kernel.Shutdown();
memory.Reset(); memory.Reset();
Network::RestartSocketOperations();
if (auto room_member = room_network.GetRoomMember().lock()) { if (auto room_member = room_network.GetRoomMember().lock()) {
Network::GameInfo game_info{}; Network::GameInfo game_info{};
@ -507,7 +511,8 @@ struct System::Impl {
CpuManager cpu_manager; CpuManager cpu_manager;
std::atomic_bool is_powered_on{}; std::atomic_bool is_powered_on{};
bool exit_lock = false; bool exit_locked = false;
bool exit_requested = false;
bool nvdec_active{}; bool nvdec_active{};
@ -943,12 +948,20 @@ const Service::Time::TimeManager& System::GetTimeManager() const {
return impl->time_manager; return impl->time_manager;
} }
void System::SetExitLock(bool locked) { void System::SetExitLocked(bool locked) {
impl->exit_lock = locked; impl->exit_locked = locked;
} }
bool System::GetExitLock() const { bool System::GetExitLocked() const {
return impl->exit_lock; return impl->exit_locked;
}
void System::SetExitRequested(bool requested) {
impl->exit_requested = requested;
}
bool System::GetExitRequested() const {
return impl->exit_requested;
} }
void System::SetApplicationProcessBuildID(const CurrentBuildProcessID& id) { void System::SetApplicationProcessBuildID(const CurrentBuildProcessID& id) {

View File

@ -412,8 +412,11 @@ public:
/// Gets an immutable reference to the Room Network. /// Gets an immutable reference to the Room Network.
[[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const; [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const;
void SetExitLock(bool locked); void SetExitLocked(bool locked);
[[nodiscard]] bool GetExitLock() const; bool GetExitLocked() const;
void SetExitRequested(bool requested);
bool GetExitRequested() const;
void SetApplicationProcessBuildID(const CurrentBuildProcessID& id); void SetApplicationProcessBuildID(const CurrentBuildProcessID& id);
[[nodiscard]] const CurrentBuildProcessID& GetApplicationProcessBuildID() const; [[nodiscard]] const CurrentBuildProcessID& GetApplicationProcessBuildID() const;

View File

@ -341,7 +341,7 @@ void ISelfController::Exit(HLERequestContext& ctx) {
void ISelfController::LockExit(HLERequestContext& ctx) { void ISelfController::LockExit(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
system.SetExitLock(true); system.SetExitLocked(true);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -350,10 +350,14 @@ void ISelfController::LockExit(HLERequestContext& ctx) {
void ISelfController::UnlockExit(HLERequestContext& ctx) { void ISelfController::UnlockExit(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
system.SetExitLock(false); system.SetExitLocked(false);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
if (system.GetExitRequested()) {
system.Exit();
}
} }
void ISelfController::EnterFatalSection(HLERequestContext& ctx) { void ISelfController::EnterFatalSection(HLERequestContext& ctx) {

View File

@ -48,15 +48,32 @@ enum class CallType {
using socklen_t = int; using socklen_t = int;
SOCKET interrupt_socket = static_cast<SOCKET>(-1);
void InterruptSocketOperations() {
closesocket(interrupt_socket);
}
void AcknowledgeInterrupt() {
interrupt_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
}
void Initialize() { void Initialize() {
WSADATA wsa_data; WSADATA wsa_data;
(void)WSAStartup(MAKEWORD(2, 2), &wsa_data); (void)WSAStartup(MAKEWORD(2, 2), &wsa_data);
AcknowledgeInterrupt();
} }
void Finalize() { void Finalize() {
InterruptSocketOperations();
WSACleanup(); WSACleanup();
} }
SOCKET GetInterruptSocket() {
return interrupt_socket;
}
sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
sockaddr_in result; sockaddr_in result;
@ -157,9 +174,39 @@ constexpr int SD_RECEIVE = SHUT_RD;
constexpr int SD_SEND = SHUT_WR; constexpr int SD_SEND = SHUT_WR;
constexpr int SD_BOTH = SHUT_RDWR; constexpr int SD_BOTH = SHUT_RDWR;
void Initialize() {} int interrupt_pipe_fd[2] = {-1, -1};
void Finalize() {} void Initialize() {
if (pipe(interrupt_pipe_fd) != 0) {
LOG_ERROR(Network, "Failed to create interrupt pipe!");
}
int flags = fcntl(interrupt_pipe_fd[0], F_GETFL);
ASSERT_MSG(fcntl(interrupt_pipe_fd[0], F_SETFL, flags | O_NONBLOCK) == 0,
"Failed to set nonblocking state for interrupt pipe");
}
void Finalize() {
if (interrupt_pipe_fd[0] >= 0) {
close(interrupt_pipe_fd[0]);
}
if (interrupt_pipe_fd[1] >= 0) {
close(interrupt_pipe_fd[1]);
}
}
void InterruptSocketOperations() {
u8 value = 0;
ASSERT(write(interrupt_pipe_fd[1], &value, sizeof(value)) == 1);
}
void AcknowledgeInterrupt() {
u8 value = 0;
read(interrupt_pipe_fd[0], &value, sizeof(value));
}
SOCKET GetInterruptSocket() {
return interrupt_pipe_fd[0];
}
sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
sockaddr_in result; sockaddr_in result;
@ -490,6 +537,14 @@ NetworkInstance::~NetworkInstance() {
Finalize(); Finalize();
} }
void CancelPendingSocketOperations() {
InterruptSocketOperations();
}
void RestartSocketOperations() {
AcknowledgeInterrupt();
}
std::optional<IPv4Address> GetHostIPv4Address() { std::optional<IPv4Address> GetHostIPv4Address() {
const auto network_interface = Network::GetSelectedNetworkInterface(); const auto network_interface = Network::GetSelectedNetworkInterface();
if (!network_interface.has_value()) { if (!network_interface.has_value()) {
@ -560,7 +615,14 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
return result; return result;
}); });
const int result = WSAPoll(host_pollfds.data(), static_cast<ULONG>(num), timeout); host_pollfds.push_back(WSAPOLLFD{
.fd = GetInterruptSocket(),
.events = POLLIN,
.revents = 0,
});
const int result =
WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), timeout);
if (result == 0) { if (result == 0) {
ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(), ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(),
[](WSAPOLLFD fd) { return fd.revents == 0; })); [](WSAPOLLFD fd) { return fd.revents == 0; }));
@ -627,6 +689,24 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() { std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() {
sockaddr_in addr; sockaddr_in addr;
socklen_t addrlen = sizeof(addr); socklen_t addrlen = sizeof(addr);
std::vector<WSAPOLLFD> host_pollfds{
WSAPOLLFD{fd, POLLIN, 0},
WSAPOLLFD{GetInterruptSocket(), POLLIN, 0},
};
while (true) {
const int pollres =
WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), -1);
if (host_pollfds[1].revents != 0) {
// Interrupt signaled before a client could be accepted, break
return {AcceptResult{}, Errno::AGAIN};
}
if (pollres > 0) {
break;
}
}
const SOCKET new_socket = accept(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen); const SOCKET new_socket = accept(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen);
if (new_socket == INVALID_SOCKET) { if (new_socket == INVALID_SOCKET) {

View File

@ -96,6 +96,9 @@ public:
~NetworkInstance(); ~NetworkInstance();
}; };
void CancelPendingSocketOperations();
void RestartSocketOperations();
#ifdef _WIN32 #ifdef _WIN32
constexpr IPv4Address TranslateIPv4(in_addr addr) { constexpr IPv4Address TranslateIPv4(in_addr addr) {
auto& bytes = addr.S_un.S_un_b; auto& bytes = addr.S_un.S_un_b;

View File

@ -835,15 +835,15 @@ public:
return input_engine->SupportsNfc(identifier); return input_engine->SupportsNfc(identifier);
} }
Common::Input::NfcState StartNfcPolling() { Common::Input::NfcState StartNfcPolling() override {
return input_engine->StartNfcPolling(identifier); return input_engine->StartNfcPolling(identifier);
} }
Common::Input::NfcState StopNfcPolling() { Common::Input::NfcState StopNfcPolling() override {
return input_engine->StopNfcPolling(identifier); return input_engine->StopNfcPolling(identifier);
} }
Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) { Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) override {
return input_engine->ReadAmiiboData(identifier, out_data); return input_engine->ReadAmiiboData(identifier, out_data);
} }
@ -852,11 +852,11 @@ public:
} }
Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request, Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request,
Common::Input::MifareRequest& out_data) { Common::Input::MifareRequest& out_data) override {
return input_engine->ReadMifareData(identifier, request, out_data); return input_engine->ReadMifareData(identifier, request, out_data);
} }
Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) { Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) override {
return input_engine->WriteMifareData(identifier, request); return input_engine->WriteMifareData(identifier, request);
} }

View File

@ -27,14 +27,24 @@ MICROPROFILE_DEFINE(MacroHLE, "GPU", "Execute macro HLE", MP_RGB(128, 192, 192))
namespace Tegra { namespace Tegra {
static void Dump(u64 hash, std::span<const u32> code) { static void Dump(u64 hash, std::span<const u32> code, bool decompiled = false) {
const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)}; const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
const auto macro_dir{base_dir / "macros"}; const auto macro_dir{base_dir / "macros"};
if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) { if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) {
LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories"); LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories");
return; return;
} }
const auto name{macro_dir / fmt::format("{:016x}.macro", hash)}; auto name{macro_dir / fmt::format("{:016x}.macro", hash)};
if (decompiled) {
auto new_name{macro_dir / fmt::format("decompiled_{:016x}.macro", hash)};
if (Common::FS::Exists(name)) {
(void)Common::FS::RenameFile(name, new_name);
return;
}
name = new_name;
}
std::fstream macro_file(name, std::ios::out | std::ios::binary); std::fstream macro_file(name, std::ios::out | std::ios::binary);
if (!macro_file) { if (!macro_file) {
LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}", LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}",
@ -90,9 +100,6 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
if (!mid_method.has_value()) { if (!mid_method.has_value()) {
cache_info.lle_program = Compile(macro_code->second); cache_info.lle_program = Compile(macro_code->second);
cache_info.hash = Common::HashValue(macro_code->second); cache_info.hash = Common::HashValue(macro_code->second);
if (Settings::values.dump_macros) {
Dump(cache_info.hash, macro_code->second);
}
} else { } else {
const auto& macro_cached = uploaded_macro_code[mid_method.value()]; const auto& macro_cached = uploaded_macro_code[mid_method.value()];
const auto rebased_method = method - mid_method.value(); const auto rebased_method = method - mid_method.value();
@ -102,9 +109,6 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
code.size() * sizeof(u32)); code.size() * sizeof(u32));
cache_info.hash = Common::HashValue(code); cache_info.hash = Common::HashValue(code);
cache_info.lle_program = Compile(code); cache_info.lle_program = Compile(code);
if (Settings::values.dump_macros) {
Dump(cache_info.hash, code);
}
} }
auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); auto hle_program = hle_macros->GetHLEProgram(cache_info.hash);
@ -117,6 +121,10 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
MICROPROFILE_SCOPE(MacroHLE); MICROPROFILE_SCOPE(MacroHLE);
cache_info.hle_program->Execute(parameters, method); cache_info.hle_program->Execute(parameters, method);
} }
if (Settings::values.dump_macros) {
Dump(cache_info.hash, macro_code->second, cache_info.has_hle_program);
}
} }
} }

View File

@ -611,9 +611,6 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))}; const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
if (Settings::values.dump_shaders) {
env.Dump(hash, key.unique_hashes[index]);
}
if (!uses_vertex_a || index != 1) { if (!uses_vertex_a || index != 1) {
// Normal path // Normal path
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
@ -624,6 +621,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
} }
if (Settings::values.dump_shaders) {
env.Dump(hash, key.unique_hashes[index]);
}
if (programs[index].info.requires_layer_emulation) { if (programs[index].info.requires_layer_emulation) {
layer_source_program = &programs[index]; layer_source_program = &programs[index];
} }

View File

@ -4,6 +4,7 @@
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // for deprecated OpenSSL functions
#endif #endif
#include <jwt/jwt.hpp> #include <jwt/jwt.hpp>
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)

View File

@ -2010,8 +2010,16 @@ bool GMainWindow::OnShutdownBegin() {
emit EmulationStopping(); emit EmulationStopping();
int shutdown_time = 1000;
if (system->DebuggerEnabled()) {
shutdown_time = 0;
} else if (system->GetExitLocked()) {
shutdown_time = 5000;
}
shutdown_timer.setSingleShot(true); shutdown_timer.setSingleShot(true);
shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000); shutdown_timer.start(shutdown_time);
connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired); connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired);
connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped); connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped);
@ -2573,50 +2581,41 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
return; return;
} }
FileSys::VirtualFile base_romfs; FileSys::VirtualFile packed_update_raw{};
if (loader->ReadRomFS(base_romfs) != Loader::ResultStatus::Success) { loader->ReadUpdateRaw(packed_update_raw);
failed();
return;
}
const auto& installed = system->GetContentProvider(); const auto& installed = system->GetContentProvider();
const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id);
if (!romfs_title_id) { u64 title_id{};
u8 raw_type{};
if (!SelectRomFSDumpTarget(installed, program_id, &title_id, &raw_type)) {
failed(); failed();
return; return;
} }
const auto type = *romfs_title_id == program_id ? FileSys::ContentRecordType::Program const auto type = static_cast<FileSys::ContentRecordType>(raw_type);
: FileSys::ContentRecordType::Data; const auto base_nca = installed.GetEntry(title_id, type);
const auto base_nca = installed.GetEntry(*romfs_title_id, type);
if (!base_nca) { if (!base_nca) {
failed(); failed();
return; return;
} }
const auto base_romfs = base_nca->GetRomFS();
if (!base_romfs) {
failed();
return;
}
const auto dump_dir = const auto dump_dir =
target == DumpRomFSTarget::Normal target == DumpRomFSTarget::Normal
? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)
: Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents"; : Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents";
const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id); const auto romfs_dir = fmt::format("{:016X}/romfs", title_id);
const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir);
FileSys::VirtualFile romfs; const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed};
auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false);
if (*romfs_title_id == program_id) {
const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), installed};
romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, nullptr, false);
} else {
romfs = installed.GetEntry(*romfs_title_id, type)->GetRomFS();
}
const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
if (extracted == nullptr) {
failed();
return;
}
const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite); const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite);
@ -2640,6 +2639,12 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
return; return;
} }
const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
if (extracted == nullptr) {
failed();
return;
}
const auto full = res == selections.constFirst(); const auto full = res == selections.constFirst();
const auto entry_size = CalculateRomFSEntrySize(extracted, full); const auto entry_size = CalculateRomFSEntrySize(extracted, full);
@ -3261,7 +3266,7 @@ void GMainWindow::OnPauseContinueGame() {
} }
void GMainWindow::OnStopGame() { void GMainWindow::OnStopGame() {
if (system->GetExitLock() && !ConfirmForceLockedExit()) { if (system->GetExitLocked() && !ConfirmForceLockedExit()) {
return; return;
} }
@ -4350,28 +4355,41 @@ bool GMainWindow::CheckSystemArchiveDecryption() {
return mii_nca->GetRomFS().get() != nullptr; return mii_nca->GetRomFS().get() != nullptr;
} }
std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
u64 program_id) { u64* selected_title_id, u8* selected_content_record_type) {
const auto dlc_entries = using ContentInfo = std::pair<FileSys::TitleType, FileSys::ContentRecordType>;
installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); boost::container::flat_map<u64, ContentInfo> available_title_ids;
std::vector<FileSys::ContentProviderEntry> dlc_match;
dlc_match.reserve(dlc_entries.size());
std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
[&program_id, &installed](const FileSys::ContentProviderEntry& entry) {
return FileSys::GetBaseTitleID(entry.title_id) == program_id &&
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
});
std::vector<u64> romfs_tids; const auto RetrieveEntries = [&](FileSys::TitleType title_type,
romfs_tids.push_back(program_id); FileSys::ContentRecordType record_type) {
for (const auto& entry : dlc_match) { const auto entries = installed.ListEntriesFilter(title_type, record_type);
romfs_tids.push_back(entry.title_id); for (const auto& entry : entries) {
if (FileSys::GetBaseTitleID(entry.title_id) == program_id &&
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) {
available_title_ids[entry.title_id] = {title_type, record_type};
}
}
};
RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
if (available_title_ids.empty()) {
return false;
} }
if (romfs_tids.size() > 1) { size_t title_index = 0;
QStringList list{QStringLiteral("Base")};
for (std::size_t i = 1; i < romfs_tids.size(); ++i) { if (available_title_ids.size() > 1) {
list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF)); QStringList list;
for (auto& [title_id, content_info] : available_title_ids) {
const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id));
if (content_info.first == FileSys::TitleType::Application) {
list.push_back(QStringLiteral("Application [%1]").arg(hex_title_id));
} else {
list.push_back(
QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id));
}
} }
bool ok; bool ok;
@ -4379,13 +4397,16 @@ std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProv
this, tr("Select RomFS Dump Target"), this, tr("Select RomFS Dump Target"),
tr("Please select which RomFS you would like to dump."), list, 0, false, &ok); tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
if (!ok) { if (!ok) {
return {}; return false;
} }
return romfs_tids[list.indexOf(res)]; title_index = list.indexOf(res);
} }
return program_id; const auto selected_info = available_title_ids.nth(title_index);
*selected_title_id = selected_info->first;
*selected_content_record_type = static_cast<u8>(selected_info->second.second);
return true;
} }
bool GMainWindow::ConfirmClose() { bool GMainWindow::ConfirmClose() {
@ -4515,6 +4536,8 @@ void GMainWindow::RequestGameExit() {
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
bool has_signalled = false; bool has_signalled = false;
system->SetExitRequested(true);
if (applet_oe != nullptr) { if (applet_oe != nullptr) {
applet_oe->GetMessageQueue()->RequestExit(); applet_oe->GetMessageQueue()->RequestExit();
has_signalled = true; has_signalled = true;

View File

@ -375,7 +375,8 @@ private:
void RemoveAllTransferableShaderCaches(u64 program_id); void RemoveAllTransferableShaderCaches(u64 program_id);
void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); void RemoveCustomConfiguration(u64 program_id, const std::string& game_path);
void RemoveCacheStorage(u64 program_id); void RemoveCacheStorage(u64 program_id);
std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id,
u64* selected_title_id, u8* selected_content_record_type);
InstallResult InstallNSPXCI(const QString& filename); InstallResult InstallNSPXCI(const QString& filename);
InstallResult InstallNCA(const QString& filename); InstallResult InstallNCA(const QString& filename);
void MigrateConfigFiles(); void MigrateConfigFiles();